You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
6.0 KiB
299 lines
6.0 KiB
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 "round" 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
|
|
}
|