// Copyright 2022 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 json

import (
	
	
	

	
	
	
	
	
)

// This file contains an optimized marshal and unmarshal implementation
// for the any type. This type is often used when the Go program has
// no knowledge of the JSON schema. This is a common enough occurrence
// to justify the complexity of adding logic for this.

// marshalValueAny marshals a Go any as a JSON value.
// This assumes that there are no special formatting directives
// for any possible nested value.
func marshalValueAny( *jsontext.Encoder,  any,  *jsonopts.Struct) error {
	switch val := .(type) {
	case nil:
		return .WriteToken(jsontext.Null)
	case bool:
		return .WriteToken(jsontext.Bool())
	case string:
		return .WriteToken(jsontext.String())
	case float64:
		return .WriteToken(jsontext.Float())
	case map[string]any:
		return marshalObjectAny(, , )
	case []any:
		return marshalArrayAny(, , )
	default:
		 := newAddressableValue(reflect.TypeOf())
		.Set(reflect.ValueOf())
		 := lookupArshaler(.Type()).marshal
		if .Marshalers != nil {
			, _ = .Marshalers.(*Marshalers).lookup(, .Type())
		}
		return (, , )
	}
}

// unmarshalValueAny unmarshals a JSON value as a Go any.
// This assumes that there are no special formatting directives
// for any possible nested value.
// Duplicate names must be rejected since this does not implement merging.
func unmarshalValueAny( *jsontext.Decoder,  *jsonopts.Struct) (any, error) {
	switch  := .PeekKind();  {
	case '{':
		return unmarshalObjectAny(, )
	case '[':
		return unmarshalArrayAny(, )
	default:
		 := export.Decoder()
		var  jsonwire.ValueFlags
		,  := .ReadValue(&)
		if  != nil {
			return nil, 
		}
		switch .Kind() {
		case 'n':
			return nil, nil
		case 'f':
			return false, nil
		case 't':
			return true, nil
		case '"':
			 = jsonwire.UnquoteMayCopy(, .IsVerbatim())
			if .StringCache == nil {
				.StringCache = new(stringCache)
			}
			return makeString(.StringCache, ), nil
		case '0':
			if .Flags.Get(jsonflags.UnmarshalAnyWithRawNumber) {
				return internal.RawNumberOf(), nil
			}
			,  := jsonwire.ParseFloat(, 64)
			if ! {
				return , newUnmarshalErrorAfterWithValue(, float64Type, strconv.ErrRange)
			}
			return , nil
		default:
			panic("BUG: invalid kind: " + .String())
		}
	}
}

// marshalObjectAny marshals a Go map[string]any as a JSON object
// (or as a JSON null if nil and [jsonflags.FormatNilMapAsNull]).
func marshalObjectAny( *jsontext.Encoder,  map[string]any,  *jsonopts.Struct) error {
	// Check for cycles.
	 := export.Encoder()
	if .Tokens.Depth() > startDetectingCyclesAfter {
		 := reflect.ValueOf()
		if  := visitPointer(&.SeenPointers, );  != nil {
			return newMarshalErrorBefore(, anyType, )
		}
		defer leavePointer(&.SeenPointers, )
	}

	// Handle empty maps.
	if len() == 0 {
		if .Flags.Get(jsonflags.FormatNilMapAsNull) &&  == nil {
			return .WriteToken(jsontext.Null)
		}
		// Optimize for marshaling an empty map without any preceding whitespace.
		if !.Flags.Get(jsonflags.AnyWhitespace) && !.Tokens.Last.NeedObjectName() {
			.Buf = append(.Tokens.MayAppendDelim(.Buf, '{'), "{}"...)
			.Tokens.Last.Increment()
			if .NeedFlush() {
				return .Flush()
			}
			return nil
		}
	}

	if  := .WriteToken(jsontext.BeginObject);  != nil {
		return 
	}
	// A Go map guarantees that each entry has a unique key
	// The only possibility of duplicates is due to invalid UTF-8.
	if !.Flags.Get(jsonflags.AllowInvalidUTF8) {
		.Tokens.Last.DisableNamespace()
	}
	if !.Flags.Get(jsonflags.Deterministic) || len() <= 1 {
		for ,  := range  {
			if  := .WriteToken(jsontext.String());  != nil {
				return 
			}
			if  := marshalValueAny(, , );  != nil {
				return 
			}
		}
	} else {
		 := getStrings(len())
		var  int
		for  := range  {
			(*)[] = 
			++
		}
		.Sort()
		for ,  := range * {
			if  := .WriteToken(jsontext.String());  != nil {
				return 
			}
			if  := marshalValueAny(, [], );  != nil {
				return 
			}
		}
		putStrings()
	}
	if  := .WriteToken(jsontext.EndObject);  != nil {
		return 
	}
	return nil
}

