/*
 * MinIO Cloud Storage, (C) 2020 MinIO, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package simdjson

import (
	"errors"
	"fmt"
	"math"
	"strconv"
)

const JSONVALUEMASK = 0xff_ffff_ffff_ffff
const JSONTAGOFFSET = 56
const JSONTAGMASK = 0xff << JSONTAGOFFSET
const STRINGBUFBIT = 0x80_0000_0000_0000
const STRINGBUFMASK = 0x7fffffffffffff

const maxdepth = 128

// FloatFlags are flags recorded when converting floats.
type FloatFlags uint64

// FloatFlag is a flag recorded when parsing floats.
type FloatFlag uint64

const (
	// FloatOverflowedInteger is set when number in JSON was in integer notation,
	// but under/overflowed both int64 and uint64 and therefore was parsed as float.
	FloatOverflowedInteger FloatFlag = 1 << iota
)

// Contains returns whether f contains the specified flag.
func (f FloatFlags) Contains(flag FloatFlag) bool {
	return FloatFlag(f)&flag == flag
}

// Flags converts the flag to FloatFlags and optionally merges more flags.
func (f FloatFlag) Flags(more ...FloatFlag) FloatFlags {
	// We operate on a copy, so we can modify f.
	for _, v := range more {
		f |= v
	}
	return FloatFlags(f)
}

type TStrings struct {
	B []byte
}

type ParsedJson struct {
	Message []byte
	Tape    []uint64
	Strings *TStrings

	// allows to reuse the internal structures without exposing it.
	internal *internalParsedJson
}

const indexSlots = 16
const indexSize = 1536                            // Seems to be a good size for the index buffering
const indexSizeWithSafetyBuffer = indexSize - 128 // Make sure we never write beyond buffer

type indexChan struct {
	index   int
	length  int
	indexes *[indexSize]uint32
}

type internalParsedJson struct {
	ParsedJson
	containingScopeOffset []uint64
	isvalid               bool
	indexChans            chan indexChan
	indexesChan           indexChan
	buffers               [indexSlots][indexSize]uint32
	buffersOffset         uint64
	ndjson                uint64
	copyStrings           bool
}

// Iter returns a new Iter.
func (pj *ParsedJson) Iter() Iter {
	return Iter{tape: *pj}
}

// stringAt returns a string at a specific offset in the stringbuffer.
func (pj *ParsedJson) stringAt(offset, length uint64) (string, error) {
	b, err := pj.stringByteAt(offset, length)
	return string(b), err
}

// stringByteAt returns a string at a specific offset in the stringbuffer.
func (pj *ParsedJson) stringByteAt(offset, length uint64) ([]byte, error) {
	if offset&STRINGBUFBIT == 0 {
		if offset+length > uint64(len(pj.Message)) {
			return nil, fmt.Errorf("string message offset (%v) outside valid area (%v)", offset+length, len(pj.Message))
		}
		return pj.Message[offset : offset+length], nil
	}

	offset = offset & STRINGBUFMASK
	if offset+length > uint64(len(pj.Strings.B)) {
		return nil, fmt.Errorf("string buffer offset (%v) outside valid area (%v)", offset+length, len(pj.Strings.B))
	}
	return pj.Strings.B[offset : offset+length], nil
}

// ForEach returns each line in NDJSON, or the top element in non-ndjson.
// This will usually be an object or an array.
// If the callback returns a non-nil error parsing stops and the errors is returned.
func (pj *ParsedJson) ForEach(fn func(i Iter) error) error {
	i := Iter{tape: *pj}
	var elem Iter
	for {
		t, err := i.AdvanceIter(&elem)
		if err != nil || t != TypeRoot {
			return err
		}
		elem.AdvanceInto()
		if err = fn(elem); err != nil {
			return err
		}
	}
}

// Clone returns a deep clone of the ParsedJson.
// If a nil destination is sent a new will be created.
func (pj *ParsedJson) Clone(dst *ParsedJson) *ParsedJson {
	if dst == nil {
		dst = &ParsedJson{
			Message:  make([]byte, len(pj.Message)),
			Tape:     make([]uint64, len(pj.Tape)),
			Strings:  &TStrings{make([]byte, len(pj.Strings.B))},
			internal: nil,
		}
	} else {
		if cap(dst.Message) < len(pj.Message) {
			dst.Message = make([]byte, len(pj.Message))
		}
		if cap(dst.Tape) < len(pj.Tape) {
			dst.Tape = make([]uint64, len(pj.Tape))
		}
		if dst.Strings == nil {
			dst.Strings = &TStrings{make([]byte, len(pj.Strings.B))}
		} else if cap(dst.Strings.B) < len(pj.Strings.B) {
			dst.Strings.B = make([]byte, len(pj.Strings.B))
		}
	}
	dst.internal = nil
	dst.Tape = dst.Tape[:len(pj.Tape)]
	copy(dst.Tape, pj.Tape)
	dst.Message = dst.Message[:len(pj.Message)]
	copy(dst.Message, pj.Message)
	dst.Strings.B = dst.Strings.B[:len(pj.Strings.B)]
	copy(dst.Strings.B, pj.Strings.B)
	return dst
}

// Iter represents a section of JSON.
// To start iterating it, use Advance() or AdvanceIter() methods
// which will queue the first element.
// If an Iter is copied, the copy will be independent.
type Iter struct {
	// The tape where this iter start.
	tape ParsedJson

	// offset of the next entry to be decoded
	off int

	// addNext is the number of entries to skip for the next entry.
	addNext int

	// current value, exclude tag in top bits
	cur uint64

	// current tag
	t Tag
}

// Advance will read the type of the next element
// and queues up the value on the same level.
func (i *Iter) Advance() Type {
	i.off += i.addNext
	if i.off >= len(i.tape.Tape) {
		i.addNext = 0
		i.t = TagEnd
		return TypeNone
	}

	v := i.tape.Tape[i.off]
	i.cur = v & JSONVALUEMASK
	i.t = Tag(v >> 56)
	i.off++
	i.calcNext(false)
	if i.addNext < 0 {
		// We can't send error, so move to end.
		i.moveToEnd()
		return TypeNone
	}
	return TagToType[i.t]
}

// AdvanceInto will read the tag of the next element
// and move into and out of arrays , objects and root elements.
// This should only be used for strictly manual parsing.
func (i *Iter) AdvanceInto() Tag {
	i.off += i.addNext
	if i.off >= len(i.tape.Tape) {
		i.addNext = 0
		i.t = TagEnd
		return TagEnd
	}

	v := i.tape.Tape[i.off]
	i.cur = v & JSONVALUEMASK
	i.t = Tag(v >> 56)
	i.off++
	i.calcNext(true)
	if i.addNext < 0 {
		// We can't send error, so end tape.
		i.moveToEnd()
		return TagEnd
	}
	return i.t
}

func (i *Iter) moveToEnd() {
	i.off = len(i.tape.Tape)
	i.addNext = 0
	i.t = TagEnd
}

// calcNext will populate addNext to the correct value to skip.
// Specify whether to move into objects/array.
func (i *Iter) calcNext(into bool) {
	i.addNext = 0
	switch i.t {
	case TagInteger, TagUint, TagFloat, TagString:
		i.addNext = 1
	case TagRoot, TagObjectStart, TagArrayStart:
		if !into {
			i.addNext = int(i.cur) - i.off
		}
	}
}

// Type returns the queued value type from the previous call to Advance.
func (i *Iter) Type() Type {
	if i.off+i.addNext > len(i.tape.Tape) {
		return TypeNone
	}
	return TagToType[i.t]
}

// AdvanceIter will read the type of the next element
// and return an iterator only containing the object.
// If dst and i are the same, both will contain the value inside.
func (i *Iter) AdvanceIter(dst *Iter) (Type, error) {
	i.off += i.addNext
	if i.off == len(i.tape.Tape) {
		i.addNext = 0
		i.t = TagEnd
		return TypeNone, nil
	}
	if i.off > len(i.tape.Tape) {
		return TypeNone, errors.New("offset bigger than tape")
	}

	// Get current value off tape.
	v := i.tape.Tape[i.off]
	i.cur = v & JSONVALUEMASK
	i.t = Tag(v >> 56)
	i.off++
	i.calcNext(false)
	if i.addNext < 0 {
		i.moveToEnd()
		return TypeNone, errors.New("element has negative offset")
	}

	// Calculate end of this object.
	iEnd := i.off + i.addNext
	typ := TagToType[i.t]

	// Copy i if different
	if i != dst {
		*dst = *i
	}
	// Move into dst
	dst.calcNext(true)
	if dst.addNext < 0 {
		i.moveToEnd()
		return TypeNone, errors.New("element has negative offset")
	}

	if iEnd > len(dst.tape.Tape) {
		return TypeNone, errors.New("element extends beyond tape")
	}

	// Restrict destination.
	dst.tape.Tape = dst.tape.Tape[:iEnd]

	return typ, nil
}

// PeekNext will return the next value type.
// Returns TypeNone if next ends iterator.
func (i *Iter) PeekNext() Type {
	if i.off+i.addNext >= len(i.tape.Tape) {
		return TypeNone
	}
	return TagToType[Tag(i.tape.Tape[i.off+i.addNext]>>56)]
}

// PeekNextTag will return the tag at the current offset.
// Will return TagEnd if at end of iterator.
func (i *Iter) PeekNextTag() Tag {
	if i.off+i.addNext >= len(i.tape.Tape) {
		return TagEnd
	}
	return Tag(i.tape.Tape[i.off+i.addNext] >> 56)
}

// MarshalJSON will marshal the entire remaining scope of the iterator.
func (i *Iter) MarshalJSON() ([]byte, error) {
	return i.MarshalJSONBuffer(nil)
}

// MarshalJSONBuffer will marshal the remaining scope of the iterator including the current value.
// An optional buffer can be provided for fewer allocations.
// Output will be appended to the destination.
func (i *Iter) MarshalJSONBuffer(dst []byte) ([]byte, error) {
	var tmpBuf []byte

	// Pre-allocate for 100 deep.
	var stackTmp [100]uint8
	// We have a stackNone on top of the stack
	stack := stackTmp[:1]
	const (
		stackNone = iota
		stackArray
		stackObject
		stackRoot
	)

writeloop:
	for {
		// Write key names.
		if stack[len(stack)-1] == stackObject && i.t != TagObjectEnd {
			sb, err := i.StringBytes()
			if err != nil {
				return nil, fmt.Errorf("expected key within object: %w", err)
			}
			dst = append(dst, '"')
			dst = escapeBytes(dst, sb)
			dst = append(dst, '"', ':')
			if i.PeekNextTag() == TagEnd {
				return nil, fmt.Errorf("unexpected end of tape within object")
			}
			i.AdvanceInto()
		}
		//fmt.Println(i.t, len(stack)-1, i.off)
	tagswitch:
		switch i.t {
		case TagRoot:
			isOpenRoot := int(i.cur) > i.off
			if len(stack) > 1 {
				if isOpenRoot {
					return dst, errors.New("root tag open, but not at top of stack")
				}
				l := stack[len(stack)-1]
				switch l {
				case stackRoot:
					if i.PeekNextTag() != TagEnd {
						dst = append(dst, '\n')
					}
					stack = stack[:len(stack)-1]
					break tagswitch
				case stackNone:
					break writeloop
				default:
					return dst, errors.New("root tag, but not at top of stack, got id " + strconv.Itoa(int(l)))
				}
			}

			if isOpenRoot {
				// Always move into root.
				i.addNext = 0
			}
			i.AdvanceInto()
			stack = append(stack, stackRoot)
			continue
		case TagString:
			sb, err := i.StringBytes()
			if err != nil {
				return nil, err
			}
			dst = append(dst, '"')
			dst = escapeBytes(dst, sb)
			dst = append(dst, '"')
			tmpBuf = tmpBuf[:0]
		case TagInteger:
			v, err := i.Int()
			if err != nil {
				return nil, err
			}
			dst = strconv.AppendInt(dst, v, 10)
		case TagUint:
			v, err := i.Uint()
			if err != nil {
				return nil, err
			}
			dst = strconv.AppendUint(dst, v, 10)
		case TagFloat:
			v, err := i.Float()
			if err != nil {
				return nil, err
			}
			dst, err = appendFloat(dst, v)
			if err != nil {
				return nil, err
			}
		case TagNull:
			dst = append(dst, []byte("null")...)
		case TagBoolTrue:
			dst = append(dst, []byte("true")...)
		case TagBoolFalse:
			dst = append(dst, []byte("false")...)
		case TagObjectStart:
			dst = append(dst, '{')
			stack = append(stack, stackObject)
			// We should not emit commas.
			i.AdvanceInto()
			continue
		case TagObjectEnd:
			dst = append(dst, '}')
			if stack[len(stack)-1] != stackObject {
				return dst, errors.New("end of object with no object on stack")
			}
			stack = stack[:len(stack)-1]
		case TagArrayStart:
			dst = append(dst, '[')
			stack = append(stack, stackArray)
			i.AdvanceInto()
			continue
		case TagArrayEnd:
			dst = append(dst, ']')
			if stack[len(stack)-1] != stackArray {
				return nil, errors.New("end of array with no array on stack")
			}
			stack = stack[:len(stack)-1]
		case TagEnd:
			if i.PeekNextTag() == TagEnd {
				return nil, errors.New("no content queued in iterator")
			}
			i.AdvanceInto()
			continue
		}

		if i.PeekNextTag() == TagEnd {
			break
		}
		i.AdvanceInto()

		// Output object separators, etc.
		switch stack[len(stack)-1] {
		case stackArray:
			switch i.t {
			case TagArrayEnd:
			default:
				dst = append(dst, ',')
			}
		case stackObject:
			switch i.t {
			case TagObjectEnd:
			default:
				dst = append(dst, ',')
			}
		}
	}
	if len(stack) > 1 {
		// Copy so "stack" doesn't escape.
		sCopy := append(make([]uint8, 0, len(stack)-1), stack[1:]...)
		return nil, fmt.Errorf("objects or arrays not closed. left on stack: %v", sCopy)
	}
	return dst, nil
}

// Float returns the float value of the next element.
// Integers are automatically converted to float.
func (i *Iter) Float() (float64, error) {
	switch i.t {
	case TagFloat:
		if i.off >= len(i.tape.Tape) {
			return 0, errors.New("corrupt input: expected float, but no more values on tape")
		}
		v := math.Float64frombits(i.tape.Tape[i.off])
		return v, nil
	case TagInteger:
		if i.off >= len(i.tape.Tape) {
			return 0, errors.New("corrupt input: expected integer, but no more values on tape")
		}
		v := int64(i.tape.Tape[i.off])
		return float64(v), nil
	case TagUint:
		if i.off >= len(i.tape.Tape) {
			return 0, errors.New("corrupt input: expected integer, but no more values on tape")
		}
		v := i.tape.Tape[i.off]
		return float64(v), nil
	default:
		return 0, fmt.Errorf("unable to convert type %v to float", i.t)
	}
}

// FloatFlags returns the float value of the next element.
// This will include flags from parsing.
// Integers are automatically converted to float.
func (i *Iter) FloatFlags() (float64, FloatFlags, error) {
	switch i.t {
	case TagFloat:
		if i.off >= len(i.tape.Tape) {
			return 0, 0, errors.New("corrupt input: expected float, but no more values on tape")
		}
		v := math.Float64frombits(i.tape.Tape[i.off])
		return v, FloatFlags(i.cur), nil
	case TagInteger:
		if i.off >= len(i.tape.Tape) {
			return 0, 0, errors.New("corrupt input: expected integer, but no more values on tape")
		}
		v := int64(i.tape.Tape[i.off])
		return float64(v), 0, nil
	case TagUint:
		if i.off >= len(i.tape.Tape) {
			return 0, 0, errors.New("corrupt input: expected integer, but no more values on tape")
		}
		v := i.tape.Tape[i.off]
		return float64(v), 0, nil
	default:
		return 0, 0, fmt.Errorf("unable to convert type %v to float", i.t)
	}
}

// SetFloat can change a float, int, uint or string with the specified value.
// Attempting to change other types will return an error.
func (i *Iter) SetFloat(v float64) error {
	switch i.t {
	case TagFloat, TagInteger, TagUint, TagString:
		i.tape.Tape[i.off-1] = uint64(TagFloat) << JSONTAGOFFSET
		i.tape.Tape[i.off] = math.Float64bits(v)
		i.t = TagFloat
		i.cur = 0
		return nil
	}
	return fmt.Errorf("cannot set tag %s to float", i.t.String())
}

// Int returns the integer value of the next element.
// Integers and floats within range are automatically converted.
func (i *Iter) Int() (int64, error) {
	switch i.t {
	case TagFloat:
		if i.off >= len(i.tape.Tape) {
			return 0, errors.New("corrupt input: expected float, but no more values on tape")
		}
		v := math.Float64frombits(i.tape.Tape[i.off])
		if v > math.MaxInt64 {
			return 0, errors.New("float value overflows int64")
		}
		if v < math.MinInt64 {
			return 0, errors.New("float value underflows int64")
		}
		return int64(v), nil
	case TagInteger:
		if i.off >= len(i.tape.Tape) {
			return 0, errors.New("corrupt input: expected integer, but no more values on tape")
		}
		v := int64(i.tape.Tape[i.off])
		return v, nil
	case TagUint:
		if i.off >= len(i.tape.Tape) {
			return 0, errors.New("corrupt input: expected integer, but no more values on tape")
		}
		v := i.tape.Tape[i.off]
		if v > math.MaxInt64 {
			return 0, errors.New("unsigned integer value overflows int64")
		}
		return int64(v), nil
	default:
		return 0, fmt.Errorf("unable to convert type %v to int", i.t)
	}
}

// SetInt can change a float, int, uint or string with the specified value.
// Attempting to change other types will return an error.
func (i *Iter) SetInt(v int64) error {
	switch i.t {
	case TagFloat, TagInteger, TagUint, TagString:
		i.tape.Tape[i.off-1] = uint64(TagInteger) << JSONTAGOFFSET
		i.tape.Tape[i.off] = uint64(v)
		i.t = TagInteger
		i.cur = uint64(v)
		return nil
	}
	return fmt.Errorf("cannot set tag %s to int", i.t.String())
}

// Uint returns the unsigned integer value of the next element.
// Positive integers and floats within range are automatically converted.
func (i *Iter) Uint() (uint64, error) {
	switch i.t {
	case TagFloat:
		if i.off >= len(i.tape.Tape) {
			return 0, errors.New("corrupt input: expected float, but no more values on tape")
		}
		v := math.Float64frombits(i.tape.Tape[i.off])
		if v > math.MaxUint64 {
			return 0, errors.New("float value overflows uint64")
		}
		if v < 0 {
			return 0, errors.New("float value is negative. cannot convert to uint")
		}
		return uint64(v), nil
	case TagInteger:
		if i.off >= len(i.tape.Tape) {
			return 0, errors.New("corrupt input: expected integer, but no more values on tape")
		}
		v := int64(i.tape.Tape[i.off])
		if v < 0 {
			return 0, errors.New("integer value is negative. cannot convert to uint")
		}

		return uint64(v), nil
	case TagUint:
		if i.off >= len(i.tape.Tape) {
			return 0, errors.New("corrupt input: expected integer, but no more values on tape")
		}
		v := i.tape.Tape[i.off]
		return v, nil
	default:
		return 0, fmt.Errorf("unable to convert type %v to uint", i.t)
	}
}

// SetUInt can change a float, int, uint or string with the specified value.
// Attempting to change other types will return an error.
func (i *Iter) SetUInt(v uint64) error {
	switch i.t {
	case TagString, TagFloat, TagInteger, TagUint:
		i.tape.Tape[i.off-1] = uint64(TagUint) << JSONTAGOFFSET
		i.tape.Tape[i.off] = v
		i.t = TagUint
		i.cur = v
		return nil
	}
	return fmt.Errorf("cannot set tag %s to uint", i.t.String())
}

// String() returns a string value.
func (i *Iter) String() (string, error) {
	if i.t != TagString {
		return "", errors.New("value is not string")
	}
	if i.off >= len(i.tape.Tape) {
		return "", errors.New("corrupt input: no string offset")
	}

	return i.tape.stringAt(i.cur, i.tape.Tape[i.off])
}

// StringBytes returns a string as byte array.
func (i *Iter) StringBytes() ([]byte, error) {
	if i.t != TagString {
		return nil, errors.New("value is not string")
	}
	if i.off >= len(i.tape.Tape) {
		return nil, errors.New("corrupt input: no string offset on tape")
	}
	return i.tape.stringByteAt(i.cur, i.tape.Tape[i.off])
}

// SetString can change a string, int, uint or float with the specified string.
// Attempting to change other types will return an error.
func (i *Iter) SetString(v string) error {
	return i.SetStringBytes([]byte(v))
}

// SetStringBytes can change a string, int, uint or float with the specified string.
// Attempting to change other types will return an error.
// Sending nil will add an empty string.
func (i *Iter) SetStringBytes(v []byte) error {
	switch i.t {
	case TagString, TagFloat, TagInteger, TagUint:
		i.cur = ((uint64(TagString) << JSONTAGOFFSET) | STRINGBUFBIT) | uint64(len(i.tape.Strings.B))
		i.tape.Tape[i.off-1] = i.cur
		i.tape.Tape[i.off] = uint64(len(v))
		i.t = TagString
		i.tape.Strings.B = append(i.tape.Strings.B, v...)
		return nil
	}
	return fmt.Errorf("cannot set tag %s to string", i.t.String())
}

// StringCvt returns a string representation of the value.
// Root, Object and Arrays are not supported.
func (i *Iter) StringCvt() (string, error) {
	switch i.t {
	case TagString:
		return i.String()
	case TagInteger:
		v, err := i.Int()
		return strconv.FormatInt(v, 10), err
	case TagUint:
		v, err := i.Uint()
		return strconv.FormatUint(v, 10), err
	case TagFloat:
		v, err := i.Float()
		if err != nil {
			return "", err
		}
		return floatToString(v)
	case TagBoolFalse:
		return "false", nil
	case TagBoolTrue:
		return "true", nil
	case TagNull:
		return "null", nil
	}
	return "", fmt.Errorf("cannot convert type %s to string", TagToType[i.t])
}

// Root returns the object embedded in root as an iterator
// along with the type of the content of the first element of the iterator.
// An optional destination can be supplied to avoid allocations.
func (i *Iter) Root(dst *Iter) (Type, *Iter, error) {
	if i.t != TagRoot {
		return TypeNone, dst, errors.New("value is not root")
	}
	if i.cur > uint64(len(i.tape.Tape)) {
		return TypeNone, dst, errors.New("root element extends beyond tape")
	}
	if dst == nil {
		c := *i
		dst = &c
	} else {
		dst.cur = i.cur
		dst.off = i.off
		dst.t = i.t
		dst.tape.Strings = i.tape.Strings
		dst.tape.Message = i.tape.Message
	}
	dst.addNext = 0
	dst.tape.Tape = i.tape.Tape[:i.cur-1]
	return dst.AdvanceInto().Type(), dst, nil
}

// FindElement allows searching for fields and objects by path from the iter and forward,
// moving into root and objects, but not arrays.
// For example "Image", "Url" will search the current root/object for an "Image"
// object and return the value of the "Url" element.
// ErrPathNotFound is returned if any part of the path cannot be found.
// If the tape contains an error it will be returned.
// The iter will *not* be advanced.
func (i *Iter) FindElement(dst *Element, path ...string) (*Element, error) {
	if len(path) == 0 {
		return dst, ErrPathNotFound
	}
	// Local copy.
	cp := *i
	for {
		switch cp.t {
		case TagObjectStart:
			var o Object
			obj, err := cp.Object(&o)
			if err != nil {
				return dst, err
			}
			return obj.FindPath(dst, path...)
		case TagRoot:
			_, _, err := cp.Root(&cp)
			if err != nil {
				return dst, err
			}
			continue
		case TagEnd:
			tag := cp.AdvanceInto()
			if tag == TagEnd {
				return dst, ErrPathNotFound
			}
			continue
		default:
			return dst, fmt.Errorf("type %q found before object was found", cp.t)
		}
	}
}

// Bool returns the bool value.
func (i *Iter) Bool() (bool, error) {
	switch i.t {
	case TagBoolTrue:
		return true, nil
	case TagBoolFalse:
		return false, nil
	}
	return false, fmt.Errorf("value is not bool, but %v", i.t)
}

// SetBool can change a bool or null type to bool with the specified value.
// Attempting to change other types will return an error.
func (i *Iter) SetBool(v bool) error {
	switch i.t {
	case TagBoolTrue, TagBoolFalse, TagNull:
		if v {
			i.t = TagBoolTrue
			i.cur = 0
			i.tape.Tape[i.off-1] = uint64(TagBoolTrue) << JSONTAGOFFSET
		} else {
			i.t = TagBoolFalse
			i.cur = 0
			i.tape.Tape[i.off-1] = uint64(TagBoolFalse) << JSONTAGOFFSET
		}
		return nil
	}
	return fmt.Errorf("cannot set tag %s to bool", i.t.String())
}

// SetNull can change a bool or null type to bool with null.
// Attempting to change other types will return an error.
func (i *Iter) SetNull() error {
	switch i.t {
	case TagBoolTrue, TagBoolFalse, TagNull:
		i.t = TagNull
		i.cur = 0
		i.tape.Tape[i.off-1] = uint64(TagNull) << JSONTAGOFFSET
		return nil
	}
	return fmt.Errorf("cannot set tag %s to null", i.t.String())
}

// Interface returns the value as an interface.
// Objects are returned as map[string]interface{}.
// Arrays are returned as []interface{}.
// Float values are returned as float64.
// Integer values are returned as int64 or uint64.
// String values are returned as string.
// Boolean values are returned as bool.
// Null values are returned as nil.
// Root objects are returned as []interface{}.
func (i *Iter) Interface() (interface{}, error) {
	switch i.t.Type() {
	case TypeUint:
		return i.Uint()
	case TypeInt:
		return i.Int()
	case TypeFloat:
		return i.Float()
	case TypeNull:
		return nil, nil
	case TypeArray:
		arr, err := i.Array(nil)
		if err != nil {
			return nil, err
		}
		return arr.Interface()
	case TypeString:
		return i.String()
	case TypeObject:
		obj, err := i.Object(nil)
		if err != nil {
			return nil, err
		}
		return obj.Map(nil)
	case TypeBool:
		return i.t == TagBoolTrue, nil
	case TypeRoot:
		var dst []interface{}
		var tmp Iter
		for {
			typ, obj, err := i.Root(&tmp)
			if err != nil {
				return nil, err
			}
			if typ == TypeNone {
				break
			}
			elem, err := obj.Interface()
			if err != nil {
				return nil, err
			}
			dst = append(dst, elem)
			typ = i.Advance()
			if typ != TypeRoot {
				break
			}
		}
		return dst, nil
	case TypeNone:
		if i.PeekNextTag() == TagEnd {
			return nil, errors.New("no content in iterator")
		}
		i.Advance()
		return i.Interface()
	default:
	}
	return nil, fmt.Errorf("unknown tag type: %v", i.t)
}

// Object will return the next element as an object.
// An optional destination can be given.
func (i *Iter) Object(dst *Object) (*Object, error) {
	if i.t != TagObjectStart {
		return nil, errors.New("next item is not object")
	}
	end := i.cur
	if end < uint64(i.off) {
		return nil, errors.New("corrupt input: object ends at index before start")
	}
	if uint64(len(i.tape.Tape)) < end {
		return nil, errors.New("corrupt input: object extended beyond tape")
	}
	if dst == nil {
		dst = &Object{}
	}
	dst.tape.Tape = i.tape.Tape[:end]
	dst.tape.Strings = i.tape.Strings
	dst.tape.Message = i.tape.Message
	dst.off = i.off

	return dst, nil
}

// Array will return the next element as an array.
// An optional destination can be given.
func (i *Iter) Array(dst *Array) (*Array, error) {
	if i.t != TagArrayStart {
		return nil, errors.New("next item is not object")
	}
	end := i.cur
	if uint64(len(i.tape.Tape)) < end {
		return nil, errors.New("corrupt input: object extended beyond tape")
	}
	if dst == nil {
		dst = &Array{}
	}
	dst.tape.Tape = i.tape.Tape[:end]
	dst.tape.Strings = i.tape.Strings
	dst.tape.Message = i.tape.Message
	dst.off = i.off

	return dst, nil
}

func (pj *ParsedJson) Reset() {
	pj.Tape = pj.Tape[:0]
	pj.Strings.B = pj.Strings.B[:0]
	pj.Message = pj.Message[:0]
}

func (pj *ParsedJson) get_current_loc() uint64 {
	return uint64(len(pj.Tape))
}

func (pj *ParsedJson) write_tape(val uint64, c byte) {
	pj.Tape = append(pj.Tape, val|(uint64(c)<<56))
}

// writeTapeTagVal will write a tag with no embedded value and a value to the tape.
func (pj *ParsedJson) writeTapeTagVal(tag Tag, val uint64) {
	pj.Tape = append(pj.Tape, uint64(tag)<<56, val)
}

func (pj *ParsedJson) writeTapeTagValFlags(id, val uint64) {
	pj.Tape = append(pj.Tape, id, val)
}

func (pj *ParsedJson) write_tape_s64(val int64) {
	pj.writeTapeTagVal(TagInteger, uint64(val))
}

func (pj *ParsedJson) write_tape_double(d float64) {
	pj.writeTapeTagVal(TagFloat, math.Float64bits(d))
}

func (pj *ParsedJson) annotate_previousloc(saved_loc uint64, val uint64) {
	pj.Tape[saved_loc] |= val
}

// Tag indicates the data type of a tape entry
type Tag uint8

const (
	TagString      = Tag('"')
	TagInteger     = Tag('l')
	TagUint        = Tag('u')
	TagFloat       = Tag('d')
	TagNull        = Tag('n')
	TagBoolTrue    = Tag('t')
	TagBoolFalse   = Tag('f')
	TagObjectStart = Tag('{')
	TagObjectEnd   = Tag('}')
	TagArrayStart  = Tag('[')
	TagArrayEnd    = Tag(']')
	TagRoot        = Tag('r')
	TagEnd         = Tag(0)
)

var tagOpenToClose = [256]Tag{
	TagObjectStart: TagObjectEnd,
	TagArrayStart:  TagArrayEnd,
	TagRoot:        TagRoot,
}

func (t Tag) String() string {
	return string([]byte{byte(t)})
}

// Type is a JSON value type.
type Type uint8

const (
	TypeNone Type = iota
	TypeNull
	TypeString
	TypeInt
	TypeUint
	TypeFloat
	TypeBool
	TypeObject
	TypeArray
	TypeRoot
)

// String returns the type as a string.
func (t Type) String() string {
	switch t {
	case TypeNone:
		return "(no type)"
	case TypeNull:
		return "null"
	case TypeString:
		return "string"
	case TypeInt:
		return "int"
	case TypeUint:
		return "uint"
	case TypeFloat:
		return "float"
	case TypeBool:
		return "bool"
	case TypeObject:
		return "object"
	case TypeArray:
		return "array"
	case TypeRoot:
		return "root"
	}
	return "(invalid)"
}

// TagToType converts a tag to type.
// For arrays and objects only the start tag will return types.
// All non-existing tags returns TypeNone.
var TagToType = [256]Type{
	TagString:      TypeString,
	TagInteger:     TypeInt,
	TagUint:        TypeUint,
	TagFloat:       TypeFloat,
	TagNull:        TypeNull,
	TagBoolTrue:    TypeBool,
	TagBoolFalse:   TypeBool,
	TagObjectStart: TypeObject,
	TagArrayStart:  TypeArray,
	TagRoot:        TypeRoot,
}

// Type converts a tag to a type.
// Only basic types and array+object start match a type.
func (t Tag) Type() Type {
	return TagToType[t]
}

var shouldEscape = [256]bool{
	'\b': true,
	'\f': true,
	'\n': true,
	'\r': true,
	'"':  true,
	'\t': true,
	'\\': true,
	// Remaining will be added in init below.
}

func init() {
	for i := range shouldEscape[:0x20] {
		shouldEscape[i] = true
	}
}

// escapeBytes will escape JSON bytes.
// Output is appended to dst.
func escapeBytes(dst, src []byte) []byte {
	esc := false
	for i, s := range src {
		if shouldEscape[s] {
			if i > 0 {
				dst = append(dst, src[:i]...)
				src = src[i:]
			}
			esc = true
			break
		}
	}
	if !esc {
		// Nothing was escaped...
		return append(dst, src...)
	}
	for _, s := range src {
		if !shouldEscape[s] {
			dst = append(dst, s)
			continue
		}
		switch s {
		case '\b':
			dst = append(dst, '\\', 'b')

		case '\f':
			dst = append(dst, '\\', 'f')

		case '\n':
			dst = append(dst, '\\', 'n')

		case '\r':
			dst = append(dst, '\\', 'r')

		case '"':
			dst = append(dst, '\\', '"')

		case '\t':
			dst = append(dst, '\\', 't')

		case '\\':
			dst = append(dst, '\\', '\\')

		default:
			dst = append(dst, '\\', 'u', '0', '0', valToHex[s>>4], valToHex[s&0xf])
		}
	}
	return dst
}

var valToHex = [16]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}

// floatToString converts a float to string similar to Go stdlib.
func floatToString(f float64) (string, error) {
	var tmp [32]byte
	v, err := appendFloat(tmp[:0], f)
	return string(v), err
}

// appendFloat converts a float to string similar to Go stdlib and appends it to dst.
func appendFloat(dst []byte, f float64) ([]byte, error) {
	if math.IsInf(f, 0) || math.IsNaN(f) {
		return nil, errors.New("INF or NaN number found")
	}

	// Convert as if by ES6 number to string conversion.
	// This matches most other JSON generators.
	// See golang.org/issue/6384 and golang.org/issue/14135.
	// Like fmt %g, but the exponent cutoffs are different
	// and exponents themselves are not padded to two digits.
	abs := math.Abs(f)
	if (abs >= 1e-6 && abs < 1e21) || abs == 0 {
		return appendFloatF(dst, f), nil
	}
	dst = strconv.AppendFloat(dst, f, 'e', -1, 64)
	// clean up e-09 to e-9
	n := len(dst)
	if n >= 4 && dst[n-4] == 'e' && dst[n-3] == '-' && dst[n-2] == '0' {
		dst[n-2] = dst[n-1]
		dst = dst[:n-1]
	}
	return dst, nil
}
