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.
DateKeys/provider/drandjson.go

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
}

Powered by TurnKey Linux.