package provider import ( "strings" "unicode/utf8" ) // The strict reading of drand's JSON (spec v0.16, ยง47.1): JSON of RFC 8259, // in UTF-8, whose value is an object; no object of the JSON repeats a name, // and names are compared exactly, code point by code point, once their // escapes are decoded, so that "\u0072ound" is round and Round is another // name; and an escape of a surrogate that does not pair with the next one // makes the JSON malformed. A common JSON reader keeps the last of two // repeated names, or does not tell upper from lower case in them, and two // readers would see two rounds in the same input. // jsonMember is a member of the outer object: its name, decoded, the kind of // its value (a byte of `"`, `0`, `{`, `[`, `t`, `f` or `n`), the text of the // value as written, and for a string, the string decoded. type jsonMember struct { name string kind byte raw []byte str string } // strictJSON reads b as a JSON object with the rules above, and returns the // members of the outer object in their order, or false when b breaks one. func strictJSON(b []byte) ([]jsonMember, bool) { if !utf8.Valid(b) { return nil, false } r := &jsonReader{b: b} r.space() if !r.at('{') { return nil, false } members, ok := r.object(true) r.space() return members, ok && r.i == len(b) } // jsonReader reads JSON from b at i. type jsonReader struct { b []byte i int } func (r *jsonReader) at(c byte) bool { return r.i < len(r.b) && r.b[r.i] == c } // space skips the four spaces of JSON. func (r *jsonReader) space() { for r.i < len(r.b) && strings.IndexByte(" \t\n\r", r.b[r.i]) >= 0 { r.i++ } } // value reads one value of any kind. func (r *jsonReader) value() (jsonMember, bool) { if r.i >= len(r.b) { return jsonMember{}, false } start := r.i m := jsonMember{kind: r.b[r.i]} ok := false switch c := r.b[r.i]; { case c == '{': _, ok = r.object(false) case c == '[': ok = r.array() case c == '"': m.str, ok = r.string() case c == 't': ok = r.literal("true") case c == 'f': ok = r.literal("false") case c == 'n': ok = r.literal("null") case c == '-' || c >= '0' && c <= '9': m.kind, ok = '0', r.number() } m.raw = r.b[start:r.i] return m, ok } // object reads an object, with no name twice, and returns its members when // keep is set. func (r *jsonReader) object(keep bool) ([]jsonMember, bool) { r.i++ // { r.space() if r.at('}') { r.i++ return nil, true } var out []jsonMember seen := map[string]bool{} for { r.space() if !r.at('"') { return nil, false } name, ok := r.string() if !ok || seen[name] { return nil, false } seen[name] = true r.space() if !r.at(':') { return nil, false } r.i++ r.space() m, ok := r.value() if !ok { return nil, false } if keep { m.name = name out = append(out, m) } r.space() switch { case r.at(','): r.i++ case r.at('}'): r.i++ return out, true default: return nil, false } } } func (r *jsonReader) array() bool { r.i++ // [ r.space() if r.at(']') { r.i++ return true } for { r.space() if _, ok := r.value(); !ok { return false } r.space() switch { case r.at(','): r.i++ case r.at(']'): r.i++ return true default: return false } } } // string reads a string and decodes its escapes; a surrogate escaped alone, // without its pair, breaks it. func (r *jsonReader) string() (string, bool) { r.i++ // " var sb strings.Builder for r.i < len(r.b) { c := r.b[r.i] switch { case c == '"': r.i++ return sb.String(), true case c < 0x20: return "", false case c != '\\': _, n := utf8.DecodeRune(r.b[r.i:]) sb.Write(r.b[r.i : r.i+n]) r.i += n continue } if r.i+1 >= len(r.b) { return "", false } e := r.b[r.i+1] r.i += 2 if k := strings.IndexByte(`"\/bfnrt`, e); k >= 0 { sb.WriteByte("\"\\/\b\f\n\r\t"[k]) continue } if e != 'u' { return "", false } u, ok := r.hex4() switch { case !ok || u >= 0xdc00 && u <= 0xdfff: return "", false case u >= 0xd800 && u <= 0xdbff: if r.i+1 >= len(r.b) || r.b[r.i] != '\\' || r.b[r.i+1] != 'u' { return "", false } r.i += 2 low, ok := r.hex4() if !ok || low < 0xdc00 || low > 0xdfff { return "", false } u = 0x10000 + (u-0xd800)<<10 + (low - 0xdc00) } sb.WriteRune(rune(u)) } return "", false } // hex4 reads the four hexadecimal digits of an escape \u. func (r *jsonReader) hex4() (int, bool) { if r.i+4 > len(r.b) { return 0, false } u := 0 for _, c := range r.b[r.i : r.i+4] { var d int switch { case c >= '0' && c <= '9': d = int(c - '0') case c >= 'a' && c <= 'f': d = int(c-'a') + 10 case c >= 'A' && c <= 'F': d = int(c-'A') + 10 default: return 0, false } u = u<<4 | d } r.i += 4 return u, true } func (r *jsonReader) literal(word string) bool { if !strings.HasPrefix(string(r.b[r.i:]), word) { return false } r.i += len(word) return true } // number reads a number of the grammar of RFC 8259: a minus, an integer // part without leading zeros, and an optional fraction and exponent. func (r *jsonReader) number() bool { digits := func() int { n := 0 for r.i < len(r.b) && r.b[r.i] >= '0' && r.b[r.i] <= '9' { r.i++ n++ } return n } if r.at('-') { r.i++ } switch { case r.at('0'): r.i++ case digits() == 0: return false } if r.at('.') { r.i++ if digits() == 0 { return false } } if r.at('e') || r.at('E') { r.i++ if r.at('+') || r.at('-') { r.i++ } if digits() == 0 { return false } } return true } // maxJSONRound is the largest round of drand's JSON, 2^53 - 1, as in the // release object. const maxJSONRound = 1<<53 - 1 // jsonRound reads the round of drand's JSON: a number without sign, fraction // or exponent, from 1 to 2^53 - 1. func jsonRound(m jsonMember) (uint64, bool) { if m.kind != '0' || len(m.raw) == 0 || len(m.raw) > 16 || m.raw[0] == '0' { return 0, false } var n uint64 for _, c := range m.raw { if c < '0' || c > '9' { return 0, false } n = n*10 + uint64(c-'0') } return n, n <= maxJSONRound }