// 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

package jsontext

import (
	
	
	
	

	
	
)

// 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 ( string) Token {
	return Token{raw: &decodeBuffer{buf: []byte(), prevStart: 0, prevEnd: len()}}
}

// Bool constructs a Token representing a JSON boolean.
func ( bool) Token {
	if  {
		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) Token {
	if len() == 0 {
		return zeroString
	}
	return Token{str: }
}

// 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) Token {
	if  != 0 && !math.IsNaN(float64()) && !math.IsInf(float64(), 0) {
		return Token{str: "F", num: uint64(math.Float32bits())}
	}
	return Float(float64()) // 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 ( float64) Token {
	switch {
	case math.Float64bits() == 0:
		return zeroNumber
	case math.IsNaN():
		return nanString
	case math.IsInf(, +1):
		return pinfString
	case math.IsInf(, -1):
		return ninfString
	}
	return Token{str: "f", num: math.Float64bits()}
}

// Int constructs a Token representing a JSON number from an int64.
func ( int64) Token {
	if  == 0 {
		return zeroNumber
	}
	return Token{str: "i", num: uint64()}
}

// Uint constructs a Token representing a JSON number from a uint64.
func ( uint64) Token {
	if  == 0 {
		return zeroNumber
	}
	return Token{str: "u", num: uint64()}
}

// 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 ( Token) () Token {
	// TODO: Allow caller to avoid any allocations?
	if  := .raw;  != nil {
		// Avoid copying globals.
		if .raw.prevStart == 0 {
			switch .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(.previousOffsetStart()) != .num {
			panic(invalidTokenPanic)
		}
		 := bytes.Clone(.previousBuffer())
		return Token{raw: &decodeBuffer{buf: , prevStart: 0, prevEnd: len()}}
	}
	return 
}

// Bool returns the value for a JSON boolean.
// It panics if the token kind is not a JSON boolean.
func ( Token) () bool {
	switch .raw {
	case True.raw:
		return true
	case False.raw:
		return false
	default:
		panic("invalid JSON token kind: " + .Kind().String())
	}
}

// appendString appends a JSON string to dst and returns it.
// It panics if t is not a JSON string.
func ( Token) ( []byte,  *jsonflags.Flags) ([]byte, error) {
	if  := .raw;  != nil {
		// Handle raw string value.
		 := .previousBuffer()
		if Kind([0]) == '"' {
			if jsonwire.ConsumeSimpleString() == len() {
				return append(, ...), nil
			}
			, ,  := jsonwire.ReformatString(, , )
			return , 
		}
	} else if len(.str) != 0 && .num == 0 {
		// Handle exact string value.
		return jsonwire.AppendQuote(, []byte(.str), )
	}

	panic("invalid JSON token kind: " + .Kind().String())
}

