vendor tsgo
This commit is contained in:
697
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/token.go
generated
vendored
Normal file
697
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/token.go
generated
vendored
Normal file
@@ -0,0 +1,697 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
// NOTE: Token is analogous to v1 json.Token.
|
||||
|
||||
const (
|
||||
maxInt64 = math.MaxInt64
|
||||
minInt64 = math.MinInt64
|
||||
maxUint64 = math.MaxUint64
|
||||
minUint64 = 0 // for consistency and readability purposes
|
||||
|
||||
invalidTokenPanic = "invalid jsontext.Token; it has been voided by a subsequent json.Decoder call"
|
||||
)
|
||||
|
||||
var errInvalidToken = errors.New("invalid jsontext.Token")
|
||||
|
||||
// Token represents a lexical JSON token, which may be one of the following:
|
||||
// - a JSON literal (i.e., null, true, or false)
|
||||
// - a JSON string (e.g., "hello, world!")
|
||||
// - a JSON number (e.g., 123.456)
|
||||
// - a begin or end delimiter for a JSON object (i.e., { or } )
|
||||
// - a begin or end delimiter for a JSON array (i.e., [ or ] )
|
||||
//
|
||||
// A Token cannot represent entire array or object values, while a [Value] can.
|
||||
// There is no Token to represent commas and colons since
|
||||
// these structural tokens can be inferred from the surrounding context.
|
||||
//
|
||||
// A Token stores data in one of two forms:
|
||||
//
|
||||
// - As raw JSON text: backed by the internal buffer of the [Decoder]
|
||||
// and only ever produced by [Decoder.ReadToken].
|
||||
// Such a token is only valid until the next call to any method on that
|
||||
// [Decoder] (e.g., [Decoder.PeekKind], [Decoder.ReadToken],
|
||||
// [Decoder.ReadValue], or [Decoder.SkipValue]).
|
||||
// Call [Token.Clone] to copy the raw text into an independent allocation
|
||||
// that persists beyond subsequent [Decoder] calls.
|
||||
//
|
||||
// - As a typed Go value: a self-contained representation produced by
|
||||
// the constructor functions (e.g., [String], [Int], [Uint], [Float]).
|
||||
// Such tokens are valid indefinitely and do not need to be cloned.
|
||||
type Token struct {
|
||||
nonComparable
|
||||
|
||||
// Tokens can exist in either a "raw" or an "exact" form.
|
||||
// Tokens produced by the Decoder are in the "raw" form.
|
||||
// Tokens returned by constructors are usually in the "exact" form.
|
||||
// The Encoder accepts Tokens in either the "raw" or "exact" form.
|
||||
//
|
||||
// The following chart shows the possible values for each Token type:
|
||||
// ╔══════════════════╦════════════╤════════════╤════════════╗
|
||||
// ║ Token type ║ raw field │ str field │ num field ║
|
||||
// ╠══════════════════╬════════════╪════════════╪════════════╣
|
||||
// ║ null (raw) ║ "null" │ "" │ 0 ║
|
||||
// ║ false (raw) ║ "false" │ "" │ 0 ║
|
||||
// ║ true (raw) ║ "true" │ "" │ 0 ║
|
||||
// ║ string (raw) ║ non-empty │ "" │ offset ║
|
||||
// ║ string (string) ║ nil │ non-empty │ 0 ║
|
||||
// ║ number (raw) ║ non-empty │ "" │ offset ║
|
||||
// ║ number (float32) ║ nil │ "F" │ non-zero ║
|
||||
// ║ number (float64) ║ nil │ "f" │ non-zero ║
|
||||
// ║ number (int64) ║ nil │ "i" │ non-zero ║
|
||||
// ║ number (uint64) ║ nil │ "u" │ non-zero ║
|
||||
// ║ object (delim) ║ "{" or "}" │ "" │ 0 ║
|
||||
// ║ array (delim) ║ "[" or "]" │ "" │ 0 ║
|
||||
// ╚══════════════════╩════════════╧════════════╧════════════╝
|
||||
//
|
||||
// Notes:
|
||||
// - For tokens stored in "raw" form, the num field contains the
|
||||
// absolute offset determined by raw.previousOffsetStart().
|
||||
// The buffer itself is stored in raw.previousBuffer().
|
||||
// - JSON literals and structural characters are always in the "raw" form.
|
||||
// - JSON strings and numbers can be in either "raw" or "exact" forms.
|
||||
// - The exact zero value of JSON strings and numbers in the "exact" forms
|
||||
// have ambiguous representation. Thus, they are always represented
|
||||
// in the "raw" form.
|
||||
|
||||
// raw contains a reference to the raw decode buffer.
|
||||
// If non-nil, then its value takes precedence over str and num.
|
||||
// It is only valid if num == raw.previousOffsetStart().
|
||||
raw *decodeBuffer
|
||||
|
||||
// str is the unescaped JSON string if num is zero.
|
||||
// Otherwise, it is "F", "f", "i", or "u" if num should be interpreted
|
||||
// as a float32, float64, int64, or uint64, respectively.
|
||||
str string
|
||||
|
||||
// num is a float32, float64, int64, or uint64 stored as a uint64 value.
|
||||
// For floating-point values, it stores the raw IEEE-754 bit-pattern.
|
||||
// It is non-zero for any JSON number in the "exact" form.
|
||||
num uint64
|
||||
}
|
||||
|
||||
// TODO: Does representing 1-byte delimiters as *decodeBuffer cause performance issues?
|
||||
|
||||
var (
|
||||
Null Token = rawToken("null")
|
||||
False Token = rawToken("false")
|
||||
True Token = rawToken("true")
|
||||
|
||||
BeginObject Token = rawToken("{")
|
||||
EndObject Token = rawToken("}")
|
||||
BeginArray Token = rawToken("[")
|
||||
EndArray Token = rawToken("]")
|
||||
|
||||
zeroString Token = rawToken(`""`)
|
||||
zeroNumber Token = rawToken(`0`)
|
||||
|
||||
nanString Token = String("NaN")
|
||||
pinfString Token = String("Infinity")
|
||||
ninfString Token = String("-Infinity")
|
||||
)
|
||||
|
||||
func rawToken(s string) Token {
|
||||
return Token{raw: &decodeBuffer{buf: []byte(s), prevStart: 0, prevEnd: len(s)}}
|
||||
}
|
||||
|
||||
// Bool constructs a Token representing a JSON boolean.
|
||||
func Bool(b bool) Token {
|
||||
if b {
|
||||
return True
|
||||
}
|
||||
return False
|
||||
}
|
||||
|
||||
// String constructs a Token representing a JSON string.
|
||||
// The provided string should contain valid UTF-8, otherwise invalid characters
|
||||
// may be mangled as the Unicode replacement character.
|
||||
func String(s string) Token {
|
||||
if len(s) == 0 {
|
||||
return zeroString
|
||||
}
|
||||
return Token{str: s}
|
||||
}
|
||||
|
||||
// Float32 constructs a Token representing a JSON number as
|
||||
// a 32-bit floating-point number formatted according to
|
||||
// ECMA-262, 10th edition, section 7.1.12.1,
|
||||
// with the exception that -0 is still formatted as -0.
|
||||
// The values NaN, +Inf, and -Inf will be represented
|
||||
// as a JSON string with the values "NaN", "Infinity", and "-Infinity".
|
||||
//
|
||||
// Note that most JSON libraries and standards assume that JSON numbers
|
||||
// are 64-bit floating-point numbers. Use of 32-bit precision should
|
||||
// only be used if the corresponding decoder knows that
|
||||
// this JSON number token is expected to only have 32-bit precision.
|
||||
// For all other situations, prefer using the [Float] constructor instead.
|
||||
func Float32(n float32) Token {
|
||||
if n != 0 && !math.IsNaN(float64(n)) && !math.IsInf(float64(n), 0) {
|
||||
return Token{str: "F", num: uint64(math.Float32bits(n))}
|
||||
}
|
||||
return Float(float64(n)) // handles ±0, NaN, and ±Inf
|
||||
}
|
||||
|
||||
// Float constructs a Token representing a JSON number as
|
||||
// a 64-bit floating-point number formatted according to
|
||||
// ECMA-262, 10th edition, section 7.1.12.1 and RFC 8785, section 3.2.2.3.
|
||||
// with the exception that -0 is still formatted as -0.
|
||||
// The values NaN, +Inf, and -Inf will be represented
|
||||
// as a JSON string with the values "NaN", "Infinity", and "-Infinity".
|
||||
func Float(n float64) Token {
|
||||
switch {
|
||||
case math.Float64bits(n) == 0:
|
||||
return zeroNumber
|
||||
case math.IsNaN(n):
|
||||
return nanString
|
||||
case math.IsInf(n, +1):
|
||||
return pinfString
|
||||
case math.IsInf(n, -1):
|
||||
return ninfString
|
||||
}
|
||||
return Token{str: "f", num: math.Float64bits(n)}
|
||||
}
|
||||
|
||||
// Int constructs a Token representing a JSON number from an int64.
|
||||
func Int(n int64) Token {
|
||||
if n == 0 {
|
||||
return zeroNumber
|
||||
}
|
||||
return Token{str: "i", num: uint64(n)}
|
||||
}
|
||||
|
||||
// Uint constructs a Token representing a JSON number from a uint64.
|
||||
func Uint(n uint64) Token {
|
||||
if n == 0 {
|
||||
return zeroNumber
|
||||
}
|
||||
return Token{str: "u", num: uint64(n)}
|
||||
}
|
||||
|
||||
// Clone returns a copy of the token with a value that is not backed by the
|
||||
// [Decoder] buffer and therefore remains valid past subsequent [Decoder] calls.
|
||||
// It has no effect on tokens produced by constructor functions,
|
||||
// since those are already self-contained.
|
||||
func (t Token) Clone() Token {
|
||||
// TODO: Allow caller to avoid any allocations?
|
||||
if raw := t.raw; raw != nil {
|
||||
// Avoid copying globals.
|
||||
if t.raw.prevStart == 0 {
|
||||
switch t.raw {
|
||||
case Null.raw:
|
||||
return Null
|
||||
case False.raw:
|
||||
return False
|
||||
case True.raw:
|
||||
return True
|
||||
case BeginObject.raw:
|
||||
return BeginObject
|
||||
case EndObject.raw:
|
||||
return EndObject
|
||||
case BeginArray.raw:
|
||||
return BeginArray
|
||||
case EndArray.raw:
|
||||
return EndArray
|
||||
}
|
||||
}
|
||||
|
||||
// This only ever needs to clone strings and numbers.
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := bytes.Clone(raw.previousBuffer())
|
||||
return Token{raw: &decodeBuffer{buf: buf, prevStart: 0, prevEnd: len(buf)}}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Bool returns the value for a JSON boolean.
|
||||
// It panics if the token kind is not a JSON boolean.
|
||||
func (t Token) Bool() bool {
|
||||
switch t.raw {
|
||||
case True.raw:
|
||||
return true
|
||||
case False.raw:
|
||||
return false
|
||||
default:
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
}
|
||||
|
||||
// appendString appends a JSON string to dst and returns it.
|
||||
// It panics if t is not a JSON string.
|
||||
func (t Token) appendString(dst []byte, flags *jsonflags.Flags) ([]byte, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw string value.
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]) == '"' {
|
||||
if jsonwire.ConsumeSimpleString(buf) == len(buf) {
|
||||
return append(dst, buf...), nil
|
||||
}
|
||||
dst, _, err := jsonwire.ReformatString(dst, buf, flags)
|
||||
return dst, err
|
||||
}
|
||||
} else if len(t.str) != 0 && t.num == 0 {
|
||||
// Handle exact string value.
|
||||
return jsonwire.AppendQuote(dst, []byte(t.str), flags)
|
||||
}
|
||||
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
|
||||
// String returns the unescaped string value for a JSON string.
|
||||
// For other JSON kinds, this returns the raw JSON representation.
|
||||
func (t Token) String() string {
|
||||
// This is inlinable to take advantage of "function outlining".
|
||||
// This avoids an allocation for the string(b) conversion
|
||||
// if the caller does not use the string in an escaping manner.
|
||||
// See https://blog.filippo.io/efficient-go-apis-with-the-inliner/
|
||||
s, b := t.string()
|
||||
if len(b) > 0 {
|
||||
return string(b)
|
||||
}
|
||||
return s
|
||||
}
|
||||
func (t Token) string() (string, []byte) {
|
||||
if raw := t.raw; raw != nil {
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.previousBuffer()
|
||||
if buf[0] == '"' {
|
||||
// TODO: Preserve ValueFlags in Token?
|
||||
isVerbatim := jsonwire.ConsumeSimpleString(buf) == len(buf)
|
||||
return "", jsonwire.UnquoteMayCopy(buf, isVerbatim)
|
||||
}
|
||||
// Handle tokens that are not JSON strings for fmt.Stringer.
|
||||
return "", buf
|
||||
}
|
||||
if len(t.str) != 0 && t.num == 0 {
|
||||
return t.str, nil
|
||||
}
|
||||
// Handle tokens that are not JSON strings for fmt.Stringer.
|
||||
if t.num > 0 {
|
||||
switch t.str[0] {
|
||||
case 'F':
|
||||
return string(jsonwire.AppendFloat(nil, float64(math.Float32frombits(uint32(t.num))), 32)), nil
|
||||
case 'f':
|
||||
return string(jsonwire.AppendFloat(nil, float64(math.Float64frombits(uint64(t.num))), 64)), nil
|
||||
case 'i':
|
||||
return strconv.FormatInt(int64(t.num), 10), nil
|
||||
case 'u':
|
||||
return strconv.FormatUint(uint64(t.num), 10), nil
|
||||
}
|
||||
}
|
||||
return "<invalid jsontext.Token>", nil
|
||||
}
|
||||
|
||||
// appendNumber appends a JSON number to dst and returns it.
|
||||
// It panics if t is not a JSON number.
|
||||
func (t Token) appendNumber(dst []byte, flags *jsonflags.Flags) ([]byte, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw number value.
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
dst, _, err := jsonwire.ReformatNumber(dst, buf, flags)
|
||||
return dst, err
|
||||
}
|
||||
} else if t.num != 0 {
|
||||
// Handle exact number value.
|
||||
switch t.str[0] {
|
||||
case 'F':
|
||||
return jsonwire.AppendFloat(dst, float64(math.Float32frombits(uint32(t.num))), 32), nil
|
||||
case 'f':
|
||||
return jsonwire.AppendFloat(dst, float64(math.Float64frombits(uint64(t.num))), 64), nil
|
||||
case 'i':
|
||||
return strconv.AppendInt(dst, int64(t.num), 10), nil
|
||||
case 'u':
|
||||
return strconv.AppendUint(dst, uint64(t.num), 10), nil
|
||||
}
|
||||
}
|
||||
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
|
||||
// Float32 returns the floating-point value for a JSON number
|
||||
// parsed according to 32 bits of precision.
|
||||
//
|
||||
// If the JSON number is outside the representable range of a float32,
|
||||
// it returns +Inf or -Inf along with an error
|
||||
// that matches [strconv.ErrRange] according to [errors.Is].
|
||||
//
|
||||
// It returns a NaN, +Inf, or -Inf value for any JSON string
|
||||
// with the values "NaN", "Infinity", or "-Infinity".
|
||||
//
|
||||
// It panics if the token kind is not a JSON number
|
||||
// or a JSON string with the aforementioned values.
|
||||
//
|
||||
// Note that most JSON libraries and standards assume that JSON numbers
|
||||
// are 64-bit floating-point numbers.
|
||||
// This method should only be used if the caller knows
|
||||
// from other context that this token is a JSON number
|
||||
// formatted only to 32 bits of precision (such as being encoded
|
||||
// using the [Float32] constructor). For all other situations,
|
||||
// prefer using the [Token.Float] accessor instead.
|
||||
func (t Token) Float32() (float32, error) {
|
||||
f, err := t.float(32)
|
||||
return float32(f), err
|
||||
}
|
||||
|
||||
// Float returns the floating-point value for a JSON number
|
||||
// parsed according to 64 bits of precision.
|
||||
//
|
||||
// If the JSON number is outside the representable range of a float64,
|
||||
// it returns +Inf or -Inf along with an error
|
||||
// that matches [strconv.ErrRange] according to [errors.Is].
|
||||
//
|
||||
// It returns a NaN, +Inf, or -Inf value for any JSON string
|
||||
// with the values "NaN", "Infinity", or "-Infinity".
|
||||
//
|
||||
// It panics if the token kind is not a JSON number
|
||||
// or a JSON string with the aforementioned values.
|
||||
func (t Token) Float() (float64, error) {
|
||||
f, err := t.float(64)
|
||||
return float64(f), err
|
||||
}
|
||||
|
||||
func (t Token) float(bits int) (float64, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw JSON number value.
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
fv, err := strconv.ParseFloat(string(buf), bits)
|
||||
if err != nil {
|
||||
err = &numError{accessor: "Float", value: t.String(), err: errors.Unwrap(err)} // only ever ErrRange
|
||||
}
|
||||
return fv, err
|
||||
}
|
||||
} else if t.num != 0 {
|
||||
// Handle typed Go number value.
|
||||
switch t.str[0] {
|
||||
case 'F':
|
||||
return float64(math.Float32frombits(uint32(t.num))), nil
|
||||
case 'f':
|
||||
f64 := float64(math.Float64frombits(uint64(t.num)))
|
||||
if bits == 32 && !math.IsInf(f64, 0) && math.IsInf(float64(float32(f64)), 0) {
|
||||
return f64, &numError{accessor: "Float", value: t.String(), err: strconv.ErrRange}
|
||||
}
|
||||
return f64, nil
|
||||
case 'i':
|
||||
return float64(int64(t.num)), nil // NOTE: This may lead to loss of precision.
|
||||
case 'u':
|
||||
return float64(uint64(t.num)), nil // NOTE: This may lead to loss of precision.
|
||||
}
|
||||
}
|
||||
|
||||
// Handle string values with "NaN", "Infinity", or "-Infinity".
|
||||
if t.Kind() == '"' {
|
||||
switch t.String() {
|
||||
case "NaN":
|
||||
return math.NaN(), nil
|
||||
case "Infinity":
|
||||
return math.Inf(+1), nil
|
||||
case "-Infinity":
|
||||
return math.Inf(-1), nil
|
||||
}
|
||||
// TODO: Should this be an error instead of a panic?
|
||||
// We can safely switch from a panic to an error in the future.
|
||||
}
|
||||
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
|
||||
// Int returns the signed integer value for a JSON number.
|
||||
//
|
||||
// It reports an error that matches [strconv.ErrSyntax] according to [errors.Is]
|
||||
// if the JSON number does not match the restricted grammar of just a signed integer.
|
||||
// It reports an error that matches [strconv.ErrRange] according to [errors.Is]
|
||||
// if the JSON number is a signed integer, but outside the range of an int64.
|
||||
// Even if an error is reported, a reasonable value is still returned.
|
||||
// The fractional component of any number is ignored (truncation toward zero).
|
||||
// Any number beyond the representation of an int64 will be saturated
|
||||
// to the closest representable value.
|
||||
//
|
||||
// It panics if the token kind is not a JSON number.
|
||||
func (t Token) Int() (int64, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw JSON number value.
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.previousBuffer()
|
||||
if len(buf) > 0 && buf[0] == '-' {
|
||||
// Prospectively parse a negative integer.
|
||||
switch abs, ok := jsonwire.ParseUint(buf[len("-"):]); {
|
||||
case abs > -minInt64:
|
||||
return minInt64, &numError{accessor: "Int", value: t.String(), err: strconv.ErrRange}
|
||||
case ok:
|
||||
return -1 * int64(abs), nil
|
||||
}
|
||||
} else {
|
||||
// Prospectively parse a non-negative integer.
|
||||
switch abs, ok := jsonwire.ParseUint(buf); {
|
||||
case abs > +maxInt64:
|
||||
return maxInt64, &numError{accessor: "Int", value: t.String(), err: strconv.ErrRange}
|
||||
case ok:
|
||||
return +1 * int64(abs), nil
|
||||
}
|
||||
}
|
||||
// This is not a signed integer, which implies ErrSyntax.
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
f64, _ := strconv.ParseFloat(string(buf), 64)
|
||||
return f64toi64(f64), &numError{accessor: "Int", value: t.String(), err: strconv.ErrSyntax}
|
||||
}
|
||||
} else if t.num != 0 {
|
||||
// Handle typed Go number value.
|
||||
switch t.str[0] {
|
||||
case 'i':
|
||||
return int64(t.num), nil
|
||||
case 'u':
|
||||
if t.num > maxInt64 {
|
||||
return maxInt64, &numError{accessor: "Int", value: t.String(), err: strconv.ErrRange}
|
||||
}
|
||||
return int64(t.num), nil
|
||||
case 'f', 'F':
|
||||
f64 := float64(math.Float64frombits(uint64(t.num)))
|
||||
if t.str[0] == 'F' {
|
||||
f64 = float64(math.Float32frombits(uint32(t.num)))
|
||||
}
|
||||
switch i64 := f64toi64(f64); {
|
||||
case math.IsNaN(f64), math.Trunc(f64) != f64:
|
||||
return i64, &numError{accessor: "Int", value: t.String(), err: strconv.ErrSyntax}
|
||||
case (i64 == minInt64 && f64 < minInt64) || (i64 == maxInt64 && f64 > maxInt64):
|
||||
return i64, &numError{accessor: "Int", value: t.String(), err: strconv.ErrRange}
|
||||
default:
|
||||
return i64, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
|
||||
func f64toi64(f64 float64) int64 {
|
||||
switch {
|
||||
case math.IsNaN(f64):
|
||||
return 0
|
||||
case f64 >= maxInt64+1:
|
||||
return maxInt64
|
||||
case f64 < minInt64:
|
||||
return minInt64
|
||||
default:
|
||||
return int64(f64) // NOTE: This may lead to loss of precision.
|
||||
}
|
||||
}
|
||||
|
||||
// Uint returns the unsigned integer value for a JSON number.
|
||||
//
|
||||
// It reports an error that matches [strconv.ErrSyntax] if the JSON number
|
||||
// does not match the restricted grammar of just an unsigned integer.
|
||||
// It reports an error that matches [strconv.ErrRange] if the JSON number
|
||||
// is an unsigned integer, but outside the representable range of a uint64.
|
||||
// Even if an error is reported, a reasonable value is still returned.
|
||||
// The fractional component of any number is ignored (truncation toward zero).
|
||||
// Any number beyond the representation of a uint64 will be saturated
|
||||
// to the closest representable value.
|
||||
//
|
||||
// It panics if the token kind is not a JSON number.
|
||||
func (t Token) Uint() (uint64, error) {
|
||||
// NOTE: This accessor returns 0 for any negative JSON number,
|
||||
// which might be surprising, but is at least consistent with the behavior
|
||||
// of saturating out-of-bounds numbers to the closest representable number.
|
||||
// We report ErrSyntax instead of ErrRange since the grammar for
|
||||
// an unsigned integer does not permit a negative sign.
|
||||
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw JSON number value.
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.previousBuffer()
|
||||
// Prospectively parse an unsigned integer.
|
||||
switch abs, ok := jsonwire.ParseUint(buf); {
|
||||
case ok:
|
||||
return abs, nil
|
||||
case abs == maxUint64: // implies overflows
|
||||
return maxUint64, &numError{accessor: "Uint", value: t.String(), err: strconv.ErrRange}
|
||||
}
|
||||
// This is not an unsigned integer, which implies ErrSyntax.
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
f64, _ := strconv.ParseFloat(string(buf), 64)
|
||||
return f64tou64(f64), &numError{accessor: "Uint", value: t.String(), err: strconv.ErrSyntax}
|
||||
}
|
||||
} else if t.num != 0 {
|
||||
// Handle typed Go number value.
|
||||
switch t.str[0] {
|
||||
case 'u':
|
||||
return t.num, nil
|
||||
case 'i':
|
||||
if int64(t.num) < minUint64 {
|
||||
return minUint64, &numError{accessor: "Uint", value: t.String(), err: strconv.ErrSyntax}
|
||||
}
|
||||
return uint64(int64(t.num)), nil
|
||||
case 'f', 'F':
|
||||
f64 := float64(math.Float64frombits(uint64(t.num)))
|
||||
if t.str[0] == 'F' {
|
||||
f64 = float64(math.Float32frombits(uint32(t.num)))
|
||||
}
|
||||
switch u64 := f64tou64(f64); {
|
||||
case math.IsNaN(f64), math.Trunc(f64) != f64, math.Signbit(f64):
|
||||
return u64, &numError{accessor: "Uint", value: t.String(), err: strconv.ErrSyntax}
|
||||
case (u64 == minUint64 && f64 < minUint64) || (u64 == maxUint64 && f64 > maxUint64):
|
||||
return u64, &numError{accessor: "Uint", value: t.String(), err: strconv.ErrRange}
|
||||
default:
|
||||
return u64, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
|
||||
func f64tou64(f64 float64) uint64 {
|
||||
switch {
|
||||
case math.IsNaN(f64):
|
||||
return 0
|
||||
case f64 >= maxUint64+1:
|
||||
return maxUint64
|
||||
case f64 < minUint64:
|
||||
return minUint64
|
||||
default:
|
||||
return uint64(f64) // NOTE: This may lead to loss of precision.
|
||||
}
|
||||
}
|
||||
|
||||
// Kind returns the token kind.
|
||||
func (t Token) Kind() Kind {
|
||||
switch {
|
||||
case t.raw != nil:
|
||||
raw := t.raw
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
return Kind(t.raw.buf[raw.prevStart]).normalize()
|
||||
case t.num != 0:
|
||||
return '0'
|
||||
case len(t.str) != 0:
|
||||
return '"'
|
||||
default:
|
||||
return invalidKind
|
||||
}
|
||||
}
|
||||
|
||||
// A Kind represents the kind of a JSON token.
|
||||
//
|
||||
// Kind represents each possible JSON token kind with a single byte,
|
||||
// which is conveniently the first byte of that kind's grammar
|
||||
// with the restriction that numbers always be represented with '0'.
|
||||
type Kind byte
|
||||
|
||||
const (
|
||||
KindInvalid Kind = 0 // invalid kind
|
||||
KindNull Kind = 'n' // null
|
||||
KindFalse Kind = 'f' // false
|
||||
KindTrue Kind = 't' // true
|
||||
KindString Kind = '"' // string
|
||||
KindNumber Kind = '0' // number
|
||||
KindBeginObject Kind = '{' // begin object
|
||||
KindEndObject Kind = '}' // end object
|
||||
KindBeginArray Kind = '[' // begin array
|
||||
KindEndArray Kind = ']' // end array
|
||||
)
|
||||
|
||||
const invalidKind Kind = 0
|
||||
|
||||
// String prints the kind in a humanly readable fashion.
|
||||
func (k Kind) String() string {
|
||||
switch k {
|
||||
case 0:
|
||||
return "invalid"
|
||||
case 'n':
|
||||
return "null"
|
||||
case 'f':
|
||||
return "false"
|
||||
case 't':
|
||||
return "true"
|
||||
case '"':
|
||||
return "string"
|
||||
case '0':
|
||||
return "number"
|
||||
case '{':
|
||||
return "{"
|
||||
case '}':
|
||||
return "}"
|
||||
case '[':
|
||||
return "["
|
||||
case ']':
|
||||
return "]"
|
||||
default:
|
||||
return "<invalid jsontext.Kind: " + jsonwire.QuoteRune([]byte{byte(k)}) + ">"
|
||||
}
|
||||
}
|
||||
|
||||
var normKind = [256]Kind{
|
||||
'n': 'n',
|
||||
'f': 'f',
|
||||
't': 't',
|
||||
'"': '"',
|
||||
'{': '{',
|
||||
'}': '}',
|
||||
'[': '[',
|
||||
']': ']',
|
||||
'-': '0',
|
||||
'0': '0',
|
||||
'1': '0',
|
||||
'2': '0',
|
||||
'3': '0',
|
||||
'4': '0',
|
||||
'5': '0',
|
||||
'6': '0',
|
||||
'7': '0',
|
||||
'8': '0',
|
||||
'9': '0',
|
||||
}
|
||||
|
||||
// normalize coalesces all possible starting characters of a number as just '0',
|
||||
// and converts all invalid kinds to 0.
|
||||
func (k Kind) normalize() Kind {
|
||||
// A lookup table keeps the inlining cost as low as possible.
|
||||
return normKind[k]
|
||||
}
|
||||
Reference in New Issue
Block a user