// unmarshalObjectAny unmarshals a JSON object as a Go map[string]any.
// It panics if not decoding a JSON object.
func unmarshalObjectAny( *jsontext.Decoder,  *jsonopts.Struct) (map[string]any, error) {
	switch ,  := .ReadToken(); {
	case  != nil:
		return nil, 
	case .Kind() != '{':
		panic("BUG: invalid kind: " + .Kind().String())
	}
	 := make(map[string]any)
	// A Go map guarantees that each entry has a unique key
	// The only possibility of duplicates is due to invalid UTF-8.
	if !.Flags.Get(jsonflags.AllowInvalidUTF8) {
		export.Decoder().Tokens.Last.DisableNamespace()
	}
	var  error
	for .PeekKind() != '}' {
		,  := .ReadToken()
		if  != nil {
			return , 
		}
		 := .String()

		// Manually check for duplicate names.
		if ,  := [];  {
			// TODO: Unread the object name.
			 := export.Decoder().PreviousTokenOrValue()
			 := newDuplicateNameError(.StackPointer(), nil, .InputOffset()-len64())
			return , 
		}

		,  := unmarshalValueAny(, )
		[] = 
		if  != nil {
			if isFatalError(, .Flags) {
				return , 
			}
			 = cmp.Or(, )
		}
	}
	if ,  := .ReadToken();  != nil {
		return , 
	}
	return , 
}

// marshalArrayAny marshals a Go []any as a JSON array
// (or as a JSON null if nil and [jsonflags.FormatNilSliceAsNull]).
func marshalArrayAny( *jsontext.Encoder,  []any,  *jsonopts.Struct) error {
	// Check for cycles.
	 := export.Encoder()
	if .Tokens.Depth() > startDetectingCyclesAfter {
		 := reflect.ValueOf()
		if  := visitPointer(&.SeenPointers, );  != nil {
			return newMarshalErrorBefore(, sliceAnyType, )
		}
		defer leavePointer(&.SeenPointers, )
	}

	// Handle empty slices.
	if len() == 0 {
		if .Flags.Get(jsonflags.FormatNilSliceAsNull) &&  == nil {
			return .WriteToken(jsontext.Null)
		}
		// Optimize for marshaling an empty slice without any preceding whitespace.
		if !.Flags.Get(jsonflags.AnyWhitespace) && !.Tokens.Last.NeedObjectName() {
			.Buf = append(.Tokens.MayAppendDelim(.Buf, '['), "[]"...)
			.Tokens.Last.Increment()
			if .NeedFlush() {
				return .Flush()
			}
			return nil
		}
	}

	if  := .WriteToken(jsontext.BeginArray);  != nil {
		return 
	}
	for ,  := range  {
		if  := marshalValueAny(, , );  != nil {
			return 
		}
	}
	if  := .WriteToken(jsontext.EndArray);  != nil {
		return 
	}
	return nil
}

// unmarshalArrayAny unmarshals a JSON array as a Go []any.
// It panics if not decoding a JSON array.
func unmarshalArrayAny( *jsontext.Decoder,  *jsonopts.Struct) ([]any, error) {
	switch ,  := .ReadToken(); {
	case  != nil:
		return nil, 
	case .Kind() != '[':
		panic("BUG: invalid kind: " + .Kind().String())
	}
	 := []any{}
	var  error
	for .PeekKind() != ']' {
		,  := unmarshalValueAny(, )
		 = append(, )
		if  != nil {
			if isFatalError(, .Flags) {
				return , 
			}
			 = cmp.Or(, )
		}
	}
	if ,  := .ReadToken();  != nil {
		return , 
	}
	return , 
}