// String returns the unescaped string value for a JSON string.
// For other JSON kinds, this returns the raw JSON representation.
func ( Token) () 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/
	,  := .string()
	if len() > 0 {
		return string()
	}
	return 
}
func ( Token) () (string, []byte) {
	if  := .raw;  != nil {
		if uint64(.previousOffsetStart()) != .num {
			panic(invalidTokenPanic)
		}
		 := .previousBuffer()
		if [0] == '"' {
			// TODO: Preserve ValueFlags in Token?
			 := jsonwire.ConsumeSimpleString() == len()
			return "", jsonwire.UnquoteMayCopy(, )
		}
		// Handle tokens that are not JSON strings for fmt.Stringer.
		return "", 
	}
	if len(.str) != 0 && .num == 0 {
		return .str, nil
	}
	// Handle tokens that are not JSON strings for fmt.Stringer.
	if .num > 0 {
		switch .str[0] {
		case 'F':
			return string(jsonwire.AppendFloat(nil, float64(math.Float32frombits(uint32(.num))), 32)), nil
		case 'f':
			return string(jsonwire.AppendFloat(nil, float64(math.Float64frombits(uint64(.num))), 64)), nil
		case 'i':
			return strconv.FormatInt(int64(.num), 10), nil
		case 'u':
			return strconv.FormatUint(uint64(.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 ( Token) ( []byte,  *jsonflags.Flags) ([]byte, error) {
	if  := .raw;  != nil {
		// Handle raw number value.
		 := .previousBuffer()
		if Kind([0]).normalize() == '0' {
			, ,  := jsonwire.ReformatNumber(, , )
			return , 
		}
	} else if .num != 0 {
		// Handle exact number value.
		switch .str[0] {
		case 'F':
			return jsonwire.AppendFloat(, float64(math.Float32frombits(uint32(.num))), 32), nil
		case 'f':
			return jsonwire.AppendFloat(, float64(math.Float64frombits(uint64(.num))), 64), nil
		case 'i':
			return strconv.AppendInt(, int64(.num), 10), nil
		case 'u':
			return strconv.AppendUint(, uint64(.num), 10), nil
		}
	}

	panic("invalid JSON token kind: " + .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 ( Token) () (float32, error) {
	,  := .float(32)
	return float32(), 
}

// 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 ( Token) () (float64, error) {
	,  := .float(64)
	return float64(), 
}

func ( Token) ( int) (float64, error) {
	if  := .raw;  != nil {
		// Handle raw JSON number value.
		if uint64(.previousOffsetStart()) != .num {
			panic(invalidTokenPanic)
		}
		 := .previousBuffer()
		if Kind([0]).normalize() == '0' {
			,  := strconv.ParseFloat(string(), )
			if  != nil {
				 = &numError{accessor: "Float", value: .String(), err: errors.Unwrap()} // only ever ErrRange
			}
			return , 
		}
	} else if .num != 0 {
		// Handle typed Go number value.
		switch .str[0] {
		case 'F':
			return float64(math.Float32frombits(uint32(.num))), nil
		case 'f':
			 := float64(math.Float64frombits(uint64(.num)))
			if  == 32 && !math.IsInf(, 0) && math.IsInf(float64(float32()), 0) {
				return , &numError{accessor: "Float", value: .String(), err: strconv.ErrRange}
			}
			return , nil
		case 'i':
			return float64(int64(.num)), nil // NOTE: This may lead to loss of precision.
		case 'u':
			return float64(uint64(.num)), nil // NOTE: This may lead to loss of precision.
		}
	}

	// Handle string values with "NaN", "Infinity", or "-Infinity".
	if .Kind() == '"' {
		switch .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: " + .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 ( Token) () (int64, error) {
	if  := .raw;  != nil {
		// Handle raw JSON number value.
		if uint64(.previousOffsetStart()) != .num {
			panic(invalidTokenPanic)
		}
		 := .previousBuffer()
		if len() > 0 && [0] == '-' {
			// Prospectively parse a negative integer.
			switch ,  := jsonwire.ParseUint([len("-"):]); {
			case  > -minInt64:
				return minInt64, &numError{accessor: "Int", value: .String(), err: strconv.ErrRange}
			case :
				return -1 * int64(), nil
			}
		} else {
			// Prospectively parse a non-negative integer.
			switch ,  := jsonwire.ParseUint(); {
			case  > +maxInt64:
				return maxInt64, &numError{accessor: "Int", value: .String(), err: strconv.ErrRange}
			case :
				return +1 * int64(), nil
			}
		}
		// This is not a signed integer, which implies ErrSyntax.
		if Kind([0]).normalize() == '0' {
			,  := strconv.ParseFloat(string(), 64)
			return f64toi64(), &numError{accessor: "Int", value: .String(), err: strconv.ErrSyntax}
		}
	} else if .num != 0 {
		// Handle typed Go number value.
		switch .str[0] {
		case 'i':
			return int64(.num), nil
		case 'u':
			if .num > maxInt64 {
				return maxInt64, &numError{accessor: "Int", value: .String(), err: strconv.ErrRange}
			}
			return int64(.num), nil
		case 'f', 'F':
			 := float64(math.Float64frombits(uint64(.num)))
			if .str[0] == 'F' {
				 = float64(math.Float32frombits(uint32(.num)))
			}
			switch  := f64toi64(); {
			case math.IsNaN(), math.Trunc() != :
				return , &numError{accessor: "Int", value: .String(), err: strconv.ErrSyntax}
			case ( == minInt64 &&  < minInt64) || ( == maxInt64 &&  > maxInt64):
				return , &numError{accessor: "Int", value: .String(), err: strconv.ErrRange}
			default:
				return , nil
			}
		}
	}

	panic("invalid JSON token kind: " + .Kind().String())
}

func ( float64) int64 {
	switch {
	case math.IsNaN():
		return 0
	case  >= maxInt64+1:
		return maxInt64
	case  < minInt64:
		return minInt64
	default:
		return int64() // 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 ( Token) () (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;  != nil {
		// Handle raw JSON number value.
		if uint64(.previousOffsetStart()) != .num {
			panic(invalidTokenPanic)
		}
		 := .previousBuffer()
		// Prospectively parse an unsigned integer.
		switch ,  := jsonwire.ParseUint(); {
		case :
			return , nil
		case  == maxUint64: // implies overflows
			return maxUint64, &numError{accessor: "Uint", value: .String(), err: strconv.ErrRange}
		}
		// This is not an unsigned integer, which implies ErrSyntax.
		if Kind([0]).normalize() == '0' {
			,  := strconv.ParseFloat(string(), 64)
			return f64tou64(), &numError{accessor: "Uint", value: .String(), err: strconv.ErrSyntax}
		}
	} else if .num != 0 {
		// Handle typed Go number value.
		switch .str[0] {
		case 'u':
			return .num, nil
		case 'i':
			if int64(.num) < minUint64 {
				return minUint64, &numError{accessor: "Uint", value: .String(), err: strconv.ErrSyntax}
			}
			return uint64(int64(.num)), nil
		case 'f', 'F':
			 := float64(math.Float64frombits(uint64(.num)))
			if .str[0] == 'F' {
				 = float64(math.Float32frombits(uint32(.num)))
			}
			switch  := f64tou64(); {
			case math.IsNaN(), math.Trunc() != , math.Signbit():
				return , &numError{accessor: "Uint", value: .String(), err: strconv.ErrSyntax}
			case ( == minUint64 &&  < minUint64) || ( == maxUint64 &&  > maxUint64):
				return , &numError{accessor: "Uint", value: .String(), err: strconv.ErrRange}
			default:
				return , nil
			}
		}
	}

	panic("invalid JSON token kind: " + .Kind().String())
}

func ( float64) uint64 {
	switch {
	case math.IsNaN():
		return 0
	case  >= maxUint64+1:
		return maxUint64
	case  < minUint64:
		return minUint64
	default:
		return uint64() // NOTE: This may lead to loss of precision.
	}
}

// Kind returns the token kind.
func ( Token) () Kind {
	switch {
	case .raw != nil:
		 := .raw
		if uint64(.previousOffsetStart()) != .num {
			panic(invalidTokenPanic)
		}
		return Kind(.raw.buf[.prevStart]).normalize()
	case .num != 0:
		return '0'
	case len(.str) != 0:
		return '"'
	default:
		return invalidKind
	}
}

// A Kind represents the kind of a JSON token.
//
// A Kind is a single byte, which is conveniently the first byte of that
// kind's symbol in the grammar (except for numbers, which are always 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 returns a string representation of k.
func ( Kind) () string {
	switch  {
	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()}) + ">"
	}
}

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 ( Kind) () Kind {
	// A lookup table keeps the inlining cost as low as possible.
	return normKind[]
}