// 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 start or end delimiter for a JSON object (i.e., { or } )
//   - a start 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.
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 (float)  ║ 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", "i", or "u" if num should be interpreted
	// as a float64, int64, or uint64, respectively.
	str string

	// num is a float64, int64, or uint64 stored as a uint64 value.
	// 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( 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: }
}

// Float constructs a Token representing a JSON number.
// 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 makes a copy of the Token such that its value remains valid
// even after a subsequent [Decoder.Read] call.
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
			}
		}

		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(, .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, math.Float64frombits(.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(, math.Float64frombits(.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())
}

// Float returns the floating-point value for a JSON number.
// It returns a NaN, +Inf, or -Inf value for any JSON string
// with the values "NaN", "Infinity", or "-Infinity".
// It panics for all other cases.
func ( Token) () float64 {
	if  := .raw;  != nil {
		// Handle raw number value.
		if uint64(.previousOffsetStart()) != .num {
			panic(invalidTokenPanic)
		}
		 := .previousBuffer()
		if Kind([0]).normalize() == '0' {
			,  := jsonwire.ParseFloat(, 64)
			return 
		}
	} else if .num != 0 {
		// Handle exact number value.
		switch .str[0] {
		case 'f':
			return math.Float64frombits(.num)
		case 'i':
			return float64(int64(.num))
		case 'u':
			return float64(uint64(.num))
		}
	}

	// Handle string values with "NaN", "Infinity", or "-Infinity".
	if .Kind() == '"' {
		switch .String() {
		case "NaN":
			return math.NaN()
		case "Infinity":
			return math.Inf(+1)
		case "-Infinity":
			return math.Inf(-1)
		}
	}

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

// Int returns the signed integer value for a JSON number.
// 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 {
	if  := .raw;  != nil {
		// Handle raw integer value.
		if uint64(.previousOffsetStart()) != .num {
			panic(invalidTokenPanic)
		}
		 := false
		 := .previousBuffer()
		if len() > 0 && [0] == '-' {
			,  = true, [1:]
		}
		if ,  := jsonwire.ParseUint();  {
			if  {
				if  > -minInt64 {
					return minInt64
				}
				return -1 * int64()
			} else {
				if  > +maxInt64 {
					return maxInt64
				}
				return +1 * int64()
			}
		}
	} else if .num != 0 {
		// Handle exact integer value.
		switch .str[0] {
		case 'i':
			return int64(.num)
		case 'u':
			if .num > maxInt64 {
				return maxInt64
			}
			return int64(.num)
		}
	}

	// Handle JSON number that is a floating-point value.
	if .Kind() == '0' {
		switch  := .Float(); {
		case  >= maxInt64:
			return maxInt64
		case  <= minInt64:
			return minInt64
		default:
			return int64() // truncation toward zero
		}
	}

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

// Uint returns the unsigned integer value for a JSON number.
// The fractional component of any number is ignored (truncation toward zero).
// Any number beyond the representation of an uint64 will be saturated
// to the closest representable value.
// It panics if the token kind is not a JSON number.
func ( Token) () uint64 {
	// 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.

	if  := .raw;  != nil {
		// Handle raw integer value.
		if uint64(.previousOffsetStart()) != .num {
			panic(invalidTokenPanic)
		}
		 := false
		 := .previousBuffer()
		if len() > 0 && [0] == '-' {
			,  = true, [1:]
		}
		if ,  := jsonwire.ParseUint();  {
			if  {
				return minUint64
			}
			return 
		}
	} else if .num != 0 {
		// Handle exact integer value.
		switch .str[0] {
		case 'u':
			return .num
		case 'i':
			if int64(.num) < minUint64 {
				return minUint64
			}
			return uint64(int64(.num))
		}
	}

	// Handle JSON number that is a floating-point value.
	if .Kind() == '0' {
		switch  := .Float(); {
		case  >= maxUint64:
			return maxUint64
		case  <= minUint64:
			return minUint64
		default:
			return uint64() // truncation toward zero
		}
	}

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

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

// 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':
//
//   - 'n': null
//   - 'f': false
//   - 't': true
//   - '"': string
//   - '0': number
//   - '{': object start
//   - '}': object end
//   - '[': array start
//   - ']': array end
//
// An invalid kind is usually represented using 0,
// but may be non-zero due to invalid JSON data.
type Kind byte

const invalidKind Kind = 0

// String prints the kind in a humanly readable fashion.
func ( Kind) () string {
	switch  {
	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(string()) + ">"
	}
}

// normalize coalesces all possible starting characters of a number as just '0'.
func ( Kind) () Kind {
	if  == '-' || ('0' <=  &&  <= '9') {
		return '0'
	}
	return 
}