| | |
| | |
| | |
| |
|
| | |
| |
|
| | package jsonwire |
| |
|
| | import ( |
| | "io" |
| | "math" |
| | "slices" |
| | "strconv" |
| | "unicode/utf16" |
| | "unicode/utf8" |
| | ) |
| |
|
| | type ValueFlags uint |
| |
|
| | const ( |
| | _ ValueFlags = (1 << iota) / 2 |
| |
|
| | stringNonVerbatim |
| | stringNonCanonical |
| | |
| | ) |
| |
|
| | func (f *ValueFlags) Join(f2 ValueFlags) { *f |= f2 } |
| | func (f ValueFlags) IsVerbatim() bool { return f&stringNonVerbatim == 0 } |
| | func (f ValueFlags) IsCanonical() bool { return f&stringNonCanonical == 0 } |
| |
|
| | |
| | func ConsumeWhitespace(b []byte) (n int) { |
| | |
| | for len(b) > n && (b[n] == ' ' || b[n] == '\t' || b[n] == '\r' || b[n] == '\n') { |
| | n++ |
| | } |
| | return n |
| | } |
| |
|
| | |
| | |
| | func ConsumeNull(b []byte) int { |
| | |
| | const literal = "null" |
| | if len(b) >= len(literal) && string(b[:len(literal)]) == literal { |
| | return len(literal) |
| | } |
| | return 0 |
| | } |
| |
|
| | |
| | |
| | func ConsumeFalse(b []byte) int { |
| | |
| | const literal = "false" |
| | if len(b) >= len(literal) && string(b[:len(literal)]) == literal { |
| | return len(literal) |
| | } |
| | return 0 |
| | } |
| |
|
| | |
| | |
| | func ConsumeTrue(b []byte) int { |
| | |
| | const literal = "true" |
| | if len(b) >= len(literal) && string(b[:len(literal)]) == literal { |
| | return len(literal) |
| | } |
| | return 0 |
| | } |
| |
|
| | |
| | |
| | func ConsumeLiteral(b []byte, lit string) (n int, err error) { |
| | for i := 0; i < len(b) && i < len(lit); i++ { |
| | if b[i] != lit[i] { |
| | return i, NewInvalidCharacterError(b[i:], "in literal "+lit+" (expecting "+strconv.QuoteRune(rune(lit[i]))+")") |
| | } |
| | } |
| | if len(b) < len(lit) { |
| | return len(b), io.ErrUnexpectedEOF |
| | } |
| | return len(lit), nil |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func ConsumeSimpleString(b []byte) (n int) { |
| | |
| | if len(b) > 0 && b[0] == '"' { |
| | n++ |
| | for len(b) > n && b[n] < utf8.RuneSelf && escapeASCII[b[n]] == 0 { |
| | n++ |
| | } |
| | if uint(len(b)) > uint(n) && b[n] == '"' { |
| | n++ |
| | return n |
| | } |
| | } |
| | return 0 |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func ConsumeString(flags *ValueFlags, b []byte, validateUTF8 bool) (n int, err error) { |
| | return ConsumeStringResumable(flags, b, 0, validateUTF8) |
| | } |
| |
|
| | |
| | |
| | func ConsumeStringResumable(flags *ValueFlags, b []byte, resumeOffset int, validateUTF8 bool) (n int, err error) { |
| | |
| | switch { |
| | case resumeOffset > 0: |
| | n = resumeOffset |
| | case uint(len(b)) == 0: |
| | return n, io.ErrUnexpectedEOF |
| | case b[0] == '"': |
| | n++ |
| | default: |
| | return n, NewInvalidCharacterError(b[n:], `at start of string (expecting '"')`) |
| | } |
| |
|
| | |
| | for uint(len(b)) > uint(n) { |
| | |
| | noEscape := func(c byte) bool { |
| | return c < utf8.RuneSelf && ' ' <= c && c != '\\' && c != '"' |
| | } |
| | for uint(len(b)) > uint(n) && noEscape(b[n]) { |
| | n++ |
| | } |
| | if uint(len(b)) <= uint(n) { |
| | return n, io.ErrUnexpectedEOF |
| | } |
| |
|
| | |
| | if b[n] == '"' { |
| | n++ |
| | return n, nil |
| | } |
| |
|
| | switch r, rn := utf8.DecodeRune(b[n:]); { |
| | |
| | |
| | |
| | case rn > 1: |
| | n += rn |
| | |
| | case r == '\\': |
| | flags.Join(stringNonVerbatim) |
| | resumeOffset = n |
| | if uint(len(b)) < uint(n+2) { |
| | return resumeOffset, io.ErrUnexpectedEOF |
| | } |
| | switch r := b[n+1]; r { |
| | case '/': |
| | |
| | |
| | flags.Join(stringNonCanonical) |
| | n += 2 |
| | case '"', '\\', 'b', 'f', 'n', 'r', 't': |
| | n += 2 |
| | case 'u': |
| | if uint(len(b)) < uint(n+6) { |
| | if hasEscapedUTF16Prefix(b[n:], false) { |
| | return resumeOffset, io.ErrUnexpectedEOF |
| | } |
| | flags.Join(stringNonCanonical) |
| | return n, NewInvalidEscapeSequenceError(b[n:]) |
| | } |
| | v1, ok := parseHexUint16(b[n+2 : n+6]) |
| | if !ok { |
| | flags.Join(stringNonCanonical) |
| | return n, NewInvalidEscapeSequenceError(b[n : n+6]) |
| | } |
| | |
| | |
| | switch v1 { |
| | |
| | case '\b', '\f', '\n', '\r', '\t': |
| | flags.Join(stringNonCanonical) |
| | default: |
| | |
| | if v1 >= ' ' { |
| | flags.Join(stringNonCanonical) |
| | } else { |
| | |
| | for _, c := range b[n+2 : n+6] { |
| | if 'A' <= c && c <= 'F' { |
| | flags.Join(stringNonCanonical) |
| | } |
| | } |
| | } |
| | } |
| | n += 6 |
| |
|
| | r := rune(v1) |
| | if validateUTF8 && utf16.IsSurrogate(r) { |
| | if uint(len(b)) < uint(n+6) { |
| | if hasEscapedUTF16Prefix(b[n:], true) { |
| | return resumeOffset, io.ErrUnexpectedEOF |
| | } |
| | flags.Join(stringNonCanonical) |
| | return n - 6, NewInvalidEscapeSequenceError(b[n-6:]) |
| | } else if v2, ok := parseHexUint16(b[n+2 : n+6]); b[n] != '\\' || b[n+1] != 'u' || !ok { |
| | flags.Join(stringNonCanonical) |
| | return n - 6, NewInvalidEscapeSequenceError(b[n-6 : n+6]) |
| | } else if r = utf16.DecodeRune(rune(v1), rune(v2)); r == utf8.RuneError { |
| | flags.Join(stringNonCanonical) |
| | return n - 6, NewInvalidEscapeSequenceError(b[n-6 : n+6]) |
| | } else { |
| | n += 6 |
| | } |
| | } |
| | default: |
| | flags.Join(stringNonCanonical) |
| | return n, NewInvalidEscapeSequenceError(b[n : n+2]) |
| | } |
| | |
| | case r == utf8.RuneError: |
| | if !utf8.FullRune(b[n:]) { |
| | return n, io.ErrUnexpectedEOF |
| | } |
| | flags.Join(stringNonVerbatim | stringNonCanonical) |
| | if validateUTF8 { |
| | return n, ErrInvalidUTF8 |
| | } |
| | n++ |
| | |
| | case r < ' ': |
| | flags.Join(stringNonVerbatim | stringNonCanonical) |
| | return n, NewInvalidCharacterError(b[n:], "in string (expecting non-control character)") |
| | default: |
| | panic("BUG: unhandled character " + QuoteRune(b[n:])) |
| | } |
| | } |
| | return n, io.ErrUnexpectedEOF |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func AppendUnquote[Bytes ~[]byte | ~string](dst []byte, src Bytes) (v []byte, err error) { |
| | dst = slices.Grow(dst, len(src)) |
| |
|
| | |
| | var i, n int |
| | switch { |
| | case uint(len(src)) == 0: |
| | return dst, io.ErrUnexpectedEOF |
| | case src[0] == '"': |
| | i, n = 1, 1 |
| | default: |
| | return dst, NewInvalidCharacterError(src, `at start of string (expecting '"')`) |
| | } |
| |
|
| | |
| | for uint(len(src)) > uint(n) { |
| | |
| | noEscape := func(c byte) bool { |
| | return c < utf8.RuneSelf && ' ' <= c && c != '\\' && c != '"' |
| | } |
| | for uint(len(src)) > uint(n) && noEscape(src[n]) { |
| | n++ |
| | } |
| | if uint(len(src)) <= uint(n) { |
| | dst = append(dst, src[i:n]...) |
| | return dst, io.ErrUnexpectedEOF |
| | } |
| |
|
| | |
| | if src[n] == '"' { |
| | dst = append(dst, src[i:n]...) |
| | n++ |
| | if n < len(src) { |
| | err = NewInvalidCharacterError(src[n:], "after string value") |
| | } |
| | return dst, err |
| | } |
| |
|
| | switch r, rn := utf8.DecodeRuneInString(string(truncateMaxUTF8(src[n:]))); { |
| | |
| | |
| | |
| | case rn > 1: |
| | n += rn |
| | |
| | case r == '\\': |
| | dst = append(dst, src[i:n]...) |
| |
|
| | |
| | if uint(len(src)) < uint(n+2) { |
| | return dst, io.ErrUnexpectedEOF |
| | } |
| | switch r := src[n+1]; r { |
| | case '"', '\\', '/': |
| | dst = append(dst, r) |
| | n += 2 |
| | case 'b': |
| | dst = append(dst, '\b') |
| | n += 2 |
| | case 'f': |
| | dst = append(dst, '\f') |
| | n += 2 |
| | case 'n': |
| | dst = append(dst, '\n') |
| | n += 2 |
| | case 'r': |
| | dst = append(dst, '\r') |
| | n += 2 |
| | case 't': |
| | dst = append(dst, '\t') |
| | n += 2 |
| | case 'u': |
| | if uint(len(src)) < uint(n+6) { |
| | if hasEscapedUTF16Prefix(src[n:], false) { |
| | return dst, io.ErrUnexpectedEOF |
| | } |
| | return dst, NewInvalidEscapeSequenceError(src[n:]) |
| | } |
| | v1, ok := parseHexUint16(src[n+2 : n+6]) |
| | if !ok { |
| | return dst, NewInvalidEscapeSequenceError(src[n : n+6]) |
| | } |
| | n += 6 |
| |
|
| | |
| | r := rune(v1) |
| | if utf16.IsSurrogate(r) { |
| | r = utf8.RuneError |
| | if uint(len(src)) < uint(n+6) { |
| | if hasEscapedUTF16Prefix(src[n:], true) { |
| | return utf8.AppendRune(dst, r), io.ErrUnexpectedEOF |
| | } |
| | err = NewInvalidEscapeSequenceError(src[n-6:]) |
| | } else if v2, ok := parseHexUint16(src[n+2 : n+6]); src[n] != '\\' || src[n+1] != 'u' || !ok { |
| | err = NewInvalidEscapeSequenceError(src[n-6 : n+6]) |
| | } else if r = utf16.DecodeRune(rune(v1), rune(v2)); r == utf8.RuneError { |
| | err = NewInvalidEscapeSequenceError(src[n-6 : n+6]) |
| | } else { |
| | n += 6 |
| | } |
| | } |
| |
|
| | dst = utf8.AppendRune(dst, r) |
| | default: |
| | return dst, NewInvalidEscapeSequenceError(src[n : n+2]) |
| | } |
| | i = n |
| | |
| | case r == utf8.RuneError: |
| | dst = append(dst, src[i:n]...) |
| | if !utf8.FullRuneInString(string(truncateMaxUTF8(src[n:]))) { |
| | return dst, io.ErrUnexpectedEOF |
| | } |
| | |
| | |
| | dst = append(dst, "\uFFFD"...) |
| | n += rn |
| | i = n |
| | err = ErrInvalidUTF8 |
| | |
| | case r < ' ': |
| | dst = append(dst, src[i:n]...) |
| | return dst, NewInvalidCharacterError(src[n:], "in string (expecting non-control character)") |
| | default: |
| | panic("BUG: unhandled character " + QuoteRune(src[n:])) |
| | } |
| | } |
| | dst = append(dst, src[i:n]...) |
| | return dst, io.ErrUnexpectedEOF |
| | } |
| |
|
| | |
| | |
| | func hasEscapedUTF16Prefix[Bytes ~[]byte | ~string](b Bytes, lowerSurrogateHalf bool) bool { |
| | for i := range len(b) { |
| | switch c := b[i]; { |
| | case i == 0 && c != '\\': |
| | return false |
| | case i == 1 && c != 'u': |
| | return false |
| | case i == 2 && lowerSurrogateHalf && c != 'd' && c != 'D': |
| | return false |
| | case i == 3 && lowerSurrogateHalf && !('c' <= c && c <= 'f') && !('C' <= c && c <= 'F'): |
| | return false |
| | case i >= 2 && i < 6 && !('0' <= c && c <= '9') && !('a' <= c && c <= 'f') && !('A' <= c && c <= 'F'): |
| | return false |
| | } |
| | } |
| | return true |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func UnquoteMayCopy(b []byte, isVerbatim bool) []byte { |
| | |
| | if isVerbatim { |
| | return b[len(`"`) : len(b)-len(`"`)] |
| | } |
| | b, _ = AppendUnquote(nil, b) |
| | return b |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func ConsumeSimpleNumber(b []byte) (n int) { |
| | |
| | if len(b) > 0 { |
| | if b[0] == '0' { |
| | n++ |
| | } else if '1' <= b[0] && b[0] <= '9' { |
| | n++ |
| | for len(b) > n && ('0' <= b[n] && b[n] <= '9') { |
| | n++ |
| | } |
| | } else { |
| | return 0 |
| | } |
| | if uint(len(b)) <= uint(n) || (b[n] != '.' && b[n] != 'e' && b[n] != 'E') { |
| | return n |
| | } |
| | } |
| | return 0 |
| | } |
| |
|
| | type ConsumeNumberState uint |
| |
|
| | const ( |
| | consumeNumberInit ConsumeNumberState = iota |
| | beforeIntegerDigits |
| | withinIntegerDigits |
| | beforeFractionalDigits |
| | withinFractionalDigits |
| | beforeExponentDigits |
| | withinExponentDigits |
| | ) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func ConsumeNumber(b []byte) (n int, err error) { |
| | n, _, err = ConsumeNumberResumable(b, 0, consumeNumberInit) |
| | return n, err |
| | } |
| |
|
| | |
| | |
| | func ConsumeNumberResumable(b []byte, resumeOffset int, state ConsumeNumberState) (n int, _ ConsumeNumberState, err error) { |
| | |
| | n = resumeOffset |
| | if state > consumeNumberInit { |
| | switch state { |
| | case withinIntegerDigits, withinFractionalDigits, withinExponentDigits: |
| | |
| | for uint(len(b)) > uint(n) && ('0' <= b[n] && b[n] <= '9') { |
| | n++ |
| | } |
| | if uint(len(b)) <= uint(n) { |
| | return n, state, nil |
| | } |
| | state++ |
| | } |
| | switch state { |
| | case beforeIntegerDigits: |
| | goto beforeInteger |
| | case beforeFractionalDigits: |
| | goto beforeFractional |
| | case beforeExponentDigits: |
| | goto beforeExponent |
| | default: |
| | return n, state, nil |
| | } |
| | } |
| |
|
| | |
| | beforeInteger: |
| | resumeOffset = n |
| | if uint(len(b)) > 0 && b[0] == '-' { |
| | n++ |
| | } |
| | switch { |
| | case uint(len(b)) <= uint(n): |
| | return resumeOffset, beforeIntegerDigits, io.ErrUnexpectedEOF |
| | case b[n] == '0': |
| | n++ |
| | state = beforeFractionalDigits |
| | case '1' <= b[n] && b[n] <= '9': |
| | n++ |
| | for uint(len(b)) > uint(n) && ('0' <= b[n] && b[n] <= '9') { |
| | n++ |
| | } |
| | state = withinIntegerDigits |
| | default: |
| | return n, state, NewInvalidCharacterError(b[n:], "in number (expecting digit)") |
| | } |
| |
|
| | |
| | beforeFractional: |
| | if uint(len(b)) > uint(n) && b[n] == '.' { |
| | resumeOffset = n |
| | n++ |
| | switch { |
| | case uint(len(b)) <= uint(n): |
| | return resumeOffset, beforeFractionalDigits, io.ErrUnexpectedEOF |
| | case '0' <= b[n] && b[n] <= '9': |
| | n++ |
| | default: |
| | return n, state, NewInvalidCharacterError(b[n:], "in number (expecting digit)") |
| | } |
| | for uint(len(b)) > uint(n) && ('0' <= b[n] && b[n] <= '9') { |
| | n++ |
| | } |
| | state = withinFractionalDigits |
| | } |
| |
|
| | |
| | beforeExponent: |
| | if uint(len(b)) > uint(n) && (b[n] == 'e' || b[n] == 'E') { |
| | resumeOffset = n |
| | n++ |
| | if uint(len(b)) > uint(n) && (b[n] == '-' || b[n] == '+') { |
| | n++ |
| | } |
| | switch { |
| | case uint(len(b)) <= uint(n): |
| | return resumeOffset, beforeExponentDigits, io.ErrUnexpectedEOF |
| | case '0' <= b[n] && b[n] <= '9': |
| | n++ |
| | default: |
| | return n, state, NewInvalidCharacterError(b[n:], "in number (expecting digit)") |
| | } |
| | for uint(len(b)) > uint(n) && ('0' <= b[n] && b[n] <= '9') { |
| | n++ |
| | } |
| | state = withinExponentDigits |
| | } |
| |
|
| | return n, state, nil |
| | } |
| |
|
| | |
| | |
| | |
| | func parseHexUint16[Bytes ~[]byte | ~string](b Bytes) (v uint16, ok bool) { |
| | if len(b) != 4 { |
| | return 0, false |
| | } |
| | for i := range 4 { |
| | c := b[i] |
| | switch { |
| | case '0' <= c && c <= '9': |
| | c = c - '0' |
| | case 'a' <= c && c <= 'f': |
| | c = 10 + c - 'a' |
| | case 'A' <= c && c <= 'F': |
| | c = 10 + c - 'A' |
| | default: |
| | return 0, false |
| | } |
| | v = v*16 + uint16(c) |
| | } |
| | return v, true |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func ParseUint(b []byte) (v uint64, ok bool) { |
| | const unsafeWidth = 20 |
| | var n int |
| | for ; len(b) > n && ('0' <= b[n] && b[n] <= '9'); n++ { |
| | v = 10*v + uint64(b[n]-'0') |
| | } |
| | switch { |
| | case n == 0 || len(b) != n || (b[0] == '0' && string(b) != "0"): |
| | return 0, false |
| | case n >= unsafeWidth && (b[0] != '1' || v < 1e19 || n > unsafeWidth): |
| | return math.MaxUint64, false |
| | } |
| | return v, true |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func ParseFloat(b []byte, bits int) (v float64, ok bool) { |
| | fv, err := strconv.ParseFloat(string(b), bits) |
| | if math.IsInf(fv, 0) { |
| | switch { |
| | case bits == 32 && math.IsInf(fv, +1): |
| | fv = +math.MaxFloat32 |
| | case bits == 64 && math.IsInf(fv, +1): |
| | fv = +math.MaxFloat64 |
| | case bits == 32 && math.IsInf(fv, -1): |
| | fv = -math.MaxFloat32 |
| | case bits == 64 && math.IsInf(fv, -1): |
| | fv = -math.MaxFloat64 |
| | } |
| | } |
| | return fv, err == nil |
| | } |
| |
|