mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-10-19 07:55:07 +00:00
239 lines
5.9 KiB
Go
239 lines
5.9 KiB
Go
|
// 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.
|
||
|
|
||
|
package json
|
||
|
|
||
|
import "reflect"
|
||
|
|
||
|
// This files 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.
|
||
|
|
||
|
func marshalValueAny(mo MarshalOptions, enc *Encoder, val any) error {
|
||
|
switch val := val.(type) {
|
||
|
case nil:
|
||
|
return enc.WriteToken(Null)
|
||
|
case bool:
|
||
|
return enc.WriteToken(Bool(val))
|
||
|
case string:
|
||
|
return enc.WriteToken(String(val))
|
||
|
case float64:
|
||
|
return enc.WriteToken(Float(val))
|
||
|
case map[string]any:
|
||
|
return marshalObjectAny(mo, enc, val)
|
||
|
case []any:
|
||
|
return marshalArrayAny(mo, enc, val)
|
||
|
default:
|
||
|
v := newAddressableValue(reflect.TypeOf(val))
|
||
|
v.Set(reflect.ValueOf(val))
|
||
|
marshal := lookupArshaler(v.Type()).marshal
|
||
|
if mo.Marshalers != nil {
|
||
|
marshal, _ = mo.Marshalers.lookup(marshal, v.Type())
|
||
|
}
|
||
|
return marshal(mo, enc, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func unmarshalValueAny(uo UnmarshalOptions, dec *Decoder) (any, error) {
|
||
|
switch k := dec.PeekKind(); k {
|
||
|
case '{':
|
||
|
return unmarshalObjectAny(uo, dec)
|
||
|
case '[':
|
||
|
return unmarshalArrayAny(uo, dec)
|
||
|
default:
|
||
|
var flags valueFlags
|
||
|
val, err := dec.readValue(&flags)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
switch val.Kind() {
|
||
|
case 'n':
|
||
|
return nil, nil
|
||
|
case 'f':
|
||
|
return false, nil
|
||
|
case 't':
|
||
|
return true, nil
|
||
|
case '"':
|
||
|
val = unescapeStringMayCopy(val, flags.isVerbatim())
|
||
|
if dec.stringCache == nil {
|
||
|
dec.stringCache = new(stringCache)
|
||
|
}
|
||
|
return dec.stringCache.make(val), nil
|
||
|
case '0':
|
||
|
fv, _ := parseFloat(val, 64) // ignore error since readValue guarantees val is valid
|
||
|
return fv, nil
|
||
|
default:
|
||
|
panic("BUG: invalid kind: " + k.String())
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func marshalObjectAny(mo MarshalOptions, enc *Encoder, obj map[string]any) error {
|
||
|
// Check for cycles.
|
||
|
if enc.tokens.depth() > startDetectingCyclesAfter {
|
||
|
v := reflect.ValueOf(obj)
|
||
|
if err := enc.seenPointers.visit(v); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer enc.seenPointers.leave(v)
|
||
|
}
|
||
|
|
||
|
// Optimize for marshaling an empty map without any preceding whitespace.
|
||
|
if len(obj) == 0 && !enc.options.multiline && !enc.tokens.last.needObjectName() {
|
||
|
enc.buf = enc.tokens.mayAppendDelim(enc.buf, '{')
|
||
|
enc.buf = append(enc.buf, "{}"...)
|
||
|
enc.tokens.last.increment()
|
||
|
if enc.needFlush() {
|
||
|
return enc.flush()
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if err := enc.WriteToken(ObjectStart); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
// A Go map guarantees that each entry has a unique key
|
||
|
// The only possibility of duplicates is due to invalid UTF-8.
|
||
|
if !enc.options.AllowInvalidUTF8 {
|
||
|
enc.tokens.last.disableNamespace()
|
||
|
}
|
||
|
if !mo.Deterministic || len(obj) <= 1 {
|
||
|
for name, val := range obj {
|
||
|
if err := enc.WriteToken(String(name)); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err := marshalValueAny(mo, enc, val); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
names := getStrings(len(obj))
|
||
|
var i int
|
||
|
for name := range obj {
|
||
|
(*names)[i] = name
|
||
|
i++
|
||
|
}
|
||
|
names.Sort()
|
||
|
for _, name := range *names {
|
||
|
if err := enc.WriteToken(String(name)); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err := marshalValueAny(mo, enc, obj[name]); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
putStrings(names)
|
||
|
}
|
||
|
if err := enc.WriteToken(ObjectEnd); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func unmarshalObjectAny(uo UnmarshalOptions, dec *Decoder) (map[string]any, error) {
|
||
|
tok, err := dec.ReadToken()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
k := tok.Kind()
|
||
|
switch k {
|
||
|
case 'n':
|
||
|
return nil, nil
|
||
|
case '{':
|
||
|
obj := 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 !dec.options.AllowInvalidUTF8 {
|
||
|
dec.tokens.last.disableNamespace()
|
||
|
}
|
||
|
for dec.PeekKind() != '}' {
|
||
|
tok, err := dec.ReadToken()
|
||
|
if err != nil {
|
||
|
return obj, err
|
||
|
}
|
||
|
name := tok.String()
|
||
|
|
||
|
// Manually check for duplicate names.
|
||
|
if _, ok := obj[name]; ok {
|
||
|
name := dec.previousBuffer()
|
||
|
err := &SyntacticError{str: "duplicate name " + string(name) + " in object"}
|
||
|
return obj, err.withOffset(dec.InputOffset() - int64(len(name)))
|
||
|
}
|
||
|
|
||
|
val, err := unmarshalValueAny(uo, dec)
|
||
|
obj[name] = val
|
||
|
if err != nil {
|
||
|
return obj, err
|
||
|
}
|
||
|
}
|
||
|
if _, err := dec.ReadToken(); err != nil {
|
||
|
return obj, err
|
||
|
}
|
||
|
return obj, nil
|
||
|
}
|
||
|
return nil, &SemanticError{action: "unmarshal", JSONKind: k, GoType: mapStringAnyType}
|
||
|
}
|
||
|
|
||
|
func marshalArrayAny(mo MarshalOptions, enc *Encoder, arr []any) error {
|
||
|
// Check for cycles.
|
||
|
if enc.tokens.depth() > startDetectingCyclesAfter {
|
||
|
v := reflect.ValueOf(arr)
|
||
|
if err := enc.seenPointers.visit(v); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer enc.seenPointers.leave(v)
|
||
|
}
|
||
|
|
||
|
// Optimize for marshaling an empty slice without any preceding whitespace.
|
||
|
if len(arr) == 0 && !enc.options.multiline && !enc.tokens.last.needObjectName() {
|
||
|
enc.buf = enc.tokens.mayAppendDelim(enc.buf, '[')
|
||
|
enc.buf = append(enc.buf, "[]"...)
|
||
|
enc.tokens.last.increment()
|
||
|
if enc.needFlush() {
|
||
|
return enc.flush()
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if err := enc.WriteToken(ArrayStart); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
for _, val := range arr {
|
||
|
if err := marshalValueAny(mo, enc, val); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
if err := enc.WriteToken(ArrayEnd); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func unmarshalArrayAny(uo UnmarshalOptions, dec *Decoder) ([]any, error) {
|
||
|
tok, err := dec.ReadToken()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
k := tok.Kind()
|
||
|
switch k {
|
||
|
case 'n':
|
||
|
return nil, nil
|
||
|
case '[':
|
||
|
arr := []any{}
|
||
|
for dec.PeekKind() != ']' {
|
||
|
val, err := unmarshalValueAny(uo, dec)
|
||
|
arr = append(arr, val)
|
||
|
if err != nil {
|
||
|
return arr, err
|
||
|
}
|
||
|
}
|
||
|
if _, err := dec.ReadToken(); err != nil {
|
||
|
return arr, err
|
||
|
}
|
||
|
return arr, nil
|
||
|
}
|
||
|
return nil, &SemanticError{action: "unmarshal", JSONKind: k, GoType: sliceAnyType}
|
||
|
}
|