Update dependencies
This commit is contained in:
48
vendor/github.com/go-json-experiment/json/README.md
generated
vendored
48
vendor/github.com/go-json-experiment/json/README.md
generated
vendored
@@ -12,7 +12,7 @@ with the string "WARNING: " near the top of the commit message.
|
||||
It is your responsibility to inspect the list of commit changes
|
||||
when upgrading the module. Not all breaking changes will lead to build failures.
|
||||
|
||||
A [Discussion about including this package in Go as `encoding/json/v2`](https://github.com/golang/go/discussions/63397) has been started on the Go Github project on 2023-10-05. Please provide your feedback there.
|
||||
A [proposal to include this module in Go as `encoding/json/v2` and `encoding/json/jsontext`](https://github.com/golang/go/issues/71497) has been started on the Go Github project on 2025-01-30. Please provide your feedback there.
|
||||
|
||||
## Goals and objectives
|
||||
|
||||
@@ -24,7 +24,7 @@ in v2 to be named the same and have a mostly compatible signature.
|
||||
Behaviorally, we should aim for 95% to 99% backwards compatibility.
|
||||
We do not aim for 100% compatibility since we want the freedom to break
|
||||
certain behaviors that are now considered to have been a mistake.
|
||||
We may provide options that can bring the v2 implementation to 100% compatibility,
|
||||
Options exist that can bring the v2 implementation to 100% compatibility,
|
||||
but it will not be the default.
|
||||
|
||||
* **More flexible:** There is a
|
||||
@@ -141,30 +141,28 @@ This table shows an overview of the changes:
|
||||
|
||||
| v1 | v2 | Details |
|
||||
| -- | -- | ------- |
|
||||
| JSON object members are unmarshaled into a Go struct using a **case-insensitive name match**. | JSON object members are unmarshaled into a Go struct using a **case-sensitive name match**. | [CaseSensitivity](/diff_test.go#:~:text=TestCaseSensitivity) |
|
||||
| When marshaling a Go struct, a struct field marked as `omitempty` is omitted if **the field value is an empty Go value**, which is defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string. | When marshaling a Go struct, a struct field marked as `omitempty` is omitted if **the field value would encode as an empty JSON value**, which is defined as a JSON null, or an empty JSON string, object, or array. | [OmitEmptyOption](/diff_test.go#:~:text=TestOmitEmptyOption) |
|
||||
| The `string` option **does affect** Go bools. | The `string` option **does not affect** Go bools. | [StringOption](/diff_test.go#:~:text=TestStringOption) |
|
||||
| The `string` option **does not recursively affect** sub-values of the Go field value. | The `string` option **does recursively affect** sub-values of the Go field value. | [StringOption](/diff_test.go#:~:text=TestStringOption) |
|
||||
| The `string` option **sometimes accepts** a JSON null escaped within a JSON string. | The `string` option **never accepts** a JSON null escaped within a JSON string. | [StringOption](/diff_test.go#:~:text=TestStringOption) |
|
||||
| A nil Go slice is marshaled as a **JSON null**. | A nil Go slice is marshaled as an **empty JSON array**. | [NilSlicesAndMaps](/diff_test.go#:~:text=TestNilSlicesAndMaps) |
|
||||
| A nil Go map is marshaled as a **JSON null**. | A nil Go map is marshaled as an **empty JSON object**. | [NilSlicesAndMaps](/diff_test.go#:~:text=TestNilSlicesAndMaps) |
|
||||
| A Go array may be unmarshaled from a **JSON array of any length**. | A Go array must be unmarshaled from a **JSON array of the same length**. | [Arrays](/diff_test.go#:~:text=Arrays) |
|
||||
| A Go byte array is represented as a **JSON array of JSON numbers**. | A Go byte array is represented as a **Base64-encoded JSON string**. | [ByteArrays](/diff_test.go#:~:text=TestByteArrays) |
|
||||
| `MarshalJSON` and `UnmarshalJSON` methods declared on a pointer receiver are **inconsistently called**. | `MarshalJSON` and `UnmarshalJSON` methods declared on a pointer receiver are **consistently called**. | [PointerReceiver](/diff_test.go#:~:text=TestPointerReceiver) |
|
||||
| A Go map is marshaled in a **deterministic order**. | A Go map is marshaled in a **non-deterministic order**. | [MapDeterminism](/diff_test.go#:~:text=TestMapDeterminism) |
|
||||
| JSON strings are encoded **with HTML-specific characters being escaped**. | JSON strings are encoded **without any characters being escaped** (unless necessary). | [EscapeHTML](/diff_test.go#:~:text=TestEscapeHTML) |
|
||||
| When marshaling, invalid UTF-8 within a Go string **are silently replaced**. | When marshaling, invalid UTF-8 within a Go string **results in an error**. | [InvalidUTF8](/diff_test.go#:~:text=TestInvalidUTF8) |
|
||||
| When unmarshaling, invalid UTF-8 within a JSON string **are silently replaced**. | When unmarshaling, invalid UTF-8 within a JSON string **results in an error**. | [InvalidUTF8](/diff_test.go#:~:text=TestInvalidUTF8) |
|
||||
| When marshaling, **an error does not occur** if the output JSON value contains objects with duplicate names. | When marshaling, **an error does occur** if the output JSON value contains objects with duplicate names. | [DuplicateNames](/diff_test.go#:~:text=TestDuplicateNames) |
|
||||
| When unmarshaling, **an error does not occur** if the input JSON value contains objects with duplicate names. | When unmarshaling, **an error does occur** if the input JSON value contains objects with duplicate names. | [DuplicateNames](/diff_test.go#:~:text=TestDuplicateNames) |
|
||||
| Unmarshaling a JSON null into a non-empty Go value **inconsistently clears the value or does nothing**. | Unmarshaling a JSON null into a non-empty Go value **always clears the value**. | [MergeNull](/diff_test.go#:~:text=TestMergeNull) |
|
||||
| Unmarshaling a JSON value into a non-empty Go value **follows inconsistent and bizarre behavior**. | Unmarshaling a JSON value into a non-empty Go value **always merges if the input is an object, and otherwise replaces**. | [MergeComposite](/diff_test.go#:~:text=TestMergeComposite) |
|
||||
| A `time.Duration` is represented as a **JSON number containing the decimal number of nanoseconds**. | A `time.Duration` is represented as a **JSON string containing the formatted duration (e.g., "1h2m3.456s")**. | [TimeDurations](/diff_test.go#:~:text=TestTimeDurations) |
|
||||
| Unmarshaling a JSON number into a Go float beyond its representation **results in an error**. | Unmarshaling a JSON number into a Go float beyond its representation **uses the closest representable value (e.g., ±`math.MaxFloat`)**. | [MaxFloats](/diff_test.go#:~:text=TestMaxFloats) |
|
||||
| A Go struct with only unexported fields **can be serialized**. | A Go struct with only unexported fields **cannot be serialized**. | [EmptyStructs](/diff_test.go#:~:text=TestEmptyStructs) |
|
||||
| A Go struct that embeds an unexported struct type **can sometimes be serialized**. | A Go struct that embeds an unexported struct type **cannot be serialized**. | [EmbedUnexported](/diff_test.go#:~:text=TestEmbedUnexported) |
|
||||
| JSON object members are unmarshaled into a Go struct using a **case-insensitive name match**. | JSON object members are unmarshaled into a Go struct using a **case-sensitive name match**. | [CaseSensitivity](/v1/diff_test.go#:~:text=TestCaseSensitivity) |
|
||||
| When marshaling a Go struct, a struct field marked as `omitempty` is omitted if **the field value is an empty Go value**, which is defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string. | When marshaling a Go struct, a struct field marked as `omitempty` is omitted if **the field value would encode as an empty JSON value**, which is defined as a JSON null, or an empty JSON string, object, or array. | [OmitEmptyOption](/v1/diff_test.go#:~:text=TestOmitEmptyOption) |
|
||||
| The `string` option **does affect** Go strings and bools. | The `string` option **does not affect** Go strings or bools. | [StringOption](/v1/diff_test.go#:~:text=TestStringOption) |
|
||||
| The `string` option **does not recursively affect** sub-values of the Go field value. | The `string` option **does recursively affect** sub-values of the Go field value. | [StringOption](/v1/diff_test.go#:~:text=TestStringOption) |
|
||||
| The `string` option **sometimes accepts** a JSON null escaped within a JSON string. | The `string` option **never accepts** a JSON null escaped within a JSON string. | [StringOption](/v1/diff_test.go#:~:text=TestStringOption) |
|
||||
| A nil Go slice is marshaled as a **JSON null**. | A nil Go slice is marshaled as an **empty JSON array**. | [NilSlicesAndMaps](/v1/diff_test.go#:~:text=TestNilSlicesAndMaps) |
|
||||
| A nil Go map is marshaled as a **JSON null**. | A nil Go map is marshaled as an **empty JSON object**. | [NilSlicesAndMaps](/v1/diff_test.go#:~:text=TestNilSlicesAndMaps) |
|
||||
| A Go array may be unmarshaled from a **JSON array of any length**. | A Go array must be unmarshaled from a **JSON array of the same length**. | [Arrays](/v1/diff_test.go#:~:text=Arrays) |
|
||||
| A Go byte array is represented as a **JSON array of JSON numbers**. | A Go byte array is represented as a **Base64-encoded JSON string**. | [ByteArrays](/v1/diff_test.go#:~:text=TestByteArrays) |
|
||||
| `MarshalJSON` and `UnmarshalJSON` methods declared on a pointer receiver are **inconsistently called**. | `MarshalJSON` and `UnmarshalJSON` methods declared on a pointer receiver are **consistently called**. | [PointerReceiver](/v1/diff_test.go#:~:text=TestPointerReceiver) |
|
||||
| A Go map is marshaled in a **deterministic order**. | A Go map is marshaled in a **non-deterministic order**. | [MapDeterminism](/v1/diff_test.go#:~:text=TestMapDeterminism) |
|
||||
| JSON strings are encoded **with HTML-specific characters being escaped**. | JSON strings are encoded **without any characters being escaped** (unless necessary). | [EscapeHTML](/v1/diff_test.go#:~:text=TestEscapeHTML) |
|
||||
| When marshaling, invalid UTF-8 within a Go string **are silently replaced**. | When marshaling, invalid UTF-8 within a Go string **results in an error**. | [InvalidUTF8](/v1/diff_test.go#:~:text=TestInvalidUTF8) |
|
||||
| When unmarshaling, invalid UTF-8 within a JSON string **are silently replaced**. | When unmarshaling, invalid UTF-8 within a JSON string **results in an error**. | [InvalidUTF8](/v1/diff_test.go#:~:text=TestInvalidUTF8) |
|
||||
| When marshaling, **an error does not occur** if the output JSON value contains objects with duplicate names. | When marshaling, **an error does occur** if the output JSON value contains objects with duplicate names. | [DuplicateNames](/v1/diff_test.go#:~:text=TestDuplicateNames) |
|
||||
| When unmarshaling, **an error does not occur** if the input JSON value contains objects with duplicate names. | When unmarshaling, **an error does occur** if the input JSON value contains objects with duplicate names. | [DuplicateNames](/v1/diff_test.go#:~:text=TestDuplicateNames) |
|
||||
| Unmarshaling a JSON null into a non-empty Go value **inconsistently clears the value or does nothing**. | Unmarshaling a JSON null into a non-empty Go value **always clears the value**. | [MergeNull](/v1/diff_test.go#:~:text=TestMergeNull) |
|
||||
| Unmarshaling a JSON value into a non-empty Go value **follows inconsistent and bizarre behavior**. | Unmarshaling a JSON value into a non-empty Go value **always merges if the input is an object, and otherwise replaces**. | [MergeComposite](/v1/diff_test.go#:~:text=TestMergeComposite) |
|
||||
| A `time.Duration` is represented as a **JSON number containing the decimal number of nanoseconds**. | A `time.Duration` is represented as a **JSON string containing the formatted duration (e.g., "1h2m3.456s")**. | [TimeDurations](/v1/diff_test.go#:~:text=TestTimeDurations) |
|
||||
| A Go struct with only unexported fields **can be serialized**. | A Go struct with only unexported fields **cannot be serialized**. | [EmptyStructs](/v1/diff_test.go#:~:text=TestEmptyStructs) |
|
||||
|
||||
See [diff_test.go](/diff_test.go) for details about every change.
|
||||
See [diff_test.go](/v1/diff_test.go) for details about every change.
|
||||
|
||||
## Performance
|
||||
|
||||
|
||||
183
vendor/github.com/go-json-experiment/json/arshal.go
generated
vendored
183
vendor/github.com/go-json-experiment/json/arshal.go
generated
vendored
@@ -6,32 +6,33 @@ package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"encoding"
|
||||
"io"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-json-experiment/json/internal"
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonopts"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
)
|
||||
|
||||
// Reference encoding and time packages to assist pkgsite
|
||||
// in being able to hotlink references to those packages.
|
||||
var (
|
||||
_ encoding.TextMarshaler
|
||||
_ encoding.TextAppender
|
||||
_ encoding.TextUnmarshaler
|
||||
_ time.Time
|
||||
_ time.Duration
|
||||
)
|
||||
|
||||
// export exposes internal functionality of the "jsontext" package.
|
||||
var export = jsontext.Internal.Export(&internal.AllowInternalUse)
|
||||
|
||||
var structOptionsPool = &sync.Pool{New: func() any { return new(jsonopts.Struct) }}
|
||||
|
||||
func getStructOptions() *jsonopts.Struct {
|
||||
return structOptionsPool.Get().(*jsonopts.Struct)
|
||||
}
|
||||
func putStructOptions(o *jsonopts.Struct) {
|
||||
*o = jsonopts.Struct{}
|
||||
structOptionsPool.Put(o)
|
||||
}
|
||||
|
||||
// Marshal serializes a Go value as a []byte according to the provided
|
||||
// marshal and encode options (while ignoring unmarshal or decode options).
|
||||
// It does not terminate the output with a newline.
|
||||
@@ -52,12 +53,16 @@ func putStructOptions(o *jsonopts.Struct) {
|
||||
// If all applicable functions return [SkipFunc],
|
||||
// then the value is encoded according to subsequent rules.
|
||||
//
|
||||
// - If the value type implements [MarshalerV2],
|
||||
// then the MarshalJSONV2 method is called to encode the value.
|
||||
// - If the value type implements [MarshalerTo],
|
||||
// then the MarshalJSONTo method is called to encode the value.
|
||||
//
|
||||
// - If the value type implements [MarshalerV1],
|
||||
// - If the value type implements [Marshaler],
|
||||
// then the MarshalJSON method is called to encode the value.
|
||||
//
|
||||
// - If the value type implements [encoding.TextAppender],
|
||||
// then the AppendText method is called to encode the value and
|
||||
// subsequently encode its result as a JSON string.
|
||||
//
|
||||
// - If the value type implements [encoding.TextMarshaler],
|
||||
// then the MarshalText method is called to encode the value and
|
||||
// subsequently encode its result as a JSON string.
|
||||
@@ -90,11 +95,12 @@ func putStructOptions(o *jsonopts.Struct) {
|
||||
// where each byte is recursively JSON-encoded as each JSON array element.
|
||||
//
|
||||
// - A Go integer is encoded as a JSON number without fractions or exponents.
|
||||
// If [StringifyNumbers] is specified, then the JSON number is
|
||||
// encoded within a JSON string. It does not support any custom format flags.
|
||||
// If [StringifyNumbers] is specified or encoding a JSON object name,
|
||||
// then the JSON number is encoded within a JSON string.
|
||||
// It does not support any custom format flags.
|
||||
//
|
||||
// - A Go float is encoded as a JSON number.
|
||||
// If [StringifyNumbers] is specified,
|
||||
// If [StringifyNumbers] is specified or encoding a JSON object name,
|
||||
// then the JSON number is encoded within a JSON string.
|
||||
// If the format is "nonfinite", then NaN, +Inf, and -Inf are encoded as
|
||||
// the JSON strings "NaN", "Infinity", and "-Infinity", respectively.
|
||||
@@ -103,10 +109,8 @@ func putStructOptions(o *jsonopts.Struct) {
|
||||
// - A Go map is encoded as a JSON object, where each Go map key and value
|
||||
// is recursively encoded as a name and value pair in the JSON object.
|
||||
// The Go map key must encode as a JSON string, otherwise this results
|
||||
// in a [SemanticError]. When encoding keys, [StringifyNumbers]
|
||||
// is automatically applied so that numeric keys encode as JSON strings.
|
||||
// The Go map is traversed in a non-deterministic order.
|
||||
// For deterministic encoding, consider using [jsontext.Value.Canonicalize].
|
||||
// in a [SemanticError]. The Go map is traversed in a non-deterministic order.
|
||||
// For deterministic encoding, consider using the [Deterministic] option.
|
||||
// If the format is "emitnull", then a nil map is encoded as a JSON null.
|
||||
// If the format is "emitempty", then a nil map is encoded as an empty JSON object,
|
||||
// regardless of whether [FormatNilMapAsNull] is specified.
|
||||
@@ -151,8 +155,6 @@ func putStructOptions(o *jsonopts.Struct) {
|
||||
// If the format is "sec", "milli", "micro", or "nano",
|
||||
// then the duration is encoded as a JSON number of the number of seconds
|
||||
// (or milliseconds, microseconds, or nanoseconds) in the duration.
|
||||
// If the format is "base60", it is encoded as a JSON string
|
||||
// using the "H:MM:SS.SSSSSSSSS" representation.
|
||||
// If the format is "units", it uses [time.Duration.String].
|
||||
//
|
||||
// - All other Go types (e.g., complex numbers, channels, and functions)
|
||||
@@ -166,6 +168,9 @@ func Marshal(in any, opts ...Options) (out []byte, err error) {
|
||||
xe := export.Encoder(enc)
|
||||
xe.Flags.Set(jsonflags.OmitTopLevelNewline | 1)
|
||||
err = marshalEncode(enc, in, &xe.Struct)
|
||||
if err != nil && xe.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return nil, internal.TransformMarshalError(in, err)
|
||||
}
|
||||
return bytes.Clone(xe.Buf), err
|
||||
}
|
||||
|
||||
@@ -178,21 +183,33 @@ func MarshalWrite(out io.Writer, in any, opts ...Options) (err error) {
|
||||
defer export.PutStreamingEncoder(enc)
|
||||
xe := export.Encoder(enc)
|
||||
xe.Flags.Set(jsonflags.OmitTopLevelNewline | 1)
|
||||
return marshalEncode(enc, in, &xe.Struct)
|
||||
err = marshalEncode(enc, in, &xe.Struct)
|
||||
if err != nil && xe.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.TransformMarshalError(in, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalEncode serializes a Go value into an [jsontext.Encoder] according to
|
||||
// the provided marshal options (while ignoring unmarshal, encode, or decode options).
|
||||
// Any marshal-relevant options already specified on the [jsontext.Encoder]
|
||||
// take lower precedence than the set of options provided by the caller.
|
||||
// Unlike [Marshal] and [MarshalWrite], encode options are ignored because
|
||||
// they must have already been specified on the provided [jsontext.Encoder].
|
||||
//
|
||||
// See [Marshal] for details about the conversion of a Go value into JSON.
|
||||
func MarshalEncode(out *jsontext.Encoder, in any, opts ...Options) (err error) {
|
||||
mo := getStructOptions()
|
||||
defer putStructOptions(mo)
|
||||
mo.Join(opts...)
|
||||
xe := export.Encoder(out)
|
||||
mo.CopyCoderOptions(&xe.Struct)
|
||||
return marshalEncode(out, in, mo)
|
||||
if len(opts) > 0 {
|
||||
optsOriginal := xe.Struct
|
||||
defer func() { xe.Struct = optsOriginal }()
|
||||
xe.Struct.JoinWithoutCoderOptions(opts...)
|
||||
}
|
||||
err = marshalEncode(out, in, &xe.Struct)
|
||||
if err != nil && xe.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.TransformMarshalError(in, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func marshalEncode(out *jsontext.Encoder, in any, mo *jsonopts.Struct) (err error) {
|
||||
@@ -202,12 +219,13 @@ func marshalEncode(out *jsontext.Encoder, in any, mo *jsonopts.Struct) (err erro
|
||||
}
|
||||
// Shallow copy non-pointer values to obtain an addressable value.
|
||||
// It is beneficial to performance to always pass pointers to avoid this.
|
||||
if v.Kind() != reflect.Pointer {
|
||||
forceAddr := v.Kind() != reflect.Pointer
|
||||
if forceAddr {
|
||||
v2 := reflect.New(v.Type())
|
||||
v2.Elem().Set(v)
|
||||
v = v2
|
||||
}
|
||||
va := addressableValue{v.Elem()} // dereferenced pointer is always addressable
|
||||
va := addressableValue{v.Elem(), forceAddr} // dereferenced pointer is always addressable
|
||||
t := va.Type()
|
||||
|
||||
// Lookup and call the marshal function for this type.
|
||||
@@ -216,9 +234,8 @@ func marshalEncode(out *jsontext.Encoder, in any, mo *jsonopts.Struct) (err erro
|
||||
marshal, _ = mo.Marshalers.(*Marshalers).lookup(marshal, t)
|
||||
}
|
||||
if err := marshal(out, va, mo); err != nil {
|
||||
xe := export.Encoder(out)
|
||||
if !xe.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
xe.Tokens.InvalidateDisabledNamespaces()
|
||||
if !mo.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
export.Encoder(out).Tokens.InvalidateDisabledNamespaces()
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -245,10 +262,10 @@ func marshalEncode(out *jsontext.Encoder, in any, mo *jsonopts.Struct) (err erro
|
||||
// value. If all applicable functions return [SkipFunc],
|
||||
// then the input is decoded according to subsequent rules.
|
||||
//
|
||||
// - If the value type implements [UnmarshalerV2],
|
||||
// then the UnmarshalJSONV2 method is called to decode the JSON value.
|
||||
// - If the value type implements [UnmarshalerFrom],
|
||||
// then the UnmarshalJSONFrom method is called to decode the JSON value.
|
||||
//
|
||||
// - If the value type implements [UnmarshalerV1],
|
||||
// - If the value type implements [Unmarshaler],
|
||||
// then the UnmarshalJSON method is called to decode the JSON value.
|
||||
//
|
||||
// - If the value type implements [encoding.TextUnmarshaler],
|
||||
@@ -293,26 +310,24 @@ func marshalEncode(out *jsontext.Encoder, in any, mo *jsonopts.Struct) (err erro
|
||||
// otherwise it fails with a [SemanticError].
|
||||
//
|
||||
// - A Go integer is decoded from a JSON number.
|
||||
// It may also be decoded from a JSON string containing a JSON number
|
||||
// if [StringifyNumbers] is specified.
|
||||
// It must be decoded from a JSON string containing a JSON number
|
||||
// if [StringifyNumbers] is specified or decoding a JSON object name.
|
||||
// It fails with a [SemanticError] if the JSON number
|
||||
// has a fractional or exponent component.
|
||||
// It also fails if it overflows the representation of the Go integer type.
|
||||
// It does not support any custom format flags.
|
||||
//
|
||||
// - A Go float is decoded from a JSON number.
|
||||
// It may also be decoded from a JSON string containing a JSON number
|
||||
// if [StringifyNumbers] is specified.
|
||||
// The JSON number is parsed as the closest representable Go float value.
|
||||
// It must be decoded from a JSON string containing a JSON number
|
||||
// if [StringifyNumbers] is specified or decoding a JSON object name.
|
||||
// It fails if it overflows the representation of the Go float type.
|
||||
// If the format is "nonfinite", then the JSON strings
|
||||
// "NaN", "Infinity", and "-Infinity" are decoded as NaN, +Inf, and -Inf.
|
||||
// Otherwise, the presence of such strings results in a [SemanticError].
|
||||
//
|
||||
// - A Go map is decoded from a JSON object,
|
||||
// where each JSON object name and value pair is recursively decoded
|
||||
// as the Go map key and value. When decoding keys,
|
||||
// [StringifyNumbers] is automatically applied so that
|
||||
// numeric keys can decode from JSON strings. Maps are not cleared.
|
||||
// as the Go map key and value. Maps are not cleared.
|
||||
// If the Go map is nil, then a new map is allocated to decode into.
|
||||
// If the decoded key matches an existing Go map entry, the entry value
|
||||
// is reused by decoding the JSON object value into it.
|
||||
@@ -368,8 +383,6 @@ func marshalEncode(out *jsontext.Encoder, in any, mo *jsonopts.Struct) (err erro
|
||||
// If the format is "sec", "milli", "micro", or "nano",
|
||||
// then the duration is decoded from a JSON number of the number of seconds
|
||||
// (or milliseconds, microseconds, or nanoseconds) in the duration.
|
||||
// If the format is "base60", it is decoded from a JSON string
|
||||
// using the "H:MM:SS.SSSSSSSSS" representation.
|
||||
// If the format is "units", it uses [time.ParseDuration].
|
||||
//
|
||||
// - All other Go types (e.g., complex numbers, channels, and functions)
|
||||
@@ -384,7 +397,11 @@ func Unmarshal(in []byte, out any, opts ...Options) (err error) {
|
||||
dec := export.GetBufferedDecoder(in, opts...)
|
||||
defer export.PutBufferedDecoder(dec)
|
||||
xd := export.Decoder(dec)
|
||||
return unmarshalFull(dec, out, &xd.Struct)
|
||||
err = unmarshalFull(dec, out, &xd.Struct)
|
||||
if err != nil && xd.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.TransformUnmarshalError(out, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalRead deserializes a Go value from an [io.Reader] according to the
|
||||
@@ -397,7 +414,11 @@ func UnmarshalRead(in io.Reader, out any, opts ...Options) (err error) {
|
||||
dec := export.GetStreamingDecoder(in, opts...)
|
||||
defer export.PutStreamingDecoder(dec)
|
||||
xd := export.Decoder(dec)
|
||||
return unmarshalFull(dec, out, &xd.Struct)
|
||||
err = unmarshalFull(dec, out, &xd.Struct)
|
||||
if err != nil && xd.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.TransformUnmarshalError(out, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func unmarshalFull(in *jsontext.Decoder, out any, uo *jsonopts.Struct) error {
|
||||
@@ -413,46 +434,53 @@ func unmarshalFull(in *jsontext.Decoder, out any, uo *jsonopts.Struct) error {
|
||||
|
||||
// UnmarshalDecode deserializes a Go value from a [jsontext.Decoder] according to
|
||||
// the provided unmarshal options (while ignoring marshal, encode, or decode options).
|
||||
// Any unmarshal options already specified on the [jsontext.Decoder]
|
||||
// take lower precedence than the set of options provided by the caller.
|
||||
// Unlike [Unmarshal] and [UnmarshalRead], decode options are ignored because
|
||||
// they must have already been specified on the provided [jsontext.Decoder].
|
||||
//
|
||||
// The input may be a stream of one or more JSON values,
|
||||
// where this only unmarshals the next JSON value in the stream.
|
||||
// The output must be a non-nil pointer.
|
||||
// See [Unmarshal] for details about the conversion of JSON into a Go value.
|
||||
func UnmarshalDecode(in *jsontext.Decoder, out any, opts ...Options) (err error) {
|
||||
uo := getStructOptions()
|
||||
defer putStructOptions(uo)
|
||||
uo.Join(opts...)
|
||||
xd := export.Decoder(in)
|
||||
uo.CopyCoderOptions(&xd.Struct)
|
||||
return unmarshalDecode(in, out, uo)
|
||||
if len(opts) > 0 {
|
||||
optsOriginal := xd.Struct
|
||||
defer func() { xd.Struct = optsOriginal }()
|
||||
xd.Struct.JoinWithoutCoderOptions(opts...)
|
||||
}
|
||||
err = unmarshalDecode(in, out, &xd.Struct)
|
||||
if err != nil && xd.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.TransformUnmarshalError(out, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct) (err error) {
|
||||
v := reflect.ValueOf(out)
|
||||
if !v.IsValid() || v.Kind() != reflect.Pointer || v.IsNil() {
|
||||
var t reflect.Type
|
||||
if v.IsValid() {
|
||||
t = v.Type()
|
||||
if t.Kind() == reflect.Pointer {
|
||||
t = t.Elem()
|
||||
}
|
||||
}
|
||||
err := errors.New("value must be passed as a non-nil pointer reference")
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: err}
|
||||
if v.Kind() != reflect.Pointer || v.IsNil() {
|
||||
return &SemanticError{action: "unmarshal", GoType: reflect.TypeOf(out), Err: internal.ErrNonNilReference}
|
||||
}
|
||||
va := addressableValue{v.Elem()} // dereferenced pointer is always addressable
|
||||
va := addressableValue{v.Elem(), false} // dereferenced pointer is always addressable
|
||||
t := va.Type()
|
||||
|
||||
// In legacy semantics, the entirety of the next JSON value
|
||||
// was validated before attempting to unmarshal it.
|
||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
if err := export.Decoder(in).CheckNextValue(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup and call the unmarshal function for this type.
|
||||
unmarshal := lookupArshaler(t).unmarshal
|
||||
if uo.Unmarshalers != nil {
|
||||
unmarshal, _ = uo.Unmarshalers.(*Unmarshalers).lookup(unmarshal, t)
|
||||
}
|
||||
if err := unmarshal(in, va, uo); err != nil {
|
||||
xd := export.Decoder(in)
|
||||
if !xd.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
xd.Tokens.InvalidateDisabledNamespaces()
|
||||
if !uo.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
export.Decoder(in).Tokens.InvalidateDisabledNamespaces()
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -465,13 +493,23 @@ func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct) (err er
|
||||
// There is no compile magic that enforces this property,
|
||||
// but rather the need to construct this type makes it easier to examine each
|
||||
// construction site to ensure that this property is upheld.
|
||||
type addressableValue struct{ reflect.Value }
|
||||
type addressableValue struct {
|
||||
reflect.Value
|
||||
|
||||
// forcedAddr reports whether this value is addressable
|
||||
// only through the use of [newAddressableValue].
|
||||
// This is only used for [jsonflags.CallMethodsWithLegacySemantics].
|
||||
forcedAddr bool
|
||||
}
|
||||
|
||||
// newAddressableValue constructs a new addressable value of type t.
|
||||
func newAddressableValue(t reflect.Type) addressableValue {
|
||||
return addressableValue{reflect.New(t).Elem()}
|
||||
return addressableValue{reflect.New(t).Elem(), true}
|
||||
}
|
||||
|
||||
// TODO: Remove *jsonopts.Struct argument from [marshaler] and [unmarshaler].
|
||||
// This can be directly accessed on the encoder or decoder.
|
||||
|
||||
// All marshal and unmarshal behavior is implemented using these signatures.
|
||||
// The *jsonopts.Struct argument is guaranteed to identical to or at least
|
||||
// a strict super-set of the options in Encoder.Struct or Decoder.Struct.
|
||||
@@ -525,7 +563,6 @@ func putStrings(s *stringSlice) {
|
||||
stringsPools.Put(s)
|
||||
}
|
||||
|
||||
// Sort sorts the string slice according to RFC 8785, section 3.2.3.
|
||||
func (ss *stringSlice) Sort() {
|
||||
slices.SortFunc(*ss, func(x, y string) int { return jsonwire.CompareUTF16(x, y) })
|
||||
slices.SortFunc(*ss, func(x, y string) int { return strings.Compare(x, y) })
|
||||
}
|
||||
|
||||
148
vendor/github.com/go-json-experiment/json/arshal_any.go
generated
vendored
148
vendor/github.com/go-json-experiment/json/arshal_any.go
generated
vendored
@@ -5,9 +5,11 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-json-experiment/json/internal"
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonopts"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
@@ -19,6 +21,9 @@ import (
|
||||
// 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(enc *jsontext.Encoder, val any, mo *jsonopts.Struct) error {
|
||||
switch val := val.(type) {
|
||||
case nil:
|
||||
@@ -44,6 +49,10 @@ func marshalValueAny(enc *jsontext.Encoder, val any, mo *jsonopts.Struct) error
|
||||
}
|
||||
}
|
||||
|
||||
// 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(dec *jsontext.Decoder, uo *jsonopts.Struct) (any, error) {
|
||||
switch k := dec.PeekKind(); k {
|
||||
case '{':
|
||||
@@ -71,9 +80,12 @@ func unmarshalValueAny(dec *jsontext.Decoder, uo *jsonopts.Struct) (any, error)
|
||||
}
|
||||
return makeString(xd.StringCache, val), nil
|
||||
case '0':
|
||||
if uo.Flags.Get(jsonflags.UnmarshalAnyWithRawNumber) {
|
||||
return internal.RawNumberOf(val), nil
|
||||
}
|
||||
fv, ok := jsonwire.ParseFloat(val, 64)
|
||||
if !ok && uo.Flags.Get(jsonflags.RejectFloatOverflow) {
|
||||
return nil, &SemanticError{action: "unmarshal", JSONKind: k, GoType: float64Type, Err: strconv.ErrRange}
|
||||
if !ok {
|
||||
return fv, newUnmarshalErrorAfterWithValue(dec, float64Type, strconv.ErrRange)
|
||||
}
|
||||
return fv, nil
|
||||
default:
|
||||
@@ -82,13 +94,15 @@ func unmarshalValueAny(dec *jsontext.Decoder, uo *jsonopts.Struct) (any, error)
|
||||
}
|
||||
}
|
||||
|
||||
// marshalObjectAny marshals a Go map[string]any as a JSON object
|
||||
// (or as a JSON null if nil and [jsonflags.FormatNilMapAsNull]).
|
||||
func marshalObjectAny(enc *jsontext.Encoder, obj map[string]any, mo *jsonopts.Struct) error {
|
||||
// Check for cycles.
|
||||
xe := export.Encoder(enc)
|
||||
if xe.Tokens.Depth() > startDetectingCyclesAfter {
|
||||
v := reflect.ValueOf(obj)
|
||||
if err := visitPointer(&xe.SeenPointers, v); err != nil {
|
||||
return err
|
||||
return newMarshalErrorBefore(enc, anyType, err)
|
||||
}
|
||||
defer leavePointer(&xe.SeenPointers, v)
|
||||
}
|
||||
@@ -99,7 +113,7 @@ func marshalObjectAny(enc *jsontext.Encoder, obj map[string]any, mo *jsonopts.St
|
||||
return enc.WriteToken(jsontext.Null)
|
||||
}
|
||||
// Optimize for marshaling an empty map without any preceding whitespace.
|
||||
if !xe.Flags.Get(jsonflags.Expand) && !xe.Tokens.Last.NeedObjectName() {
|
||||
if !mo.Flags.Get(jsonflags.AnyWhitespace) && !xe.Tokens.Last.NeedObjectName() {
|
||||
xe.Buf = append(xe.Tokens.MayAppendDelim(xe.Buf, '{'), "{}"...)
|
||||
xe.Tokens.Last.Increment()
|
||||
if xe.NeedFlush() {
|
||||
@@ -109,12 +123,12 @@ func marshalObjectAny(enc *jsontext.Encoder, obj map[string]any, mo *jsonopts.St
|
||||
}
|
||||
}
|
||||
|
||||
if err := enc.WriteToken(jsontext.ObjectStart); err != nil {
|
||||
if err := enc.WriteToken(jsontext.BeginObject); 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 !xe.Flags.Get(jsonflags.AllowInvalidUTF8) {
|
||||
if !mo.Flags.Get(jsonflags.AllowInvalidUTF8) {
|
||||
xe.Tokens.Last.DisableNamespace()
|
||||
}
|
||||
if !mo.Flags.Get(jsonflags.Deterministic) || len(obj) <= 1 {
|
||||
@@ -144,64 +158,67 @@ func marshalObjectAny(enc *jsontext.Encoder, obj map[string]any, mo *jsonopts.St
|
||||
}
|
||||
putStrings(names)
|
||||
}
|
||||
if err := enc.WriteToken(jsontext.ObjectEnd); err != nil {
|
||||
if err := enc.WriteToken(jsontext.EndObject); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// unmarshalObjectAny unmarshals a JSON object as a Go map[string]any.
|
||||
// It panics if not decoding a JSON object.
|
||||
func unmarshalObjectAny(dec *jsontext.Decoder, uo *jsonopts.Struct) (map[string]any, error) {
|
||||
tok, err := dec.ReadToken()
|
||||
if err != nil {
|
||||
switch tok, err := dec.ReadToken(); {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case tok.Kind() != '{':
|
||||
panic("BUG: invalid kind: " + tok.Kind().String())
|
||||
}
|
||||
k := tok.Kind()
|
||||
switch k {
|
||||
case 'n':
|
||||
return nil, nil
|
||||
case '{':
|
||||
xd := export.Decoder(dec)
|
||||
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 !xd.Flags.Get(jsonflags.AllowInvalidUTF8) {
|
||||
xd.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 := xd.PreviousBuffer()
|
||||
err := export.NewDuplicateNameError(name, dec.InputOffset()-len64(name))
|
||||
return obj, err
|
||||
}
|
||||
|
||||
val, err := unmarshalValueAny(dec, uo)
|
||||
obj[name] = val
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
}
|
||||
if _, err := dec.ReadToken(); err != nil {
|
||||
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 !uo.Flags.Get(jsonflags.AllowInvalidUTF8) {
|
||||
export.Decoder(dec).Tokens.Last.DisableNamespace()
|
||||
}
|
||||
var errUnmarshal error
|
||||
for dec.PeekKind() != '}' {
|
||||
tok, err := dec.ReadToken()
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
return obj, nil
|
||||
name := tok.String()
|
||||
|
||||
// Manually check for duplicate names.
|
||||
if _, ok := obj[name]; ok {
|
||||
// TODO: Unread the object name.
|
||||
name := export.Decoder(dec).PreviousTokenOrValue()
|
||||
err := newDuplicateNameError(dec.StackPointer(), nil, dec.InputOffset()-len64(name))
|
||||
return obj, err
|
||||
}
|
||||
|
||||
val, err := unmarshalValueAny(dec, uo)
|
||||
obj[name] = val
|
||||
if err != nil {
|
||||
if isFatalError(err, uo.Flags) {
|
||||
return obj, err
|
||||
}
|
||||
errUnmarshal = cmp.Or(err, errUnmarshal)
|
||||
}
|
||||
}
|
||||
return nil, &SemanticError{action: "unmarshal", JSONKind: k, GoType: mapStringAnyType}
|
||||
if _, err := dec.ReadToken(); err != nil {
|
||||
return obj, err
|
||||
}
|
||||
return obj, errUnmarshal
|
||||
}
|
||||
|
||||
// marshalArrayAny marshals a Go []any as a JSON array
|
||||
// (or as a JSON null if nil and [jsonflags.FormatNilSliceAsNull]).
|
||||
func marshalArrayAny(enc *jsontext.Encoder, arr []any, mo *jsonopts.Struct) error {
|
||||
// Check for cycles.
|
||||
xe := export.Encoder(enc)
|
||||
if xe.Tokens.Depth() > startDetectingCyclesAfter {
|
||||
v := reflect.ValueOf(arr)
|
||||
if err := visitPointer(&xe.SeenPointers, v); err != nil {
|
||||
return err
|
||||
return newMarshalErrorBefore(enc, sliceAnyType, err)
|
||||
}
|
||||
defer leavePointer(&xe.SeenPointers, v)
|
||||
}
|
||||
@@ -212,7 +229,7 @@ func marshalArrayAny(enc *jsontext.Encoder, arr []any, mo *jsonopts.Struct) erro
|
||||
return enc.WriteToken(jsontext.Null)
|
||||
}
|
||||
// Optimize for marshaling an empty slice without any preceding whitespace.
|
||||
if !xe.Flags.Get(jsonflags.Expand) && !xe.Tokens.Last.NeedObjectName() {
|
||||
if !mo.Flags.Get(jsonflags.AnyWhitespace) && !xe.Tokens.Last.NeedObjectName() {
|
||||
xe.Buf = append(xe.Tokens.MayAppendDelim(xe.Buf, '['), "[]"...)
|
||||
xe.Tokens.Last.Increment()
|
||||
if xe.NeedFlush() {
|
||||
@@ -222,7 +239,7 @@ func marshalArrayAny(enc *jsontext.Encoder, arr []any, mo *jsonopts.Struct) erro
|
||||
}
|
||||
}
|
||||
|
||||
if err := enc.WriteToken(jsontext.ArrayStart); err != nil {
|
||||
if err := enc.WriteToken(jsontext.BeginArray); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, val := range arr {
|
||||
@@ -230,34 +247,35 @@ func marshalArrayAny(enc *jsontext.Encoder, arr []any, mo *jsonopts.Struct) erro
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := enc.WriteToken(jsontext.ArrayEnd); err != nil {
|
||||
if err := enc.WriteToken(jsontext.EndArray); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// unmarshalArrayAny unmarshals a JSON array as a Go []any.
|
||||
// It panics if not decoding a JSON array.
|
||||
func unmarshalArrayAny(dec *jsontext.Decoder, uo *jsonopts.Struct) ([]any, error) {
|
||||
tok, err := dec.ReadToken()
|
||||
if err != nil {
|
||||
switch tok, err := dec.ReadToken(); {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case tok.Kind() != '[':
|
||||
panic("BUG: invalid kind: " + tok.Kind().String())
|
||||
}
|
||||
k := tok.Kind()
|
||||
switch k {
|
||||
case 'n':
|
||||
return nil, nil
|
||||
case '[':
|
||||
arr := []any{}
|
||||
for dec.PeekKind() != ']' {
|
||||
val, err := unmarshalValueAny(dec, uo)
|
||||
arr = append(arr, val)
|
||||
if err != nil {
|
||||
arr := []any{}
|
||||
var errUnmarshal error
|
||||
for dec.PeekKind() != ']' {
|
||||
val, err := unmarshalValueAny(dec, uo)
|
||||
arr = append(arr, val)
|
||||
if err != nil {
|
||||
if isFatalError(err, uo.Flags) {
|
||||
return arr, err
|
||||
}
|
||||
errUnmarshal = cmp.Or(errUnmarshal, err)
|
||||
}
|
||||
if _, err := dec.ReadToken(); err != nil {
|
||||
return arr, err
|
||||
}
|
||||
return arr, nil
|
||||
}
|
||||
return nil, &SemanticError{action: "unmarshal", JSONKind: k, GoType: sliceAnyType}
|
||||
if _, err := dec.ReadToken(); err != nil {
|
||||
return arr, err
|
||||
}
|
||||
return arr, errUnmarshal
|
||||
}
|
||||
|
||||
831
vendor/github.com/go-json-experiment/json/arshal_default.go
generated
vendored
831
vendor/github.com/go-json-experiment/json/arshal_default.go
generated
vendored
File diff suppressed because it is too large
Load Diff
110
vendor/github.com/go-json-experiment/json/arshal_funcs.go
generated
vendored
110
vendor/github.com/go-json-experiment/json/arshal_funcs.go
generated
vendored
@@ -10,12 +10,13 @@ import (
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/go-json-experiment/json/internal"
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonopts"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
)
|
||||
|
||||
// SkipFunc may be returned by [MarshalFuncV2] and [UnmarshalFuncV2] functions.
|
||||
// SkipFunc may be returned by [MarshalToFunc] and [UnmarshalFromFunc] functions.
|
||||
//
|
||||
// Any function that returns SkipFunc must not cause observable side effects
|
||||
// on the provided [jsontext.Encoder] or [jsontext.Decoder].
|
||||
@@ -24,6 +25,9 @@ import (
|
||||
// [jsontext.Encoder.WriteToken] since such methods mutate the state.
|
||||
var SkipFunc = errors.New("json: skip function")
|
||||
|
||||
var errSkipMutation = errors.New("must not read or write any tokens when skipping")
|
||||
var errNonSingularValue = errors.New("must read or write exactly one value")
|
||||
|
||||
// Marshalers is a list of functions that may override the marshal behavior
|
||||
// of specific types. Populate [WithMarshalers] to use it with
|
||||
// [Marshal], [MarshalWrite], or [MarshalEncode].
|
||||
@@ -31,7 +35,7 @@ var SkipFunc = errors.New("json: skip function")
|
||||
// There are no exported fields or methods on Marshalers.
|
||||
type Marshalers = typedMarshalers
|
||||
|
||||
// NewMarshalers constructs a flattened list of marshal functions.
|
||||
// JoinMarshalers constructs a flattened list of marshal functions.
|
||||
// If multiple functions in the list are applicable for a value of a given type,
|
||||
// then those earlier in the list take precedence over those that come later.
|
||||
// If a function returns [SkipFunc], then the next applicable function is called,
|
||||
@@ -39,10 +43,10 @@ type Marshalers = typedMarshalers
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// m1 := NewMarshalers(f1, f2)
|
||||
// m2 := NewMarshalers(f0, m1, f3) // equivalent to m3
|
||||
// m3 := NewMarshalers(f0, f1, f2, f3) // equivalent to m2
|
||||
func NewMarshalers(ms ...*Marshalers) *Marshalers {
|
||||
// m1 := JoinMarshalers(f1, f2)
|
||||
// m2 := JoinMarshalers(f0, m1, f3) // equivalent to m3
|
||||
// m3 := JoinMarshalers(f0, f1, f2, f3) // equivalent to m2
|
||||
func JoinMarshalers(ms ...*Marshalers) *Marshalers {
|
||||
return newMarshalers(ms...)
|
||||
}
|
||||
|
||||
@@ -53,7 +57,7 @@ func NewMarshalers(ms ...*Marshalers) *Marshalers {
|
||||
// There are no exported fields or methods on Unmarshalers.
|
||||
type Unmarshalers = typedUnmarshalers
|
||||
|
||||
// NewUnmarshalers constructs a flattened list of unmarshal functions.
|
||||
// JoinUnmarshalers constructs a flattened list of unmarshal functions.
|
||||
// If multiple functions in the list are applicable for a value of a given type,
|
||||
// then those earlier in the list take precedence over those that come later.
|
||||
// If a function returns [SkipFunc], then the next applicable function is called,
|
||||
@@ -61,10 +65,10 @@ type Unmarshalers = typedUnmarshalers
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// u1 := NewUnmarshalers(f1, f2)
|
||||
// u2 := NewUnmarshalers(f0, u1, f3) // equivalent to u3
|
||||
// u3 := NewUnmarshalers(f0, f1, f2, f3) // equivalent to u2
|
||||
func NewUnmarshalers(us ...*Unmarshalers) *Unmarshalers {
|
||||
// u1 := JoinUnmarshalers(f1, f2)
|
||||
// u2 := JoinUnmarshalers(f0, u1, f3) // equivalent to u3
|
||||
// u3 := JoinUnmarshalers(f0, f1, f2, f3) // equivalent to u2
|
||||
func JoinUnmarshalers(us ...*Unmarshalers) *Unmarshalers {
|
||||
return newUnmarshalers(us...)
|
||||
}
|
||||
|
||||
@@ -156,7 +160,7 @@ func (a *typedArshalers[Coder]) lookup(fnc func(*Coder, addressableValue, *jsono
|
||||
return v.(func(*Coder, addressableValue, *jsonopts.Struct) error), true
|
||||
}
|
||||
|
||||
// MarshalFuncV1 constructs a type-specific marshaler that
|
||||
// MarshalFunc constructs a type-specific marshaler that
|
||||
// specifies how to marshal values of type T.
|
||||
// T can be any type except a named pointer.
|
||||
// The function is always provided with a non-nil pointer value
|
||||
@@ -165,8 +169,8 @@ func (a *typedArshalers[Coder]) lookup(fnc func(*Coder, addressableValue, *jsono
|
||||
// The function must marshal exactly one JSON value.
|
||||
// The value of T must not be retained outside the function call.
|
||||
// It may not return [SkipFunc].
|
||||
func MarshalFuncV1[T any](fn func(T) ([]byte, error)) *Marshalers {
|
||||
t := reflect.TypeOf((*T)(nil)).Elem()
|
||||
func MarshalFunc[T any](fn func(T) ([]byte, error)) *Marshalers {
|
||||
t := reflect.TypeFor[T]()
|
||||
assertCastableTo(t, true)
|
||||
typFnc := typedMarshaler{
|
||||
typ: t,
|
||||
@@ -174,12 +178,20 @@ func MarshalFuncV1[T any](fn func(T) ([]byte, error)) *Marshalers {
|
||||
val, err := fn(va.castTo(t).Interface().(T))
|
||||
if err != nil {
|
||||
err = wrapSkipFunc(err, "marshal function of type func(T) ([]byte, error)")
|
||||
// TODO: Avoid wrapping semantic errors.
|
||||
return &SemanticError{action: "marshal", GoType: t, Err: err}
|
||||
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalFunc") // unlike unmarshal, always wrapped
|
||||
}
|
||||
err = newMarshalErrorBefore(enc, t, err)
|
||||
return collapseSemanticErrors(err)
|
||||
}
|
||||
if err := enc.WriteValue(val); err != nil {
|
||||
// TODO: Avoid wrapping semantic or I/O errors.
|
||||
return &SemanticError{action: "marshal", JSONKind: jsontext.Value(val).Kind(), GoType: t, Err: err}
|
||||
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalFunc") // unlike unmarshal, always wrapped
|
||||
}
|
||||
if isSyntacticError(err) {
|
||||
err = newMarshalErrorBefore(enc, t, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@@ -187,7 +199,7 @@ func MarshalFuncV1[T any](fn func(T) ([]byte, error)) *Marshalers {
|
||||
return &Marshalers{fncVals: []typedMarshaler{typFnc}, fromAny: castableToFromAny(t)}
|
||||
}
|
||||
|
||||
// MarshalFuncV2 constructs a type-specific marshaler that
|
||||
// MarshalToFunc constructs a type-specific marshaler that
|
||||
// specifies how to marshal values of type T.
|
||||
// T can be any type except a named pointer.
|
||||
// The function is always provided with a non-nil pointer value
|
||||
@@ -197,10 +209,10 @@ func MarshalFuncV1[T any](fn func(T) ([]byte, error)) *Marshalers {
|
||||
// on the provided encoder. It may return [SkipFunc] such that marshaling can
|
||||
// move on to the next marshal function. However, no mutable method calls may
|
||||
// be called on the encoder if [SkipFunc] is returned.
|
||||
// The pointer to [jsontext.Encoder], the value of T, and the [Options] value
|
||||
// The pointer to [jsontext.Encoder] and the value of T
|
||||
// must not be retained outside the function call.
|
||||
func MarshalFuncV2[T any](fn func(*jsontext.Encoder, T, Options) error) *Marshalers {
|
||||
t := reflect.TypeOf((*T)(nil)).Elem()
|
||||
func MarshalToFunc[T any](fn func(*jsontext.Encoder, T) error) *Marshalers {
|
||||
t := reflect.TypeFor[T]()
|
||||
assertCastableTo(t, true)
|
||||
typFnc := typedMarshaler{
|
||||
typ: t,
|
||||
@@ -208,21 +220,26 @@ func MarshalFuncV2[T any](fn func(*jsontext.Encoder, T, Options) error) *Marshal
|
||||
xe := export.Encoder(enc)
|
||||
prevDepth, prevLength := xe.Tokens.DepthLength()
|
||||
xe.Flags.Set(jsonflags.WithinArshalCall | 1)
|
||||
err := fn(enc, va.castTo(t).Interface().(T), mo)
|
||||
err := fn(enc, va.castTo(t).Interface().(T))
|
||||
xe.Flags.Set(jsonflags.WithinArshalCall | 0)
|
||||
currDepth, currLength := xe.Tokens.DepthLength()
|
||||
if err == nil && (prevDepth != currDepth || prevLength+1 != currLength) {
|
||||
err = errors.New("must write exactly one JSON value")
|
||||
err = errNonSingularValue
|
||||
}
|
||||
if err != nil {
|
||||
if err == SkipFunc {
|
||||
if prevDepth == currDepth && prevLength == currLength {
|
||||
return SkipFunc
|
||||
}
|
||||
err = errors.New("must not write any JSON tokens when skipping")
|
||||
err = errSkipMutation
|
||||
}
|
||||
// TODO: Avoid wrapping semantic or I/O errors.
|
||||
return &SemanticError{action: "marshal", GoType: t, Err: err}
|
||||
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalToFunc") // unlike unmarshal, always wrapped
|
||||
}
|
||||
if !export.IsIOError(err) {
|
||||
err = newSemanticErrorWithPosition(enc, t, prevDepth, prevLength, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@@ -231,7 +248,7 @@ func MarshalFuncV2[T any](fn func(*jsontext.Encoder, T, Options) error) *Marshal
|
||||
return &Marshalers{fncVals: []typedMarshaler{typFnc}, fromAny: castableToFromAny(t)}
|
||||
}
|
||||
|
||||
// UnmarshalFuncV1 constructs a type-specific unmarshaler that
|
||||
// UnmarshalFunc constructs a type-specific unmarshaler that
|
||||
// specifies how to unmarshal values of type T.
|
||||
// T must be an unnamed pointer or an interface type.
|
||||
// The function is always provided with a non-nil pointer value.
|
||||
@@ -240,8 +257,8 @@ func MarshalFuncV2[T any](fn func(*jsontext.Encoder, T, Options) error) *Marshal
|
||||
// The input []byte must not be mutated.
|
||||
// The input []byte and value T must not be retained outside the function call.
|
||||
// It may not return [SkipFunc].
|
||||
func UnmarshalFuncV1[T any](fn func([]byte, T) error) *Unmarshalers {
|
||||
t := reflect.TypeOf((*T)(nil)).Elem()
|
||||
func UnmarshalFunc[T any](fn func([]byte, T) error) *Unmarshalers {
|
||||
t := reflect.TypeFor[T]()
|
||||
assertCastableTo(t, false)
|
||||
typFnc := typedUnmarshaler{
|
||||
typ: t,
|
||||
@@ -253,8 +270,11 @@ func UnmarshalFuncV1[T any](fn func([]byte, T) error) *Unmarshalers {
|
||||
err = fn(val, va.castTo(t).Interface().(T))
|
||||
if err != nil {
|
||||
err = wrapSkipFunc(err, "unmarshal function of type func([]byte, T) error")
|
||||
// TODO: Avoid wrapping semantic, syntactic, or I/O errors.
|
||||
return &SemanticError{action: "unmarshal", JSONKind: val.Kind(), GoType: t, Err: err}
|
||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return err // unlike marshal, never wrapped
|
||||
}
|
||||
err = newUnmarshalErrorAfter(dec, t, err)
|
||||
return collapseSemanticErrors(err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@@ -262,7 +282,7 @@ func UnmarshalFuncV1[T any](fn func([]byte, T) error) *Unmarshalers {
|
||||
return &Unmarshalers{fncVals: []typedUnmarshaler{typFnc}, fromAny: castableToFromAny(t)}
|
||||
}
|
||||
|
||||
// UnmarshalFuncV2 constructs a type-specific unmarshaler that
|
||||
// UnmarshalFromFunc constructs a type-specific unmarshaler that
|
||||
// specifies how to unmarshal values of type T.
|
||||
// T must be an unnamed pointer or an interface type.
|
||||
// The function is always provided with a non-nil pointer value.
|
||||
@@ -271,10 +291,10 @@ func UnmarshalFuncV1[T any](fn func([]byte, T) error) *Unmarshalers {
|
||||
// on the provided decoder. It may return [SkipFunc] such that unmarshaling can
|
||||
// move on to the next unmarshal function. However, no mutable method calls may
|
||||
// be called on the decoder if [SkipFunc] is returned.
|
||||
// The pointer to [jsontext.Decoder], the value of T, and [Options] value
|
||||
// The pointer to [jsontext.Decoder] and the value of T
|
||||
// must not be retained outside the function call.
|
||||
func UnmarshalFuncV2[T any](fn func(*jsontext.Decoder, T, Options) error) *Unmarshalers {
|
||||
t := reflect.TypeOf((*T)(nil)).Elem()
|
||||
func UnmarshalFromFunc[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers {
|
||||
t := reflect.TypeFor[T]()
|
||||
assertCastableTo(t, false)
|
||||
typFnc := typedUnmarshaler{
|
||||
typ: t,
|
||||
@@ -282,21 +302,29 @@ func UnmarshalFuncV2[T any](fn func(*jsontext.Decoder, T, Options) error) *Unmar
|
||||
xd := export.Decoder(dec)
|
||||
prevDepth, prevLength := xd.Tokens.DepthLength()
|
||||
xd.Flags.Set(jsonflags.WithinArshalCall | 1)
|
||||
err := fn(dec, va.castTo(t).Interface().(T), uo)
|
||||
err := fn(dec, va.castTo(t).Interface().(T))
|
||||
xd.Flags.Set(jsonflags.WithinArshalCall | 0)
|
||||
currDepth, currLength := xd.Tokens.DepthLength()
|
||||
if err == nil && (prevDepth != currDepth || prevLength+1 != currLength) {
|
||||
err = errors.New("must read exactly one JSON value")
|
||||
err = errNonSingularValue
|
||||
}
|
||||
if err != nil {
|
||||
if err == SkipFunc {
|
||||
if prevDepth == currDepth && prevLength == currLength {
|
||||
return SkipFunc
|
||||
}
|
||||
err = errors.New("must not read any JSON tokens when skipping")
|
||||
err = errSkipMutation
|
||||
}
|
||||
// TODO: Avoid wrapping semantic, syntactic, or I/O errors.
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: err}
|
||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
if err2 := xd.SkipUntil(prevDepth, prevLength+1); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err // unlike marshal, never wrapped
|
||||
}
|
||||
if !isSyntacticError(err) && !export.IsIOError(err) {
|
||||
err = newSemanticErrorWithPosition(dec, t, prevDepth, prevLength, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
52
vendor/github.com/go-json-experiment/json/arshal_inlined.go
generated
vendored
52
vendor/github.com/go-json-experiment/json/arshal_inlined.go
generated
vendored
@@ -29,13 +29,15 @@ import (
|
||||
// represent any arbitrary JSON object member. Explicitly named fields take
|
||||
// precedence over the inlined fallback. Only one inlined fallback is allowed.
|
||||
|
||||
var jsontextValueType = reflect.TypeOf((*jsontext.Value)(nil)).Elem()
|
||||
var errRawInlinedNotObject = errors.New("inlined raw value must be a JSON object")
|
||||
|
||||
var jsontextValueType = reflect.TypeFor[jsontext.Value]()
|
||||
|
||||
// marshalInlinedFallbackAll marshals all the members in an inlined fallback.
|
||||
func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct, f *structField, insertUnquotedName func([]byte) bool) error {
|
||||
v := addressableValue{va.Field(f.index[0])} // addressable if struct value is addressable
|
||||
if len(f.index) > 1 {
|
||||
v = v.fieldByIndex(f.index[1:], false)
|
||||
v := addressableValue{va.Field(f.index0), va.forcedAddr} // addressable if struct value is addressable
|
||||
if len(f.index) > 0 {
|
||||
v = v.fieldByIndex(f.index, false)
|
||||
if !v.IsValid() {
|
||||
return nil // implies a nil inlined field
|
||||
}
|
||||
@@ -62,23 +64,22 @@ func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *j
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return &SemanticError{action: "marshal", GoType: jsontextValueType, Err: err}
|
||||
return newMarshalErrorBefore(enc, v.Type(), err)
|
||||
}
|
||||
if tok.Kind() != '{' {
|
||||
err := errors.New("inlined raw value must be a JSON object")
|
||||
return &SemanticError{action: "marshal", JSONKind: tok.Kind(), GoType: jsontextValueType, Err: err}
|
||||
return newMarshalErrorBefore(enc, v.Type(), errRawInlinedNotObject)
|
||||
}
|
||||
for dec.PeekKind() != '}' {
|
||||
// Parse the JSON object name.
|
||||
var flags jsonwire.ValueFlags
|
||||
val, err := xd.ReadValue(&flags)
|
||||
if err != nil {
|
||||
return &SemanticError{action: "marshal", GoType: jsontextValueType, Err: err}
|
||||
return newMarshalErrorBefore(enc, v.Type(), err)
|
||||
}
|
||||
if insertUnquotedName != nil {
|
||||
name := jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
|
||||
if !insertUnquotedName(name) {
|
||||
return export.NewDuplicateNameError(val, 0)
|
||||
return newDuplicateNameError(enc.StackPointer().Parent(), val, enc.OutputOffset())
|
||||
}
|
||||
}
|
||||
if err := enc.WriteValue(val); err != nil {
|
||||
@@ -88,38 +89,37 @@ func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *j
|
||||
// Parse the JSON object value.
|
||||
val, err = xd.ReadValue(&flags)
|
||||
if err != nil {
|
||||
return &SemanticError{action: "marshal", GoType: jsontextValueType, Err: err}
|
||||
return newMarshalErrorBefore(enc, v.Type(), err)
|
||||
}
|
||||
if err := enc.WriteValue(val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := dec.ReadToken(); err != nil {
|
||||
return &SemanticError{action: "marshal", GoType: jsontextValueType, Err: err}
|
||||
return newMarshalErrorBefore(enc, v.Type(), err)
|
||||
}
|
||||
if err := xd.CheckEOF(); err != nil {
|
||||
return &SemanticError{action: "marshal", GoType: jsontextValueType, Err: err}
|
||||
return newMarshalErrorBefore(enc, v.Type(), err)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
m := v // must be a map[string]V
|
||||
m := v // must be a map[~string]V
|
||||
n := m.Len()
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
mk := newAddressableValue(stringType)
|
||||
mk := newAddressableValue(m.Type().Key())
|
||||
mv := newAddressableValue(m.Type().Elem())
|
||||
marshalKey := func(mk addressableValue) error {
|
||||
xe := export.Encoder(enc)
|
||||
b, err := jsonwire.AppendQuote(enc.UnusedBuffer(), mk.String(), &xe.Flags)
|
||||
b, err := jsonwire.AppendQuote(enc.UnusedBuffer(), mk.String(), &mo.Flags)
|
||||
if err != nil {
|
||||
return err
|
||||
return newMarshalErrorBefore(enc, m.Type().Key(), err)
|
||||
}
|
||||
if insertUnquotedName != nil {
|
||||
isVerbatim := bytes.IndexByte(b, '\\') < 0
|
||||
name := jsonwire.UnquoteMayCopy(b, isVerbatim)
|
||||
if !insertUnquotedName(name) {
|
||||
return export.NewDuplicateNameError(b, 0)
|
||||
return newDuplicateNameError(enc.StackPointer().Parent(), b, enc.OutputOffset())
|
||||
}
|
||||
}
|
||||
return enc.WriteValue(b)
|
||||
@@ -165,9 +165,9 @@ func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *j
|
||||
|
||||
// unmarshalInlinedFallbackNext unmarshals only the next member in an inlined fallback.
|
||||
func unmarshalInlinedFallbackNext(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct, f *structField, quotedName, unquotedName []byte) error {
|
||||
v := addressableValue{va.Field(f.index[0])} // addressable if struct value is addressable
|
||||
if len(f.index) > 1 {
|
||||
v = v.fieldByIndex(f.index[1:], true)
|
||||
v := addressableValue{va.Field(f.index0), va.forcedAddr} // addressable if struct value is addressable
|
||||
if len(f.index) > 0 {
|
||||
v = v.fieldByIndex(f.index, true)
|
||||
}
|
||||
v = v.indirect(true)
|
||||
|
||||
@@ -186,8 +186,7 @@ func unmarshalInlinedFallbackNext(dec *jsontext.Decoder, va addressableValue, uo
|
||||
*b = append(*b, ',')
|
||||
}
|
||||
} else {
|
||||
err := errors.New("inlined raw value must be a JSON object")
|
||||
return &SemanticError{action: "unmarshal", GoType: jsontextValueType, Err: err}
|
||||
return newUnmarshalErrorAfterWithSkipping(dec, uo, v.Type(), errRawInlinedNotObject)
|
||||
}
|
||||
}
|
||||
*b = append(*b, quotedName...)
|
||||
@@ -202,12 +201,15 @@ func unmarshalInlinedFallbackNext(dec *jsontext.Decoder, va addressableValue, uo
|
||||
} else {
|
||||
name := string(unquotedName) // TODO: Intern this?
|
||||
|
||||
m := v // must be a map[string]V
|
||||
m := v // must be a map[~string]V
|
||||
if m.IsNil() {
|
||||
m.Set(reflect.MakeMap(m.Type()))
|
||||
}
|
||||
mk := reflect.ValueOf(name)
|
||||
mv := newAddressableValue(v.Type().Elem()) // TODO: Cache across calls?
|
||||
if mkt := m.Type().Key(); mkt != stringType {
|
||||
mk = mk.Convert(mkt)
|
||||
}
|
||||
mv := newAddressableValue(m.Type().Elem()) // TODO: Cache across calls?
|
||||
if v2 := m.MapIndex(mk); v2.IsValid() {
|
||||
mv.Set(v2)
|
||||
}
|
||||
|
||||
360
vendor/github.com/go-json-experiment/json/arshal_methods.go
generated
vendored
360
vendor/github.com/go-json-experiment/json/arshal_methods.go
generated
vendored
@@ -9,62 +9,58 @@ import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-json-experiment/json/internal"
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonopts"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
)
|
||||
|
||||
var errNonStringValue = errors.New("JSON value must be string type")
|
||||
|
||||
// Interfaces for custom serialization.
|
||||
var (
|
||||
jsonMarshalerV1Type = reflect.TypeOf((*MarshalerV1)(nil)).Elem()
|
||||
jsonMarshalerV2Type = reflect.TypeOf((*MarshalerV2)(nil)).Elem()
|
||||
jsonUnmarshalerV1Type = reflect.TypeOf((*UnmarshalerV1)(nil)).Elem()
|
||||
jsonUnmarshalerV2Type = reflect.TypeOf((*UnmarshalerV2)(nil)).Elem()
|
||||
textAppenderType = reflect.TypeOf((*encodingTextAppender)(nil)).Elem()
|
||||
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
jsonMarshalerType = reflect.TypeFor[Marshaler]()
|
||||
jsonMarshalerToType = reflect.TypeFor[MarshalerTo]()
|
||||
jsonUnmarshalerType = reflect.TypeFor[Unmarshaler]()
|
||||
jsonUnmarshalerFromType = reflect.TypeFor[UnmarshalerFrom]()
|
||||
textAppenderType = reflect.TypeFor[encoding.TextAppender]()
|
||||
textMarshalerType = reflect.TypeFor[encoding.TextMarshaler]()
|
||||
textUnmarshalerType = reflect.TypeFor[encoding.TextUnmarshaler]()
|
||||
|
||||
// TODO(https://go.dev/issue/62384): Use encoding.TextAppender instead of this hack.
|
||||
// This exists for now to provide performance benefits to netip types.
|
||||
// There is no semantic difference with this change.
|
||||
appenderToType = reflect.TypeOf((*interface{ AppendTo([]byte) []byte })(nil)).Elem()
|
||||
allMarshalerTypes = []reflect.Type{jsonMarshalerToType, jsonMarshalerType, textAppenderType, textMarshalerType}
|
||||
allUnmarshalerTypes = []reflect.Type{jsonUnmarshalerFromType, jsonUnmarshalerType, textUnmarshalerType}
|
||||
allMethodTypes = append(allMarshalerTypes, allUnmarshalerTypes...)
|
||||
)
|
||||
|
||||
// TODO(https://go.dev/issue/62384): Use encoding.TextAppender instead
|
||||
// and document public support for this method in json.Marshal.
|
||||
type encodingTextAppender interface {
|
||||
AppendText(b []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// MarshalerV1 is implemented by types that can marshal themselves.
|
||||
// It is recommended that types implement [MarshalerV2] unless the implementation
|
||||
// Marshaler is implemented by types that can marshal themselves.
|
||||
// It is recommended that types implement [MarshalerTo] unless the implementation
|
||||
// is trying to avoid a hard dependency on the "jsontext" package.
|
||||
//
|
||||
// It is recommended that implementations return a buffer that is safe
|
||||
// for the caller to retain and potentially mutate.
|
||||
type MarshalerV1 interface {
|
||||
type Marshaler interface {
|
||||
MarshalJSON() ([]byte, error)
|
||||
}
|
||||
|
||||
// MarshalerV2 is implemented by types that can marshal themselves.
|
||||
// It is recommended that types implement MarshalerV2 instead of [MarshalerV1]
|
||||
// MarshalerTo is implemented by types that can marshal themselves.
|
||||
// It is recommended that types implement MarshalerTo instead of [Marshaler]
|
||||
// since this is both more performant and flexible.
|
||||
// If a type implements both MarshalerV1 and MarshalerV2,
|
||||
// then MarshalerV2 takes precedence. In such a case, both implementations
|
||||
// If a type implements both Marshaler and MarshalerTo,
|
||||
// then MarshalerTo takes precedence. In such a case, both implementations
|
||||
// should aim to have equivalent behavior for the default marshal options.
|
||||
//
|
||||
// The implementation must write only one JSON value to the Encoder and
|
||||
// must not retain the pointer to [jsontext.Encoder] or the [Options] value.
|
||||
type MarshalerV2 interface {
|
||||
MarshalJSONV2(*jsontext.Encoder, Options) error
|
||||
// must not retain the pointer to [jsontext.Encoder].
|
||||
type MarshalerTo interface {
|
||||
MarshalJSONTo(*jsontext.Encoder) error
|
||||
|
||||
// TODO: Should users call the MarshalEncode function or
|
||||
// should/can they call this method directly? Does it matter?
|
||||
}
|
||||
|
||||
// UnmarshalerV1 is implemented by types that can unmarshal themselves.
|
||||
// It is recommended that types implement [UnmarshalerV2] unless the implementation
|
||||
// Unmarshaler is implemented by types that can unmarshal themselves.
|
||||
// It is recommended that types implement [UnmarshalerFrom] unless the implementation
|
||||
// is trying to avoid a hard dependency on the "jsontext" package.
|
||||
//
|
||||
// The input can be assumed to be a valid encoding of a JSON value
|
||||
@@ -74,25 +70,24 @@ type MarshalerV2 interface {
|
||||
// unmarshaling into a pre-populated value.
|
||||
//
|
||||
// Implementations must not retain or mutate the input []byte.
|
||||
type UnmarshalerV1 interface {
|
||||
type Unmarshaler interface {
|
||||
UnmarshalJSON([]byte) error
|
||||
}
|
||||
|
||||
// UnmarshalerV2 is implemented by types that can unmarshal themselves.
|
||||
// It is recommended that types implement UnmarshalerV2 instead of [UnmarshalerV1]
|
||||
// UnmarshalerFrom is implemented by types that can unmarshal themselves.
|
||||
// It is recommended that types implement UnmarshalerFrom instead of [Unmarshaler]
|
||||
// since this is both more performant and flexible.
|
||||
// If a type implements both UnmarshalerV1 and UnmarshalerV2,
|
||||
// then UnmarshalerV2 takes precedence. In such a case, both implementations
|
||||
// If a type implements both Unmarshaler and UnmarshalerFrom,
|
||||
// then UnmarshalerFrom takes precedence. In such a case, both implementations
|
||||
// should aim to have equivalent behavior for the default unmarshal options.
|
||||
//
|
||||
// The implementation must read only one JSON value from the Decoder.
|
||||
// It is recommended that UnmarshalJSONV2 implement merge semantics when
|
||||
// It is recommended that UnmarshalJSONFrom implement merge semantics when
|
||||
// unmarshaling into a pre-populated value.
|
||||
//
|
||||
// Implementations must not retain the pointer to [jsontext.Decoder] or
|
||||
// the [Options] value.
|
||||
type UnmarshalerV2 interface {
|
||||
UnmarshalJSONV2(*jsontext.Decoder, Options) error
|
||||
// Implementations must not retain the pointer to [jsontext.Decoder].
|
||||
type UnmarshalerFrom interface {
|
||||
UnmarshalJSONFrom(*jsontext.Decoder) error
|
||||
|
||||
// TODO: Should users call the UnmarshalDecode function or
|
||||
// should/can they call this method directly? Does it matter?
|
||||
@@ -106,121 +101,118 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
return fncs
|
||||
}
|
||||
|
||||
// Handle custom marshaler.
|
||||
switch which := implementsWhich(t, jsonMarshalerV2Type, jsonMarshalerV1Type, textAppenderType, textMarshalerType); which {
|
||||
case jsonMarshalerV2Type:
|
||||
if needAddr, ok := implements(t, textMarshalerType); ok {
|
||||
fncs.nonDefault = true
|
||||
prevMarshal := fncs.marshal
|
||||
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
||||
xe := export.Encoder(enc)
|
||||
prevDepth, prevLength := xe.Tokens.DepthLength()
|
||||
xe.Flags.Set(jsonflags.WithinArshalCall | 1)
|
||||
err := va.Addr().Interface().(MarshalerV2).MarshalJSONV2(enc, mo)
|
||||
xe.Flags.Set(jsonflags.WithinArshalCall | 0)
|
||||
currDepth, currLength := xe.Tokens.DepthLength()
|
||||
if (prevDepth != currDepth || prevLength+1 != currLength) && err == nil {
|
||||
err = errors.New("must write exactly one JSON value")
|
||||
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
||||
(needAddr && va.forcedAddr) {
|
||||
return prevMarshal(enc, va, mo)
|
||||
}
|
||||
if err != nil {
|
||||
err = wrapSkipFunc(err, "marshal method")
|
||||
// TODO: Avoid wrapping semantic or I/O errors.
|
||||
return &SemanticError{action: "marshal", GoType: t, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case jsonMarshalerV1Type:
|
||||
fncs.nonDefault = true
|
||||
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
||||
marshaler := va.Addr().Interface().(MarshalerV1)
|
||||
val, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
err = wrapSkipFunc(err, "marshal method")
|
||||
// TODO: Avoid wrapping semantic errors.
|
||||
return &SemanticError{action: "marshal", GoType: t, Err: err}
|
||||
}
|
||||
if err := enc.WriteValue(val); err != nil {
|
||||
// TODO: Avoid wrapping semantic or I/O errors.
|
||||
return &SemanticError{action: "marshal", JSONKind: jsontext.Value(val).Kind(), GoType: t, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case textAppenderType:
|
||||
fncs.nonDefault = true
|
||||
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) (err error) {
|
||||
appender := va.Addr().Interface().(encodingTextAppender)
|
||||
if err := export.Encoder(enc).AppendRaw('"', false, appender.AppendText); err != nil {
|
||||
// TODO: Avoid wrapping semantic, syntactic, or I/O errors.
|
||||
err = wrapSkipFunc(err, "append method")
|
||||
return &SemanticError{action: "marshal", JSONKind: '"', GoType: t, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case textMarshalerType:
|
||||
fncs.nonDefault = true
|
||||
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
||||
marshaler := va.Addr().Interface().(encoding.TextMarshaler)
|
||||
if err := export.Encoder(enc).AppendRaw('"', false, func(b []byte) ([]byte, error) {
|
||||
b2, err := marshaler.MarshalText()
|
||||
return append(b, b2...), err
|
||||
}); err != nil {
|
||||
// TODO: Avoid wrapping semantic, syntactic, or I/O errors.
|
||||
err = wrapSkipFunc(err, "marshal method")
|
||||
return &SemanticError{action: "marshal", JSONKind: '"', GoType: t, Err: err}
|
||||
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalText") // unlike unmarshal, always wrapped
|
||||
}
|
||||
if !isSemanticError(err) && !export.IsIOError(err) {
|
||||
err = newMarshalErrorBefore(enc, t, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// TODO(https://go.dev/issue/62384): Rely on encoding.TextAppender instead.
|
||||
if implementsWhich(t, appenderToType) != nil && t.PkgPath() == "net/netip" {
|
||||
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
||||
appender := va.Addr().Interface().(interface{ AppendTo([]byte) []byte })
|
||||
if err := export.Encoder(enc).AppendRaw('"', false, func(b []byte) ([]byte, error) {
|
||||
return appender.AppendTo(b), nil
|
||||
}); err != nil {
|
||||
// TODO: Avoid wrapping semantic, syntactic, or I/O errors.
|
||||
err = wrapSkipFunc(err, "append method")
|
||||
return &SemanticError{action: "marshal", JSONKind: '"', GoType: t, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle custom unmarshaler.
|
||||
switch which := implementsWhich(t, jsonUnmarshalerV2Type, jsonUnmarshalerV1Type, textUnmarshalerType); which {
|
||||
case jsonUnmarshalerV2Type:
|
||||
if needAddr, ok := implements(t, textAppenderType); ok {
|
||||
fncs.nonDefault = true
|
||||
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
|
||||
xd := export.Decoder(dec)
|
||||
prevDepth, prevLength := xd.Tokens.DepthLength()
|
||||
xd.Flags.Set(jsonflags.WithinArshalCall | 1)
|
||||
err := va.Addr().Interface().(UnmarshalerV2).UnmarshalJSONV2(dec, uo)
|
||||
xd.Flags.Set(jsonflags.WithinArshalCall | 0)
|
||||
currDepth, currLength := xd.Tokens.DepthLength()
|
||||
prevMarshal := fncs.marshal
|
||||
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) (err error) {
|
||||
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
||||
(needAddr && va.forcedAddr) {
|
||||
return prevMarshal(enc, va, mo)
|
||||
}
|
||||
appender := va.Addr().Interface().(encoding.TextAppender)
|
||||
if err := export.Encoder(enc).AppendRaw('"', false, appender.AppendText); err != nil {
|
||||
err = wrapSkipFunc(err, "append method")
|
||||
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.NewMarshalerError(va.Addr().Interface(), err, "AppendText") // unlike unmarshal, always wrapped
|
||||
}
|
||||
if !isSemanticError(err) && !export.IsIOError(err) {
|
||||
err = newMarshalErrorBefore(enc, t, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if needAddr, ok := implements(t, jsonMarshalerType); ok {
|
||||
fncs.nonDefault = true
|
||||
prevMarshal := fncs.marshal
|
||||
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
||||
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
||||
((needAddr && va.forcedAddr) || export.Encoder(enc).Tokens.Last.NeedObjectName()) {
|
||||
return prevMarshal(enc, va, mo)
|
||||
}
|
||||
marshaler := va.Addr().Interface().(Marshaler)
|
||||
val, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
err = wrapSkipFunc(err, "marshal method")
|
||||
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSON") // unlike unmarshal, always wrapped
|
||||
}
|
||||
err = newMarshalErrorBefore(enc, t, err)
|
||||
return collapseSemanticErrors(err)
|
||||
}
|
||||
if err := enc.WriteValue(val); err != nil {
|
||||
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSON") // unlike unmarshal, always wrapped
|
||||
}
|
||||
if isSyntacticError(err) {
|
||||
err = newMarshalErrorBefore(enc, t, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if needAddr, ok := implements(t, jsonMarshalerToType); ok {
|
||||
fncs.nonDefault = true
|
||||
prevMarshal := fncs.marshal
|
||||
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
||||
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
||||
((needAddr && va.forcedAddr) || export.Encoder(enc).Tokens.Last.NeedObjectName()) {
|
||||
return prevMarshal(enc, va, mo)
|
||||
}
|
||||
xe := export.Encoder(enc)
|
||||
prevDepth, prevLength := xe.Tokens.DepthLength()
|
||||
xe.Flags.Set(jsonflags.WithinArshalCall | 1)
|
||||
err := va.Addr().Interface().(MarshalerTo).MarshalJSONTo(enc)
|
||||
xe.Flags.Set(jsonflags.WithinArshalCall | 0)
|
||||
currDepth, currLength := xe.Tokens.DepthLength()
|
||||
if (prevDepth != currDepth || prevLength+1 != currLength) && err == nil {
|
||||
err = errors.New("must read exactly one JSON value")
|
||||
err = errNonSingularValue
|
||||
}
|
||||
if err != nil {
|
||||
err = wrapSkipFunc(err, "unmarshal method")
|
||||
// TODO: Avoid wrapping semantic, syntactic, or I/O errors.
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: err}
|
||||
err = wrapSkipFunc(err, "marshal method")
|
||||
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSONTo") // unlike unmarshal, always wrapped
|
||||
}
|
||||
if !export.IsIOError(err) {
|
||||
err = newSemanticErrorWithPosition(enc, t, prevDepth, prevLength, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case jsonUnmarshalerV1Type:
|
||||
fncs.nonDefault = true
|
||||
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
|
||||
val, err := dec.ReadValue()
|
||||
if err != nil {
|
||||
return err // must be a syntactic or I/O error
|
||||
}
|
||||
unmarshaler := va.Addr().Interface().(UnmarshalerV1)
|
||||
if err := unmarshaler.UnmarshalJSON(val); err != nil {
|
||||
err = wrapSkipFunc(err, "unmarshal method")
|
||||
// TODO: Avoid wrapping semantic, syntactic, or I/O errors.
|
||||
return &SemanticError{action: "unmarshal", JSONKind: val.Kind(), GoType: t, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case textUnmarshalerType:
|
||||
}
|
||||
|
||||
if _, ok := implements(t, textUnmarshalerType); ok {
|
||||
fncs.nonDefault = true
|
||||
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
|
||||
xd := export.Decoder(dec)
|
||||
@@ -229,16 +221,85 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
if err != nil {
|
||||
return err // must be a syntactic or I/O error
|
||||
}
|
||||
if val.Kind() == 'n' {
|
||||
if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
|
||||
va.SetZero()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if val.Kind() != '"' {
|
||||
err = errors.New("JSON value must be string type")
|
||||
return &SemanticError{action: "unmarshal", JSONKind: val.Kind(), GoType: t, Err: err}
|
||||
return newUnmarshalErrorAfter(dec, t, errNonStringValue)
|
||||
}
|
||||
s := jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
|
||||
unmarshaler := va.Addr().Interface().(encoding.TextUnmarshaler)
|
||||
if err := unmarshaler.UnmarshalText(s); err != nil {
|
||||
err = wrapSkipFunc(err, "unmarshal method")
|
||||
// TODO: Avoid wrapping semantic, syntactic, or I/O errors.
|
||||
return &SemanticError{action: "unmarshal", JSONKind: val.Kind(), GoType: t, Err: err}
|
||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return err // unlike marshal, never wrapped
|
||||
}
|
||||
if !isSemanticError(err) && !isSyntacticError(err) && !export.IsIOError(err) {
|
||||
err = newUnmarshalErrorAfter(dec, t, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := implements(t, jsonUnmarshalerType); ok {
|
||||
fncs.nonDefault = true
|
||||
prevUnmarshal := fncs.unmarshal
|
||||
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
|
||||
if uo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
||||
export.Decoder(dec).Tokens.Last.NeedObjectName() {
|
||||
return prevUnmarshal(dec, va, uo)
|
||||
}
|
||||
val, err := dec.ReadValue()
|
||||
if err != nil {
|
||||
return err // must be a syntactic or I/O error
|
||||
}
|
||||
unmarshaler := va.Addr().Interface().(Unmarshaler)
|
||||
if err := unmarshaler.UnmarshalJSON(val); err != nil {
|
||||
err = wrapSkipFunc(err, "unmarshal method")
|
||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return err // unlike marshal, never wrapped
|
||||
}
|
||||
err = newUnmarshalErrorAfter(dec, t, err)
|
||||
return collapseSemanticErrors(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := implements(t, jsonUnmarshalerFromType); ok {
|
||||
fncs.nonDefault = true
|
||||
prevUnmarshal := fncs.unmarshal
|
||||
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
|
||||
if uo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
||||
export.Decoder(dec).Tokens.Last.NeedObjectName() {
|
||||
return prevUnmarshal(dec, va, uo)
|
||||
}
|
||||
xd := export.Decoder(dec)
|
||||
prevDepth, prevLength := xd.Tokens.DepthLength()
|
||||
xd.Flags.Set(jsonflags.WithinArshalCall | 1)
|
||||
err := va.Addr().Interface().(UnmarshalerFrom).UnmarshalJSONFrom(dec)
|
||||
xd.Flags.Set(jsonflags.WithinArshalCall | 0)
|
||||
currDepth, currLength := xd.Tokens.DepthLength()
|
||||
if (prevDepth != currDepth || prevLength+1 != currLength) && err == nil {
|
||||
err = errNonSingularValue
|
||||
}
|
||||
if err != nil {
|
||||
err = wrapSkipFunc(err, "unmarshal method")
|
||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
if err2 := xd.SkipUntil(prevDepth, prevLength+1); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err // unlike marshal, never wrapped
|
||||
}
|
||||
if !isSyntacticError(err) && !export.IsIOError(err) {
|
||||
err = newSemanticErrorWithPosition(dec, t, prevDepth, prevLength, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -247,13 +308,28 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
return fncs
|
||||
}
|
||||
|
||||
// implementsWhich is like t.Implements(ifaceType) for a list of interfaces,
|
||||
// implementsAny is like t.Implements(ifaceType) for a list of interfaces,
|
||||
// but checks whether either t or reflect.PointerTo(t) implements the interface.
|
||||
func implementsWhich(t reflect.Type, ifaceTypes ...reflect.Type) (which reflect.Type) {
|
||||
func implementsAny(t reflect.Type, ifaceTypes ...reflect.Type) bool {
|
||||
for _, ifaceType := range ifaceTypes {
|
||||
if t.Implements(ifaceType) || reflect.PointerTo(t).Implements(ifaceType) {
|
||||
return ifaceType
|
||||
if _, ok := implements(t, ifaceType); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return false
|
||||
}
|
||||
|
||||
// implements is like t.Implements(ifaceType) but checks whether
|
||||
// either t or reflect.PointerTo(t) implements the interface.
|
||||
// It also reports whether the value needs to be addressed
|
||||
// in order to satisfy the interface.
|
||||
func implements(t, ifaceType reflect.Type) (needAddr, ok bool) {
|
||||
switch {
|
||||
case t.Implements(ifaceType):
|
||||
return false, true
|
||||
case reflect.PointerTo(t).Implements(ifaceType):
|
||||
return true, true
|
||||
default:
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
|
||||
187
vendor/github.com/go-json-experiment/json/arshal_time.go
generated
vendored
187
vendor/github.com/go-json-experiment/json/arshal_time.go
generated
vendored
@@ -6,6 +6,7 @@ package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-json-experiment/json/internal"
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonopts"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
@@ -22,12 +24,12 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
timeDurationType = reflect.TypeOf((*time.Duration)(nil)).Elem()
|
||||
timeTimeType = reflect.TypeOf((*time.Time)(nil)).Elem()
|
||||
timeDurationType = reflect.TypeFor[time.Duration]()
|
||||
timeTimeType = reflect.TypeFor[time.Time]()
|
||||
)
|
||||
|
||||
func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
// Ideally, time types would implement MarshalerV2 and UnmarshalerV2,
|
||||
// Ideally, time types would implement MarshalerTo and UnmarshalerFrom,
|
||||
// but that would incur a dependency on package json from package time.
|
||||
// Given how widely used time is, it is more acceptable that we incur a
|
||||
// dependency on time from json.
|
||||
@@ -44,17 +46,20 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
var m durationArshaler
|
||||
if mo.Format != "" && mo.FormatDepth == xe.Tokens.Depth() {
|
||||
if !m.initFormat(mo.Format) {
|
||||
return newInvalidFormatError("marshal", t, mo.Format)
|
||||
return newInvalidFormatError(enc, t, mo)
|
||||
}
|
||||
} else if mo.Flags.Get(jsonflags.FormatTimeDurationAsNanosecond) {
|
||||
} else if mo.Flags.Get(jsonflags.FormatTimeWithLegacySemantics) {
|
||||
return marshalNano(enc, va, mo)
|
||||
}
|
||||
|
||||
// TODO(https://go.dev/issue/62121): Use reflect.Value.AssertTo.
|
||||
m.td = *va.Addr().Interface().(*time.Duration)
|
||||
k := stringOrNumberKind(!m.isNumeric() || mo.Flags.Get(jsonflags.StringifyNumbers))
|
||||
k := stringOrNumberKind(!m.isNumeric() || xe.Tokens.Last.NeedObjectName() || mo.Flags.Get(jsonflags.StringifyNumbers))
|
||||
if err := xe.AppendRaw(k, true, m.appendMarshal); err != nil {
|
||||
return &SemanticError{action: "marshal", GoType: t, Err: err}
|
||||
if !isSyntacticError(err) && !export.IsIOError(err) {
|
||||
err = newMarshalErrorBefore(enc, t, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -64,12 +69,13 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
var u durationArshaler
|
||||
if uo.Format != "" && uo.FormatDepth == xd.Tokens.Depth() {
|
||||
if !u.initFormat(uo.Format) {
|
||||
return newInvalidFormatError("unmarshal", t, uo.Format)
|
||||
return newInvalidFormatError(dec, t, uo)
|
||||
}
|
||||
} else if uo.Flags.Get(jsonflags.FormatTimeDurationAsNanosecond) {
|
||||
} else if uo.Flags.Get(jsonflags.FormatTimeWithLegacySemantics) {
|
||||
return unmarshalNano(dec, va, uo)
|
||||
}
|
||||
|
||||
stringify := !u.isNumeric() || xd.Tokens.Last.NeedObjectName() || uo.Flags.Get(jsonflags.StringifyNumbers)
|
||||
var flags jsonwire.ValueFlags
|
||||
td := va.Addr().Interface().(*time.Duration)
|
||||
val, err := xd.ReadValue(&flags)
|
||||
@@ -78,30 +84,31 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
}
|
||||
switch k := val.Kind(); k {
|
||||
case 'n':
|
||||
*td = time.Duration(0)
|
||||
if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
|
||||
*td = time.Duration(0)
|
||||
}
|
||||
return nil
|
||||
case '"':
|
||||
if u.isNumeric() && !uo.Flags.Get(jsonflags.StringifyNumbers) {
|
||||
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t}
|
||||
if !stringify {
|
||||
break
|
||||
}
|
||||
val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
|
||||
if err := u.unmarshal(val); err != nil {
|
||||
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err}
|
||||
return newUnmarshalErrorAfter(dec, t, err)
|
||||
}
|
||||
*td = u.td
|
||||
return nil
|
||||
case '0':
|
||||
if !u.isNumeric() {
|
||||
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t}
|
||||
if stringify {
|
||||
break
|
||||
}
|
||||
if err := u.unmarshal(val); err != nil {
|
||||
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err}
|
||||
return newUnmarshalErrorAfter(dec, t, err)
|
||||
}
|
||||
*td = u.td
|
||||
return nil
|
||||
default:
|
||||
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t}
|
||||
}
|
||||
return newUnmarshalErrorAfter(dec, t, nil)
|
||||
}
|
||||
case timeTimeType:
|
||||
fncs.nonDefault = true
|
||||
@@ -110,15 +117,21 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
var m timeArshaler
|
||||
if mo.Format != "" && mo.FormatDepth == xe.Tokens.Depth() {
|
||||
if !m.initFormat(mo.Format) {
|
||||
return newInvalidFormatError("marshal", t, mo.Format)
|
||||
return newInvalidFormatError(enc, t, mo)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(https://go.dev/issue/62121): Use reflect.Value.AssertTo.
|
||||
m.tt = *va.Addr().Interface().(*time.Time)
|
||||
k := stringOrNumberKind(!m.isNumeric() || mo.Flags.Get(jsonflags.StringifyNumbers))
|
||||
k := stringOrNumberKind(!m.isNumeric() || xe.Tokens.Last.NeedObjectName() || mo.Flags.Get(jsonflags.StringifyNumbers))
|
||||
if err := xe.AppendRaw(k, !m.hasCustomFormat(), m.appendMarshal); err != nil {
|
||||
return &SemanticError{action: "marshal", GoType: t, Err: err}
|
||||
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSON") // unlike unmarshal, always wrapped
|
||||
}
|
||||
if !isSyntacticError(err) && !export.IsIOError(err) {
|
||||
err = newMarshalErrorBefore(enc, t, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -127,10 +140,13 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
var u timeArshaler
|
||||
if uo.Format != "" && uo.FormatDepth == xd.Tokens.Depth() {
|
||||
if !u.initFormat(uo.Format) {
|
||||
return newInvalidFormatError("unmarshal", t, uo.Format)
|
||||
return newInvalidFormatError(dec, t, uo)
|
||||
}
|
||||
} else if uo.Flags.Get(jsonflags.FormatTimeWithLegacySemantics) {
|
||||
u.looseRFC3339 = true
|
||||
}
|
||||
|
||||
stringify := !u.isNumeric() || xd.Tokens.Last.NeedObjectName() || uo.Flags.Get(jsonflags.StringifyNumbers)
|
||||
var flags jsonwire.ValueFlags
|
||||
tt := va.Addr().Interface().(*time.Time)
|
||||
val, err := xd.ReadValue(&flags)
|
||||
@@ -139,30 +155,37 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
}
|
||||
switch k := val.Kind(); k {
|
||||
case 'n':
|
||||
*tt = time.Time{}
|
||||
if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
|
||||
*tt = time.Time{}
|
||||
}
|
||||
return nil
|
||||
case '"':
|
||||
if u.isNumeric() && !uo.Flags.Get(jsonflags.StringifyNumbers) {
|
||||
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t}
|
||||
if !stringify {
|
||||
break
|
||||
}
|
||||
val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
|
||||
if err := u.unmarshal(val); err != nil {
|
||||
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err}
|
||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return err // unlike marshal, never wrapped
|
||||
}
|
||||
return newUnmarshalErrorAfter(dec, t, err)
|
||||
}
|
||||
*tt = u.tt
|
||||
return nil
|
||||
case '0':
|
||||
if !u.isNumeric() {
|
||||
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t}
|
||||
if stringify {
|
||||
break
|
||||
}
|
||||
if err := u.unmarshal(val); err != nil {
|
||||
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err}
|
||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return err // unlike marshal, never wrapped
|
||||
}
|
||||
return newUnmarshalErrorAfter(dec, t, err)
|
||||
}
|
||||
*tt = u.tt
|
||||
return nil
|
||||
default:
|
||||
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t}
|
||||
}
|
||||
return newUnmarshalErrorAfter(dec, t, nil)
|
||||
}
|
||||
}
|
||||
return fncs
|
||||
@@ -175,8 +198,7 @@ type durationArshaler struct {
|
||||
// - 0 uses time.Duration.String
|
||||
// - 1e0, 1e3, 1e6, or 1e9 use a decimal encoding of the duration as
|
||||
// nanoseconds, microseconds, milliseconds, or seconds.
|
||||
// - 60 uses a "H:MM:SS.SSSSSSSSS" encoding
|
||||
base uint
|
||||
base uint64
|
||||
}
|
||||
|
||||
func (a *durationArshaler) initFormat(format string) (ok bool) {
|
||||
@@ -191,8 +213,6 @@ func (a *durationArshaler) initFormat(format string) (ok bool) {
|
||||
a.base = 1e3
|
||||
case "nano":
|
||||
a.base = 1e0
|
||||
case "base60": // see https://en.wikipedia.org/wiki/Sexagesimal#Modern_usage
|
||||
a.base = 60
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -207,8 +227,6 @@ func (a *durationArshaler) appendMarshal(b []byte) ([]byte, error) {
|
||||
switch a.base {
|
||||
case 0:
|
||||
return append(b, a.td.String()...), nil
|
||||
case 60:
|
||||
return appendDurationBase60(b, a.td), nil
|
||||
default:
|
||||
return appendDurationBase10(b, a.td, a.base), nil
|
||||
}
|
||||
@@ -218,8 +236,6 @@ func (a *durationArshaler) unmarshal(b []byte) (err error) {
|
||||
switch a.base {
|
||||
case 0:
|
||||
a.td, err = time.ParseDuration(string(b))
|
||||
case 60:
|
||||
a.td, err = parseDurationBase60(b)
|
||||
default:
|
||||
a.td, err = parseDurationBase10(b, a.base)
|
||||
}
|
||||
@@ -234,8 +250,10 @@ type timeArshaler struct {
|
||||
// - 1e0, 1e3, 1e6, or 1e9 use a decimal encoding of the timestamp as
|
||||
// seconds, milliseconds, microseconds, or nanoseconds since Unix epoch.
|
||||
// - math.MaxUint uses time.Time.Format to encode the timestamp
|
||||
base uint
|
||||
base uint64
|
||||
format string // time format passed to time.Parse
|
||||
|
||||
looseRFC3339 bool
|
||||
}
|
||||
|
||||
func (a *timeArshaler) initFormat(format string) bool {
|
||||
@@ -317,11 +335,7 @@ func (a *timeArshaler) hasCustomFormat() bool {
|
||||
func (a *timeArshaler) appendMarshal(b []byte) ([]byte, error) {
|
||||
switch a.base {
|
||||
case 0:
|
||||
// TODO(https://go.dev/issue/60204): Use cmp.Or(a.format, time.RFC3339Nano).
|
||||
format := a.format
|
||||
if format == "" {
|
||||
format = time.RFC3339Nano
|
||||
}
|
||||
format := cmp.Or(a.format, time.RFC3339Nano)
|
||||
n0 := len(b)
|
||||
b = a.tt.AppendFormat(b, format)
|
||||
// Not all Go timestamps can be represented as valid RFC 3339.
|
||||
@@ -360,6 +374,8 @@ func (a *timeArshaler) unmarshal(b []byte) (err error) {
|
||||
return &time.ParseError{Layout: layout, Value: value, LayoutElem: layoutElem, ValueElem: valueElem, Message: message}
|
||||
}
|
||||
switch {
|
||||
case a.looseRFC3339:
|
||||
return nil
|
||||
case b[len("2006-01-02T")+1] == ':': // hour must be two digits
|
||||
return newParseError(time.RFC3339, string(b), "15", string(b[len("2006-01-02T"):][:1]), "")
|
||||
case b[len("2006-01-02T15:04:05")] == ',': // sub-second separator must be a period
|
||||
@@ -384,16 +400,16 @@ func (a *timeArshaler) unmarshal(b []byte) (err error) {
|
||||
|
||||
// appendDurationBase10 appends d formatted as a decimal fractional number,
|
||||
// where pow10 is a power-of-10 used to scale down the number.
|
||||
func appendDurationBase10(b []byte, d time.Duration, pow10 uint) []byte {
|
||||
func appendDurationBase10(b []byte, d time.Duration, pow10 uint64) []byte {
|
||||
b, n := mayAppendDurationSign(b, d) // append sign
|
||||
whole, frac := bits.Div64(0, n, uint64(pow10)) // compute whole and frac fields
|
||||
b = strconv.AppendUint(b, whole, 10) // append whole field
|
||||
return appendFracBase10(b, uint(frac), pow10) // append frac field
|
||||
return appendFracBase10(b, frac, pow10) // append frac field
|
||||
}
|
||||
|
||||
// parseDurationBase10 parses d from a decimal fractional number,
|
||||
// where pow10 is a power-of-10 used to scale up the number.
|
||||
func parseDurationBase10(b []byte, pow10 uint) (time.Duration, error) {
|
||||
func parseDurationBase10(b []byte, pow10 uint64) (time.Duration, error) {
|
||||
suffix, neg := consumeSign(b) // consume sign
|
||||
wholeBytes, fracBytes := bytesCutByte(suffix, '.', true) // consume whole and frac fields
|
||||
whole, okWhole := jsonwire.ParseUint(wholeBytes) // parse whole field; may overflow
|
||||
@@ -410,45 +426,6 @@ func parseDurationBase10(b []byte, pow10 uint) (time.Duration, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// appendDurationBase60 appends d formatted with H:MM:SS.SSS notation.
|
||||
func appendDurationBase60(b []byte, d time.Duration) []byte {
|
||||
b, n := mayAppendDurationSign(b, d) // append sign
|
||||
n, nsec := bits.Div64(0, n, 1e9) // compute nsec field
|
||||
n, sec := bits.Div64(0, n, 60) // compute sec field
|
||||
hour, min := bits.Div64(0, n, 60) // compute hour and min fields
|
||||
b = strconv.AppendUint(b, hour, 10) // append hour field
|
||||
b = append(b, ':', '0'+byte(min/10), '0'+byte(min%10)) // append min field
|
||||
b = append(b, ':', '0'+byte(sec/10), '0'+byte(sec%10)) // append sec field
|
||||
return appendFracBase10(b, uint(nsec), 1e9) // append nsec field
|
||||
}
|
||||
|
||||
// parseDurationBase60 parses d formatted with H:MM:SS.SSS notation.
|
||||
// The exact grammar is `-?(0|[1-9][0-9]*):[0-5][0-9]:[0-5][0-9]([.][0-9]+)?`.
|
||||
func parseDurationBase60(b []byte) (time.Duration, error) {
|
||||
checkBase60 := func(b []byte) bool {
|
||||
return len(b) == 2 && ('0' <= b[0] && b[0] <= '5') && '0' <= b[1] && b[1] <= '9'
|
||||
}
|
||||
suffix, neg := consumeSign(b) // consume sign
|
||||
hourBytes, suffix := bytesCutByte(suffix, ':', false) // consume hour field
|
||||
minBytes, suffix := bytesCutByte(suffix, ':', false) // consume min field
|
||||
secBytes, nsecBytes := bytesCutByte(suffix, '.', true) // consume sec and nsec fields
|
||||
hour, okHour := jsonwire.ParseUint(hourBytes) // parse hour field; may overflow
|
||||
min := parseDec2(minBytes) // parse min field
|
||||
sec := parseDec2(secBytes) // parse sec field
|
||||
nsec, okNsec := parseFracBase10(nsecBytes, 1e9) // parse nsec field
|
||||
n := uint64(min)*60*1e9 + uint64(sec)*1e9 + uint64(nsec) // cannot overflow
|
||||
hi, lo := bits.Mul64(hour, 60*60*1e9) // overflow if hi > 0
|
||||
sum, co := bits.Add64(lo, n, 0) // overflow if co > 0
|
||||
switch d := mayApplyDurationSign(sum, neg); { // overflow if neg != (d < 0)
|
||||
case (!okHour && hour != math.MaxUint64) || !checkBase60(minBytes) || !checkBase60(secBytes) || !okNsec:
|
||||
return 0, fmt.Errorf("invalid duration %q: %w", b, strconv.ErrSyntax)
|
||||
case !okHour || hi > 0 || co > 0 || neg != (d < 0):
|
||||
return 0, fmt.Errorf("invalid duration %q: %w", b, strconv.ErrRange)
|
||||
default:
|
||||
return d, nil
|
||||
}
|
||||
}
|
||||
|
||||
// mayAppendDurationSign appends a negative sign if n is negative.
|
||||
func mayAppendDurationSign(b []byte, d time.Duration) ([]byte, uint64) {
|
||||
if d < 0 {
|
||||
@@ -469,7 +446,7 @@ func mayApplyDurationSign(n uint64, neg bool) time.Duration {
|
||||
|
||||
// appendTimeUnix appends t formatted as a decimal fractional number,
|
||||
// where pow10 is a power-of-10 used to scale up the number.
|
||||
func appendTimeUnix(b []byte, t time.Time, pow10 uint) []byte {
|
||||
func appendTimeUnix(b []byte, t time.Time, pow10 uint64) []byte {
|
||||
sec, nsec := t.Unix(), int64(t.Nanosecond())
|
||||
if sec < 0 {
|
||||
b = append(b, '-')
|
||||
@@ -478,20 +455,20 @@ func appendTimeUnix(b []byte, t time.Time, pow10 uint) []byte {
|
||||
switch {
|
||||
case pow10 == 1e0: // fast case where units is in seconds
|
||||
b = strconv.AppendUint(b, uint64(sec), 10)
|
||||
return appendFracBase10(b, uint(nsec), 1e9)
|
||||
return appendFracBase10(b, uint64(nsec), 1e9)
|
||||
case uint64(sec) < 1e9: // intermediate case where units is not seconds, but no overflow
|
||||
b = strconv.AppendUint(b, uint64(sec)*uint64(pow10)+uint64(uint(nsec)/(1e9/pow10)), 10)
|
||||
return appendFracBase10(b, (uint(nsec)*pow10)%1e9, 1e9)
|
||||
b = strconv.AppendUint(b, uint64(sec)*uint64(pow10)+uint64(uint64(nsec)/(1e9/pow10)), 10)
|
||||
return appendFracBase10(b, (uint64(nsec)*pow10)%1e9, 1e9)
|
||||
default: // slow case where units is not seconds and overflow would occur
|
||||
b = strconv.AppendUint(b, uint64(sec), 10)
|
||||
b = appendPaddedBase10(b, uint(uint(nsec)/(1e9/pow10)), pow10)
|
||||
return appendFracBase10(b, (uint(nsec)*pow10)%1e9, 1e9)
|
||||
b = appendPaddedBase10(b, uint64(nsec)/(1e9/pow10), pow10)
|
||||
return appendFracBase10(b, (uint64(nsec)*pow10)%1e9, 1e9)
|
||||
}
|
||||
}
|
||||
|
||||
// parseTimeUnix parses t formatted as a decimal fractional number,
|
||||
// where pow10 is a power-of-10 used to scale down the number.
|
||||
func parseTimeUnix(b []byte, pow10 uint) (time.Time, error) {
|
||||
func parseTimeUnix(b []byte, pow10 uint64) (time.Time, error) {
|
||||
suffix, neg := consumeSign(b) // consume sign
|
||||
wholeBytes, fracBytes := bytesCutByte(suffix, '.', true) // consume whole and frac fields
|
||||
whole, okWhole := jsonwire.ParseUint(wholeBytes) // parse whole field; may overflow
|
||||
@@ -502,14 +479,14 @@ func parseTimeUnix(b []byte, pow10 uint) (time.Time, error) {
|
||||
sec = int64(whole) // check overflow later after negation
|
||||
nsec = int64(frac) // cannot overflow
|
||||
case okWhole: // intermediate case where units is not seconds, but no overflow
|
||||
sec = int64(whole / uint64(pow10)) // check overflow later after negation
|
||||
nsec = int64((uint(whole)%pow10)*(1e9/pow10) + uint(frac)) // cannot overflow
|
||||
sec = int64(whole / pow10) // check overflow later after negation
|
||||
nsec = int64((whole%pow10)*(1e9/pow10) + frac) // cannot overflow
|
||||
case !okWhole && whole == math.MaxUint64: // slow case where units is not seconds and overflow occurred
|
||||
width := int(math.Log10(float64(pow10))) // compute len(strconv.Itoa(pow10-1))
|
||||
whole, okWhole = jsonwire.ParseUint(wholeBytes[:len(wholeBytes)-width]) // parse the upper whole field
|
||||
mid, _ := parsePaddedBase10(wholeBytes[len(wholeBytes)-width:], pow10) // parse the lower whole field
|
||||
sec = int64(whole) // check overflow later after negation
|
||||
nsec = int64(uint(mid)*(1e9/pow10) + frac) // cannot overflow
|
||||
nsec = int64(mid*(1e9/pow10) + frac) // cannot overflow
|
||||
}
|
||||
if neg {
|
||||
sec, nsec = negateSecNano(sec, nsec)
|
||||
@@ -535,7 +512,7 @@ func negateSecNano(sec, nsec int64) (int64, int64) {
|
||||
|
||||
// appendFracBase10 appends the fraction of n/max10,
|
||||
// where max10 is a power-of-10 that is larger than n.
|
||||
func appendFracBase10(b []byte, n, max10 uint) []byte {
|
||||
func appendFracBase10(b []byte, n, max10 uint64) []byte {
|
||||
if n == 0 {
|
||||
return b
|
||||
}
|
||||
@@ -544,7 +521,7 @@ func appendFracBase10(b []byte, n, max10 uint) []byte {
|
||||
|
||||
// parseFracBase10 parses the fraction of n/max10,
|
||||
// where max10 is a power-of-10 that is larger than n.
|
||||
func parseFracBase10(b []byte, max10 uint) (n uint, ok bool) {
|
||||
func parseFracBase10(b []byte, max10 uint64) (n uint64, ok bool) {
|
||||
switch {
|
||||
case len(b) == 0:
|
||||
return 0, true
|
||||
@@ -556,31 +533,31 @@ func parseFracBase10(b []byte, max10 uint) (n uint, ok bool) {
|
||||
|
||||
// appendPaddedBase10 appends a zero-padded encoding of n,
|
||||
// where max10 is a power-of-10 that is larger than n.
|
||||
func appendPaddedBase10(b []byte, n, max10 uint) []byte {
|
||||
func appendPaddedBase10(b []byte, n, max10 uint64) []byte {
|
||||
if n < max10/10 {
|
||||
// Formatting of n is shorter than log10(max10),
|
||||
// so add max10/10 to ensure the length is equal to log10(max10).
|
||||
i := len(b)
|
||||
b = strconv.AppendUint(b, uint64(n+max10/10), 10)
|
||||
b = strconv.AppendUint(b, n+max10/10, 10)
|
||||
b[i]-- // subtract the addition of max10/10
|
||||
return b
|
||||
}
|
||||
return strconv.AppendUint(b, uint64(n), 10)
|
||||
return strconv.AppendUint(b, n, 10)
|
||||
}
|
||||
|
||||
// parsePaddedBase10 parses b as the zero-padded encoding of n,
|
||||
// where max10 is a power-of-10 that is larger than n.
|
||||
// Truncated suffix is treated as implicit zeros.
|
||||
// Extended suffix is ignored, but verified to contain only digits.
|
||||
func parsePaddedBase10(b []byte, max10 uint) (n uint, ok bool) {
|
||||
pow10 := uint(1)
|
||||
func parsePaddedBase10(b []byte, max10 uint64) (n uint64, ok bool) {
|
||||
pow10 := uint64(1)
|
||||
for pow10 < max10 {
|
||||
n *= 10
|
||||
if len(b) > 0 {
|
||||
if b[0] < '0' || '9' < b[0] {
|
||||
return n, false
|
||||
}
|
||||
n += uint(b[0] - '0')
|
||||
n += uint64(b[0] - '0')
|
||||
b = b[1:]
|
||||
}
|
||||
pow10 *= 10
|
||||
|
||||
BIN
vendor/github.com/go-json-experiment/json/benchmark-marshal-concrete.png
generated
vendored
BIN
vendor/github.com/go-json-experiment/json/benchmark-marshal-concrete.png
generated
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
BIN
vendor/github.com/go-json-experiment/json/benchmark-marshal-interface.png
generated
vendored
BIN
vendor/github.com/go-json-experiment/json/benchmark-marshal-interface.png
generated
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
BIN
vendor/github.com/go-json-experiment/json/benchmark-marshal-rawvalue.png
generated
vendored
BIN
vendor/github.com/go-json-experiment/json/benchmark-marshal-rawvalue.png
generated
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
BIN
vendor/github.com/go-json-experiment/json/benchmark-unmarshal-concrete.png
generated
vendored
BIN
vendor/github.com/go-json-experiment/json/benchmark-unmarshal-concrete.png
generated
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
BIN
vendor/github.com/go-json-experiment/json/benchmark-unmarshal-interface.png
generated
vendored
BIN
vendor/github.com/go-json-experiment/json/benchmark-unmarshal-interface.png
generated
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
BIN
vendor/github.com/go-json-experiment/json/benchmark-unmarshal-rawvalue.png
generated
vendored
BIN
vendor/github.com/go-json-experiment/json/benchmark-unmarshal-rawvalue.png
generated
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
36
vendor/github.com/go-json-experiment/json/doc.go
generated
vendored
36
vendor/github.com/go-json-experiment/json/doc.go
generated
vendored
@@ -30,10 +30,10 @@
|
||||
// of how the JSON and Go type systems correspond.
|
||||
//
|
||||
// Arbitrary Go types can customize their JSON representation by implementing
|
||||
// [MarshalerV1], [MarshalerV2], [UnmarshalerV1], or [UnmarshalerV2].
|
||||
// [Marshaler], [MarshalerTo], [Unmarshaler], or [UnmarshalerFrom].
|
||||
// This provides authors of Go types with control over how their types are
|
||||
// serialized as JSON. Alternatively, users can implement functions that match
|
||||
// [MarshalFuncV1], [MarshalFuncV2], [UnmarshalFuncV1], or [UnmarshalFuncV2]
|
||||
// [MarshalFunc], [MarshalToFunc], [UnmarshalFunc], or [UnmarshalFromFunc]
|
||||
// to specify the JSON representation for arbitrary types.
|
||||
// This provides callers of JSON functionality with control over
|
||||
// how any arbitrary type is serialized as JSON.
|
||||
@@ -54,6 +54,8 @@
|
||||
// "json" struct field tag, where the tag is a comma separated list of options.
|
||||
// As a special case, if the entire tag is `json:"-"`,
|
||||
// then the field is ignored with regard to its JSON representation.
|
||||
// Some options also have equivalent behavior controlled by a caller-specified [Options].
|
||||
// Field-specified options take precedence over caller-specified options.
|
||||
//
|
||||
// The first option is the JSON object name override for the Go struct field.
|
||||
// If the name is not specified, then the Go struct field name
|
||||
@@ -81,24 +83,20 @@
|
||||
// - string: The "string" option specifies that [StringifyNumbers]
|
||||
// be set when marshaling or unmarshaling a struct field value.
|
||||
// This causes numeric types to be encoded as a JSON number
|
||||
// within a JSON string, and to be decoded from either a JSON number or
|
||||
// a JSON string containing a JSON number.
|
||||
// within a JSON string, and to be decoded from a JSON string
|
||||
// containing the JSON number without any surrounding whitespace.
|
||||
// This extra level of encoding is often necessary since
|
||||
// many JSON parsers cannot precisely represent 64-bit integers.
|
||||
//
|
||||
// - nocase: When unmarshaling, the "nocase" option specifies that
|
||||
// if the JSON object name does not exactly match the JSON name
|
||||
// for any of the struct fields, then it attempts to match the struct field
|
||||
// using a case-insensitive match that also ignores dashes and underscores.
|
||||
// If multiple fields match,
|
||||
// - case: When unmarshaling, the "case" option specifies how
|
||||
// JSON object names are matched with the JSON name for Go struct fields.
|
||||
// The option is a key-value pair specified as "case:value" where
|
||||
// the value must either be 'ignore' or 'strict'.
|
||||
// The 'ignore' value specifies that matching is case-insensitive
|
||||
// where dashes and underscores are also ignored. If multiple fields match,
|
||||
// the first declared field in breadth-first order takes precedence.
|
||||
// This takes precedence even if [MatchCaseInsensitiveNames] is set to false.
|
||||
// This cannot be specified together with the "strictcase" option.
|
||||
//
|
||||
// - strictcase: When unmarshaling, the "strictcase" option specifies that the
|
||||
// JSON object name must exactly match the JSON name for the struct field.
|
||||
// This takes precedence even if [MatchCaseInsensitiveNames] is set to true.
|
||||
// This cannot be specified together with the "nocase" option.
|
||||
// The 'strict' value specifies that matching is case-sensitive.
|
||||
// This takes precedence over the [MatchCaseInsensitiveNames] option.
|
||||
//
|
||||
// - inline: The "inline" option specifies that
|
||||
// the JSON representable content of this field type is to be promoted
|
||||
@@ -107,9 +105,9 @@
|
||||
// A Go embedded field is implicitly inlined unless an explicit JSON name
|
||||
// is specified. The inlined field must be a Go struct
|
||||
// (that does not implement any JSON methods), [jsontext.Value],
|
||||
// map[string]T, or an unnamed pointer to such types. When marshaling,
|
||||
// map[~string]T, or an unnamed pointer to such types. When marshaling,
|
||||
// inlined fields from a pointer type are omitted if it is nil.
|
||||
// Inlined fields of type [jsontext.Value] and map[string]T are called
|
||||
// Inlined fields of type [jsontext.Value] and map[~string]T are called
|
||||
// “inlined fallbacks” as they can represent all possible
|
||||
// JSON object members not directly handled by the parent struct.
|
||||
// Only one inlined fallback field may be specified in a struct,
|
||||
@@ -119,7 +117,7 @@
|
||||
// - unknown: The "unknown" option is a specialized variant
|
||||
// of the inlined fallback to indicate that this Go struct field
|
||||
// contains any number of unknown JSON object members. The field type must
|
||||
// be a [jsontext.Value], map[string]T, or an unnamed pointer to such types.
|
||||
// be a [jsontext.Value], map[~string]T, or an unnamed pointer to such types.
|
||||
// If [DiscardUnknownMembers] is specified when marshaling,
|
||||
// the contents of this field are ignored.
|
||||
// If [RejectUnknownMembers] is specified when unmarshaling,
|
||||
|
||||
348
vendor/github.com/go-json-experiment/json/errors.go
generated
vendored
348
vendor/github.com/go-json-experiment/json/errors.go
generated
vendored
@@ -5,15 +5,58 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonopts"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
)
|
||||
|
||||
// ErrUnknownName indicates that a JSON object member could not be
|
||||
// unmarshaled because the name is not known to the target Go struct.
|
||||
// This error is directly wrapped within a [SemanticError] when produced.
|
||||
//
|
||||
// The name of an unknown JSON object member can be extracted as:
|
||||
//
|
||||
// err := ...
|
||||
// var serr json.SemanticError
|
||||
// if errors.As(err, &serr) && serr.Err == json.ErrUnknownName {
|
||||
// ptr := serr.JSONPointer // JSON pointer to unknown name
|
||||
// name := ptr.LastToken() // unknown name itself
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// This error is only returned if [RejectUnknownMembers] is true.
|
||||
var ErrUnknownName = errors.New("unknown object member name")
|
||||
|
||||
const errorPrefix = "json: "
|
||||
|
||||
func isSemanticError(err error) bool {
|
||||
_, ok := err.(*SemanticError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isSyntacticError(err error) bool {
|
||||
_, ok := err.(*jsontext.SyntacticError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// isFatalError reports whether this error must terminate asharling.
|
||||
// All errors are considered fatal unless operating under
|
||||
// [jsonflags.ReportErrorsWithLegacySemantics] in which case only
|
||||
// syntactic errors and I/O errors are considered fatal.
|
||||
func isFatalError(err error, flags jsonflags.Flags) bool {
|
||||
return !flags.Get(jsonflags.ReportErrorsWithLegacySemantics) ||
|
||||
isSyntacticError(err) || export.IsIOError(err)
|
||||
}
|
||||
|
||||
// SemanticError describes an error determining the meaning
|
||||
// of JSON data as Go data or vice-versa.
|
||||
//
|
||||
@@ -28,10 +71,13 @@ type SemanticError struct {
|
||||
ByteOffset int64
|
||||
// JSONPointer indicates that an error occurred within this JSON value
|
||||
// as indicated using the JSON Pointer notation (see RFC 6901).
|
||||
JSONPointer string
|
||||
JSONPointer jsontext.Pointer
|
||||
|
||||
// JSONKind is the JSON kind that could not be handled.
|
||||
JSONKind jsontext.Kind // may be zero if unknown
|
||||
// JSONValue is the JSON number or string that could not be unmarshaled.
|
||||
// It is not populated during marshaling.
|
||||
JSONValue jsontext.Value // may be nil if irrelevant or unknown
|
||||
// GoType is the Go type that could not be handled.
|
||||
GoType reflect.Type // may be nil if unknown
|
||||
|
||||
@@ -39,18 +85,218 @@ type SemanticError struct {
|
||||
Err error // may be nil
|
||||
}
|
||||
|
||||
// coder is implemented by [jsontext.Encoder] or [jsontext.Decoder].
|
||||
type coder interface{ StackPointer() jsontext.Pointer }
|
||||
|
||||
// newInvalidFormatError wraps err in a SemanticError because
|
||||
// the current type t cannot handle the provided options format.
|
||||
// This error must be called before producing or consuming the next value.
|
||||
//
|
||||
// If [jsonflags.ReportErrorsWithLegacySemantics] is specified,
|
||||
// then this automatically skips the next value when unmarshaling
|
||||
// to ensure that the value is fully consumed.
|
||||
func newInvalidFormatError(c coder, t reflect.Type, o *jsonopts.Struct) error {
|
||||
err := fmt.Errorf("invalid format flag %q", o.Format)
|
||||
switch c := c.(type) {
|
||||
case *jsontext.Encoder:
|
||||
err = newMarshalErrorBefore(c, t, err)
|
||||
case *jsontext.Decoder:
|
||||
err = newUnmarshalErrorBeforeWithSkipping(c, o, t, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// newMarshalErrorBefore wraps err in a SemanticError assuming that e
|
||||
// is positioned right before the next token or value, which causes an error.
|
||||
func newMarshalErrorBefore(e *jsontext.Encoder, t reflect.Type, err error) error {
|
||||
return &SemanticError{action: "marshal", GoType: t, Err: err,
|
||||
ByteOffset: e.OutputOffset() + int64(export.Encoder(e).CountNextDelimWhitespace()),
|
||||
JSONPointer: jsontext.Pointer(export.Encoder(e).AppendStackPointer(nil, +1))}
|
||||
}
|
||||
|
||||
// newUnmarshalErrorBefore wraps err in a SemanticError assuming that d
|
||||
// is positioned right before the next token or value, which causes an error.
|
||||
// It does not record the next JSON kind as this error is used to indicate
|
||||
// the receiving Go value is invalid to unmarshal into (and not a JSON error).
|
||||
func newUnmarshalErrorBefore(d *jsontext.Decoder, t reflect.Type, err error) error {
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: err,
|
||||
ByteOffset: d.InputOffset() + int64(export.Decoder(d).CountNextDelimWhitespace()),
|
||||
JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, +1))}
|
||||
}
|
||||
|
||||
// newUnmarshalErrorBeforeWithSkipping is like [newUnmarshalErrorBefore],
|
||||
// but automatically skips the next value if
|
||||
// [jsonflags.ReportErrorsWithLegacySemantics] is specified.
|
||||
func newUnmarshalErrorBeforeWithSkipping(d *jsontext.Decoder, o *jsonopts.Struct, t reflect.Type, err error) error {
|
||||
err = newUnmarshalErrorBefore(d, t, err)
|
||||
if o.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
if err2 := export.Decoder(d).SkipValue(); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// newUnmarshalErrorAfter wraps err in a SemanticError assuming that d
|
||||
// is positioned right after the previous token or value, which caused an error.
|
||||
func newUnmarshalErrorAfter(d *jsontext.Decoder, t reflect.Type, err error) error {
|
||||
tokOrVal := export.Decoder(d).PreviousTokenOrValue()
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: err,
|
||||
ByteOffset: d.InputOffset() - int64(len(tokOrVal)),
|
||||
JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, -1)),
|
||||
JSONKind: jsontext.Value(tokOrVal).Kind()}
|
||||
}
|
||||
|
||||
// newUnmarshalErrorAfter wraps err in a SemanticError assuming that d
|
||||
// is positioned right after the previous token or value, which caused an error.
|
||||
// It also stores a copy of the last JSON value if it is a string or number.
|
||||
func newUnmarshalErrorAfterWithValue(d *jsontext.Decoder, t reflect.Type, err error) error {
|
||||
serr := newUnmarshalErrorAfter(d, t, err).(*SemanticError)
|
||||
if serr.JSONKind == '"' || serr.JSONKind == '0' {
|
||||
serr.JSONValue = jsontext.Value(export.Decoder(d).PreviousTokenOrValue()).Clone()
|
||||
}
|
||||
return serr
|
||||
}
|
||||
|
||||
// newUnmarshalErrorAfterWithSkipping is like [newUnmarshalErrorAfter],
|
||||
// but automatically skips the remainder of the current value if
|
||||
// [jsonflags.ReportErrorsWithLegacySemantics] is specified.
|
||||
func newUnmarshalErrorAfterWithSkipping(d *jsontext.Decoder, o *jsonopts.Struct, t reflect.Type, err error) error {
|
||||
err = newUnmarshalErrorAfter(d, t, err)
|
||||
if o.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
if err2 := export.Decoder(d).SkipValueRemainder(); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// newSemanticErrorWithPosition wraps err in a SemanticError assuming that
|
||||
// the error occurred at the provided depth, and length.
|
||||
// If err is already a SemanticError, then position information is only
|
||||
// injected if it is currently unpopulated.
|
||||
//
|
||||
// If the position is unpopulated, it is ambiguous where the error occurred
|
||||
// in the user code, whether it was before or after the current position.
|
||||
// For the byte offset, we assume that the error occurred before the last read
|
||||
// token or value when decoding, or before the next value when encoding.
|
||||
// For the JSON pointer, we point to the parent object or array unless
|
||||
// we can be certain that it happened with an object member.
|
||||
//
|
||||
// This is used to annotate errors returned by user-provided
|
||||
// v2 MarshalJSON or UnmarshalJSON methods or functions.
|
||||
func newSemanticErrorWithPosition(c coder, t reflect.Type, prevDepth int, prevLength int64, err error) error {
|
||||
serr, _ := err.(*SemanticError)
|
||||
if serr == nil {
|
||||
serr = &SemanticError{Err: err}
|
||||
}
|
||||
var currDepth int
|
||||
var currLength int64
|
||||
var coderState interface{ AppendStackPointer([]byte, int) []byte }
|
||||
var offset int64
|
||||
switch c := c.(type) {
|
||||
case *jsontext.Encoder:
|
||||
e := export.Encoder(c)
|
||||
serr.action = cmp.Or(serr.action, "marshal")
|
||||
currDepth, currLength = e.Tokens.DepthLength()
|
||||
offset = c.OutputOffset() + int64(export.Encoder(c).CountNextDelimWhitespace())
|
||||
coderState = e
|
||||
case *jsontext.Decoder:
|
||||
d := export.Decoder(c)
|
||||
serr.action = cmp.Or(serr.action, "unmarshal")
|
||||
currDepth, currLength = d.Tokens.DepthLength()
|
||||
tokOrVal := d.PreviousTokenOrValue()
|
||||
offset = c.InputOffset() - int64(len(tokOrVal))
|
||||
if (prevDepth == currDepth && prevLength == currLength) || len(tokOrVal) == 0 {
|
||||
// If no Read method was called in the user-defined method or
|
||||
// if the Peek method was called, then use the offset of the next value.
|
||||
offset = c.InputOffset() + int64(export.Decoder(c).CountNextDelimWhitespace())
|
||||
}
|
||||
coderState = d
|
||||
}
|
||||
serr.ByteOffset = cmp.Or(serr.ByteOffset, offset)
|
||||
if serr.JSONPointer == "" {
|
||||
where := 0 // default to ambiguous positioning
|
||||
switch {
|
||||
case prevDepth == currDepth && prevLength+0 == currLength:
|
||||
where = +1
|
||||
case prevDepth == currDepth && prevLength+1 == currLength:
|
||||
where = -1
|
||||
}
|
||||
serr.JSONPointer = jsontext.Pointer(coderState.AppendStackPointer(nil, where))
|
||||
}
|
||||
serr.GoType = cmp.Or(serr.GoType, t)
|
||||
return serr
|
||||
}
|
||||
|
||||
// collapseSemanticErrors collapses double SemanticErrors at the outer levels
|
||||
// into a single SemanticError by preserving the inner error,
|
||||
// but prepending the ByteOffset and JSONPointer with the outer error.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// collapseSemanticErrors(&SemanticError{
|
||||
// ByteOffset: len64(`[0,{"alpha":[0,1,`),
|
||||
// JSONPointer: "/1/alpha/2",
|
||||
// GoType: reflect.TypeFor[outerType](),
|
||||
// Err: &SemanticError{
|
||||
// ByteOffset: len64(`{"foo":"bar","fizz":[0,`),
|
||||
// JSONPointer: "/fizz/1",
|
||||
// GoType: reflect.TypeFor[innerType](),
|
||||
// Err: ...,
|
||||
// },
|
||||
// })
|
||||
//
|
||||
// results in:
|
||||
//
|
||||
// &SemanticError{
|
||||
// ByteOffset: len64(`[0,{"alpha":[0,1,`) + len64(`{"foo":"bar","fizz":[0,`),
|
||||
// JSONPointer: "/1/alpha/2" + "/fizz/1",
|
||||
// GoType: reflect.TypeFor[innerType](),
|
||||
// Err: ...,
|
||||
// }
|
||||
//
|
||||
// This is used to annotate errors returned by user-provided
|
||||
// v1 MarshalJSON or UnmarshalJSON methods with precise position information
|
||||
// if they themselves happened to return a SemanticError.
|
||||
// Since MarshalJSON and UnmarshalJSON are not operating on the root JSON value,
|
||||
// their positioning must be relative to the nested JSON value
|
||||
// returned by UnmarshalJSON or passed to MarshalJSON.
|
||||
// Therefore, we can construct an absolute position by concatenating
|
||||
// the outer with the inner positions.
|
||||
//
|
||||
// Note that we do not use collapseSemanticErrors with user-provided functions
|
||||
// that take in an [jsontext.Encoder] or [jsontext.Decoder] since they contain
|
||||
// methods to report position relative to the root JSON value.
|
||||
// We assume user-constructed errors are correctly precise about position.
|
||||
func collapseSemanticErrors(err error) error {
|
||||
if serr1, ok := err.(*SemanticError); ok {
|
||||
if serr2, ok := serr1.Err.(*SemanticError); ok {
|
||||
serr2.ByteOffset = serr1.ByteOffset + serr2.ByteOffset
|
||||
serr2.JSONPointer = serr1.JSONPointer + serr2.JSONPointer
|
||||
*serr1 = *serr2
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// errorModalVerb is a modal verb like "cannot" or "unable to".
|
||||
//
|
||||
// Once per process, Hyrum-proof the error message by deliberately
|
||||
// switching between equivalent renderings of the same error message.
|
||||
// The randomization is tied to the Hyrum-proofing already applied
|
||||
// on map iteration in Go.
|
||||
var errorModalVerb = sync.OnceValue(func() string {
|
||||
for phrase := range map[string]struct{}{"cannot": {}, "unable to": {}} {
|
||||
return phrase // use whichever phrase we get in the first iteration
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
func (e *SemanticError) Error() string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(errorPrefix)
|
||||
|
||||
// Hyrum-proof the error message by deliberately switching between
|
||||
// two equivalent renderings of the same error message.
|
||||
// The randomization is tied to the Hyrum-proofing already applied
|
||||
// on map iteration in Go.
|
||||
for phrase := range map[string]struct{}{"cannot": {}, "unable to": {}} {
|
||||
sb.WriteString(phrase)
|
||||
break // use whichever phrase we get in the first iteration
|
||||
}
|
||||
sb.WriteString(errorModalVerb())
|
||||
|
||||
// Format action.
|
||||
var preposition string
|
||||
@@ -67,7 +313,6 @@ func (e *SemanticError) Error() string {
|
||||
}
|
||||
|
||||
// Format JSON kind.
|
||||
var omitPreposition bool
|
||||
switch e.JSONKind {
|
||||
case 'n':
|
||||
sb.WriteString(" JSON null")
|
||||
@@ -82,45 +327,92 @@ func (e *SemanticError) Error() string {
|
||||
case '[', ']':
|
||||
sb.WriteString(" JSON array")
|
||||
default:
|
||||
omitPreposition = true
|
||||
if e.action == "" {
|
||||
preposition = ""
|
||||
}
|
||||
}
|
||||
if len(e.JSONValue) > 0 && len(e.JSONValue) < 100 {
|
||||
sb.WriteByte(' ')
|
||||
sb.Write(e.JSONValue)
|
||||
}
|
||||
|
||||
// Format Go type.
|
||||
if e.GoType != nil {
|
||||
if !omitPreposition {
|
||||
sb.WriteString(preposition)
|
||||
typeString := e.GoType.String()
|
||||
if len(typeString) > 100 {
|
||||
// An excessively long type string most likely occurs for
|
||||
// an anonymous struct declaration with many fields.
|
||||
// Reduce the noise by just printing the kind,
|
||||
// and optionally prepending it with the package name
|
||||
// if the struct happens to include an unexported field.
|
||||
typeString = e.GoType.Kind().String()
|
||||
if e.GoType.Kind() == reflect.Struct && e.GoType.Name() == "" {
|
||||
for i := range e.GoType.NumField() {
|
||||
if pkgPath := e.GoType.Field(i).PkgPath; pkgPath != "" {
|
||||
typeString = pkgPath[strings.LastIndexByte(pkgPath, '/')+len("/"):] + ".struct"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.WriteString(" Go value of type ")
|
||||
sb.WriteString(e.GoType.String())
|
||||
sb.WriteString(preposition)
|
||||
sb.WriteString(" Go ")
|
||||
sb.WriteString(typeString)
|
||||
}
|
||||
|
||||
// Special handling for unknown names.
|
||||
if e.Err == ErrUnknownName {
|
||||
sb.WriteString(": ")
|
||||
sb.WriteString(ErrUnknownName.Error())
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(strconv.Quote(e.JSONPointer.LastToken()))
|
||||
if parent := e.JSONPointer.Parent(); parent != "" {
|
||||
sb.WriteString(" within ")
|
||||
sb.WriteString(strconv.Quote(jsonwire.TruncatePointer(string(parent), 100)))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// Format where.
|
||||
switch {
|
||||
// Avoid printing if it overlaps with a wrapped SyntacticError.
|
||||
switch serr, _ := e.Err.(*jsontext.SyntacticError); {
|
||||
case e.JSONPointer != "":
|
||||
sb.WriteString(" within JSON value at ")
|
||||
sb.WriteString(strconv.Quote(e.JSONPointer))
|
||||
if serr == nil || !e.JSONPointer.Contains(serr.JSONPointer) {
|
||||
sb.WriteString(" within ")
|
||||
sb.WriteString(strconv.Quote(jsonwire.TruncatePointer(string(e.JSONPointer), 100)))
|
||||
}
|
||||
case e.ByteOffset > 0:
|
||||
sb.WriteString(" after byte offset ")
|
||||
sb.WriteString(strconv.FormatInt(e.ByteOffset, 10))
|
||||
if serr == nil || !(e.ByteOffset <= serr.ByteOffset) {
|
||||
sb.WriteString(" after offset ")
|
||||
sb.WriteString(strconv.FormatInt(e.ByteOffset, 10))
|
||||
}
|
||||
}
|
||||
|
||||
// Format underlying error.
|
||||
if e.Err != nil {
|
||||
errString := e.Err.Error()
|
||||
if isSyntacticError(e.Err) {
|
||||
errString = strings.TrimPrefix(errString, "jsontext: ")
|
||||
}
|
||||
sb.WriteString(": ")
|
||||
sb.WriteString(e.Err.Error())
|
||||
sb.WriteString(errString)
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (e *SemanticError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func firstError(errs ...error) error {
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func newDuplicateNameError(ptr jsontext.Pointer, quotedName []byte, offset int64) error {
|
||||
if quotedName != nil {
|
||||
name, _ := jsonwire.AppendUnquote(nil, quotedName)
|
||||
ptr = ptr.AppendToken(string(name))
|
||||
}
|
||||
return &jsontext.SyntacticError{
|
||||
ByteOffset: offset,
|
||||
JSONPointer: ptr,
|
||||
Err: jsontext.ErrDuplicateName,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
317
vendor/github.com/go-json-experiment/json/fields.go
generated
vendored
317
vendor/github.com/go-json-experiment/json/fields.go
generated
vendored
@@ -20,13 +20,11 @@ import (
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
var errIgnoredField = errors.New("ignored field")
|
||||
|
||||
type isZeroer interface {
|
||||
IsZero() bool
|
||||
}
|
||||
|
||||
var isZeroerType = reflect.TypeOf((*isZeroer)(nil)).Elem()
|
||||
var isZeroerType = reflect.TypeFor[isZeroer]()
|
||||
|
||||
type structFields struct {
|
||||
flattened []structField // listed in depth-first ordering
|
||||
@@ -35,6 +33,27 @@ type structFields struct {
|
||||
inlinedFallback *structField
|
||||
}
|
||||
|
||||
// reindex recomputes index to avoid bounds check during runtime.
|
||||
//
|
||||
// During the construction of each [structField] in [makeStructFields],
|
||||
// the index field is 0-indexed. However, before it returns,
|
||||
// the 0th field is stored in index0 and index stores the remainder.
|
||||
func (sf *structFields) reindex() {
|
||||
reindex := func(f *structField) {
|
||||
f.index0 = f.index[0]
|
||||
f.index = f.index[1:]
|
||||
if len(f.index) == 0 {
|
||||
f.index = nil // avoid pinning the backing slice
|
||||
}
|
||||
}
|
||||
for i := range sf.flattened {
|
||||
reindex(&sf.flattened[i])
|
||||
}
|
||||
if sf.inlinedFallback != nil {
|
||||
reindex(sf.inlinedFallback)
|
||||
}
|
||||
}
|
||||
|
||||
// lookupByFoldedName looks up name by a case-insensitive match
|
||||
// that also ignores the presence of dashes and underscores.
|
||||
func (fs *structFields) lookupByFoldedName(name []byte) []*structField {
|
||||
@@ -43,7 +62,8 @@ func (fs *structFields) lookupByFoldedName(name []byte) []*structField {
|
||||
|
||||
type structField struct {
|
||||
id int // unique numeric ID in breadth-first ordering
|
||||
index []int // index into a struct according to reflect.Type.FieldByIndex
|
||||
index0 int // 0th index into a struct according to [reflect.Type.FieldByIndex]
|
||||
index []int // 1st index and remainder according to [reflect.Type.FieldByIndex]
|
||||
typ reflect.Type
|
||||
fncs *arshaler
|
||||
isZero func(addressableValue) bool
|
||||
@@ -51,7 +71,13 @@ type structField struct {
|
||||
fieldOptions
|
||||
}
|
||||
|
||||
func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
var errNoExportedFields = errors.New("Go struct has no exported fields")
|
||||
|
||||
func makeStructFields(root reflect.Type) (fs structFields, serr *SemanticError) {
|
||||
orErrorf := func(serr *SemanticError, t reflect.Type, f string, a ...any) *SemanticError {
|
||||
return cmp.Or(serr, &SemanticError{GoType: t, Err: fmt.Errorf(f, a...)})
|
||||
}
|
||||
|
||||
// Setup a queue for a breath-first search.
|
||||
var queueIndex int
|
||||
type queueEntry struct {
|
||||
@@ -74,14 +100,15 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
namesIndex := make(map[string]int) // index of each field with a given JSON object name in current struct
|
||||
var hasAnyJSONTag bool // whether any Go struct field has a `json` tag
|
||||
var hasAnyJSONField bool // whether any JSON serializable fields exist in current struct
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
for i := range t.NumField() {
|
||||
sf := t.Field(i)
|
||||
_, hasTag := sf.Tag.Lookup("json")
|
||||
hasAnyJSONTag = hasAnyJSONTag || hasTag
|
||||
options, ignored, err := parseFieldOptions(sf)
|
||||
if err != nil {
|
||||
return structFields{}, &SemanticError{GoType: t, Err: err}
|
||||
} else if ignored {
|
||||
serr = cmp.Or(serr, &SemanticError{GoType: t, Err: err})
|
||||
}
|
||||
if ignored {
|
||||
continue
|
||||
}
|
||||
hasAnyJSONField = true
|
||||
@@ -94,52 +121,55 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
fieldOptions: options,
|
||||
}
|
||||
if sf.Anonymous && !f.hasName {
|
||||
f.inline = true // implied by use of Go embedding without an explicit name
|
||||
if indirectType(f.typ).Kind() != reflect.Struct {
|
||||
serr = orErrorf(serr, t, "embedded Go struct field %s of non-struct type must be explicitly given a JSON name", sf.Name)
|
||||
} else {
|
||||
f.inline = true // implied by use of Go embedding without an explicit name
|
||||
}
|
||||
}
|
||||
if f.inline || f.unknown {
|
||||
// Handle an inlined field that serializes to/from
|
||||
// zero or more JSON object members.
|
||||
|
||||
if f.inline && f.unknown {
|
||||
err := fmt.Errorf("Go struct field %s cannot have both `inline` and `unknown` specified", sf.Name)
|
||||
return structFields{}, &SemanticError{GoType: t, Err: err}
|
||||
}
|
||||
switch f.fieldOptions {
|
||||
case fieldOptions{name: f.name, quotedName: f.quotedName, inline: true}:
|
||||
case fieldOptions{name: f.name, quotedName: f.quotedName, unknown: true}:
|
||||
case fieldOptions{name: f.name, quotedName: f.quotedName, inline: true, unknown: true}:
|
||||
serr = orErrorf(serr, t, "Go struct field %s cannot have both `inline` and `unknown` specified", sf.Name)
|
||||
f.inline = false // let `unknown` take precedence
|
||||
default:
|
||||
err := fmt.Errorf("Go struct field %s cannot have any options other than `inline` or `unknown` specified", sf.Name)
|
||||
return structFields{}, &SemanticError{GoType: t, Err: err}
|
||||
serr = orErrorf(serr, t, "Go struct field %s cannot have any options other than `inline` or `unknown` specified", sf.Name)
|
||||
if f.hasName {
|
||||
continue // invalid inlined field; treat as ignored
|
||||
}
|
||||
f.fieldOptions = fieldOptions{name: f.name, quotedName: f.quotedName, inline: f.inline, unknown: f.unknown}
|
||||
if f.inline && f.unknown {
|
||||
f.inline = false // let `unknown` take precedence
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap one level of pointer indirection similar to how Go
|
||||
// only allows embedding either T or *T, but not **T.
|
||||
tf := f.typ
|
||||
if tf.Kind() == reflect.Pointer && tf.Name() == "" {
|
||||
tf = tf.Elem()
|
||||
}
|
||||
// Reject any types with custom serialization otherwise
|
||||
// it becomes impossible to know what sub-fields to inline.
|
||||
if which := implementsWhich(tf,
|
||||
jsonMarshalerV2Type, jsonMarshalerV1Type, textMarshalerType,
|
||||
jsonUnmarshalerV2Type, jsonUnmarshalerV1Type, textUnmarshalerType,
|
||||
); which != nil && tf != jsontextValueType {
|
||||
err := fmt.Errorf("inlined Go struct field %s of type %s must not implement JSON marshal or unmarshal methods", sf.Name, tf)
|
||||
return structFields{}, &SemanticError{GoType: t, Err: err}
|
||||
tf := indirectType(f.typ)
|
||||
if implementsAny(tf, allMethodTypes...) && tf != jsontextValueType {
|
||||
serr = orErrorf(serr, t, "inlined Go struct field %s of type %s must not implement marshal or unmarshal methods", sf.Name, tf)
|
||||
}
|
||||
|
||||
// Handle an inlined field that serializes to/from
|
||||
// a finite number of JSON object members backed by a Go struct.
|
||||
if tf.Kind() == reflect.Struct {
|
||||
if f.unknown {
|
||||
err := fmt.Errorf("inlined Go struct field %s of type %s with `unknown` tag must be a Go map of string key or a jsontext.Value", sf.Name, tf)
|
||||
return structFields{}, &SemanticError{GoType: t, Err: err}
|
||||
serr = orErrorf(serr, t, "inlined Go struct field %s of type %s with `unknown` tag must be a Go map of string key or a jsontext.Value", sf.Name, tf)
|
||||
continue // invalid inlined field; treat as ignored
|
||||
}
|
||||
if qe.visitChildren {
|
||||
queue = append(queue, queueEntry{tf, f.index, !seen[tf]})
|
||||
}
|
||||
seen[tf] = true
|
||||
continue
|
||||
} else if !sf.IsExported() {
|
||||
serr = orErrorf(serr, t, "inlined Go struct field %s is not exported", sf.Name)
|
||||
continue // invalid inlined field; treat as ignored
|
||||
}
|
||||
|
||||
// Handle an inlined field that serializes to/from any number of
|
||||
@@ -147,17 +177,22 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
switch {
|
||||
case tf == jsontextValueType:
|
||||
f.fncs = nil // specially handled in arshal_inlined.go
|
||||
case tf.Kind() == reflect.Map && tf.Key() == stringType:
|
||||
case tf.Kind() == reflect.Map && tf.Key().Kind() == reflect.String:
|
||||
if implementsAny(tf.Key(), allMethodTypes...) {
|
||||
serr = orErrorf(serr, t, "inlined map field %s of type %s must have a string key that does not implement marshal or unmarshal methods", sf.Name, tf)
|
||||
continue // invalid inlined field; treat as ignored
|
||||
}
|
||||
f.fncs = lookupArshaler(tf.Elem())
|
||||
default:
|
||||
err := fmt.Errorf("inlined Go struct field %s of type %s must be a Go struct, Go map of string key, or jsontext.Value", sf.Name, tf)
|
||||
return structFields{}, &SemanticError{GoType: t, Err: err}
|
||||
serr = orErrorf(serr, t, "inlined Go struct field %s of type %s must be a Go struct, Go map of string key, or jsontext.Value", sf.Name, tf)
|
||||
continue // invalid inlined field; treat as ignored
|
||||
}
|
||||
|
||||
// Reject multiple inlined fallback fields within the same struct.
|
||||
if inlinedFallbackIndex >= 0 {
|
||||
err := fmt.Errorf("inlined Go struct fields %s and %s cannot both be a Go map or jsontext.Value", t.Field(inlinedFallbackIndex).Name, sf.Name)
|
||||
return structFields{}, &SemanticError{GoType: t, Err: err}
|
||||
serr = orErrorf(serr, t, "inlined Go struct fields %s and %s cannot both be a Go map or jsontext.Value", t.Field(inlinedFallbackIndex).Name, sf.Name)
|
||||
// Still append f to inlinedFallbacks as there is still a
|
||||
// check for a dominant inlined fallback before returning.
|
||||
}
|
||||
inlinedFallbackIndex = i
|
||||
|
||||
@@ -166,6 +201,24 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
// Handle normal Go struct field that serializes to/from
|
||||
// a single JSON object member.
|
||||
|
||||
// Unexported fields cannot be serialized except for
|
||||
// embedded fields of a struct type,
|
||||
// which might promote exported fields of their own.
|
||||
if !sf.IsExported() {
|
||||
tf := indirectType(f.typ)
|
||||
if !(sf.Anonymous && tf.Kind() == reflect.Struct) {
|
||||
serr = orErrorf(serr, t, "Go struct field %s is not exported", sf.Name)
|
||||
continue
|
||||
}
|
||||
// Unfortunately, methods on the unexported field
|
||||
// still cannot be called.
|
||||
if implementsAny(tf, allMethodTypes...) ||
|
||||
(f.omitzero && implementsAny(tf, isZeroerType)) {
|
||||
serr = orErrorf(serr, t, "Go struct field %s is not exported for method calls", sf.Name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Provide a function that uses a type's IsZero method.
|
||||
switch {
|
||||
case sf.Type.Kind() == reflect.Interface && sf.Type.Implements(isZeroerType):
|
||||
@@ -194,15 +247,11 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
f.isEmpty = func(va addressableValue) bool { return va.IsNil() }
|
||||
}
|
||||
|
||||
// Reject user-specified names with invalid UTF-8.
|
||||
if !utf8.ValidString(f.name) {
|
||||
err := fmt.Errorf("Go struct field %s has JSON object name %q with invalid UTF-8", sf.Name, f.name)
|
||||
return structFields{}, &SemanticError{GoType: t, Err: err}
|
||||
}
|
||||
// Reject multiple fields with same name within the same struct.
|
||||
if j, ok := namesIndex[f.name]; ok {
|
||||
err := fmt.Errorf("Go struct fields %s and %s conflict over JSON object name %q", t.Field(j).Name, sf.Name, f.name)
|
||||
return structFields{}, &SemanticError{GoType: t, Err: err}
|
||||
serr = orErrorf(serr, t, "Go struct fields %s and %s conflict over JSON object name %q", t.Field(j).Name, sf.Name, f.name)
|
||||
// Still append f to allFields as there is still a
|
||||
// check for a dominant field before returning.
|
||||
}
|
||||
namesIndex[f.name] = i
|
||||
|
||||
@@ -223,8 +272,7 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
// errors returned by errors.New would fail to serialize.
|
||||
isEmptyStruct := t.NumField() == 0
|
||||
if !isEmptyStruct && !hasAnyJSONTag && !hasAnyJSONField {
|
||||
err := errors.New("Go struct has no exported fields")
|
||||
return structFields{}, &SemanticError{GoType: t, Err: err}
|
||||
serr = cmp.Or(serr, &SemanticError{GoType: t, Err: errNoExportedFields})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,19 +284,11 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
// or the one that is uniquely tagged with a JSON name.
|
||||
// Otherwise, no dominant field exists for the set.
|
||||
flattened := allFields[:0]
|
||||
slices.SortFunc(allFields, func(x, y structField) int {
|
||||
switch {
|
||||
case x.name != y.name:
|
||||
return strings.Compare(x.name, y.name)
|
||||
case len(x.index) != len(y.index):
|
||||
return cmp.Compare(len(x.index), len(y.index))
|
||||
case x.hasName && !y.hasName:
|
||||
return -1
|
||||
case !x.hasName && y.hasName:
|
||||
return +1
|
||||
default:
|
||||
return 0 // TODO(https://go.dev/issue/61643): Compare bools better.
|
||||
}
|
||||
slices.SortStableFunc(allFields, func(x, y structField) int {
|
||||
return cmp.Or(
|
||||
strings.Compare(x.name, y.name),
|
||||
cmp.Compare(len(x.index), len(y.index)),
|
||||
boolsCompare(!x.hasName, !y.hasName))
|
||||
})
|
||||
for len(allFields) > 0 {
|
||||
n := 1 // number of fields with the same exact name
|
||||
@@ -279,7 +319,7 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
|
||||
// Compute the mapping of fields in the byActualName map.
|
||||
// Pre-fold all names so that we can lookup folded names quickly.
|
||||
fs := structFields{
|
||||
fs = structFields{
|
||||
flattened: flattened,
|
||||
byActualName: make(map[string]*structField, len(flattened)),
|
||||
byFoldedName: make(map[string][]*structField, len(flattened)),
|
||||
@@ -291,7 +331,7 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
}
|
||||
for foldedName, fields := range fs.byFoldedName {
|
||||
if len(fields) > 1 {
|
||||
// The precedence order for conflicting nocase names
|
||||
// The precedence order for conflicting ignoreCase names
|
||||
// is by breadth-first order, rather than depth-first order.
|
||||
slices.SortFunc(fields, func(x, y *structField) int {
|
||||
return cmp.Compare(x.id, y.id)
|
||||
@@ -302,17 +342,27 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
if n := len(inlinedFallbacks); n == 1 || (n > 1 && len(inlinedFallbacks[0].index) != len(inlinedFallbacks[1].index)) {
|
||||
fs.inlinedFallback = &inlinedFallbacks[0] // dominant inlined fallback field
|
||||
}
|
||||
fs.reindex()
|
||||
return fs, serr
|
||||
}
|
||||
|
||||
return fs, nil
|
||||
// indirectType unwraps one level of pointer indirection
|
||||
// similar to how Go only allows embedding either T or *T,
|
||||
// but not **T or P (which is a named pointer).
|
||||
func indirectType(t reflect.Type) reflect.Type {
|
||||
if t.Kind() == reflect.Pointer && t.Name() == "" {
|
||||
t = t.Elem()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// matchFoldedName matches a case-insensitive name depending on the options.
|
||||
// It assumes that foldName(f.name) == foldName(name).
|
||||
//
|
||||
// Case-insensitive matching is used if the `nocase` tag option is specified
|
||||
// Case-insensitive matching is used if the `case:ignore` tag option is specified
|
||||
// or the MatchCaseInsensitiveNames call option is specified
|
||||
// (and the `strictcase` tag option is not specified).
|
||||
// Functionally, the `nocase` and `strictcase` tag options take precedence.
|
||||
// (and the `case:strict` tag option is not specified).
|
||||
// Functionally, the `case:ignore` and `case:strict` tag options take precedence.
|
||||
//
|
||||
// The v1 definition of case-insensitivity operated under strings.EqualFold
|
||||
// and would strictly compare dashes and underscores,
|
||||
@@ -320,7 +370,7 @@ func makeStructFields(root reflect.Type) (structFields, *SemanticError) {
|
||||
// Thus, if the MatchCaseSensitiveDelimiter call option is specified,
|
||||
// the match is further restricted to using strings.EqualFold.
|
||||
func (f *structField) matchFoldedName(name []byte, flags *jsonflags.Flags) bool {
|
||||
if f.casing == nocase || (flags.Get(jsonflags.MatchCaseInsensitiveNames) && f.casing != strictcase) {
|
||||
if f.casing == caseIgnore || (flags.Get(jsonflags.MatchCaseInsensitiveNames) && f.casing != caseStrict) {
|
||||
if !flags.Get(jsonflags.MatchCaseSensitiveDelimiter) || strings.EqualFold(string(name), f.name) {
|
||||
return true
|
||||
}
|
||||
@@ -329,21 +379,22 @@ func (f *structField) matchFoldedName(name []byte, flags *jsonflags.Flags) bool
|
||||
}
|
||||
|
||||
const (
|
||||
nocase = 1
|
||||
strictcase = 2
|
||||
caseIgnore = 1
|
||||
caseStrict = 2
|
||||
)
|
||||
|
||||
type fieldOptions struct {
|
||||
name string
|
||||
quotedName string // quoted name per RFC 8785, section 3.2.2.2.
|
||||
hasName bool
|
||||
casing int8 // either 0, nocase, or strictcase
|
||||
inline bool
|
||||
unknown bool
|
||||
omitzero bool
|
||||
omitempty bool
|
||||
string bool
|
||||
format string
|
||||
name string
|
||||
quotedName string // quoted name per RFC 8785, section 3.2.2.2.
|
||||
hasName bool
|
||||
nameNeedEscape bool
|
||||
casing int8 // either 0, caseIgnore, or caseStrict
|
||||
inline bool
|
||||
unknown bool
|
||||
omitzero bool
|
||||
omitempty bool
|
||||
string bool
|
||||
format string
|
||||
}
|
||||
|
||||
// parseFieldOptions parses the `json` tag in a Go struct field as
|
||||
@@ -357,19 +408,19 @@ func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool,
|
||||
return fieldOptions{}, true, nil
|
||||
}
|
||||
|
||||
// Check whether this field is unexported.
|
||||
if !sf.IsExported() {
|
||||
// In contrast to v1, v2 no longer forwards exported fields from
|
||||
// embedded fields of unexported types since Go reflection does not
|
||||
// allow the same set of operations that are available in normal cases
|
||||
// of purely exported fields.
|
||||
// See https://go.dev/issue/21357 and https://go.dev/issue/24153.
|
||||
if sf.Anonymous {
|
||||
err = firstError(err, fmt.Errorf("embedded Go struct field %s of an unexported type must be explicitly ignored with a `json:\"-\"` tag", sf.Type.Name()))
|
||||
}
|
||||
// Check whether this field is unexported and not embedded,
|
||||
// which Go reflection cannot mutate for the sake of serialization.
|
||||
//
|
||||
// An embedded field of an unexported type is still capable of
|
||||
// forwarding exported fields, which may be JSON serialized.
|
||||
// This technically operates on the edge of what is permissible by
|
||||
// the Go language, but the most recent decision is to permit this.
|
||||
//
|
||||
// See https://go.dev/issue/24153 and https://go.dev/issue/32772.
|
||||
if !sf.IsExported() && !sf.Anonymous {
|
||||
// Tag options specified on an unexported field suggests user error.
|
||||
if hasTag {
|
||||
err = firstError(err, fmt.Errorf("unexported Go struct field %s cannot have non-ignored `json:%q` tag", sf.Name, tag))
|
||||
err = cmp.Or(err, fmt.Errorf("unexported Go struct field %s cannot have non-ignored `json:%q` tag", sf.Name, tag))
|
||||
}
|
||||
return fieldOptions{}, true, err
|
||||
}
|
||||
@@ -384,21 +435,31 @@ func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool,
|
||||
n := len(tag) - len(strings.TrimLeftFunc(tag, func(r rune) bool {
|
||||
return !strings.ContainsRune(",\\'\"`", r) // reserve comma, backslash, and quotes
|
||||
}))
|
||||
opt := tag[:n]
|
||||
if n == 0 {
|
||||
// Allow a single quoted string for arbitrary names.
|
||||
var err2 error
|
||||
opt, n, err2 = consumeTagOption(tag)
|
||||
name := tag[:n]
|
||||
|
||||
// If the next character is not a comma, then the name is either
|
||||
// malformed (if n > 0) or a single-quoted name.
|
||||
// In either case, call consumeTagOption to handle it further.
|
||||
var err2 error
|
||||
if !strings.HasPrefix(tag[n:], ",") && len(name) != len(tag) {
|
||||
name, n, err2 = consumeTagOption(tag)
|
||||
if err2 != nil {
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s has malformed `json` tag: %v", sf.Name, err2))
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has malformed `json` tag: %v", sf.Name, err2))
|
||||
}
|
||||
}
|
||||
out.hasName = true
|
||||
out.name = opt
|
||||
if !utf8.ValidString(name) {
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has JSON object name %q with invalid UTF-8", sf.Name, name))
|
||||
name = string([]rune(name)) // replace invalid UTF-8 with utf8.RuneError
|
||||
}
|
||||
if err2 == nil {
|
||||
out.hasName = true
|
||||
out.name = name
|
||||
}
|
||||
tag = tag[n:]
|
||||
}
|
||||
b, _ := jsonwire.AppendQuote(nil, out.name, &jsonflags.Flags{})
|
||||
out.quotedName = string(b)
|
||||
out.nameNeedEscape = jsonwire.NeedEscape(out.name)
|
||||
|
||||
// Handle any additional tag options (if any).
|
||||
var wasFormat bool
|
||||
@@ -406,11 +467,11 @@ func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool,
|
||||
for len(tag) > 0 {
|
||||
// Consume comma delimiter.
|
||||
if tag[0] != ',' {
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s has malformed `json` tag: invalid character %q before next option (expecting ',')", sf.Name, tag[0]))
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has malformed `json` tag: invalid character %q before next option (expecting ',')", sf.Name, tag[0]))
|
||||
} else {
|
||||
tag = tag[len(","):]
|
||||
if len(tag) == 0 {
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s has malformed `json` tag: invalid trailing ',' character", sf.Name))
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has malformed `json` tag: invalid trailing ',' character", sf.Name))
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -418,21 +479,41 @@ func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool,
|
||||
// Consume and process the tag option.
|
||||
opt, n, err2 := consumeTagOption(tag)
|
||||
if err2 != nil {
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s has malformed `json` tag: %v", sf.Name, err2))
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has malformed `json` tag: %v", sf.Name, err2))
|
||||
}
|
||||
rawOpt := tag[:n]
|
||||
tag = tag[n:]
|
||||
switch {
|
||||
case wasFormat:
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s has `format` tag option that was not specified last", sf.Name))
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has `format` tag option that was not specified last", sf.Name))
|
||||
case strings.HasPrefix(rawOpt, "'") && strings.TrimFunc(opt, isLetterOrDigit) == "":
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s has unnecessarily quoted appearance of `%s` tag option; specify `%s` instead", sf.Name, rawOpt, opt))
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has unnecessarily quoted appearance of `%s` tag option; specify `%s` instead", sf.Name, rawOpt, opt))
|
||||
}
|
||||
switch opt {
|
||||
case "nocase":
|
||||
out.casing |= nocase
|
||||
case "strictcase":
|
||||
out.casing |= strictcase
|
||||
case "case":
|
||||
if !strings.HasPrefix(tag, ":") {
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s is missing value for `case` tag option; specify `case:ignore` or `case:strict` instead", sf.Name))
|
||||
break
|
||||
}
|
||||
tag = tag[len(":"):]
|
||||
opt, n, err2 := consumeTagOption(tag)
|
||||
if err2 != nil {
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has malformed value for `case` tag option: %v", sf.Name, err2))
|
||||
break
|
||||
}
|
||||
rawOpt := tag[:n]
|
||||
tag = tag[n:]
|
||||
if strings.HasPrefix(rawOpt, "'") {
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has unnecessarily quoted appearance of `case:%s` tag option; specify `case:%s` instead", sf.Name, rawOpt, opt))
|
||||
}
|
||||
switch opt {
|
||||
case "ignore":
|
||||
out.casing |= caseIgnore
|
||||
case "strict":
|
||||
out.casing |= caseStrict
|
||||
default:
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has unknown `case:%s` tag value", sf.Name, rawOpt))
|
||||
}
|
||||
case "inline":
|
||||
out.inline = true
|
||||
case "unknown":
|
||||
@@ -445,13 +526,13 @@ func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool,
|
||||
out.string = true
|
||||
case "format":
|
||||
if !strings.HasPrefix(tag, ":") {
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s is missing value for `format` tag option", sf.Name))
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s is missing value for `format` tag option", sf.Name))
|
||||
break
|
||||
}
|
||||
tag = tag[len(":"):]
|
||||
opt, n, err2 := consumeTagOption(tag)
|
||||
if err2 != nil {
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s has malformed value for `format` tag option: %v", sf.Name, err2))
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has malformed value for `format` tag option: %v", sf.Name, err2))
|
||||
break
|
||||
}
|
||||
tag = tag[n:]
|
||||
@@ -462,8 +543,8 @@ func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool,
|
||||
// This catches invalid mutants such as "omitEmpty" or "omit_empty".
|
||||
normOpt := strings.ReplaceAll(strings.ToLower(opt), "_", "")
|
||||
switch normOpt {
|
||||
case "nocase", "strictcase", "inline", "unknown", "omitzero", "omitempty", "string", "format":
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s has invalid appearance of `%s` tag option; specify `%s` instead", sf.Name, opt, normOpt))
|
||||
case "case", "inline", "unknown", "omitzero", "omitempty", "string", "format":
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has invalid appearance of `%s` tag option; specify `%s` instead", sf.Name, opt, normOpt))
|
||||
}
|
||||
|
||||
// NOTE: Everything else is ignored. This does not mean it is
|
||||
@@ -473,16 +554,20 @@ func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool,
|
||||
|
||||
// Reject duplicates.
|
||||
switch {
|
||||
case out.casing == nocase|strictcase:
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s cannot have both `nocase` and `structcase` tag options", sf.Name))
|
||||
case out.casing == caseIgnore|caseStrict:
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s cannot have both `case:ignore` and `case:strict` tag options", sf.Name))
|
||||
case seenOpts[opt]:
|
||||
err = firstError(err, fmt.Errorf("Go struct field %s has duplicate appearance of `%s` tag option", sf.Name, rawOpt))
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has duplicate appearance of `%s` tag option", sf.Name, rawOpt))
|
||||
}
|
||||
seenOpts[opt] = true
|
||||
}
|
||||
return out, false, err
|
||||
}
|
||||
|
||||
// consumeTagOption consumes the next option,
|
||||
// which is either a Go identifier or a single-quoted string.
|
||||
// If the next option is invalid, it returns all of in until the next comma,
|
||||
// and reports an error.
|
||||
func consumeTagOption(in string) (string, int, error) {
|
||||
// For legacy compatibility with v1, assume options are comma-separated.
|
||||
i := strings.IndexByte(in, ',')
|
||||
@@ -545,3 +630,15 @@ func consumeTagOption(in string) (string, int, error) {
|
||||
func isLetterOrDigit(r rune) bool {
|
||||
return r == '_' || unicode.IsLetter(r) || unicode.IsNumber(r)
|
||||
}
|
||||
|
||||
// boolsCompare compares x and y, ordering false before true.
|
||||
func boolsCompare(x, y bool) int {
|
||||
switch {
|
||||
case !x && y:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
case x && !y:
|
||||
return +1
|
||||
}
|
||||
}
|
||||
|
||||
25
vendor/github.com/go-json-experiment/json/internal/internal.go
generated
vendored
25
vendor/github.com/go-json-experiment/json/internal/internal.go
generated
vendored
@@ -4,6 +4,8 @@
|
||||
|
||||
package internal
|
||||
|
||||
import "errors"
|
||||
|
||||
// NotForPublicUse is a marker type that an API is for internal use only.
|
||||
// It does not perfectly prevent usage of that API, but helps to restrict usage.
|
||||
// Anything with this marker is not covered by the Go compatibility agreement.
|
||||
@@ -12,3 +14,26 @@ type NotForPublicUse struct{}
|
||||
// AllowInternalUse is passed from "json" to "jsontext" to authenticate
|
||||
// that the caller can have access to internal functionality.
|
||||
var AllowInternalUse NotForPublicUse
|
||||
|
||||
// Sentinel error values internally shared between jsonv1 and jsonv2.
|
||||
var (
|
||||
ErrCycle = errors.New("encountered a cycle")
|
||||
ErrNonNilReference = errors.New("value must be passed as a non-nil pointer reference")
|
||||
)
|
||||
|
||||
var (
|
||||
// TransformMarshalError converts a v2 error into a v1 error.
|
||||
// It is called only at the top-level of a Marshal function.
|
||||
TransformMarshalError func(any, error) error
|
||||
// NewMarshalerError constructs a jsonv1.MarshalerError.
|
||||
// It is called after a user-defined Marshal method/function fails.
|
||||
NewMarshalerError func(any, error, string) error
|
||||
// TransformUnmarshalError converts a v2 error into a v1 error.
|
||||
// It is called only at the top-level of a Unmarshal function.
|
||||
TransformUnmarshalError func(any, error) error
|
||||
|
||||
// NewRawNumber returns new(jsonv1.Number).
|
||||
NewRawNumber func() any
|
||||
// RawNumberOf returns jsonv1.Number(b).
|
||||
RawNumberOf func(b []byte) any
|
||||
)
|
||||
|
||||
83
vendor/github.com/go-json-experiment/json/internal/jsonflags/flags.go
generated
vendored
83
vendor/github.com/go-json-experiment/json/internal/jsonflags/flags.go
generated
vendored
@@ -14,7 +14,7 @@ import "github.com/go-json-experiment/json/internal"
|
||||
//
|
||||
// In common usage, this is OR'd with 0 or 1. For example:
|
||||
// - (AllowInvalidUTF8 | 0) means "AllowInvalidUTF8 is false"
|
||||
// - (Expand | Indent | 1) means "Expand and Indent are true"
|
||||
// - (Multiline | Indent | 1) means "Multiline and Indent are true"
|
||||
type Bools uint64
|
||||
|
||||
func (Bools) JSONOptions(internal.NotForPublicUse) {}
|
||||
@@ -50,40 +50,59 @@ const (
|
||||
AllowInvalidUTF8 |
|
||||
EscapeForHTML |
|
||||
EscapeForJS |
|
||||
EscapeInvalidUTF8 |
|
||||
PreserveRawStrings |
|
||||
Deterministic |
|
||||
FormatNilMapAsNull |
|
||||
FormatNilSliceAsNull |
|
||||
MatchCaseInsensitiveNames |
|
||||
FormatByteArrayAsArray |
|
||||
FormatTimeDurationAsNanosecond |
|
||||
IgnoreStructErrors |
|
||||
CallMethodsWithLegacySemantics |
|
||||
FormatBytesWithLegacySemantics |
|
||||
FormatTimeWithLegacySemantics |
|
||||
MatchCaseSensitiveDelimiter |
|
||||
MergeWithLegacySemantics |
|
||||
OmitEmptyWithLegacyDefinition |
|
||||
RejectFloatOverflow |
|
||||
ReportLegacyErrorValues |
|
||||
SkipUnaddressableMethods |
|
||||
ReportErrorsWithLegacySemantics |
|
||||
StringifyWithLegacySemantics |
|
||||
UnmarshalArrayFromAnyLength
|
||||
|
||||
// AnyWhitespace reports whether the encoded output might have any whitespace.
|
||||
AnyWhitespace = Multiline | SpaceAfterColon | SpaceAfterComma
|
||||
|
||||
// WhitespaceFlags is the set of flags related to whitespace formatting.
|
||||
// In contrast to AnyWhitespace, this includes Indent and IndentPrefix
|
||||
// as those settings take no effect if Multiline is false.
|
||||
WhitespaceFlags = AnyWhitespace | Indent | IndentPrefix
|
||||
|
||||
// AnyEscape is the set of flags related to escaping in a JSON string.
|
||||
AnyEscape = EscapeForHTML | EscapeForJS | EscapeInvalidUTF8
|
||||
|
||||
// CanonicalizeNumbers is the set of flags related to raw number canonicalization.
|
||||
CanonicalizeNumbers = CanonicalizeRawInts | CanonicalizeRawFloats
|
||||
)
|
||||
|
||||
// Encoder and decoder flags.
|
||||
const (
|
||||
initFlag Bools = 1 << iota // reserved for the boolean value itself
|
||||
|
||||
AllowDuplicateNames // encode or decode
|
||||
AllowInvalidUTF8 // encode or decode
|
||||
WithinArshalCall // encode or decode; for internal use by json.Marshal and json.Unmarshal
|
||||
OmitTopLevelNewline // encode only; for internal use by json.Marshal and json.MarshalWrite
|
||||
PreserveRawStrings // encode only; for internal use by jsontext.Value.Canonicalize
|
||||
CanonicalizeNumbers // encode only; for internal use by jsontext.Value.Canonicalize
|
||||
EscapeForHTML // encode only
|
||||
EscapeForJS // encode only
|
||||
Expand // encode only
|
||||
Indent // encode only; non-boolean flag
|
||||
IndentPrefix // encode only; non-boolean flag
|
||||
ByteLimit // encode or decode; non-boolean flag
|
||||
DepthLimit // encode or decode; non-boolean flag
|
||||
AllowDuplicateNames // encode or decode
|
||||
AllowInvalidUTF8 // encode or decode
|
||||
WithinArshalCall // encode or decode; for internal use by json.Marshal and json.Unmarshal
|
||||
OmitTopLevelNewline // encode only; for internal use by json.Marshal and json.MarshalWrite
|
||||
PreserveRawStrings // encode only
|
||||
CanonicalizeRawInts // encode only
|
||||
CanonicalizeRawFloats // encode only
|
||||
ReorderRawObjects // encode only
|
||||
EscapeForHTML // encode only
|
||||
EscapeForJS // encode only
|
||||
EscapeInvalidUTF8 // encode only; only exposed in v1
|
||||
Multiline // encode only
|
||||
SpaceAfterColon // encode only
|
||||
SpaceAfterComma // encode only
|
||||
Indent // encode only; non-boolean flag
|
||||
IndentPrefix // encode only; non-boolean flag
|
||||
ByteLimit // encode or decode; non-boolean flag
|
||||
DepthLimit // encode or decode; non-boolean flag
|
||||
|
||||
maxCoderFlag
|
||||
)
|
||||
@@ -96,6 +115,7 @@ const (
|
||||
Deterministic // marshal only
|
||||
FormatNilMapAsNull // marshal only
|
||||
FormatNilSliceAsNull // marshal only
|
||||
OmitZeroStructFields // marshal only
|
||||
MatchCaseInsensitiveNames // marshal or unmarshal
|
||||
DiscardUnknownMembers // marshal only
|
||||
RejectUnknownMembers // unmarshal only
|
||||
@@ -109,18 +129,17 @@ const (
|
||||
const (
|
||||
_ Bools = (maxArshalV2Flag >> 1) << iota
|
||||
|
||||
FormatByteArrayAsArray // marshal or unmarshal
|
||||
FormatTimeDurationAsNanosecond // marshal or unmarshal
|
||||
IgnoreStructErrors // marshal or unmarshal
|
||||
MatchCaseSensitiveDelimiter // marshal or unmarshal
|
||||
MergeWithLegacySemantics // unmarshal
|
||||
OmitEmptyWithLegacyDefinition // marshal
|
||||
RejectFloatOverflow // unmarshal
|
||||
ReportLegacyErrorValues // marshal or unmarshal
|
||||
SkipUnaddressableMethods // marshal or unmarshal
|
||||
StringifyWithLegacySemantics // marshal or unmarshal
|
||||
UnmarshalAnyWithRawNumber // unmarshal; for internal use by jsonv1.Decoder.UseNumber
|
||||
UnmarshalArrayFromAnyLength // unmarshal
|
||||
CallMethodsWithLegacySemantics // marshal or unmarshal
|
||||
FormatBytesWithLegacySemantics // marshal or unmarshal
|
||||
FormatTimeWithLegacySemantics // marshal or unmarshal
|
||||
MatchCaseSensitiveDelimiter // marshal or unmarshal
|
||||
MergeWithLegacySemantics // unmarshal
|
||||
OmitEmptyWithLegacyDefinition // marshal
|
||||
ReportErrorsWithLegacySemantics // marshal or unmarshal
|
||||
StringifyWithLegacySemantics // marshal or unmarshal
|
||||
StringifyBoolsAndStrings // marshal or unmarshal; for internal use by jsonv2.makeStructArshaler
|
||||
UnmarshalAnyWithRawNumber // unmarshal; for internal use by jsonv1.Decoder.UseNumber
|
||||
UnmarshalArrayFromAnyLength // unmarshal
|
||||
|
||||
maxArshalV1Flag
|
||||
)
|
||||
|
||||
68
vendor/github.com/go-json-experiment/json/internal/jsonopts/options.go
generated
vendored
68
vendor/github.com/go-json-experiment/json/internal/jsonopts/options.go
generated
vendored
@@ -46,30 +46,17 @@ type ArshalValues struct {
|
||||
// DefaultOptionsV2 is the set of all options that define default v2 behavior.
|
||||
var DefaultOptionsV2 = Struct{
|
||||
Flags: jsonflags.Flags{
|
||||
Presence: uint64(jsonflags.AllFlags),
|
||||
Presence: uint64(jsonflags.AllFlags & ^jsonflags.WhitespaceFlags),
|
||||
Values: uint64(0),
|
||||
},
|
||||
CoderValues: CoderValues{Indent: "\t"}, // Indent is set, but Expand is set to false
|
||||
}
|
||||
|
||||
// DefaultOptionsV1 is the set of all options that define default v1 behavior.
|
||||
var DefaultOptionsV1 = Struct{
|
||||
Flags: jsonflags.Flags{
|
||||
Presence: uint64(jsonflags.AllFlags),
|
||||
Presence: uint64(jsonflags.AllFlags & ^jsonflags.WhitespaceFlags),
|
||||
Values: uint64(jsonflags.DefaultV1Flags),
|
||||
},
|
||||
CoderValues: CoderValues{Indent: "\t"}, // Indent is set, but Expand is set to false
|
||||
}
|
||||
|
||||
// CopyCoderOptions copies coder-specific options from src to dst.
|
||||
// This is used by json.MarshalEncode and json.UnmarshalDecode since those
|
||||
// functions ignore any coder-specific options and uses the options from the
|
||||
// Encoder or Decoder that is passed in.
|
||||
func (dst *Struct) CopyCoderOptions(src *Struct) {
|
||||
srcFlags := src.Flags
|
||||
srcFlags.Clear(^jsonflags.AllCoderFlags)
|
||||
dst.Flags.Join(srcFlags)
|
||||
dst.CoderValues = src.CoderValues
|
||||
}
|
||||
|
||||
func (*Struct) JSONOptions(internal.NotForPublicUse) {}
|
||||
@@ -125,50 +112,73 @@ func GetOption[T any](opts Options, setter func(T) Options) (T, bool) {
|
||||
var JoinUnknownOption = func(*Struct, Options) { panic("unknown option") }
|
||||
|
||||
func (dst *Struct) Join(srcs ...Options) {
|
||||
dst.join(false, srcs...)
|
||||
}
|
||||
|
||||
func (dst *Struct) JoinWithoutCoderOptions(srcs ...Options) {
|
||||
dst.join(true, srcs...)
|
||||
}
|
||||
|
||||
func (dst *Struct) join(excludeCoderOptions bool, srcs ...Options) {
|
||||
for _, src := range srcs {
|
||||
switch src := src.(type) {
|
||||
case nil:
|
||||
continue
|
||||
case jsonflags.Bools:
|
||||
if excludeCoderOptions {
|
||||
src &= ^jsonflags.AllCoderFlags
|
||||
}
|
||||
dst.Flags.Set(src)
|
||||
case Indent:
|
||||
dst.Flags.Set(jsonflags.Expand | jsonflags.Indent | 1)
|
||||
if excludeCoderOptions {
|
||||
continue
|
||||
}
|
||||
dst.Flags.Set(jsonflags.Multiline | jsonflags.Indent | 1)
|
||||
dst.Indent = string(src)
|
||||
case IndentPrefix:
|
||||
dst.Flags.Set(jsonflags.Expand | jsonflags.IndentPrefix | 1)
|
||||
if excludeCoderOptions {
|
||||
continue
|
||||
}
|
||||
dst.Flags.Set(jsonflags.Multiline | jsonflags.IndentPrefix | 1)
|
||||
dst.IndentPrefix = string(src)
|
||||
case ByteLimit:
|
||||
if excludeCoderOptions {
|
||||
continue
|
||||
}
|
||||
dst.Flags.Set(jsonflags.ByteLimit | 1)
|
||||
dst.ByteLimit = int64(src)
|
||||
case DepthLimit:
|
||||
if excludeCoderOptions {
|
||||
continue
|
||||
}
|
||||
dst.Flags.Set(jsonflags.DepthLimit | 1)
|
||||
dst.DepthLimit = int(src)
|
||||
case *Struct:
|
||||
dst.Flags.Join(src.Flags)
|
||||
if src.Flags.Has(jsonflags.NonBooleanFlags) {
|
||||
if src.Flags.Has(jsonflags.Indent) {
|
||||
srcFlags := src.Flags // shallow copy the flags
|
||||
if excludeCoderOptions {
|
||||
srcFlags.Clear(jsonflags.AllCoderFlags)
|
||||
}
|
||||
dst.Flags.Join(srcFlags)
|
||||
if srcFlags.Has(jsonflags.NonBooleanFlags) {
|
||||
if srcFlags.Has(jsonflags.Indent) {
|
||||
dst.Indent = src.Indent
|
||||
}
|
||||
if src.Flags.Has(jsonflags.IndentPrefix) {
|
||||
if srcFlags.Has(jsonflags.IndentPrefix) {
|
||||
dst.IndentPrefix = src.IndentPrefix
|
||||
}
|
||||
if src.Flags.Has(jsonflags.ByteLimit) {
|
||||
if srcFlags.Has(jsonflags.ByteLimit) {
|
||||
dst.ByteLimit = src.ByteLimit
|
||||
}
|
||||
if src.Flags.Has(jsonflags.DepthLimit) {
|
||||
if srcFlags.Has(jsonflags.DepthLimit) {
|
||||
dst.DepthLimit = src.DepthLimit
|
||||
}
|
||||
if src.Flags.Has(jsonflags.Marshalers) {
|
||||
if srcFlags.Has(jsonflags.Marshalers) {
|
||||
dst.Marshalers = src.Marshalers
|
||||
}
|
||||
if src.Flags.Has(jsonflags.Unmarshalers) {
|
||||
if srcFlags.Has(jsonflags.Unmarshalers) {
|
||||
dst.Unmarshalers = src.Unmarshalers
|
||||
}
|
||||
}
|
||||
if src.Format != "" {
|
||||
dst.Format = src.Format
|
||||
dst.FormatDepth = src.FormatDepth
|
||||
}
|
||||
default:
|
||||
JoinUnknownOption(dst, src)
|
||||
}
|
||||
|
||||
29
vendor/github.com/go-json-experiment/json/internal/jsonwire/decode.go
generated
vendored
29
vendor/github.com/go-json-experiment/json/internal/jsonwire/decode.go
generated
vendored
@@ -74,7 +74,7 @@ func ConsumeTrue(b []byte) int {
|
||||
func ConsumeLiteral(b []byte, lit string) (n int, err error) {
|
||||
for i := 0; i < len(b) && i < len(lit); i++ {
|
||||
if b[i] != lit[i] {
|
||||
return i, NewInvalidCharacterError(b[i:], "within literal "+lit+" (expecting "+strconv.QuoteRune(rune(lit[i]))+")")
|
||||
return i, NewInvalidCharacterError(b[i:], "in literal "+lit+" (expecting "+strconv.QuoteRune(rune(lit[i]))+")")
|
||||
}
|
||||
}
|
||||
if len(b) < len(lit) {
|
||||
@@ -240,7 +240,7 @@ func ConsumeStringResumable(flags *ValueFlags, b []byte, resumeOffset int, valid
|
||||
// Handle invalid control characters.
|
||||
case r < ' ':
|
||||
flags.Join(stringNonVerbatim | stringNonCanonical)
|
||||
return n, NewInvalidCharacterError(b[n:], "within string (expecting non-control character)")
|
||||
return n, NewInvalidCharacterError(b[n:], "in string (expecting non-control character)")
|
||||
default:
|
||||
panic("BUG: unhandled character " + QuoteRune(b[n:]))
|
||||
}
|
||||
@@ -374,7 +374,7 @@ func AppendUnquote[Bytes ~[]byte | ~string](dst []byte, src Bytes) (v []byte, er
|
||||
// Handle invalid control characters.
|
||||
case r < ' ':
|
||||
dst = append(dst, src[i:n]...)
|
||||
return dst, NewInvalidCharacterError(src[n:], "within string (expecting non-control character)")
|
||||
return dst, NewInvalidCharacterError(src[n:], "in string (expecting non-control character)")
|
||||
default:
|
||||
panic("BUG: unhandled character " + QuoteRune(src[n:]))
|
||||
}
|
||||
@@ -386,7 +386,7 @@ func AppendUnquote[Bytes ~[]byte | ~string](dst []byte, src Bytes) (v []byte, er
|
||||
// hasEscapedUTF16Prefix reports whether b is possibly
|
||||
// the truncated prefix of a \uFFFF escape sequence.
|
||||
func hasEscapedUTF16Prefix[Bytes ~[]byte | ~string](b Bytes, lowerSurrogateHalf bool) bool {
|
||||
for i := 0; i < len(b); i++ {
|
||||
for i := range len(b) {
|
||||
switch c := b[i]; {
|
||||
case i == 0 && c != '\\':
|
||||
return false
|
||||
@@ -513,7 +513,7 @@ beforeInteger:
|
||||
}
|
||||
state = withinIntegerDigits
|
||||
default:
|
||||
return n, state, NewInvalidCharacterError(b[n:], "within number (expecting digit)")
|
||||
return n, state, NewInvalidCharacterError(b[n:], "in number (expecting digit)")
|
||||
}
|
||||
|
||||
// Consume optional fractional component.
|
||||
@@ -527,7 +527,7 @@ beforeFractional:
|
||||
case '0' <= b[n] && b[n] <= '9':
|
||||
n++
|
||||
default:
|
||||
return n, state, NewInvalidCharacterError(b[n:], "within number (expecting digit)")
|
||||
return n, state, NewInvalidCharacterError(b[n:], "in number (expecting digit)")
|
||||
}
|
||||
for uint(len(b)) > uint(n) && ('0' <= b[n] && b[n] <= '9') {
|
||||
n++
|
||||
@@ -549,7 +549,7 @@ beforeExponent:
|
||||
case '0' <= b[n] && b[n] <= '9':
|
||||
n++
|
||||
default:
|
||||
return n, state, NewInvalidCharacterError(b[n:], "within number (expecting digit)")
|
||||
return n, state, NewInvalidCharacterError(b[n:], "in number (expecting digit)")
|
||||
}
|
||||
for uint(len(b)) > uint(n) && ('0' <= b[n] && b[n] <= '9') {
|
||||
n++
|
||||
@@ -567,7 +567,7 @@ func parseHexUint16[Bytes ~[]byte | ~string](b Bytes) (v uint16, ok bool) {
|
||||
if len(b) != 4 {
|
||||
return 0, false
|
||||
}
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
c := b[i]
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
@@ -610,19 +610,6 @@ func ParseUint(b []byte) (v uint64, ok bool) {
|
||||
// then we return MaxFloat since any finite value will always be infinitely
|
||||
// more accurate at representing another finite value than an infinite value.
|
||||
func ParseFloat(b []byte, bits int) (v float64, ok bool) {
|
||||
// Fast path for exact integer numbers which fit in the
|
||||
// 24-bit or 53-bit significand of a float32 or float64.
|
||||
var negLen int // either 0 or 1
|
||||
if len(b) > 0 && b[0] == '-' {
|
||||
negLen = 1
|
||||
}
|
||||
u, ok := ParseUint(b[negLen:])
|
||||
if ok && ((bits == 32 && u <= 1<<24) || (bits == 64 && u <= 1<<53)) {
|
||||
return math.Copysign(float64(u), float64(-1*negLen)), true
|
||||
}
|
||||
|
||||
// Note that the []byte->string conversion unfortunately allocates.
|
||||
// See https://go.dev/issue/42429 for more information.
|
||||
fv, err := strconv.ParseFloat(string(b), bits)
|
||||
if math.IsInf(fv, 0) {
|
||||
switch {
|
||||
|
||||
134
vendor/github.com/go-json-experiment/json/internal/jsonwire/encode.go
generated
vendored
134
vendor/github.com/go-json-experiment/json/internal/jsonwire/encode.go
generated
vendored
@@ -66,35 +66,41 @@ func AppendQuote[Bytes ~[]byte | ~string](dst []byte, src Bytes, flags *jsonflag
|
||||
dst = slices.Grow(dst, len(`"`)+len(src)+len(`"`))
|
||||
dst = append(dst, '"')
|
||||
for uint(len(src)) > uint(n) {
|
||||
// Handle single-byte ASCII.
|
||||
if c := src[n]; c < utf8.RuneSelf {
|
||||
// Handle single-byte ASCII.
|
||||
n++
|
||||
if escapeASCII[c] > 0 {
|
||||
if (c == '<' || c == '>' || c == '&') && !flags.Get(jsonflags.EscapeForHTML) {
|
||||
continue
|
||||
}
|
||||
if escapeASCII[c] == 0 {
|
||||
continue // no escaping possibly needed
|
||||
}
|
||||
// Handle escaping of single-byte ASCII.
|
||||
if !(c == '<' || c == '>' || c == '&') || flags.Get(jsonflags.EscapeForHTML) {
|
||||
dst = append(dst, src[i:n-1]...)
|
||||
dst = appendEscapedASCII(dst, c)
|
||||
i = n
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle multi-byte Unicode.
|
||||
switch r, rn := utf8.DecodeRuneInString(string(truncateMaxUTF8(src[n:]))); {
|
||||
case r == utf8.RuneError && rn == 1:
|
||||
hasInvalidUTF8 = true
|
||||
dst = append(dst, src[i:n]...)
|
||||
dst = append(dst, "\ufffd"...)
|
||||
n += rn
|
||||
i = n
|
||||
case (r == '\u2028' || r == '\u2029') && flags.Get(jsonflags.EscapeForJS):
|
||||
dst = append(dst, src[i:n]...)
|
||||
dst = appendEscapedUnicode(dst, r)
|
||||
n += rn
|
||||
i = n
|
||||
default:
|
||||
} else {
|
||||
// Handle multi-byte Unicode.
|
||||
r, rn := utf8.DecodeRuneInString(string(truncateMaxUTF8(src[n:])))
|
||||
n += rn
|
||||
if r != utf8.RuneError && r != '\u2028' && r != '\u2029' {
|
||||
continue // no escaping possibly needed
|
||||
}
|
||||
// Handle escaping of multi-byte Unicode.
|
||||
switch {
|
||||
case isInvalidUTF8(r, rn):
|
||||
hasInvalidUTF8 = true
|
||||
dst = append(dst, src[i:n-rn]...)
|
||||
if flags.Get(jsonflags.EscapeInvalidUTF8) {
|
||||
dst = append(dst, `\ufffd`...)
|
||||
} else {
|
||||
dst = append(dst, "\ufffd"...)
|
||||
}
|
||||
i = n
|
||||
case (r == '\u2028' || r == '\u2029') && flags.Get(jsonflags.EscapeForJS):
|
||||
dst = append(dst, src[i:n-rn]...)
|
||||
dst = appendEscapedUnicode(dst, r)
|
||||
i = n
|
||||
}
|
||||
}
|
||||
}
|
||||
dst = append(dst, src[i:n]...)
|
||||
@@ -141,7 +147,7 @@ func appendEscapedUTF16(dst []byte, x uint16) []byte {
|
||||
}
|
||||
|
||||
// ReformatString consumes a JSON string from src and appends it to dst,
|
||||
// reformatting it if necessary for the given escapeRune parameter.
|
||||
// reformatting it if necessary according to the specified flags.
|
||||
// It returns the appended output and the number of consumed input bytes.
|
||||
func ReformatString(dst, src []byte, flags *jsonflags.Flags) ([]byte, int, error) {
|
||||
// TODO: Should this update ValueFlags as input?
|
||||
@@ -150,18 +156,48 @@ func ReformatString(dst, src []byte, flags *jsonflags.Flags) ([]byte, int, error
|
||||
if err != nil {
|
||||
return dst, n, err
|
||||
}
|
||||
isCanonical := !flags.Get(jsonflags.EscapeForHTML | jsonflags.EscapeForJS)
|
||||
if flags.Get(jsonflags.PreserveRawStrings) || (isCanonical && valFlags.IsCanonical()) {
|
||||
|
||||
// If the output requires no special escapes, and the input
|
||||
// is already in canonical form or should be preserved verbatim,
|
||||
// then directly copy the input to the output.
|
||||
if !flags.Get(jsonflags.AnyEscape) &&
|
||||
(valFlags.IsCanonical() || flags.Get(jsonflags.PreserveRawStrings)) {
|
||||
dst = append(dst, src[:n]...) // copy the string verbatim
|
||||
return dst, n, nil
|
||||
}
|
||||
|
||||
// TODO: Implement a direct, raw-to-raw reformat for strings.
|
||||
// If the escapeRune option would have resulted in no changes to the output,
|
||||
// it would be faster to simply append src to dst without going through
|
||||
// an intermediary representation in a separate buffer.
|
||||
// Under [jsonflags.PreserveRawStrings], any pre-escaped sequences
|
||||
// remain escaped, however we still need to respect the
|
||||
// [jsonflags.EscapeForHTML] and [jsonflags.EscapeForJS] options.
|
||||
if flags.Get(jsonflags.PreserveRawStrings) {
|
||||
var i, lastAppendIndex int
|
||||
for i < n {
|
||||
if c := src[i]; c < utf8.RuneSelf {
|
||||
if (c == '<' || c == '>' || c == '&') && flags.Get(jsonflags.EscapeForHTML) {
|
||||
dst = append(dst, src[lastAppendIndex:i]...)
|
||||
dst = appendEscapedASCII(dst, c)
|
||||
lastAppendIndex = i + 1
|
||||
}
|
||||
i++
|
||||
} else {
|
||||
r, rn := utf8.DecodeRune(truncateMaxUTF8(src[i:]))
|
||||
if (r == '\u2028' || r == '\u2029') && flags.Get(jsonflags.EscapeForJS) {
|
||||
dst = append(dst, src[lastAppendIndex:i]...)
|
||||
dst = appendEscapedUnicode(dst, r)
|
||||
lastAppendIndex = i + rn
|
||||
}
|
||||
i += rn
|
||||
}
|
||||
}
|
||||
return append(dst, src[lastAppendIndex:n]...), n, nil
|
||||
}
|
||||
|
||||
// The input contains characters that might need escaping,
|
||||
// unnecessary escape sequences, or invalid UTF-8.
|
||||
// Perform a round-trip unquote and quote to properly reformat
|
||||
// these sequences according the current flags.
|
||||
b, _ := AppendUnquote(nil, src[:n])
|
||||
dst, _ = AppendQuote(dst, string(b), flags)
|
||||
dst, _ = AppendQuote(dst, b, flags)
|
||||
return dst, n, nil
|
||||
}
|
||||
|
||||
@@ -204,23 +240,45 @@ func AppendFloat(dst []byte, src float64, bits int) []byte {
|
||||
// ReformatNumber consumes a JSON string from src and appends it to dst,
|
||||
// canonicalizing it if specified.
|
||||
// It returns the appended output and the number of consumed input bytes.
|
||||
func ReformatNumber(dst, src []byte, canonicalize bool) ([]byte, int, error) {
|
||||
func ReformatNumber(dst, src []byte, flags *jsonflags.Flags) ([]byte, int, error) {
|
||||
n, err := ConsumeNumber(src)
|
||||
if err != nil {
|
||||
return dst, n, err
|
||||
}
|
||||
if !canonicalize {
|
||||
if !flags.Get(jsonflags.CanonicalizeNumbers) {
|
||||
dst = append(dst, src[:n]...) // copy the number verbatim
|
||||
return dst, n, nil
|
||||
}
|
||||
|
||||
// Canonicalize the number per RFC 8785, section 3.2.2.3.
|
||||
// As an optimization, we can copy integer numbers below 2⁵³ verbatim.
|
||||
const maxExactIntegerDigits = 16 // len(strconv.AppendUint(nil, 1<<53, 10))
|
||||
if n < maxExactIntegerDigits && ConsumeSimpleNumber(src[:n]) == n {
|
||||
dst = append(dst, src[:n]...) // copy the number verbatim
|
||||
return dst, n, nil
|
||||
// Identify the kind of number.
|
||||
var isFloat bool
|
||||
for _, c := range src[:n] {
|
||||
if c == '.' || c == 'e' || c == 'E' {
|
||||
isFloat = true // has fraction or exponent
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check if need to canonicalize this kind of number.
|
||||
switch {
|
||||
case string(src[:n]) == "-0":
|
||||
break // canonicalize -0 as 0 regardless of kind
|
||||
case isFloat:
|
||||
if !flags.Get(jsonflags.CanonicalizeRawFloats) {
|
||||
dst = append(dst, src[:n]...) // copy the number verbatim
|
||||
return dst, n, nil
|
||||
}
|
||||
default:
|
||||
// As an optimization, we can copy integer numbers below 2⁵³ verbatim
|
||||
// since the canonical form is always identical.
|
||||
const maxExactIntegerDigits = 16 // len(strconv.AppendUint(nil, 1<<53, 10))
|
||||
if !flags.Get(jsonflags.CanonicalizeRawInts) || n < maxExactIntegerDigits {
|
||||
dst = append(dst, src[:n]...) // copy the number verbatim
|
||||
return dst, n, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Parse and reformat the number (which uses a canonical format).
|
||||
fv, _ := strconv.ParseFloat(string(src[:n]), 64)
|
||||
switch {
|
||||
case fv == 0:
|
||||
|
||||
76
vendor/github.com/go-json-experiment/json/internal/jsonwire/wire.go
generated
vendored
76
vendor/github.com/go-json-experiment/json/internal/jsonwire/wire.go
generated
vendored
@@ -76,13 +76,8 @@ func CompareUTF16[Bytes ~[]byte | ~string](x, y Bytes) int {
|
||||
return ('\u0000' <= r && r <= '\uD7FF') || ('\uE000' <= r && r <= '\uFFFF')
|
||||
}
|
||||
|
||||
var invalidUTF8 bool
|
||||
x0, y0 := x, y
|
||||
for {
|
||||
if len(x) == 0 || len(y) == 0 {
|
||||
if len(x) == len(y) && invalidUTF8 {
|
||||
return strings.Compare(string(x0), string(y0))
|
||||
}
|
||||
return cmp.Compare(len(x), len(y))
|
||||
}
|
||||
|
||||
@@ -114,7 +109,14 @@ func CompareUTF16[Bytes ~[]byte | ~string](x, y Bytes) int {
|
||||
if rx != ry {
|
||||
return cmp.Compare(rx, ry)
|
||||
}
|
||||
invalidUTF8 = invalidUTF8 || (rx == utf8.RuneError && nx == 1) || (ry == utf8.RuneError && ny == 1)
|
||||
|
||||
// Check for invalid UTF-8, in which case,
|
||||
// we just perform a byte-for-byte comparison.
|
||||
if isInvalidUTF8(rx, nx) || isInvalidUTF8(ry, ny) {
|
||||
if x[0] != y[0] {
|
||||
return cmp.Compare(x[0], y[0])
|
||||
}
|
||||
}
|
||||
x, y = x[nx:], y[ny:]
|
||||
}
|
||||
}
|
||||
@@ -141,16 +143,12 @@ func truncateMaxUTF8[Bytes ~[]byte | ~string](b Bytes) Bytes {
|
||||
return b
|
||||
}
|
||||
|
||||
// NewError and ErrInvalidUTF8 are injected by the "jsontext" package,
|
||||
// so that these error types use the jsontext.SyntacticError type.
|
||||
var (
|
||||
NewError = errors.New
|
||||
ErrInvalidUTF8 = errors.New("invalid UTF-8 within string")
|
||||
)
|
||||
// TODO(https://go.dev/issue/70547): Use utf8.ErrInvalid instead.
|
||||
var ErrInvalidUTF8 = errors.New("invalid UTF-8")
|
||||
|
||||
func NewInvalidCharacterError[Bytes ~[]byte | ~string](prefix Bytes, where string) error {
|
||||
what := QuoteRune(prefix)
|
||||
return NewError("invalid character " + what + " " + where)
|
||||
return errors.New("invalid character " + what + " " + where)
|
||||
}
|
||||
|
||||
func NewInvalidEscapeSequenceError[Bytes ~[]byte | ~string](what Bytes) error {
|
||||
@@ -162,8 +160,56 @@ func NewInvalidEscapeSequenceError[Bytes ~[]byte | ~string](what Bytes) error {
|
||||
return r == '`' || r == utf8.RuneError || unicode.IsSpace(r) || !unicode.IsPrint(r)
|
||||
}) >= 0
|
||||
if needEscape {
|
||||
return NewError("invalid " + label + " " + strconv.Quote(string(what)) + " within string")
|
||||
return errors.New("invalid " + label + " " + strconv.Quote(string(what)) + " in string")
|
||||
} else {
|
||||
return NewError("invalid " + label + " `" + string(what) + "` within string")
|
||||
return errors.New("invalid " + label + " `" + string(what) + "` in string")
|
||||
}
|
||||
}
|
||||
|
||||
// TruncatePointer optionally truncates the JSON pointer,
|
||||
// enforcing that the length roughly does not exceed n.
|
||||
func TruncatePointer(s string, n int) string {
|
||||
if len(s) <= n {
|
||||
return s
|
||||
}
|
||||
i := n / 2
|
||||
j := len(s) - n/2
|
||||
|
||||
// Avoid truncating a name if there are multiple names present.
|
||||
if k := strings.LastIndexByte(s[:i], '/'); k > 0 {
|
||||
i = k
|
||||
}
|
||||
if k := strings.IndexByte(s[j:], '/'); k >= 0 {
|
||||
j += k + len("/")
|
||||
}
|
||||
|
||||
// Avoid truncation in the middle of a UTF-8 rune.
|
||||
for i > 0 && isInvalidUTF8(utf8.DecodeLastRuneInString(s[:i])) {
|
||||
i--
|
||||
}
|
||||
for j < len(s) && isInvalidUTF8(utf8.DecodeRuneInString(s[j:])) {
|
||||
j++
|
||||
}
|
||||
|
||||
// Determine the right middle fragment to use.
|
||||
var middle string
|
||||
switch strings.Count(s[i:j], "/") {
|
||||
case 0:
|
||||
middle = "…"
|
||||
case 1:
|
||||
middle = "…/…"
|
||||
default:
|
||||
middle = "…/…/…"
|
||||
}
|
||||
if strings.HasPrefix(s[i:j], "/") && middle != "…" {
|
||||
middle = strings.TrimPrefix(middle, "…")
|
||||
}
|
||||
if strings.HasSuffix(s[i:j], "/") && middle != "…" {
|
||||
middle = strings.TrimSuffix(middle, "…")
|
||||
}
|
||||
return s[:i] + middle + s[j:]
|
||||
}
|
||||
|
||||
func isInvalidUTF8(r rune, rn int) bool {
|
||||
return r == utf8.RuneError && rn == 1
|
||||
}
|
||||
|
||||
278
vendor/github.com/go-json-experiment/json/jsontext/decode.go
generated
vendored
278
vendor/github.com/go-json-experiment/json/jsontext/decode.go
generated
vendored
@@ -125,16 +125,16 @@ func NewDecoder(r io.Reader, opts ...Options) *Decoder {
|
||||
|
||||
// Reset resets a decoder such that it is reading afresh from r and
|
||||
// configured with the provided options. Reset must not be called on an
|
||||
// a Decoder passed to the [encoding/json/v2.UnmarshalerV2.UnmarshalJSONV2] method
|
||||
// or the [encoding/json/v2.UnmarshalFuncV2] function.
|
||||
// a Decoder passed to the [encoding/json/v2.UnmarshalerFrom.UnmarshalJSONFrom] method
|
||||
// or the [encoding/json/v2.UnmarshalFromFunc] function.
|
||||
func (d *Decoder) Reset(r io.Reader, opts ...Options) {
|
||||
switch {
|
||||
case d == nil:
|
||||
panic("jsontext: invalid nil Decoder")
|
||||
case r == nil:
|
||||
panic("jsontext: invalid nil io.Writer")
|
||||
panic("jsontext: invalid nil io.Reader")
|
||||
case d.s.Flags.Get(jsonflags.WithinArshalCall):
|
||||
panic("jsontext: cannot reset Decoder passed to json.UnmarshalerV2")
|
||||
panic("jsontext: cannot reset Decoder passed to json.UnmarshalerFrom")
|
||||
}
|
||||
d.s.reset(nil, r, opts...)
|
||||
}
|
||||
@@ -142,8 +142,21 @@ func (d *Decoder) Reset(r io.Reader, opts ...Options) {
|
||||
func (d *decoderState) reset(b []byte, r io.Reader, opts ...Options) {
|
||||
d.state.reset()
|
||||
d.decodeBuffer = decodeBuffer{buf: b, rd: r}
|
||||
d.Struct = jsonopts.Struct{}
|
||||
d.Struct.Join(opts...)
|
||||
opts2 := jsonopts.Struct{} // avoid mutating d.Struct in case it is part of opts
|
||||
opts2.Join(opts...)
|
||||
d.Struct = opts2
|
||||
}
|
||||
|
||||
// Options returns the options used to construct the encoder and
|
||||
// may additionally contain semantic options passed to a
|
||||
// [encoding/json/v2.UnmarshalDecode] call.
|
||||
//
|
||||
// If operating within
|
||||
// a [encoding/json/v2.UnmarshalerFrom.UnmarshalJSONFrom] method call or
|
||||
// a [encoding/json/v2.UnmarshalFromFunc] function call,
|
||||
// then the returned options are only valid within the call.
|
||||
func (d *Decoder) Options() Options {
|
||||
return &d.s.Struct
|
||||
}
|
||||
|
||||
var errBufferWriteAfterNext = errors.New("invalid bytes.Buffer.Write call after calling bytes.Buffer.Next")
|
||||
@@ -255,23 +268,40 @@ func (d *decodeBuffer) needMore(pos int) bool {
|
||||
return pos == len(d.buf)
|
||||
}
|
||||
|
||||
// injectSyntacticErrorWithPosition wraps a SyntacticError with the position,
|
||||
// otherwise it returns the error as is.
|
||||
// It takes a position relative to the start of the start of d.buf.
|
||||
func (d *decodeBuffer) injectSyntacticErrorWithPosition(err error, pos int) error {
|
||||
if serr, ok := err.(*SyntacticError); ok {
|
||||
return serr.withOffset(d.baseOffset + int64(pos))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *decodeBuffer) offsetAt(pos int) int64 { return d.baseOffset + int64(pos) }
|
||||
func (d *decodeBuffer) previousOffsetStart() int64 { return d.baseOffset + int64(d.prevStart) }
|
||||
func (d *decodeBuffer) previousOffsetEnd() int64 { return d.baseOffset + int64(d.prevEnd) }
|
||||
func (d *decodeBuffer) PreviousBuffer() []byte { return d.buf[d.prevStart:d.prevEnd] }
|
||||
func (d *decodeBuffer) previousBuffer() []byte { return d.buf[d.prevStart:d.prevEnd] }
|
||||
func (d *decodeBuffer) unreadBuffer() []byte { return d.buf[d.prevEnd:len(d.buf)] }
|
||||
|
||||
// PreviousTokenOrValue returns the previously read token or value
|
||||
// unless it has been invalidated by a call to PeekKind.
|
||||
// If a token is just a delimiter, then this returns a 1-byte buffer.
|
||||
// This method is used for error reporting at the semantic layer.
|
||||
func (d *decodeBuffer) PreviousTokenOrValue() []byte {
|
||||
b := d.previousBuffer()
|
||||
// If peek was called, then the previous token or buffer is invalidated.
|
||||
if d.peekPos > 0 || len(b) > 0 && b[0] == invalidateBufferByte {
|
||||
return nil
|
||||
}
|
||||
// ReadToken does not preserve the buffer for null, bools, or delimiters.
|
||||
// Manually re-construct that buffer.
|
||||
if len(b) == 0 {
|
||||
b = d.buf[:d.prevEnd] // entirety of the previous buffer
|
||||
for _, tok := range []string{"null", "false", "true", "{", "}", "[", "]"} {
|
||||
if len(b) >= len(tok) && string(b[len(b)-len(tok):]) == tok {
|
||||
return b[len(b)-len(tok):]
|
||||
}
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// PeekKind retrieves the next token kind, but does not advance the read offset.
|
||||
// It returns 0 if there are no more tokens.
|
||||
//
|
||||
// It returns 0 if an error occurs. Any such error is cached until
|
||||
// the next read call and it is the caller's responsibility to eventually
|
||||
// follow up a PeekKind call with a read call.
|
||||
func (d *Decoder) PeekKind() Kind {
|
||||
return d.s.PeekKind()
|
||||
}
|
||||
@@ -292,7 +322,7 @@ func (d *decoderState) PeekKind() Kind {
|
||||
if err == io.ErrUnexpectedEOF && d.Tokens.Depth() == 1 {
|
||||
err = io.EOF // EOF possibly if no Tokens present after top-level value
|
||||
}
|
||||
d.peekPos, d.peekErr = -1, err
|
||||
d.peekPos, d.peekErr = -1, wrapSyntacticError(d, err, pos, 0)
|
||||
return invalidKind
|
||||
}
|
||||
}
|
||||
@@ -305,6 +335,7 @@ func (d *decoderState) PeekKind() Kind {
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
if d.needMore(pos) {
|
||||
if pos, err = d.consumeWhitespace(pos); err != nil {
|
||||
err = wrapSyntacticError(d, err, pos, 0)
|
||||
d.peekPos, d.peekErr = -1, d.checkDelimBeforeIOError(delim, err)
|
||||
return invalidKind
|
||||
}
|
||||
@@ -339,12 +370,33 @@ func (d *decoderState) checkDelimBeforeIOError(delim byte, err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// CountNextDelimWhitespace counts the number of upcoming bytes of
|
||||
// delimiter or whitespace characters.
|
||||
// This method is used for error reporting at the semantic layer.
|
||||
func (d *decoderState) CountNextDelimWhitespace() int {
|
||||
d.PeekKind() // populate unreadBuffer
|
||||
return len(d.unreadBuffer()) - len(bytes.TrimLeft(d.unreadBuffer(), ",: \n\r\t"))
|
||||
}
|
||||
|
||||
// checkDelim checks whether delim is valid for the given next kind.
|
||||
func (d *decoderState) checkDelim(delim byte, next Kind) error {
|
||||
where := "at start of value"
|
||||
switch d.Tokens.needDelim(next) {
|
||||
case delim:
|
||||
return nil
|
||||
case ':':
|
||||
where = "after object name (expecting ':')"
|
||||
case ',':
|
||||
if d.Tokens.Last.isObject() {
|
||||
where = "after object value (expecting ',' or '}')"
|
||||
} else {
|
||||
where = "after array element (expecting ',' or ']')"
|
||||
}
|
||||
}
|
||||
pos := d.prevEnd // restore position to right after leading whitespace
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
err := d.Tokens.checkDelim(delim, next)
|
||||
return d.injectSyntacticErrorWithPosition(err, pos)
|
||||
err := jsonwire.NewInvalidCharacterError(d.buf[pos:], where)
|
||||
return wrapSyntacticError(d, err, pos, 0)
|
||||
}
|
||||
|
||||
// SkipValue is semantically equivalent to calling [Decoder.ReadValue] and discarding
|
||||
@@ -377,6 +429,30 @@ func (d *decoderState) SkipValue() error {
|
||||
}
|
||||
}
|
||||
|
||||
// SkipValueRemainder skips the remainder of a value
|
||||
// after reading a '{' or '[' token.
|
||||
func (d *decoderState) SkipValueRemainder() error {
|
||||
if d.Tokens.Depth()-1 > 0 && d.Tokens.Last.Length() == 0 {
|
||||
for n := d.Tokens.Depth(); d.Tokens.Depth() >= n; {
|
||||
if _, err := d.ReadToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SkipUntil skips all tokens until the state machine
|
||||
// is at or past the specified depth and length.
|
||||
func (d *decoderState) SkipUntil(depth int, length int64) error {
|
||||
for d.Tokens.Depth() > depth || (d.Tokens.Depth() == depth && d.Tokens.Last.Length() < length) {
|
||||
if _, err := d.ReadToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadToken reads the next [Token], advancing the read offset.
|
||||
// The returned token is only valid until the next Peek, Read, or Skip call.
|
||||
// It returns [io.EOF] if there are no more tokens.
|
||||
@@ -408,7 +484,7 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
if err == io.ErrUnexpectedEOF && d.Tokens.Depth() == 1 {
|
||||
err = io.EOF // EOF possibly if no Tokens present after top-level value
|
||||
}
|
||||
return Token{}, err
|
||||
return Token{}, wrapSyntacticError(d, err, pos, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,6 +496,7 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
if d.needMore(pos) {
|
||||
if pos, err = d.consumeWhitespace(pos); err != nil {
|
||||
err = wrapSyntacticError(d, err, pos, 0)
|
||||
return Token{}, d.checkDelimBeforeIOError(delim, err)
|
||||
}
|
||||
}
|
||||
@@ -437,13 +514,13 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
if jsonwire.ConsumeNull(d.buf[pos:]) == 0 {
|
||||
pos, err = d.consumeLiteral(pos, "null")
|
||||
if err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
} else {
|
||||
pos += len("null")
|
||||
}
|
||||
if err = d.Tokens.appendLiteral(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-len("null")) // report position at start of literal
|
||||
return Token{}, wrapSyntacticError(d, err, pos-len("null"), +1) // report position at start of literal
|
||||
}
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return Null, nil
|
||||
@@ -452,13 +529,13 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
if jsonwire.ConsumeFalse(d.buf[pos:]) == 0 {
|
||||
pos, err = d.consumeLiteral(pos, "false")
|
||||
if err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
} else {
|
||||
pos += len("false")
|
||||
}
|
||||
if err = d.Tokens.appendLiteral(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-len("false")) // report position at start of literal
|
||||
return Token{}, wrapSyntacticError(d, err, pos-len("false"), +1) // report position at start of literal
|
||||
}
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return False, nil
|
||||
@@ -467,13 +544,13 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
if jsonwire.ConsumeTrue(d.buf[pos:]) == 0 {
|
||||
pos, err = d.consumeLiteral(pos, "true")
|
||||
if err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
} else {
|
||||
pos += len("true")
|
||||
}
|
||||
if err = d.Tokens.appendLiteral(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-len("true")) // report position at start of literal
|
||||
return Token{}, wrapSyntacticError(d, err, pos-len("true"), +1) // report position at start of literal
|
||||
}
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return True, nil
|
||||
@@ -486,23 +563,25 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
newAbsPos := d.baseOffset + int64(pos)
|
||||
n = int(newAbsPos - oldAbsPos)
|
||||
if err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
} else {
|
||||
pos += n
|
||||
}
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) && d.Tokens.Last.NeedObjectName() {
|
||||
if !d.Tokens.Last.isValidNamespace() {
|
||||
return Token{}, errInvalidNamespace
|
||||
}
|
||||
if d.Tokens.Last.isActiveNamespace() && !d.Namespaces.Last().insertQuoted(d.buf[pos-n:pos], flags.IsVerbatim()) {
|
||||
err = newDuplicateNameError(d.buf[pos-n : pos])
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-n) // report position at start of string
|
||||
if d.Tokens.Last.NeedObjectName() {
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !d.Tokens.Last.isValidNamespace() {
|
||||
return Token{}, wrapSyntacticError(d, errInvalidNamespace, pos-n, +1)
|
||||
}
|
||||
if d.Tokens.Last.isActiveNamespace() && !d.Namespaces.Last().insertQuoted(d.buf[pos-n:pos], flags.IsVerbatim()) {
|
||||
err = wrapWithObjectName(ErrDuplicateName, d.buf[pos-n:pos])
|
||||
return Token{}, wrapSyntacticError(d, err, pos-n, +1) // report position at start of string
|
||||
}
|
||||
}
|
||||
d.Names.ReplaceLastQuotedOffset(pos - n) // only replace if insertQuoted succeeds
|
||||
}
|
||||
if err = d.Tokens.appendString(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-n) // report position at start of string
|
||||
return Token{}, wrapSyntacticError(d, err, pos-n, +1) // report position at start of string
|
||||
}
|
||||
d.prevStart, d.prevEnd = pos-n, pos
|
||||
return Token{raw: &d.decodeBuffer, num: uint64(d.previousOffsetStart())}, nil
|
||||
@@ -516,60 +595,60 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
newAbsPos := d.baseOffset + int64(pos)
|
||||
n = int(newAbsPos - oldAbsPos)
|
||||
if err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
} else {
|
||||
pos += n
|
||||
}
|
||||
if err = d.Tokens.appendNumber(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-n) // report position at start of number
|
||||
return Token{}, wrapSyntacticError(d, err, pos-n, +1) // report position at start of number
|
||||
}
|
||||
d.prevStart, d.prevEnd = pos-n, pos
|
||||
return Token{raw: &d.decodeBuffer, num: uint64(d.previousOffsetStart())}, nil
|
||||
|
||||
case '{':
|
||||
if err = d.Tokens.pushObject(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
d.Names.push()
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
d.Names.push()
|
||||
d.Namespaces.push()
|
||||
}
|
||||
pos += 1
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return ObjectStart, nil
|
||||
return BeginObject, nil
|
||||
|
||||
case '}':
|
||||
if err = d.Tokens.popObject(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
d.Names.pop()
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
d.Names.pop()
|
||||
d.Namespaces.pop()
|
||||
}
|
||||
pos += 1
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return ObjectEnd, nil
|
||||
return EndObject, nil
|
||||
|
||||
case '[':
|
||||
if err = d.Tokens.pushArray(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
pos += 1
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return ArrayStart, nil
|
||||
return BeginArray, nil
|
||||
|
||||
case ']':
|
||||
if err = d.Tokens.popArray(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
pos += 1
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return ArrayEnd, nil
|
||||
return EndArray, nil
|
||||
|
||||
default:
|
||||
err = newInvalidCharacterError(d.buf[pos:], "at start of token")
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
err = jsonwire.NewInvalidCharacterError(d.buf[pos:], "at start of value")
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,7 +691,7 @@ func (d *decoderState) ReadValue(flags *jsonwire.ValueFlags) (Value, error) {
|
||||
if err == io.ErrUnexpectedEOF && d.Tokens.Depth() == 1 {
|
||||
err = io.EOF // EOF possibly if no Tokens present after top-level value
|
||||
}
|
||||
return nil, err
|
||||
return nil, wrapSyntacticError(d, err, pos, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,6 +703,7 @@ func (d *decoderState) ReadValue(flags *jsonwire.ValueFlags) (Value, error) {
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
if d.needMore(pos) {
|
||||
if pos, err = d.consumeWhitespace(pos); err != nil {
|
||||
err = wrapSyntacticError(d, err, pos, 0)
|
||||
return nil, d.checkDelimBeforeIOError(delim, err)
|
||||
}
|
||||
}
|
||||
@@ -640,20 +720,22 @@ func (d *decoderState) ReadValue(flags *jsonwire.ValueFlags) (Value, error) {
|
||||
newAbsPos := d.baseOffset + int64(pos)
|
||||
n := int(newAbsPos - oldAbsPos)
|
||||
if err != nil {
|
||||
return nil, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return nil, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
switch next {
|
||||
case 'n', 't', 'f':
|
||||
err = d.Tokens.appendLiteral()
|
||||
case '"':
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) && d.Tokens.Last.NeedObjectName() {
|
||||
if !d.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if d.Tokens.Last.isActiveNamespace() && !d.Namespaces.Last().insertQuoted(d.buf[pos-n:pos], flags.IsVerbatim()) {
|
||||
err = newDuplicateNameError(d.buf[pos-n : pos])
|
||||
break
|
||||
if d.Tokens.Last.NeedObjectName() {
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !d.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if d.Tokens.Last.isActiveNamespace() && !d.Namespaces.Last().insertQuoted(d.buf[pos-n:pos], flags.IsVerbatim()) {
|
||||
err = wrapWithObjectName(ErrDuplicateName, d.buf[pos-n:pos])
|
||||
break
|
||||
}
|
||||
}
|
||||
d.Names.ReplaceLastQuotedOffset(pos - n) // only replace if insertQuoted succeeds
|
||||
}
|
||||
@@ -676,19 +758,36 @@ func (d *decoderState) ReadValue(flags *jsonwire.ValueFlags) (Value, error) {
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, d.injectSyntacticErrorWithPosition(err, pos-n) // report position at start of value
|
||||
return nil, wrapSyntacticError(d, err, pos-n, +1) // report position at start of value
|
||||
}
|
||||
d.prevEnd = pos
|
||||
d.prevStart = pos - n
|
||||
return d.buf[pos-n : pos : pos], nil
|
||||
}
|
||||
|
||||
// CheckNextValue checks whether the next value is syntactically valid,
|
||||
// but does not advance the read offset.
|
||||
func (d *decoderState) CheckNextValue() error {
|
||||
d.PeekKind() // populates d.peekPos and d.peekErr
|
||||
pos, err := d.peekPos, d.peekErr
|
||||
d.peekPos, d.peekErr = 0, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var flags jsonwire.ValueFlags
|
||||
if pos, err := d.consumeValue(&flags, pos, d.Tokens.Depth()); err != nil {
|
||||
return wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckEOF verifies that the input has no more data.
|
||||
func (d *decoderState) CheckEOF() error {
|
||||
switch pos, err := d.consumeWhitespace(d.prevEnd); err {
|
||||
case nil:
|
||||
err := newInvalidCharacterError(d.buf[pos:], "after top-level value")
|
||||
return d.injectSyntacticErrorWithPosition(err, pos)
|
||||
err := jsonwire.NewInvalidCharacterError(d.buf[pos:], "after top-level value")
|
||||
return wrapSyntacticError(d, err, pos, 0)
|
||||
case io.ErrUnexpectedEOF:
|
||||
return nil
|
||||
default:
|
||||
@@ -763,14 +862,17 @@ func (d *decoderState) consumeValue(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
case '[':
|
||||
return d.consumeArray(flags, pos, depth)
|
||||
default:
|
||||
return pos, newInvalidCharacterError(d.buf[pos:], "at start of value")
|
||||
if (d.Tokens.Last.isObject() && next == ']') || (d.Tokens.Last.isArray() && next == '}') {
|
||||
return pos, errMismatchDelim
|
||||
}
|
||||
return pos, jsonwire.NewInvalidCharacterError(d.buf[pos:], "at start of value")
|
||||
}
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
absPos := d.baseOffset + int64(pos)
|
||||
err = d.fetch() // will mutate d.buf and invalidate pos
|
||||
pos = int(absPos - d.baseOffset)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
return pos + n, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -788,7 +890,7 @@ func (d *decoderState) consumeLiteral(pos int, lit string) (newPos int, err erro
|
||||
err = d.fetch() // will mutate d.buf and invalidate pos
|
||||
pos = int(absPos - d.baseOffset)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
return pos + n, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -807,7 +909,7 @@ func (d *decoderState) consumeString(flags *jsonwire.ValueFlags, pos int) (newPo
|
||||
err = d.fetch() // will mutate d.buf and invalidate pos
|
||||
pos = int(absPos - d.baseOffset)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
return pos + n, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -894,19 +996,21 @@ func (d *decoderState) consumeObject(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
} else {
|
||||
pos += n
|
||||
}
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) && !names.insertQuoted(d.buf[pos-n:pos], flags2.IsVerbatim()) {
|
||||
return pos - n, newDuplicateNameError(d.buf[pos-n : pos])
|
||||
quotedName := d.buf[pos-n : pos]
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) && !names.insertQuoted(quotedName, flags2.IsVerbatim()) {
|
||||
return pos - n, wrapWithObjectName(ErrDuplicateName, quotedName)
|
||||
}
|
||||
|
||||
// Handle after name.
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
if d.needMore(pos) {
|
||||
if pos, err = d.consumeWhitespace(pos); err != nil {
|
||||
return pos, err
|
||||
return pos, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
}
|
||||
if d.buf[pos] != ':' {
|
||||
return pos, newInvalidCharacterError(d.buf[pos:], "after object name (expecting ':')")
|
||||
err := jsonwire.NewInvalidCharacterError(d.buf[pos:], "after object name (expecting ':')")
|
||||
return pos, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
pos++
|
||||
|
||||
@@ -914,12 +1018,12 @@ func (d *decoderState) consumeObject(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
if d.needMore(pos) {
|
||||
if pos, err = d.consumeWhitespace(pos); err != nil {
|
||||
return pos, err
|
||||
return pos, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
}
|
||||
pos, err = d.consumeValue(flags, pos, depth)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
return pos, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
|
||||
// Handle after value.
|
||||
@@ -937,7 +1041,7 @@ func (d *decoderState) consumeObject(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
pos++
|
||||
return pos, nil
|
||||
default:
|
||||
return pos, newInvalidCharacterError(d.buf[pos:], "after object value (expecting ',' or '}')")
|
||||
return pos, jsonwire.NewInvalidCharacterError(d.buf[pos:], "after object value (expecting ',' or '}')")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -965,6 +1069,7 @@ func (d *decoderState) consumeArray(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
var idx int64
|
||||
depth++
|
||||
for {
|
||||
// Handle before value.
|
||||
@@ -976,7 +1081,7 @@ func (d *decoderState) consumeArray(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
}
|
||||
pos, err = d.consumeValue(flags, pos, depth)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
return pos, wrapWithArrayIndex(err, idx)
|
||||
}
|
||||
|
||||
// Handle after value.
|
||||
@@ -989,12 +1094,13 @@ func (d *decoderState) consumeArray(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
switch d.buf[pos] {
|
||||
case ',':
|
||||
pos++
|
||||
idx++
|
||||
continue
|
||||
case ']':
|
||||
pos++
|
||||
return pos, nil
|
||||
default:
|
||||
return pos, newInvalidCharacterError(d.buf[pos:], "after array value (expecting ',' or ']')")
|
||||
return pos, jsonwire.NewInvalidCharacterError(d.buf[pos:], "after array element (expecting ',' or ']')")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1017,8 +1123,8 @@ func (d *Decoder) UnreadBuffer() []byte {
|
||||
|
||||
// StackDepth returns the depth of the state machine for read JSON data.
|
||||
// Each level on the stack represents a nested JSON object or array.
|
||||
// It is incremented whenever an [ObjectStart] or [ArrayStart] token is encountered
|
||||
// and decremented whenever an [ObjectEnd] or [ArrayEnd] token is encountered.
|
||||
// It is incremented whenever an [BeginObject] or [BeginArray] token is encountered
|
||||
// and decremented whenever an [EndObject] or [EndArray] token is encountered.
|
||||
// The depth is zero-indexed, where zero represents the top-level JSON value.
|
||||
func (d *Decoder) StackDepth() int {
|
||||
// NOTE: Keep in sync with Encoder.StackDepth.
|
||||
@@ -1037,7 +1143,7 @@ func (d *Decoder) StackDepth() int {
|
||||
// Each name and value in a JSON object is counted separately,
|
||||
// so the effective number of members would be half the length.
|
||||
// A complete JSON object must have an even length.
|
||||
func (d *Decoder) StackIndex(i int) (Kind, int) {
|
||||
func (d *Decoder) StackIndex(i int) (Kind, int64) {
|
||||
// NOTE: Keep in sync with Encoder.StackIndex.
|
||||
switch s := d.s.Tokens.index(i); {
|
||||
case i > 0 && s.isObject():
|
||||
@@ -1050,9 +1156,11 @@ func (d *Decoder) StackIndex(i int) (Kind, int) {
|
||||
}
|
||||
|
||||
// StackPointer returns a JSON Pointer (RFC 6901) to the most recently read value.
|
||||
// Object names are only present if [AllowDuplicateNames] is false, otherwise
|
||||
// object members are represented using their index within the object.
|
||||
func (d *Decoder) StackPointer() string {
|
||||
d.s.Names.copyQuotedBuffer(d.s.buf)
|
||||
return string(d.s.appendStackPointer(nil))
|
||||
func (d *Decoder) StackPointer() Pointer {
|
||||
return Pointer(d.s.AppendStackPointer(nil, -1))
|
||||
}
|
||||
|
||||
func (d *decoderState) AppendStackPointer(b []byte, where int) []byte {
|
||||
d.Names.copyQuotedBuffer(d.buf)
|
||||
return d.state.appendStackPointer(b, where)
|
||||
}
|
||||
|
||||
266
vendor/github.com/go-json-experiment/json/jsontext/encode.go
generated
vendored
266
vendor/github.com/go-json-experiment/json/jsontext/encode.go
generated
vendored
@@ -25,19 +25,19 @@ import (
|
||||
//
|
||||
// can be composed with the following calls (ignoring errors for brevity):
|
||||
//
|
||||
// e.WriteToken(ObjectStart) // {
|
||||
// e.WriteToken(BeginObject) // {
|
||||
// e.WriteToken(String("name")) // "name"
|
||||
// e.WriteToken(String("value")) // "value"
|
||||
// e.WriteValue(Value(`"array"`)) // "array"
|
||||
// e.WriteToken(ArrayStart) // [
|
||||
// e.WriteToken(BeginArray) // [
|
||||
// e.WriteToken(Null) // null
|
||||
// e.WriteToken(False) // false
|
||||
// e.WriteValue(Value("true")) // true
|
||||
// e.WriteToken(Float(3.14159)) // 3.14159
|
||||
// e.WriteToken(ArrayEnd) // ]
|
||||
// e.WriteToken(EndArray) // ]
|
||||
// e.WriteValue(Value(`"object"`)) // "object"
|
||||
// e.WriteValue(Value(`{"k":"v"}`)) // {"k":"v"}
|
||||
// e.WriteToken(ObjectEnd) // }
|
||||
// e.WriteToken(EndObject) // }
|
||||
//
|
||||
// The above is one of many possible sequence of calls and
|
||||
// may not represent the most sensible method to call for any given token/value.
|
||||
@@ -94,8 +94,8 @@ func NewEncoder(w io.Writer, opts ...Options) *Encoder {
|
||||
|
||||
// Reset resets an encoder such that it is writing afresh to w and
|
||||
// configured with the provided options. Reset must not be called on
|
||||
// a Encoder passed to the [encoding/json/v2.MarshalerV2.MarshalJSONV2] method
|
||||
// or the [encoding/json/v2.MarshalFuncV2] function.
|
||||
// a Encoder passed to the [encoding/json/v2.MarshalerTo.MarshalJSONTo] method
|
||||
// or the [encoding/json/v2.MarshalToFunc] function.
|
||||
func (e *Encoder) Reset(w io.Writer, opts ...Options) {
|
||||
switch {
|
||||
case e == nil:
|
||||
@@ -103,7 +103,7 @@ func (e *Encoder) Reset(w io.Writer, opts ...Options) {
|
||||
case w == nil:
|
||||
panic("jsontext: invalid nil io.Writer")
|
||||
case e.s.Flags.Get(jsonflags.WithinArshalCall):
|
||||
panic("jsontext: cannot reset Encoder passed to json.MarshalerV2")
|
||||
panic("jsontext: cannot reset Encoder passed to json.MarshalerTo")
|
||||
}
|
||||
e.s.reset(nil, w, opts...)
|
||||
}
|
||||
@@ -114,13 +114,35 @@ func (e *encoderState) reset(b []byte, w io.Writer, opts ...Options) {
|
||||
if bb, ok := w.(*bytes.Buffer); ok && bb != nil {
|
||||
e.Buf = bb.Bytes()[bb.Len():] // alias the unused buffer of bb
|
||||
}
|
||||
e.Struct = jsonopts.Struct{}
|
||||
e.Struct.Join(opts...)
|
||||
if e.Flags.Get(jsonflags.Expand) && !e.Flags.Has(jsonflags.Indent) {
|
||||
e.Indent = "\t"
|
||||
opts2 := jsonopts.Struct{} // avoid mutating e.Struct in case it is part of opts
|
||||
opts2.Join(opts...)
|
||||
e.Struct = opts2
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
if !e.Flags.Has(jsonflags.SpaceAfterColon) {
|
||||
e.Flags.Set(jsonflags.SpaceAfterColon | 1)
|
||||
}
|
||||
if !e.Flags.Has(jsonflags.SpaceAfterComma) {
|
||||
e.Flags.Set(jsonflags.SpaceAfterComma | 0)
|
||||
}
|
||||
if !e.Flags.Has(jsonflags.Indent) {
|
||||
e.Flags.Set(jsonflags.Indent | 1)
|
||||
e.Indent = "\t"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Options returns the options used to construct the decoder and
|
||||
// may additionally contain semantic options passed to a
|
||||
// [encoding/json/v2.MarshalEncode] call.
|
||||
//
|
||||
// If operating within
|
||||
// a [encoding/json/v2.MarshalerTo.MarshalJSONTo] method call or
|
||||
// a [encoding/json/v2.MarshalToFunc] function call,
|
||||
// then the returned options are only valid within the call.
|
||||
func (e *Encoder) Options() Options {
|
||||
return &e.s.Struct
|
||||
}
|
||||
|
||||
// NeedFlush determines whether to flush at this point.
|
||||
func (e *encoderState) NeedFlush() bool {
|
||||
// NOTE: This function is carefully written to be inlinable.
|
||||
@@ -200,17 +222,7 @@ func (e *encoderState) Flush() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// injectSyntacticErrorWithPosition wraps a SyntacticError with the position,
|
||||
// otherwise it returns the error as is.
|
||||
// It takes a position relative to the start of the start of e.buf.
|
||||
func (e *encodeBuffer) injectSyntacticErrorWithPosition(err error, pos int) error {
|
||||
if serr, ok := err.(*SyntacticError); ok {
|
||||
return serr.withOffset(e.baseOffset + int64(pos))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *encodeBuffer) offsetAt(pos int) int64 { return d.baseOffset + int64(pos) }
|
||||
func (e *encodeBuffer) previousOffsetEnd() int64 { return e.baseOffset + int64(len(e.Buf)) }
|
||||
func (e *encodeBuffer) unflushedBuffer() []byte { return e.Buf }
|
||||
|
||||
@@ -219,7 +231,7 @@ func (e *encodeBuffer) unflushedBuffer() []byte { return e.Buf }
|
||||
func (e *encoderState) avoidFlush() bool {
|
||||
switch {
|
||||
case e.Tokens.Last.Length() == 0:
|
||||
// Never flush after ObjectStart or ArrayStart since we don't know yet
|
||||
// Never flush after BeginObject or BeginArray since we don't know yet
|
||||
// if the object or array will end up being empty.
|
||||
return true
|
||||
case e.Tokens.Last.needObjectValue():
|
||||
@@ -286,11 +298,11 @@ func (e *encoderState) UnwriteEmptyObjectMember(prevName *string) bool {
|
||||
if e.Tokens.Last.isActiveNamespace() {
|
||||
e.Namespaces.Last().removeLast()
|
||||
}
|
||||
e.Names.clearLast()
|
||||
if prevName != nil {
|
||||
e.Names.copyQuotedBuffer(e.Buf) // required by objectNameStack.replaceLastUnquotedName
|
||||
e.Names.replaceLastUnquotedName(*prevName)
|
||||
}
|
||||
}
|
||||
e.Names.clearLast()
|
||||
if prevName != nil {
|
||||
e.Names.copyQuotedBuffer(e.Buf) // required by objectNameStack.replaceLastUnquotedName
|
||||
e.Names.replaceLastUnquotedName(*prevName)
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -314,8 +326,8 @@ func (e *encoderState) UnwriteOnlyObjectMemberName() string {
|
||||
if e.Tokens.Last.isActiveNamespace() {
|
||||
e.Namespaces.Last().removeLast()
|
||||
}
|
||||
e.Names.clearLast()
|
||||
}
|
||||
e.Names.clearLast()
|
||||
return name
|
||||
}
|
||||
|
||||
@@ -337,7 +349,7 @@ func (e *encoderState) WriteToken(t Token) error {
|
||||
|
||||
// Append any delimiters or optional whitespace.
|
||||
b = e.Tokens.MayAppendDelim(b, k)
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.AnyWhitespace) {
|
||||
b = e.appendWhitespace(b, k)
|
||||
}
|
||||
pos := len(b) // offset before the token
|
||||
@@ -358,20 +370,22 @@ func (e *encoderState) WriteToken(t Token) error {
|
||||
if b, err = t.appendString(b, &e.Flags); err != nil {
|
||||
break
|
||||
}
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) && e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], false) {
|
||||
err = newDuplicateNameError(b[pos:])
|
||||
break
|
||||
if e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], false) {
|
||||
err = wrapWithObjectName(ErrDuplicateName, b[pos:])
|
||||
break
|
||||
}
|
||||
}
|
||||
e.Names.ReplaceLastQuotedOffset(pos) // only replace if insertQuoted succeeds
|
||||
}
|
||||
err = e.Tokens.appendString()
|
||||
case '0':
|
||||
if b, err = t.appendNumber(b, e.Flags.Get(jsonflags.CanonicalizeNumbers)); err != nil {
|
||||
if b, err = t.appendNumber(b, &e.Flags); err != nil {
|
||||
break
|
||||
}
|
||||
err = e.Tokens.appendNumber()
|
||||
@@ -380,8 +394,8 @@ func (e *encoderState) WriteToken(t Token) error {
|
||||
if err = e.Tokens.pushObject(); err != nil {
|
||||
break
|
||||
}
|
||||
e.Names.push()
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
e.Names.push()
|
||||
e.Namespaces.push()
|
||||
}
|
||||
case '}':
|
||||
@@ -389,8 +403,8 @@ func (e *encoderState) WriteToken(t Token) error {
|
||||
if err = e.Tokens.popObject(); err != nil {
|
||||
break
|
||||
}
|
||||
e.Names.pop()
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
e.Names.pop()
|
||||
e.Namespaces.pop()
|
||||
}
|
||||
case '[':
|
||||
@@ -400,10 +414,10 @@ func (e *encoderState) WriteToken(t Token) error {
|
||||
b = append(b, ']')
|
||||
err = e.Tokens.popArray()
|
||||
default:
|
||||
err = &SyntacticError{str: "invalid json.Token"}
|
||||
err = errInvalidToken
|
||||
}
|
||||
if err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
|
||||
// Finish off the buffer and store it back into e.
|
||||
@@ -428,7 +442,7 @@ func (e *encoderState) AppendRaw(k Kind, safeASCII bool, appendFn func([]byte) (
|
||||
|
||||
// Append any delimiters or optional whitespace.
|
||||
b = e.Tokens.MayAppendDelim(b, k)
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.AnyWhitespace) {
|
||||
b = e.appendWhitespace(b, k)
|
||||
}
|
||||
pos := len(b) // offset before the token
|
||||
@@ -453,30 +467,32 @@ func (e *encoderState) AppendRaw(k Kind, safeASCII bool, appendFn func([]byte) (
|
||||
b, err = jsonwire.AppendQuote(b[:pos], string(b2), &e.Flags)
|
||||
e.unusedCache = b2[:0]
|
||||
if err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the state machine.
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) && e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
return errInvalidNamespace
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], isVerbatim) {
|
||||
err := newDuplicateNameError(b[pos:])
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
if e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], isVerbatim) {
|
||||
err = wrapWithObjectName(ErrDuplicateName, b[pos:])
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
}
|
||||
e.Names.ReplaceLastQuotedOffset(pos) // only replace if insertQuoted succeeds
|
||||
}
|
||||
if err := e.Tokens.appendString(); err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
case '0':
|
||||
if b, err = appendFn(b); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := e.Tokens.appendNumber(); err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
default:
|
||||
panic("BUG: invalid kind")
|
||||
@@ -513,7 +529,7 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
|
||||
// Append any delimiters or optional whitespace.
|
||||
b = e.Tokens.MayAppendDelim(b, k)
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.AnyWhitespace) {
|
||||
b = e.appendWhitespace(b, k)
|
||||
}
|
||||
pos := len(b) // offset before the value
|
||||
@@ -523,13 +539,13 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
n += jsonwire.ConsumeWhitespace(v[n:])
|
||||
b, m, err := e.reformatValue(b, v[n:], e.Tokens.Depth())
|
||||
if err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos+n+m)
|
||||
return wrapSyntacticError(e, err, pos+n+m, +1)
|
||||
}
|
||||
n += m
|
||||
n += jsonwire.ConsumeWhitespace(v[n:])
|
||||
if len(v) > n {
|
||||
err = newInvalidCharacterError(v[n:], "after top-level value")
|
||||
return e.injectSyntacticErrorWithPosition(err, pos+n)
|
||||
err = jsonwire.NewInvalidCharacterError(v[n:], "after top-level value")
|
||||
return wrapSyntacticError(e, err, pos+n, 0)
|
||||
}
|
||||
|
||||
// Append the kind to the state machine.
|
||||
@@ -537,14 +553,16 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
case 'n', 'f', 't':
|
||||
err = e.Tokens.appendLiteral()
|
||||
case '"':
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) && e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], false) {
|
||||
err = newDuplicateNameError(b[pos:])
|
||||
break
|
||||
if e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], false) {
|
||||
err = wrapWithObjectName(ErrDuplicateName, b[pos:])
|
||||
break
|
||||
}
|
||||
}
|
||||
e.Names.ReplaceLastQuotedOffset(pos) // only replace if insertQuoted succeeds
|
||||
}
|
||||
@@ -558,6 +576,9 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
if err = e.Tokens.popObject(); err != nil {
|
||||
panic("BUG: popObject should never fail immediately after pushObject: " + err.Error())
|
||||
}
|
||||
if e.Flags.Get(jsonflags.ReorderRawObjects) {
|
||||
mustReorderObjects(b[pos:])
|
||||
}
|
||||
case '[':
|
||||
if err = e.Tokens.pushArray(); err != nil {
|
||||
break
|
||||
@@ -565,9 +586,12 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
if err = e.Tokens.popArray(); err != nil {
|
||||
panic("BUG: popArray should never fail immediately after pushArray: " + err.Error())
|
||||
}
|
||||
if e.Flags.Get(jsonflags.ReorderRawObjects) {
|
||||
mustReorderObjects(b[pos:])
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
|
||||
// Finish off the buffer and store it back into e.
|
||||
@@ -578,13 +602,47 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CountNextDelimWhitespace counts the number of bytes of delimiter and
|
||||
// whitespace bytes assuming the upcoming token is a JSON value.
|
||||
// This method is used for error reporting at the semantic layer.
|
||||
func (e *encoderState) CountNextDelimWhitespace() (n int) {
|
||||
const next = Kind('"') // arbitrary kind as next JSON value
|
||||
delim := e.Tokens.needDelim(next)
|
||||
if delim > 0 {
|
||||
n += len(",") | len(":")
|
||||
}
|
||||
if delim == ':' {
|
||||
if e.Flags.Get(jsonflags.SpaceAfterColon) {
|
||||
n += len(" ")
|
||||
}
|
||||
} else {
|
||||
if delim == ',' && e.Flags.Get(jsonflags.SpaceAfterComma) {
|
||||
n += len(" ")
|
||||
}
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
if m := e.Tokens.NeedIndent(next); m > 0 {
|
||||
n += len("\n") + len(e.IndentPrefix) + (m-1)*len(e.Indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// appendWhitespace appends whitespace that immediately precedes the next token.
|
||||
func (e *encoderState) appendWhitespace(b []byte, next Kind) []byte {
|
||||
if e.Tokens.needDelim(next) == ':' {
|
||||
return append(b, ' ')
|
||||
if delim := e.Tokens.needDelim(next); delim == ':' {
|
||||
if e.Flags.Get(jsonflags.SpaceAfterColon) {
|
||||
b = append(b, ' ')
|
||||
}
|
||||
} else {
|
||||
return e.AppendIndent(b, e.Tokens.NeedIndent(next))
|
||||
if delim == ',' && e.Flags.Get(jsonflags.SpaceAfterComma) {
|
||||
b = append(b, ' ')
|
||||
}
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
b = e.AppendIndent(b, e.Tokens.NeedIndent(next))
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// AppendIndent appends the appropriate number of indentation characters
|
||||
@@ -630,22 +688,22 @@ func (e *encoderState) reformatValue(dst []byte, src Value, depth int) ([]byte,
|
||||
return append(dst, "true"...), len("true"), nil
|
||||
case '"':
|
||||
if n := jsonwire.ConsumeSimpleString(src); n > 0 {
|
||||
dst, src = append(dst, src[:n]...), src[n:] // copy simple strings verbatim
|
||||
dst = append(dst, src[:n]...) // copy simple strings verbatim
|
||||
return dst, n, nil
|
||||
}
|
||||
return jsonwire.ReformatString(dst, src, &e.Flags)
|
||||
case '0':
|
||||
if n := jsonwire.ConsumeSimpleNumber(src); n > 0 && !e.Flags.Get(jsonflags.CanonicalizeNumbers) {
|
||||
dst, src = append(dst, src[:n]...), src[n:] // copy simple numbers verbatim
|
||||
dst = append(dst, src[:n]...) // copy simple numbers verbatim
|
||||
return dst, n, nil
|
||||
}
|
||||
return jsonwire.ReformatNumber(dst, src, e.Flags.Get(jsonflags.CanonicalizeNumbers))
|
||||
return jsonwire.ReformatNumber(dst, src, &e.Flags)
|
||||
case '{':
|
||||
return e.reformatObject(dst, src, depth)
|
||||
case '[':
|
||||
return e.reformatArray(dst, src, depth)
|
||||
default:
|
||||
return dst, 0, newInvalidCharacterError(src, "at start of value")
|
||||
return dst, 0, jsonwire.NewInvalidCharacterError(src, "at start of value")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,7 +741,7 @@ func (e *encoderState) reformatObject(dst []byte, src Value, depth int) ([]byte,
|
||||
depth++
|
||||
for {
|
||||
// Append optional newline and indentation.
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth)
|
||||
}
|
||||
|
||||
@@ -693,7 +751,8 @@ func (e *encoderState) reformatObject(dst []byte, src Value, depth int) ([]byte,
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
}
|
||||
m := jsonwire.ConsumeSimpleString(src[n:])
|
||||
if m > 0 {
|
||||
isVerbatim := m > 0
|
||||
if isVerbatim {
|
||||
dst = append(dst, src[n:n+m]...)
|
||||
} else {
|
||||
dst, m, err = jsonwire.ReformatString(dst, src[n:], &e.Flags)
|
||||
@@ -701,34 +760,35 @@ func (e *encoderState) reformatObject(dst []byte, src Value, depth int) ([]byte,
|
||||
return dst, n + m, err
|
||||
}
|
||||
}
|
||||
// TODO: Specify whether the name is verbatim or not.
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) && !names.insertQuoted(src[n:n+m], false) {
|
||||
return dst, n, newDuplicateNameError(src[n : n+m])
|
||||
quotedName := src[n : n+m]
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) && !names.insertQuoted(quotedName, isVerbatim) {
|
||||
return dst, n, wrapWithObjectName(ErrDuplicateName, quotedName)
|
||||
}
|
||||
n += m
|
||||
|
||||
// Append colon.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
return dst, n, wrapWithObjectName(io.ErrUnexpectedEOF, quotedName)
|
||||
}
|
||||
if src[n] != ':' {
|
||||
return dst, n, newInvalidCharacterError(src[n:], "after object name (expecting ':')")
|
||||
err = jsonwire.NewInvalidCharacterError(src[n:], "after object name (expecting ':')")
|
||||
return dst, n, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
dst = append(dst, ':')
|
||||
n += len(":")
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.SpaceAfterColon) {
|
||||
dst = append(dst, ' ')
|
||||
}
|
||||
|
||||
// Append object value.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
return dst, n, wrapWithObjectName(io.ErrUnexpectedEOF, quotedName)
|
||||
}
|
||||
dst, m, err = e.reformatValue(dst, src[n:], depth)
|
||||
if err != nil {
|
||||
return dst, n + m, err
|
||||
return dst, n + m, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
n += m
|
||||
|
||||
@@ -740,17 +800,20 @@ func (e *encoderState) reformatObject(dst []byte, src Value, depth int) ([]byte,
|
||||
switch src[n] {
|
||||
case ',':
|
||||
dst = append(dst, ',')
|
||||
if e.Flags.Get(jsonflags.SpaceAfterComma) {
|
||||
dst = append(dst, ' ')
|
||||
}
|
||||
n += len(",")
|
||||
continue
|
||||
case '}':
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth-1)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
n += len("}")
|
||||
return dst, n, nil
|
||||
default:
|
||||
return dst, n, newInvalidCharacterError(src[n:], "after object value (expecting ',' or '}')")
|
||||
return dst, n, jsonwire.NewInvalidCharacterError(src[n:], "after object value (expecting ',' or '}')")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -779,11 +842,12 @@ func (e *encoderState) reformatArray(dst []byte, src Value, depth int) ([]byte,
|
||||
return dst, n, nil
|
||||
}
|
||||
|
||||
var idx int64
|
||||
var err error
|
||||
depth++
|
||||
for {
|
||||
// Append optional newline and indentation.
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth)
|
||||
}
|
||||
|
||||
@@ -795,7 +859,7 @@ func (e *encoderState) reformatArray(dst []byte, src Value, depth int) ([]byte,
|
||||
var m int
|
||||
dst, m, err = e.reformatValue(dst, src[n:], depth)
|
||||
if err != nil {
|
||||
return dst, n + m, err
|
||||
return dst, n + m, wrapWithArrayIndex(err, idx)
|
||||
}
|
||||
n += m
|
||||
|
||||
@@ -807,17 +871,21 @@ func (e *encoderState) reformatArray(dst []byte, src Value, depth int) ([]byte,
|
||||
switch src[n] {
|
||||
case ',':
|
||||
dst = append(dst, ',')
|
||||
if e.Flags.Get(jsonflags.SpaceAfterComma) {
|
||||
dst = append(dst, ' ')
|
||||
}
|
||||
n += len(",")
|
||||
idx++
|
||||
continue
|
||||
case ']':
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth-1)
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
n += len("]")
|
||||
return dst, n, nil
|
||||
default:
|
||||
return dst, n, newInvalidCharacterError(src[n:], "after array value (expecting ',' or ']')")
|
||||
return dst, n, jsonwire.NewInvalidCharacterError(src[n:], "after array value (expecting ',' or ']')")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -859,8 +927,8 @@ func (e *Encoder) UnusedBuffer() []byte {
|
||||
|
||||
// StackDepth returns the depth of the state machine for written JSON data.
|
||||
// Each level on the stack represents a nested JSON object or array.
|
||||
// It is incremented whenever an [ObjectStart] or [ArrayStart] token is encountered
|
||||
// and decremented whenever an [ObjectEnd] or [ArrayEnd] token is encountered.
|
||||
// It is incremented whenever an [BeginObject] or [BeginArray] token is encountered
|
||||
// and decremented whenever an [EndObject] or [EndArray] token is encountered.
|
||||
// The depth is zero-indexed, where zero represents the top-level JSON value.
|
||||
func (e *Encoder) StackDepth() int {
|
||||
// NOTE: Keep in sync with Decoder.StackDepth.
|
||||
@@ -879,7 +947,7 @@ func (e *Encoder) StackDepth() int {
|
||||
// Each name and value in a JSON object is counted separately,
|
||||
// so the effective number of members would be half the length.
|
||||
// A complete JSON object must have an even length.
|
||||
func (e *Encoder) StackIndex(i int) (Kind, int) {
|
||||
func (e *Encoder) StackIndex(i int) (Kind, int64) {
|
||||
// NOTE: Keep in sync with Decoder.StackIndex.
|
||||
switch s := e.s.Tokens.index(i); {
|
||||
case i > 0 && s.isObject():
|
||||
@@ -892,9 +960,11 @@ func (e *Encoder) StackIndex(i int) (Kind, int) {
|
||||
}
|
||||
|
||||
// StackPointer returns a JSON Pointer (RFC 6901) to the most recently written value.
|
||||
// Object names are only present if [AllowDuplicateNames] is false, otherwise
|
||||
// object members are represented using their index within the object.
|
||||
func (e *Encoder) StackPointer() string {
|
||||
e.s.Names.copyQuotedBuffer(e.s.Buf)
|
||||
return string(e.s.appendStackPointer(nil))
|
||||
func (e *Encoder) StackPointer() Pointer {
|
||||
return Pointer(e.s.AppendStackPointer(nil, -1))
|
||||
}
|
||||
|
||||
func (e *encoderState) AppendStackPointer(b []byte, where int) []byte {
|
||||
e.Names.copyQuotedBuffer(e.Buf)
|
||||
return e.state.appendStackPointer(b, where)
|
||||
}
|
||||
|
||||
152
vendor/github.com/go-json-experiment/json/jsontext/errors.go
generated
vendored
152
vendor/github.com/go-json-experiment/json/jsontext/errors.go
generated
vendored
@@ -5,6 +5,10 @@
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
@@ -32,29 +36,145 @@ type SyntacticError struct {
|
||||
|
||||
// ByteOffset indicates that an error occurred after this byte offset.
|
||||
ByteOffset int64
|
||||
str string
|
||||
// JSONPointer indicates that an error occurred within this JSON value
|
||||
// as indicated using the JSON Pointer notation (see RFC 6901).
|
||||
JSONPointer Pointer
|
||||
|
||||
// Err is the underlying error.
|
||||
Err error
|
||||
}
|
||||
|
||||
// wrapSyntacticError wraps an error and annotates it with a precise location
|
||||
// using the provided [encoderState] or [decoderState].
|
||||
// If err is an [ioError] or [io.EOF], then it is not wrapped.
|
||||
//
|
||||
// It takes a relative offset pos that can be resolved into
|
||||
// an absolute offset using state.offsetAt.
|
||||
//
|
||||
// It takes a where that specify how the JSON pointer is derived.
|
||||
// If the underlying error is a [pointerSuffixError],
|
||||
// then the suffix is appended to the derived pointer.
|
||||
func wrapSyntacticError(state interface {
|
||||
offsetAt(pos int) int64
|
||||
AppendStackPointer(b []byte, where int) []byte
|
||||
}, err error, pos, where int) error {
|
||||
if _, ok := err.(*ioError); err == io.EOF || ok {
|
||||
return err
|
||||
}
|
||||
offset := state.offsetAt(pos)
|
||||
ptr := state.AppendStackPointer(nil, where)
|
||||
if serr, ok := err.(*pointerSuffixError); ok {
|
||||
ptr = serr.appendPointer(ptr)
|
||||
err = serr.error
|
||||
}
|
||||
if d, ok := state.(*decoderState); ok && err == errMismatchDelim {
|
||||
where := "at start of value"
|
||||
if len(d.Tokens.Stack) > 0 && d.Tokens.Last.Length() > 0 {
|
||||
switch {
|
||||
case d.Tokens.Last.isArray():
|
||||
where = "after array element (expecting ',' or ']')"
|
||||
ptr = []byte(Pointer(ptr).Parent()) // problem is with parent array
|
||||
case d.Tokens.Last.isObject():
|
||||
where = "after object value (expecting ',' or '}')"
|
||||
ptr = []byte(Pointer(ptr).Parent()) // problem is with parent object
|
||||
}
|
||||
}
|
||||
err = jsonwire.NewInvalidCharacterError(d.buf[pos:], where)
|
||||
}
|
||||
return &SyntacticError{ByteOffset: offset, JSONPointer: Pointer(ptr), Err: err}
|
||||
}
|
||||
|
||||
func (e *SyntacticError) Error() string {
|
||||
return errorPrefix + e.str
|
||||
}
|
||||
func (e *SyntacticError) withOffset(pos int64) error {
|
||||
return &SyntacticError{ByteOffset: pos, str: e.str}
|
||||
pointer := e.JSONPointer
|
||||
offset := e.ByteOffset
|
||||
b := []byte(errorPrefix)
|
||||
if e.Err != nil {
|
||||
b = append(b, e.Err.Error()...)
|
||||
if e.Err == ErrDuplicateName {
|
||||
b = strconv.AppendQuote(append(b, ' '), pointer.LastToken())
|
||||
pointer = pointer.Parent()
|
||||
offset = 0 // not useful to print offset for duplicate names
|
||||
}
|
||||
} else {
|
||||
b = append(b, "syntactic error"...)
|
||||
}
|
||||
if pointer != "" {
|
||||
b = strconv.AppendQuote(append(b, " within "...), jsonwire.TruncatePointer(string(pointer), 100))
|
||||
}
|
||||
if offset > 0 {
|
||||
b = strconv.AppendInt(append(b, " after offset "...), offset, 10)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func newDuplicateNameError[Bytes ~[]byte | ~string](quoted Bytes) *SyntacticError {
|
||||
return &SyntacticError{str: "duplicate name " + string(quoted) + " in object"}
|
||||
func (e *SyntacticError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func newInvalidCharacterError[Bytes ~[]byte | ~string](prefix Bytes, where string) *SyntacticError {
|
||||
what := jsonwire.QuoteRune(prefix)
|
||||
return &SyntacticError{str: "invalid character " + what + " " + where}
|
||||
// pointerSuffixError represents a JSON pointer suffix to be appended
|
||||
// to [SyntacticError.JSONPointer]. It is an internal error type
|
||||
// used within this package and does not appear in the public API.
|
||||
//
|
||||
// This type is primarily used to annotate errors in Encoder.WriteValue
|
||||
// and Decoder.ReadValue with precise positions.
|
||||
// At the time WriteValue or ReadValue is called, a JSON pointer to the
|
||||
// upcoming value can be constructed using the Encoder/Decoder state.
|
||||
// However, tracking pointers within values during normal operation
|
||||
// would incur a performance penalty in the error-free case.
|
||||
//
|
||||
// To provide precise error locations without this overhead,
|
||||
// the error is wrapped with object names or array indices
|
||||
// as the call stack is popped when an error occurs.
|
||||
// Since this happens in reverse order, pointerSuffixError holds
|
||||
// the pointer in reverse and is only later reversed when appending to
|
||||
// the pointer prefix.
|
||||
//
|
||||
// For example, if the encoder is at "/alpha/bravo/charlie"
|
||||
// and an error occurs in WriteValue at "/xray/yankee/zulu", then
|
||||
// the final pointer should be "/alpha/bravo/charlie/xray/yankee/zulu".
|
||||
//
|
||||
// As pointerSuffixError is populated during the error return path,
|
||||
// it first contains "/zulu", then "/zulu/yankee",
|
||||
// and finally "/zulu/yankee/xray".
|
||||
// These tokens are reversed and concatenated to "/alpha/bravo/charlie"
|
||||
// to form the full pointer.
|
||||
type pointerSuffixError struct {
|
||||
error
|
||||
|
||||
// reversePointer is a JSON pointer, but with each token in reverse order.
|
||||
reversePointer []byte
|
||||
}
|
||||
|
||||
// TODO: Error types between "json", "jsontext", and "jsonwire" is a mess.
|
||||
// Clean this up.
|
||||
func init() {
|
||||
// Inject behavior in "jsonwire" so that it can produce SyntacticError types.
|
||||
jsonwire.NewError = func(s string) error { return &SyntacticError{str: s} }
|
||||
jsonwire.ErrInvalidUTF8 = &SyntacticError{str: jsonwire.ErrInvalidUTF8.Error()}
|
||||
// wrapWithObjectName wraps err with a JSON object name access,
|
||||
// which must be a valid quoted JSON string.
|
||||
func wrapWithObjectName(err error, quotedName []byte) error {
|
||||
serr, _ := err.(*pointerSuffixError)
|
||||
if serr == nil {
|
||||
serr = &pointerSuffixError{error: err}
|
||||
}
|
||||
name := jsonwire.UnquoteMayCopy(quotedName, false)
|
||||
serr.reversePointer = appendEscapePointerName(append(serr.reversePointer, '/'), name)
|
||||
return serr
|
||||
}
|
||||
|
||||
// wrapWithArrayIndex wraps err with a JSON array index access.
|
||||
func wrapWithArrayIndex(err error, index int64) error {
|
||||
serr, _ := err.(*pointerSuffixError)
|
||||
if serr == nil {
|
||||
serr = &pointerSuffixError{error: err}
|
||||
}
|
||||
serr.reversePointer = strconv.AppendUint(append(serr.reversePointer, '/'), uint64(index), 10)
|
||||
return serr
|
||||
}
|
||||
|
||||
// appendPointer appends the path encoded in e to the end of pointer.
|
||||
func (e *pointerSuffixError) appendPointer(pointer []byte) []byte {
|
||||
// Copy each token in reversePointer to the end of pointer in reverse order.
|
||||
// Double reversal means that the appended suffix is now in forward order.
|
||||
bi, bo := e.reversePointer, pointer
|
||||
for len(bi) > 0 {
|
||||
i := bytes.LastIndexByte(bi, '/')
|
||||
bi, bo = bi[:i], append(bo, bi[i:]...)
|
||||
}
|
||||
return bo
|
||||
}
|
||||
|
||||
14
vendor/github.com/go-json-experiment/json/jsontext/export.go
generated
vendored
14
vendor/github.com/go-json-experiment/json/jsontext/export.go
generated
vendored
@@ -69,15 +69,7 @@ func (export) PutStreamingDecoder(d *Decoder) {
|
||||
putStreamingDecoder(d)
|
||||
}
|
||||
|
||||
func (export) NewDuplicateNameError(quoted []byte, pos int64) error {
|
||||
return newDuplicateNameError(quoted).withOffset(pos)
|
||||
}
|
||||
func (export) NewInvalidCharacterError(prefix, where string, pos int64) error {
|
||||
return newInvalidCharacterError(prefix, where).withOffset(pos)
|
||||
}
|
||||
func (export) NewMissingNameError(pos int64) error {
|
||||
return errMissingName.withOffset(pos)
|
||||
}
|
||||
func (export) NewInvalidUTF8Error(pos int64) error {
|
||||
return errInvalidUTF8.withOffset(pos)
|
||||
func (export) IsIOError(err error) bool {
|
||||
_, ok := err.(*ioError)
|
||||
return ok
|
||||
}
|
||||
|
||||
149
vendor/github.com/go-json-experiment/json/jsontext/options.go
generated
vendored
149
vendor/github.com/go-json-experiment/json/jsontext/options.go
generated
vendored
@@ -17,6 +17,25 @@ import (
|
||||
// Each function takes in a variadic list of options, where properties
|
||||
// set in latter options override the value of previously set properties.
|
||||
//
|
||||
// There is a single Options type, which is used with both encoding and decoding.
|
||||
// Some options affect both operations, while others only affect one operation:
|
||||
//
|
||||
// - [AllowDuplicateNames] affects encoding and decoding
|
||||
// - [AllowInvalidUTF8] affects encoding and decoding
|
||||
// - [EscapeForHTML] affects encoding only
|
||||
// - [EscapeForJS] affects encoding only
|
||||
// - [PreserveRawStrings] affects encoding only
|
||||
// - [CanonicalizeRawInts] affects encoding only
|
||||
// - [CanonicalizeRawFloats] affects encoding only
|
||||
// - [ReorderRawObjects] affects encoding only
|
||||
// - [SpaceAfterColon] affects encoding only
|
||||
// - [SpaceAfterComma] affects encoding only
|
||||
// - [Multiline] affects encoding only
|
||||
// - [WithIndent] affects encoding only
|
||||
// - [WithIndentPrefix] affects encoding only
|
||||
//
|
||||
// Options that do not affect a particular operation are ignored.
|
||||
//
|
||||
// The Options type is identical to [encoding/json.Options] and
|
||||
// [encoding/json/v2.Options]. Options from the other packages may
|
||||
// be passed to functionality in this package, but are ignored.
|
||||
@@ -78,20 +97,122 @@ func EscapeForJS(v bool) Options {
|
||||
}
|
||||
}
|
||||
|
||||
// Expand specifies that the JSON output should be expanded,
|
||||
// where every JSON object member or JSON array element
|
||||
// appears on a new, indented line according to the nesting depth.
|
||||
// If an indent is not already specified, then it defaults to using "\t".
|
||||
//
|
||||
// If set to false, then the output is compact,
|
||||
// where no whitespace is emitted between JSON values.
|
||||
// PreserveRawStrings specifies that when encoding a raw JSON string in a
|
||||
// [Token] or [Value], pre-escaped sequences
|
||||
// in a JSON string are preserved to the output.
|
||||
// However, raw strings still respect [EscapeForHTML] and [EscapeForJS]
|
||||
// such that the relevant characters are escaped.
|
||||
// If [AllowInvalidUTF8] is enabled, bytes of invalid UTF-8
|
||||
// are preserved to the output.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func Expand(v bool) Options {
|
||||
func PreserveRawStrings(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.Expand | 1
|
||||
return jsonflags.PreserveRawStrings | 1
|
||||
} else {
|
||||
return jsonflags.Expand | 0
|
||||
return jsonflags.PreserveRawStrings | 0
|
||||
}
|
||||
}
|
||||
|
||||
// CanonicalizeRawInts specifies that when encoding a raw JSON
|
||||
// integer number (i.e., a number without a fraction and exponent) in a
|
||||
// [Token] or [Value], the number is canonicalized
|
||||
// according to RFC 8785, section 3.2.2.3. As a special case,
|
||||
// the number -0 is canonicalized as 0.
|
||||
//
|
||||
// JSON numbers are treated as IEEE 754 double precision numbers.
|
||||
// Any numbers with precision beyond what is representable by that form
|
||||
// will lose their precision when canonicalized. For example,
|
||||
// integer values beyond ±2⁵³ will lose their precision.
|
||||
// For example, 1234567890123456789 is formatted as 1234567890123456800.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func CanonicalizeRawInts(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.CanonicalizeRawInts | 1
|
||||
} else {
|
||||
return jsonflags.CanonicalizeRawInts | 0
|
||||
}
|
||||
}
|
||||
|
||||
// CanonicalizeRawFloats specifies that when encoding a raw JSON
|
||||
// floating-point number (i.e., a number with a fraction or exponent) in a
|
||||
// [Token] or [Value], the number is canonicalized
|
||||
// according to RFC 8785, section 3.2.2.3. As a special case,
|
||||
// the number -0 is canonicalized as 0.
|
||||
//
|
||||
// JSON numbers are treated as IEEE 754 double precision numbers.
|
||||
// It is safe to canonicalize a serialized single precision number and
|
||||
// parse it back as a single precision number and expect the same value.
|
||||
// If a number exceeds ±1.7976931348623157e+308, which is the maximum
|
||||
// finite number, then it saturated at that value and formatted as such.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func CanonicalizeRawFloats(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.CanonicalizeRawFloats | 1
|
||||
} else {
|
||||
return jsonflags.CanonicalizeRawFloats | 0
|
||||
}
|
||||
}
|
||||
|
||||
// ReorderRawObjects specifies that when encoding a raw JSON object in a
|
||||
// [Value], the object members are reordered according to
|
||||
// RFC 8785, section 3.2.3.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func ReorderRawObjects(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.ReorderRawObjects | 1
|
||||
} else {
|
||||
return jsonflags.ReorderRawObjects | 0
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceAfterColon specifies that the JSON output should emit a space character
|
||||
// after each colon separator following a JSON object name.
|
||||
// If false, then no space character appears after the colon separator.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func SpaceAfterColon(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.SpaceAfterColon | 1
|
||||
} else {
|
||||
return jsonflags.SpaceAfterColon | 0
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceAfterComma specifies that the JSON output should emit a space character
|
||||
// after each comma separator following a JSON object value or array element.
|
||||
// If false, then no space character appears after the comma separator.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func SpaceAfterComma(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.SpaceAfterComma | 1
|
||||
} else {
|
||||
return jsonflags.SpaceAfterComma | 0
|
||||
}
|
||||
}
|
||||
|
||||
// Multiline specifies that the JSON output should expand to multiple lines,
|
||||
// where every JSON object member or JSON array element appears on
|
||||
// a new, indented line according to the nesting depth.
|
||||
//
|
||||
// If [SpaceAfterColon] is not specified, then the default is true.
|
||||
// If [SpaceAfterComma] is not specified, then the default is false.
|
||||
// If [WithIndent] is not specified, then the default is "\t".
|
||||
//
|
||||
// If set to false, then the output is a single-line,
|
||||
// where the only whitespace emitted is determined by the current
|
||||
// values of [SpaceAfterColon] and [SpaceAfterComma].
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func Multiline(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.Multiline | 1
|
||||
} else {
|
||||
return jsonflags.Multiline | 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,10 +223,10 @@ func Expand(v bool) Options {
|
||||
// The indent must only be composed of space or tab characters.
|
||||
//
|
||||
// If the intent to emit indented output without a preference for
|
||||
// the particular indent string, then use [Expand] instead.
|
||||
// the particular indent string, then use [Multiline] instead.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
// Use of this option implies [Expand] being set to true.
|
||||
// Use of this option implies [Multiline] being set to true.
|
||||
func WithIndent(indent string) Options {
|
||||
// Fast-path: Return a constant for common indents, which avoids allocating.
|
||||
// These are derived from analyzing the Go module proxy on 2023-07-01.
|
||||
@@ -138,7 +259,7 @@ func WithIndent(indent string) Options {
|
||||
// The prefix must only be composed of space or tab characters.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
// Use of this option implies [Expand] being set to true.
|
||||
// Use of this option implies [Multiline] being set to true.
|
||||
func WithIndentPrefix(prefix string) Options {
|
||||
if s := strings.Trim(prefix, " \t"); len(s) > 0 {
|
||||
panic("json: invalid character " + jsonwire.QuoteRune(s) + " in indent prefix")
|
||||
@@ -160,6 +281,7 @@ func WithIndentPrefix(prefix string) Options {
|
||||
//
|
||||
// A non-positive limit is equivalent to no limit at all.
|
||||
// If unspecified, the default limit is no limit at all.
|
||||
// This affects either encoding or decoding.
|
||||
func WithByteLimit(n int64) Options {
|
||||
return jsonopts.ByteLimit(max(n, 0))
|
||||
}
|
||||
@@ -172,6 +294,7 @@ func WithByteLimit(n int64) Options {
|
||||
//
|
||||
// A non-positive limit is equivalent to no limit at all.
|
||||
// If unspecified, the default limit is 10000.
|
||||
// This affects either encoding or decoding.
|
||||
func WithDepthLimit(n int) Options {
|
||||
return jsonopts.DepthLimit(max(n, 0))
|
||||
}
|
||||
|
||||
16
vendor/github.com/go-json-experiment/json/jsontext/quote.go
generated
vendored
16
vendor/github.com/go-json-experiment/json/jsontext/quote.go
generated
vendored
@@ -9,15 +9,18 @@ import (
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
var errInvalidUTF8 = &SyntacticError{str: "invalid UTF-8 within string"}
|
||||
|
||||
// AppendQuote appends a double-quoted JSON string literal representing src
|
||||
// to dst and returns the extended buffer.
|
||||
// It uses the minimal string representation per RFC 8785, section 3.2.2.2.
|
||||
// Invalid UTF-8 bytes are replaced with the Unicode replacement character
|
||||
// and an error is returned at the end indicating the presence of invalid UTF-8.
|
||||
// The dst must not overlap with the src.
|
||||
func AppendQuote[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error) {
|
||||
return jsonwire.AppendQuote(dst, src, &jsonflags.Flags{})
|
||||
dst, err := jsonwire.AppendQuote(dst, src, &jsonflags.Flags{})
|
||||
if err != nil {
|
||||
err = &SyntacticError{Err: err}
|
||||
}
|
||||
return dst, err
|
||||
}
|
||||
|
||||
// AppendUnquote appends the decoded interpretation of src as a
|
||||
@@ -26,6 +29,11 @@ func AppendQuote[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error)
|
||||
// Invalid UTF-8 bytes are replaced with the Unicode replacement character
|
||||
// and an error is returned at the end indicating the presence of invalid UTF-8.
|
||||
// Any trailing bytes after the JSON string literal results in an error.
|
||||
// The dst must not overlap with the src.
|
||||
func AppendUnquote[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error) {
|
||||
return jsonwire.AppendUnquote(dst, src)
|
||||
dst, err := jsonwire.AppendUnquote(dst, src)
|
||||
if err != nil {
|
||||
err = &SyntacticError{Err: err}
|
||||
}
|
||||
return dst, err
|
||||
}
|
||||
|
||||
225
vendor/github.com/go-json-experiment/json/jsontext/state.go
generated
vendored
225
vendor/github.com/go-json-experiment/json/jsontext/state.go
generated
vendored
@@ -5,21 +5,45 @@
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"iter"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingName = &SyntacticError{str: "missing string for object name"}
|
||||
errMissingColon = &SyntacticError{str: "missing character ':' after object name"}
|
||||
errMissingValue = &SyntacticError{str: "missing value after object name"}
|
||||
errMissingComma = &SyntacticError{str: "missing character ',' after object or array value"}
|
||||
errMismatchDelim = &SyntacticError{str: "mismatching structural token for object or array"}
|
||||
errMaxDepth = &SyntacticError{str: "exceeded max depth"}
|
||||
// ErrDuplicateName indicates that a JSON token could not be
|
||||
// encoded or decoded because it results in a duplicate JSON object name.
|
||||
// This error is directly wrapped within a [SyntacticError] when produced.
|
||||
//
|
||||
// The name of a duplicate JSON object member can be extracted as:
|
||||
//
|
||||
// err := ...
|
||||
// var serr jsontext.SyntacticError
|
||||
// if errors.As(err, &serr) && serr.Err == jsontext.ErrDuplicateName {
|
||||
// ptr := serr.JSONPointer // JSON pointer to duplicate name
|
||||
// name := ptr.LastToken() // duplicate name itself
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// This error is only returned if [AllowDuplicateNames] is false.
|
||||
var ErrDuplicateName = errors.New("duplicate object member name")
|
||||
|
||||
errInvalidNamespace = &SyntacticError{str: "object namespace is in an invalid state"}
|
||||
// ErrNonStringName indicates that a JSON token could not be
|
||||
// encoded or decoded because it is not a string,
|
||||
// as required for JSON object names according to RFC 8259, section 4.
|
||||
// This error is directly wrapped within a [SyntacticError] when produced.
|
||||
var ErrNonStringName = errors.New("object member name must be a string")
|
||||
|
||||
var (
|
||||
errMissingValue = errors.New("missing value after object name")
|
||||
errMismatchDelim = errors.New("mismatching structural token for object or array")
|
||||
errMaxDepth = errors.New("exceeded max depth")
|
||||
|
||||
errInvalidNamespace = errors.New("object namespace is in an invalid state")
|
||||
)
|
||||
|
||||
// Per RFC 8259, section 9, implementations may enforce a maximum depth.
|
||||
@@ -31,7 +55,6 @@ type state struct {
|
||||
Tokens stateMachine
|
||||
|
||||
// Names is a stack of object names.
|
||||
// Not used if AllowDuplicateNames is true.
|
||||
Names objectNameStack
|
||||
|
||||
// Namespaces is a stack of object namespaces.
|
||||
@@ -42,48 +65,151 @@ type state struct {
|
||||
Namespaces objectNamespaceStack
|
||||
}
|
||||
|
||||
// needObjectValue reports whether the next token should be an object value.
|
||||
// This method is used by [wrapSyntacticError].
|
||||
func (s *state) needObjectValue() bool {
|
||||
return s.Tokens.Last.needObjectValue()
|
||||
}
|
||||
|
||||
func (s *state) reset() {
|
||||
s.Tokens.reset()
|
||||
s.Names.reset()
|
||||
s.Namespaces.reset()
|
||||
}
|
||||
|
||||
// Pointer is a JSON Pointer (RFC 6901) that references a particular JSON value
|
||||
// relative to the root of the top-level JSON value.
|
||||
//
|
||||
// A Pointer is a slash-separated list of tokens, where each token is
|
||||
// either a JSON object name or an index to a JSON array element
|
||||
// encoded as a base-10 integer value.
|
||||
// It is impossible to distinguish between an array index and an object name
|
||||
// (that happens to be an base-10 encoded integer) without also knowing
|
||||
// the structure of the top-level JSON value that the pointer refers to.
|
||||
//
|
||||
// There is exactly one representation of a pointer to a particular value,
|
||||
// so comparability of Pointer values is equivalent to checking whether
|
||||
// they both point to the exact same value.
|
||||
type Pointer string
|
||||
|
||||
// IsValid reports whether p is a valid JSON Pointer according to RFC 6901.
|
||||
// Note that the concatenation of two valid pointers produces a valid pointer.
|
||||
func (p Pointer) IsValid() bool {
|
||||
for i, r := range p {
|
||||
switch {
|
||||
case r == '~' && (i+1 == len(p) || (p[i+1] != '0' && p[i+1] != '1')):
|
||||
return false // invalid escape
|
||||
case r == '\ufffd' && !strings.HasPrefix(string(p[i:]), "\ufffd"):
|
||||
return false // invalid UTF-8
|
||||
}
|
||||
}
|
||||
return len(p) == 0 || p[0] == '/'
|
||||
}
|
||||
|
||||
// Contains reports whether the JSON value that p points to
|
||||
// is equal to or contains the JSON value that pc points to.
|
||||
func (p Pointer) Contains(pc Pointer) bool {
|
||||
// Invariant: len(p) <= len(pc) if p.Contains(pc)
|
||||
suffix, ok := strings.CutPrefix(string(pc), string(p))
|
||||
return ok && (suffix == "" || suffix[0] == '/')
|
||||
}
|
||||
|
||||
// Parent strips off the last token and returns the remaining pointer.
|
||||
// The parent of an empty p is an empty string.
|
||||
func (p Pointer) Parent() Pointer {
|
||||
return p[:max(strings.LastIndexByte(string(p), '/'), 0)]
|
||||
}
|
||||
|
||||
// LastToken returns the last token in the pointer.
|
||||
// The last token of an empty p is an empty string.
|
||||
func (p Pointer) LastToken() string {
|
||||
last := p[max(strings.LastIndexByte(string(p), '/'), 0):]
|
||||
return unescapePointerToken(strings.TrimPrefix(string(last), "/"))
|
||||
}
|
||||
|
||||
// AppendToken appends a token to the end of p and returns the full pointer.
|
||||
func (p Pointer) AppendToken(tok string) Pointer {
|
||||
return Pointer(appendEscapePointerName([]byte(p+"/"), tok))
|
||||
}
|
||||
|
||||
// TODO: Add Pointer.AppendTokens,
|
||||
// but should this take in a ...string or an iter.Seq[string]?
|
||||
|
||||
// Tokens returns an iterator over the reference tokens in the JSON pointer,
|
||||
// starting from the first token until the last token (unless stopped early).
|
||||
func (p Pointer) Tokens() iter.Seq[string] {
|
||||
return func(yield func(string) bool) {
|
||||
for len(p) > 0 {
|
||||
p = Pointer(strings.TrimPrefix(string(p), "/"))
|
||||
i := min(uint(strings.IndexByte(string(p), '/')), uint(len(p)))
|
||||
if !yield(unescapePointerToken(string(p)[:i])) {
|
||||
return
|
||||
}
|
||||
p = p[i:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unescapePointerToken(token string) string {
|
||||
if strings.Contains(token, "~") {
|
||||
// Per RFC 6901, section 3, unescape '~' and '/' characters.
|
||||
token = strings.ReplaceAll(token, "~1", "/")
|
||||
token = strings.ReplaceAll(token, "~0", "~")
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
// appendStackPointer appends a JSON Pointer (RFC 6901) to the current value.
|
||||
// The returned pointer is only accurate if s.names is populated,
|
||||
// otherwise it uses the numeric index as the object member name.
|
||||
//
|
||||
// - If where is -1, then it points to the previously processed token.
|
||||
//
|
||||
// - If where is 0, then it points to the parent JSON object or array,
|
||||
// or an object member if in-between an object member key and value.
|
||||
// This is useful when the position is ambiguous whether
|
||||
// we are interested in the previous or next token, or
|
||||
// when we are uncertain whether the next token
|
||||
// continues or terminates the current object or array.
|
||||
//
|
||||
// - If where is +1, then it points to the next expected value,
|
||||
// assuming that it continues the current JSON object or array.
|
||||
// As a special case, if the next token is a JSON object name,
|
||||
// then it points to the parent JSON object.
|
||||
//
|
||||
// Invariant: Must call s.names.copyQuotedBuffer beforehand.
|
||||
func (s state) appendStackPointer(b []byte) []byte {
|
||||
func (s state) appendStackPointer(b []byte, where int) []byte {
|
||||
var objectDepth int
|
||||
for i := 1; i < s.Tokens.Depth(); i++ {
|
||||
e := s.Tokens.index(i)
|
||||
if e.Length() == 0 {
|
||||
break // empty object or array
|
||||
arrayDelta := -1 // by default point to previous array element
|
||||
if isLast := i == s.Tokens.Depth()-1; isLast {
|
||||
switch {
|
||||
case where < 0 && e.Length() == 0 || where == 0 && !e.needObjectValue() || where > 0 && e.NeedObjectName():
|
||||
return b
|
||||
case where > 0 && e.isArray():
|
||||
arrayDelta = 0 // point to next array element
|
||||
}
|
||||
}
|
||||
b = append(b, '/')
|
||||
switch {
|
||||
case e.isObject():
|
||||
if objectDepth < s.Names.length() {
|
||||
for _, c := range s.Names.getUnquoted(objectDepth) {
|
||||
// Per RFC 6901, section 3, escape '~' and '/' characters.
|
||||
switch c {
|
||||
case '~':
|
||||
b = append(b, "~0"...)
|
||||
case '/':
|
||||
b = append(b, "~1"...)
|
||||
default:
|
||||
b = append(b, c)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Since the names stack is unpopulated, the name is unknown.
|
||||
// As a best-effort replacement, use the numeric member index.
|
||||
// While inaccurate, it produces a syntactically valid pointer.
|
||||
b = strconv.AppendUint(b, uint64((e.Length()-1)/2), 10)
|
||||
}
|
||||
b = appendEscapePointerName(append(b, '/'), s.Names.getUnquoted(objectDepth))
|
||||
objectDepth++
|
||||
case e.isArray():
|
||||
b = strconv.AppendUint(b, uint64(e.Length()-1), 10)
|
||||
b = strconv.AppendUint(append(b, '/'), uint64(e.Length()+int64(arrayDelta)), 10)
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func appendEscapePointerName[Bytes ~[]byte | ~string](b []byte, name Bytes) []byte {
|
||||
for _, r := range string(name) {
|
||||
// Per RFC 6901, section 3, escape '~' and '/' characters.
|
||||
switch r {
|
||||
case '~':
|
||||
b = append(b, "~0"...)
|
||||
case '/':
|
||||
b = append(b, "~1"...)
|
||||
default:
|
||||
b = utf8.AppendRune(b, r)
|
||||
}
|
||||
}
|
||||
return b
|
||||
@@ -133,7 +259,7 @@ func (m *stateMachine) index(i int) *stateEntry {
|
||||
|
||||
// DepthLength reports the current nested depth and
|
||||
// the length of the last JSON object or array.
|
||||
func (m stateMachine) DepthLength() (int, int) {
|
||||
func (m stateMachine) DepthLength() (int, int64) {
|
||||
return m.Depth(), m.Last.Length()
|
||||
}
|
||||
|
||||
@@ -142,7 +268,7 @@ func (m stateMachine) DepthLength() (int, int) {
|
||||
func (m *stateMachine) appendLiteral() error {
|
||||
switch {
|
||||
case m.Last.NeedObjectName():
|
||||
return errMissingName
|
||||
return ErrNonStringName
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
default:
|
||||
@@ -174,7 +300,7 @@ func (m *stateMachine) appendNumber() error {
|
||||
func (m *stateMachine) pushObject() error {
|
||||
switch {
|
||||
case m.Last.NeedObjectName():
|
||||
return errMissingName
|
||||
return ErrNonStringName
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
case len(m.Stack) == maxNestingDepth:
|
||||
@@ -209,7 +335,7 @@ func (m *stateMachine) popObject() error {
|
||||
func (m *stateMachine) pushArray() error {
|
||||
switch {
|
||||
case m.Last.NeedObjectName():
|
||||
return errMissingName
|
||||
return ErrNonStringName
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
case len(m.Stack) == maxNestingDepth:
|
||||
@@ -283,21 +409,6 @@ func (m stateMachine) needDelim(next Kind) (delim byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// checkDelim reports whether the specified delimiter should be there given
|
||||
// the kind of the next token that appears immediately afterwards.
|
||||
func (m stateMachine) checkDelim(delim byte, next Kind) error {
|
||||
switch needDelim := m.needDelim(next); {
|
||||
case needDelim == delim:
|
||||
return nil
|
||||
case needDelim == ':':
|
||||
return errMissingColon
|
||||
case needDelim == ',':
|
||||
return errMissingComma
|
||||
default:
|
||||
return newInvalidCharacterError([]byte{delim}, "before next token")
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidateDisabledNamespaces marks all disabled namespaces as invalid.
|
||||
//
|
||||
// For efficiency, Marshal and Unmarshal may disable namespaces since there are
|
||||
@@ -306,7 +417,7 @@ func (m stateMachine) checkDelim(delim byte, next Kind) error {
|
||||
// Mark the namespaces as invalid so that future method calls on
|
||||
// Encoder or Decoder will return an error.
|
||||
func (m *stateMachine) InvalidateDisabledNamespaces() {
|
||||
for i := 0; i < m.Depth(); i++ {
|
||||
for i := range m.Depth() {
|
||||
e := m.index(i)
|
||||
if !e.isActiveNamespace() {
|
||||
e.invalidateNamespace()
|
||||
@@ -342,8 +453,8 @@ const (
|
||||
|
||||
// Length reports the number of elements in the JSON object or array.
|
||||
// Each name and value in an object entry is treated as a separate element.
|
||||
func (e stateEntry) Length() int {
|
||||
return int(e & stateCountMask)
|
||||
func (e stateEntry) Length() int64 {
|
||||
return int64(e & stateCountMask)
|
||||
}
|
||||
|
||||
// isObject reports whether this is a JSON object.
|
||||
@@ -453,7 +564,7 @@ func (ns *objectNameStack) length() int {
|
||||
return len(ns.offsets)
|
||||
}
|
||||
|
||||
// getUnquoted retrieves the ith unquoted name in the namespace.
|
||||
// getUnquoted retrieves the ith unquoted name in the stack.
|
||||
// It returns an empty string if the last object is empty.
|
||||
//
|
||||
// Invariant: Must call copyQuotedBuffer beforehand.
|
||||
|
||||
56
vendor/github.com/go-json-experiment/json/jsontext/token.go
generated
vendored
56
vendor/github.com/go-json-experiment/json/jsontext/token.go
generated
vendored
@@ -5,6 +5,8 @@
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
@@ -20,9 +22,11 @@ const (
|
||||
maxUint64 = math.MaxUint64
|
||||
minUint64 = 0 // for consistency and readability purposes
|
||||
|
||||
invalidTokenPanic = "invalid json.Token; it has been voided by a subsequent json.Decoder call"
|
||||
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!")
|
||||
@@ -90,10 +94,10 @@ var (
|
||||
False Token = rawToken("false")
|
||||
True Token = rawToken("true")
|
||||
|
||||
ObjectStart Token = rawToken("{")
|
||||
ObjectEnd Token = rawToken("}")
|
||||
ArrayStart Token = rawToken("[")
|
||||
ArrayEnd Token = rawToken("]")
|
||||
BeginObject Token = rawToken("{")
|
||||
EndObject Token = rawToken("}")
|
||||
BeginArray Token = rawToken("[")
|
||||
EndArray Token = rawToken("]")
|
||||
|
||||
zeroString Token = rawToken(`""`)
|
||||
zeroNumber Token = rawToken(`0`)
|
||||
@@ -172,22 +176,21 @@ func (t Token) Clone() Token {
|
||||
return False
|
||||
case True.raw:
|
||||
return True
|
||||
case ObjectStart.raw:
|
||||
return ObjectStart
|
||||
case ObjectEnd.raw:
|
||||
return ObjectEnd
|
||||
case ArrayStart.raw:
|
||||
return ArrayStart
|
||||
case ArrayEnd.raw:
|
||||
return ArrayEnd
|
||||
case BeginObject.raw:
|
||||
return BeginObject
|
||||
case EndObject.raw:
|
||||
return EndObject
|
||||
case BeginArray.raw:
|
||||
return BeginArray
|
||||
case EndArray.raw:
|
||||
return EndArray
|
||||
}
|
||||
}
|
||||
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
// TODO(https://go.dev/issue/45038): Use bytes.Clone.
|
||||
buf := append([]byte(nil), raw.PreviousBuffer()...)
|
||||
buf := bytes.Clone(raw.previousBuffer())
|
||||
return Token{raw: &decodeBuffer{buf: buf, prevStart: 0, prevEnd: len(buf)}}
|
||||
}
|
||||
return t
|
||||
@@ -211,7 +214,7 @@ func (t Token) Bool() bool {
|
||||
func (t Token) appendString(dst []byte, flags *jsonflags.Flags) ([]byte, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw string value.
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]) == '"' {
|
||||
if jsonwire.ConsumeSimpleString(buf) == len(buf) {
|
||||
return append(dst, buf...), nil
|
||||
@@ -245,7 +248,7 @@ func (t Token) string() (string, []byte) {
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if buf[0] == '"' {
|
||||
// TODO: Preserve ValueFlags in Token?
|
||||
isVerbatim := jsonwire.ConsumeSimpleString(buf) == len(buf)
|
||||
@@ -268,20 +271,17 @@ func (t Token) string() (string, []byte) {
|
||||
return strconv.FormatUint(uint64(t.num), 10), nil
|
||||
}
|
||||
}
|
||||
return "<invalid json.Token>", 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 (t Token) appendNumber(dst []byte, canonicalize bool) ([]byte, error) {
|
||||
func (t Token) appendNumber(dst []byte, flags *jsonflags.Flags) ([]byte, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw number value.
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
if !canonicalize {
|
||||
return append(dst, buf...), nil
|
||||
}
|
||||
dst, _, err := jsonwire.ReformatNumber(dst, buf, canonicalize)
|
||||
dst, _, err := jsonwire.ReformatNumber(dst, buf, flags)
|
||||
return dst, err
|
||||
}
|
||||
} else if t.num != 0 {
|
||||
@@ -309,7 +309,7 @@ func (t Token) Float() float64 {
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
fv, _ := jsonwire.ParseFloat(buf, 64)
|
||||
return fv
|
||||
@@ -353,7 +353,7 @@ func (t Token) Int() int64 {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
neg := false
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if len(buf) > 0 && buf[0] == '-' {
|
||||
neg, buf = true, buf[1:]
|
||||
}
|
||||
@@ -414,7 +414,7 @@ func (t Token) Uint() uint64 {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
neg := false
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if len(buf) > 0 && buf[0] == '-' {
|
||||
neg, buf = true, buf[1:]
|
||||
}
|
||||
@@ -512,7 +512,7 @@ func (k Kind) String() string {
|
||||
case ']':
|
||||
return "]"
|
||||
default:
|
||||
return "<invalid json.Kind: " + jsonwire.QuoteRune(string(k)) + ">"
|
||||
return "<invalid jsontext.Kind: " + jsonwire.QuoteRune(string(k)) + ">"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
324
vendor/github.com/go-json-experiment/json/jsontext/value.go
generated
vendored
324
vendor/github.com/go-json-experiment/json/jsontext/value.go
generated
vendored
@@ -9,7 +9,6 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
@@ -18,6 +17,22 @@ import (
|
||||
|
||||
// NOTE: Value is analogous to v1 json.RawMessage.
|
||||
|
||||
// AppendFormat formats the JSON value in src and appends it to dst
|
||||
// according to the specified options.
|
||||
// See [Value.Format] for more details about the formatting behavior.
|
||||
//
|
||||
// The dst and src may overlap.
|
||||
// If an error is reported, then the entirety of src is appended to dst.
|
||||
func AppendFormat(dst, src []byte, opts ...Options) ([]byte, error) {
|
||||
e := getBufferedEncoder(opts...)
|
||||
defer putBufferedEncoder(e)
|
||||
e.s.Flags.Set(jsonflags.OmitTopLevelNewline | 1)
|
||||
if err := e.s.WriteValue(src); err != nil {
|
||||
return append(dst, src...), err
|
||||
}
|
||||
return append(dst, e.s.Buf...), nil
|
||||
}
|
||||
|
||||
// Value represents a single raw JSON value, which may be one of the following:
|
||||
// - a JSON literal (i.e., null, true, or false)
|
||||
// - a JSON string (e.g., "hello, world!")
|
||||
@@ -43,70 +58,159 @@ func (v Value) String() string {
|
||||
}
|
||||
|
||||
// IsValid reports whether the raw JSON value is syntactically valid
|
||||
// according to RFC 7493.
|
||||
// according to the specified options.
|
||||
//
|
||||
// By default (if no options are specified), it validates according to RFC 7493.
|
||||
// It verifies whether the input is properly encoded as UTF-8,
|
||||
// that escape sequences within strings decode to valid Unicode codepoints, and
|
||||
// that all names in each object are unique.
|
||||
// It does not verify whether numbers are representable within the limits
|
||||
// of any common numeric type (e.g., float64, int64, or uint64).
|
||||
func (v Value) IsValid() bool {
|
||||
d := getBufferedDecoder(v)
|
||||
//
|
||||
// Relevant options include:
|
||||
// - [AllowDuplicateNames]
|
||||
// - [AllowInvalidUTF8]
|
||||
//
|
||||
// All other options are ignored.
|
||||
func (v Value) IsValid(opts ...Options) bool {
|
||||
// TODO: Document support for [WithByteLimit] and [WithDepthLimit].
|
||||
d := getBufferedDecoder(v, opts...)
|
||||
defer putBufferedDecoder(d)
|
||||
_, errVal := d.ReadValue()
|
||||
_, errEOF := d.ReadToken()
|
||||
return errVal == nil && errEOF == io.EOF
|
||||
}
|
||||
|
||||
// Format formats the raw JSON value in place.
|
||||
//
|
||||
// By default (if no options are specified), it validates according to RFC 7493
|
||||
// and produces the minimal JSON representation, where
|
||||
// all whitespace is elided and JSON strings use the shortest encoding.
|
||||
//
|
||||
// Relevant options include:
|
||||
// - [AllowDuplicateNames]
|
||||
// - [AllowInvalidUTF8]
|
||||
// - [EscapeForHTML]
|
||||
// - [EscapeForJS]
|
||||
// - [PreserveRawStrings]
|
||||
// - [CanonicalizeRawInts]
|
||||
// - [CanonicalizeRawFloats]
|
||||
// - [ReorderRawObjects]
|
||||
// - [SpaceAfterColon]
|
||||
// - [SpaceAfterComma]
|
||||
// - [Multiline]
|
||||
// - [WithIndent]
|
||||
// - [WithIndentPrefix]
|
||||
//
|
||||
// All other options are ignored.
|
||||
//
|
||||
// It is guaranteed to succeed if the value is valid according to the same options.
|
||||
// If the value is already formatted, then the buffer is not mutated.
|
||||
func (v *Value) Format(opts ...Options) error {
|
||||
// TODO: Document support for [WithByteLimit] and [WithDepthLimit].
|
||||
return v.format(opts, nil)
|
||||
}
|
||||
|
||||
// format accepts two []Options to avoid the allocation appending them together.
|
||||
// It is equivalent to v.Format(append(opts1, opts2...)...).
|
||||
func (v *Value) format(opts1, opts2 []Options) error {
|
||||
e := getBufferedEncoder(opts1...)
|
||||
defer putBufferedEncoder(e)
|
||||
e.s.Join(opts2...)
|
||||
e.s.Flags.Set(jsonflags.OmitTopLevelNewline | 1)
|
||||
if err := e.s.WriteValue(*v); err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(*v, e.s.Buf) {
|
||||
*v = append((*v)[:0], e.s.Buf...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compact removes all whitespace from the raw JSON value.
|
||||
//
|
||||
// It does not reformat JSON strings to use any other representation.
|
||||
// It is guaranteed to succeed if the input is valid.
|
||||
// If the value is already compacted, then the buffer is not mutated.
|
||||
func (v *Value) Compact() error {
|
||||
return v.reformat(false, false, "", "")
|
||||
// It does not reformat JSON strings or numbers to use any other representation.
|
||||
// To maximize the set of JSON values that can be formatted,
|
||||
// this permits values with duplicate names and invalid UTF-8.
|
||||
//
|
||||
// Compact is equivalent to calling [Value.Format] with the following options:
|
||||
// - [AllowDuplicateNames](true)
|
||||
// - [AllowInvalidUTF8](true)
|
||||
// - [PreserveRawStrings](true)
|
||||
//
|
||||
// Any options specified by the caller are applied after the initial set
|
||||
// and may deliberately override prior options.
|
||||
func (v *Value) Compact(opts ...Options) error {
|
||||
return v.format([]Options{
|
||||
AllowDuplicateNames(true),
|
||||
AllowInvalidUTF8(true),
|
||||
PreserveRawStrings(true),
|
||||
}, opts)
|
||||
}
|
||||
|
||||
// Indent reformats the whitespace in the raw JSON value so that each element
|
||||
// in a JSON object or array begins on a new, indented line beginning with
|
||||
// prefix followed by one or more copies of indent according to the nesting.
|
||||
// The value does not begin with the prefix nor any indention,
|
||||
// to make it easier to embed inside other formatted JSON data.
|
||||
// in a JSON object or array begins on a indented line according to the nesting.
|
||||
//
|
||||
// It does not reformat JSON strings to use any other representation.
|
||||
// It is guaranteed to succeed if the input is valid.
|
||||
// If the value is already indented properly, then the buffer is not mutated.
|
||||
// It does not reformat JSON strings or numbers to use any other representation.
|
||||
// To maximize the set of JSON values that can be formatted,
|
||||
// this permits values with duplicate names and invalid UTF-8.
|
||||
//
|
||||
// The prefix and indent strings must be composed of only spaces and/or tabs.
|
||||
func (v *Value) Indent(prefix, indent string) error {
|
||||
return v.reformat(false, true, prefix, indent)
|
||||
// Indent is equivalent to calling [Value.Format] with the following options:
|
||||
// - [AllowDuplicateNames](true)
|
||||
// - [AllowInvalidUTF8](true)
|
||||
// - [PreserveRawStrings](true)
|
||||
// - [Multiline](true)
|
||||
//
|
||||
// Any options specified by the caller are applied after the initial set
|
||||
// and may deliberately override prior options.
|
||||
func (v *Value) Indent(opts ...Options) error {
|
||||
return v.format([]Options{
|
||||
AllowDuplicateNames(true),
|
||||
AllowInvalidUTF8(true),
|
||||
PreserveRawStrings(true),
|
||||
Multiline(true),
|
||||
}, opts)
|
||||
}
|
||||
|
||||
// Canonicalize canonicalizes the raw JSON value according to the
|
||||
// JSON Canonicalization Scheme (JCS) as defined by RFC 8785
|
||||
// where it produces a stable representation of a JSON value.
|
||||
//
|
||||
// JSON strings are formatted to use their minimal representation,
|
||||
// JSON numbers are formatted as double precision numbers according
|
||||
// to some stable serialization algorithm.
|
||||
// JSON object members are sorted in ascending order by name.
|
||||
// All whitespace is removed.
|
||||
//
|
||||
// The output stability is dependent on the stability of the application data
|
||||
// (see RFC 8785, Appendix E). It cannot produce stable output from
|
||||
// fundamentally unstable input. For example, if the JSON value
|
||||
// contains ephemeral data (e.g., a frequently changing timestamp),
|
||||
// then the value is still unstable regardless of whether this is called.
|
||||
//
|
||||
// Canonicalize is equivalent to calling [Value.Format] with the following options:
|
||||
// - [CanonicalizeRawInts](true)
|
||||
// - [CanonicalizeRawFloats](true)
|
||||
// - [ReorderRawObjects](true)
|
||||
//
|
||||
// Any options specified by the caller are applied after the initial set
|
||||
// and may deliberately override prior options.
|
||||
//
|
||||
// Note that JCS treats all JSON numbers as IEEE 754 double precision numbers.
|
||||
// Any numbers with precision beyond what is representable by that form
|
||||
// will lose their precision when canonicalized. For example, integer values
|
||||
// beyond ±2⁵³ will lose their precision. It is recommended that
|
||||
// int64 and uint64 data types be represented as a JSON string.
|
||||
// beyond ±2⁵³ will lose their precision. To preserve the original representation
|
||||
// of JSON integers, additionally set [CanonicalizeRawInts] to false:
|
||||
//
|
||||
// It is guaranteed to succeed if the input is valid.
|
||||
// If the value is already canonicalized, then the buffer is not mutated.
|
||||
func (v *Value) Canonicalize() error {
|
||||
return v.reformat(true, false, "", "")
|
||||
// v.Canonicalize(jsontext.CanonicalizeRawInts(false))
|
||||
func (v *Value) Canonicalize(opts ...Options) error {
|
||||
return v.format([]Options{
|
||||
CanonicalizeRawInts(true),
|
||||
CanonicalizeRawFloats(true),
|
||||
ReorderRawObjects(true),
|
||||
}, opts)
|
||||
}
|
||||
|
||||
// TODO: Instead of implementing the v1 Marshaler/Unmarshaler,
|
||||
// consider implementing the v2 versions instead.
|
||||
|
||||
// MarshalJSON returns v as the JSON encoding of v.
|
||||
// It returns the stored value as the raw JSON output without any validation.
|
||||
// If v is nil, then this returns a JSON null.
|
||||
@@ -123,7 +227,7 @@ func (v Value) MarshalJSON() ([]byte, error) {
|
||||
func (v *Value) UnmarshalJSON(b []byte) error {
|
||||
// NOTE: This matches the behavior of v1 json.RawMessage.UnmarshalJSON.
|
||||
if v == nil {
|
||||
return errors.New("json.Value: UnmarshalJSON on nil pointer")
|
||||
return errors.New("jsontext.Value: UnmarshalJSON on nil pointer")
|
||||
}
|
||||
*v = append((*v)[:0], b...)
|
||||
return nil
|
||||
@@ -138,93 +242,62 @@ func (v Value) Kind() Kind {
|
||||
return invalidKind
|
||||
}
|
||||
|
||||
func (v *Value) reformat(canonical, multiline bool, prefix, indent string) error {
|
||||
// Write the entire value to reformat all tokens and whitespace.
|
||||
e := getBufferedEncoder()
|
||||
defer putBufferedEncoder(e)
|
||||
eo := &e.s.Struct
|
||||
if canonical {
|
||||
eo.Flags.Set(jsonflags.AllowInvalidUTF8 | 0) // per RFC 8785, section 3.2.4
|
||||
eo.Flags.Set(jsonflags.AllowDuplicateNames | 0) // per RFC 8785, section 3.1
|
||||
eo.Flags.Set(jsonflags.CanonicalizeNumbers | 1) // per RFC 8785, section 3.2.2.3
|
||||
eo.Flags.Set(jsonflags.PreserveRawStrings | 0) // per RFC 8785, section 3.2.2.2
|
||||
eo.Flags.Set(jsonflags.EscapeForHTML | 0) // per RFC 8785, section 3.2.2.2
|
||||
eo.Flags.Set(jsonflags.EscapeForJS | 0) // per RFC 8785, section 3.2.2.2
|
||||
eo.Flags.Set(jsonflags.Expand | 0) // per RFC 8785, section 3.2.1
|
||||
} else {
|
||||
if s := strings.TrimLeft(prefix, " \t"); len(s) > 0 {
|
||||
panic("json: invalid character " + jsonwire.QuoteRune(s) + " in indent prefix")
|
||||
}
|
||||
if s := strings.TrimLeft(indent, " \t"); len(s) > 0 {
|
||||
panic("json: invalid character " + jsonwire.QuoteRune(s) + " in indent")
|
||||
}
|
||||
eo.Flags.Set(jsonflags.AllowInvalidUTF8 | 1)
|
||||
eo.Flags.Set(jsonflags.AllowDuplicateNames | 1)
|
||||
eo.Flags.Set(jsonflags.PreserveRawStrings | 1)
|
||||
if multiline {
|
||||
eo.Flags.Set(jsonflags.Expand | 1)
|
||||
eo.Flags.Set(jsonflags.Indent | 1)
|
||||
eo.Flags.Set(jsonflags.IndentPrefix | 1)
|
||||
eo.IndentPrefix = prefix
|
||||
eo.Indent = indent
|
||||
} else {
|
||||
eo.Flags.Set(jsonflags.Expand | 0)
|
||||
}
|
||||
}
|
||||
eo.Flags.Set(jsonflags.OmitTopLevelNewline | 1)
|
||||
if err := e.s.WriteValue(*v); err != nil {
|
||||
return err
|
||||
}
|
||||
const commaAndWhitespace = ", \n\r\t"
|
||||
|
||||
// For canonical output, we may need to reorder object members.
|
||||
if canonical {
|
||||
// Obtain a buffered encoder just to use its internal buffer as
|
||||
// a scratch buffer in reorderObjects for reordering object members.
|
||||
e2 := getBufferedEncoder()
|
||||
defer putBufferedEncoder(e2)
|
||||
|
||||
// Disable redundant checks performed earlier during encoding.
|
||||
d := getBufferedDecoder(e.s.Buf)
|
||||
defer putBufferedDecoder(d)
|
||||
d.s.Flags.Set(jsonflags.AllowDuplicateNames | jsonflags.AllowInvalidUTF8 | 1)
|
||||
reorderObjects(d, &e2.s.Buf) // per RFC 8785, section 3.2.3
|
||||
}
|
||||
|
||||
// Store the result back into the value if different.
|
||||
if !bytes.Equal(*v, e.s.Buf) {
|
||||
*v = append((*v)[:0], e.s.Buf...)
|
||||
}
|
||||
return nil
|
||||
type objectMember struct {
|
||||
// name is the unquoted name.
|
||||
name []byte // e.g., "name"
|
||||
// buffer is the entirety of the raw JSON object member
|
||||
// starting from right after the previous member (or opening '{')
|
||||
// until right after the member value.
|
||||
buffer []byte // e.g., `, \n\r\t"name": "value"`
|
||||
}
|
||||
|
||||
type memberName struct {
|
||||
// name is the unescaped name.
|
||||
name []byte
|
||||
// before and after are byte offsets into Decoder.buf that represents
|
||||
// the entire name/value pair. It may contain leading commas.
|
||||
before, after int64
|
||||
func (x objectMember) Compare(y objectMember) int {
|
||||
if c := jsonwire.CompareUTF16(x.name, y.name); c != 0 {
|
||||
return c
|
||||
}
|
||||
// With [AllowDuplicateNames] or [AllowInvalidUTF8],
|
||||
// names could be identical, so also sort using the member value.
|
||||
return jsonwire.CompareUTF16(
|
||||
bytes.TrimLeft(x.buffer, commaAndWhitespace),
|
||||
bytes.TrimLeft(y.buffer, commaAndWhitespace))
|
||||
}
|
||||
|
||||
var memberNamePool = sync.Pool{New: func() any { return new([]memberName) }}
|
||||
var objectMemberPool = sync.Pool{New: func() any { return new([]objectMember) }}
|
||||
|
||||
func getMemberNames() *[]memberName {
|
||||
ns := memberNamePool.Get().(*[]memberName)
|
||||
func getObjectMembers() *[]objectMember {
|
||||
ns := objectMemberPool.Get().(*[]objectMember)
|
||||
*ns = (*ns)[:0]
|
||||
return ns
|
||||
}
|
||||
func putMemberNames(ns *[]memberName) {
|
||||
func putObjectMembers(ns *[]objectMember) {
|
||||
if cap(*ns) < 1<<10 {
|
||||
clear(*ns) // avoid pinning name
|
||||
memberNamePool.Put(ns)
|
||||
clear(*ns) // avoid pinning name and buffer
|
||||
objectMemberPool.Put(ns)
|
||||
}
|
||||
}
|
||||
|
||||
// reorderObjects recursively reorders all object members in place
|
||||
// mustReorderObjects reorders in-place all object members in a JSON value,
|
||||
// which must be valid otherwise it panics.
|
||||
func mustReorderObjects(b []byte) {
|
||||
// Obtain a buffered encoder just to use its internal buffer as
|
||||
// a scratch buffer for reordering object members.
|
||||
e2 := getBufferedEncoder()
|
||||
defer putBufferedEncoder(e2)
|
||||
|
||||
// Disable unnecessary checks to syntactically parse the JSON value.
|
||||
d := getBufferedDecoder(b)
|
||||
defer putBufferedDecoder(d)
|
||||
d.s.Flags.Set(jsonflags.AllowDuplicateNames | jsonflags.AllowInvalidUTF8 | 1)
|
||||
mustReorderObjectsFromDecoder(d, &e2.s.Buf) // per RFC 8785, section 3.2.3
|
||||
}
|
||||
|
||||
// mustReorderObjectsFromDecoder recursively reorders all object members in place
|
||||
// according to the ordering specified in RFC 8785, section 3.2.3.
|
||||
//
|
||||
// Pre-conditions:
|
||||
// - The value is valid (i.e., no decoder errors should ever occur).
|
||||
// - The value is compact (i.e., no whitespace is present).
|
||||
// - Initial call is provided a Decoder reading from the start of v.
|
||||
//
|
||||
// Post-conditions:
|
||||
@@ -234,13 +307,13 @@ func putMemberNames(ns *[]memberName) {
|
||||
//
|
||||
// The runtime is approximately O(n·log(n)) + O(m·log(m)),
|
||||
// where n is len(v) and m is the total number of object members.
|
||||
func reorderObjects(d *Decoder, scratch *[]byte) {
|
||||
switch tok, _ := d.ReadToken(); tok.Kind() {
|
||||
func mustReorderObjectsFromDecoder(d *Decoder, scratch *[]byte) {
|
||||
switch tok, err := d.ReadToken(); tok.Kind() {
|
||||
case '{':
|
||||
// Iterate and collect the name and offsets for every object member.
|
||||
members := getMemberNames()
|
||||
defer putMemberNames(members)
|
||||
var prevName []byte
|
||||
members := getObjectMembers()
|
||||
defer putObjectMembers(members)
|
||||
var prevMember objectMember
|
||||
isSorted := true
|
||||
|
||||
beforeBody := d.InputOffset() // offset after '{'
|
||||
@@ -249,14 +322,15 @@ func reorderObjects(d *Decoder, scratch *[]byte) {
|
||||
var flags jsonwire.ValueFlags
|
||||
name, _ := d.s.ReadValue(&flags)
|
||||
name = jsonwire.UnquoteMayCopy(name, flags.IsVerbatim())
|
||||
reorderObjects(d, scratch)
|
||||
mustReorderObjectsFromDecoder(d, scratch)
|
||||
afterValue := d.InputOffset()
|
||||
|
||||
currMember := objectMember{name, d.s.buf[beforeName:afterValue]}
|
||||
if isSorted && len(*members) > 0 {
|
||||
isSorted = jsonwire.CompareUTF16(prevName, []byte(name)) < 0
|
||||
isSorted = objectMember.Compare(prevMember, currMember) < 0
|
||||
}
|
||||
*members = append(*members, memberName{name, beforeName, afterValue})
|
||||
prevName = name
|
||||
*members = append(*members, currMember)
|
||||
prevMember = currMember
|
||||
}
|
||||
afterBody := d.InputOffset() // offset before '}'
|
||||
d.ReadToken()
|
||||
@@ -265,9 +339,9 @@ func reorderObjects(d *Decoder, scratch *[]byte) {
|
||||
if isSorted {
|
||||
return
|
||||
}
|
||||
slices.SortFunc(*members, func(x, y memberName) int {
|
||||
return jsonwire.CompareUTF16(x.name, y.name)
|
||||
})
|
||||
firstBufferBeforeSorting := (*members)[0].buffer
|
||||
slices.SortFunc(*members, objectMember.Compare)
|
||||
firstBufferAfterSorting := (*members)[0].buffer
|
||||
|
||||
// Append the reordered members to a new buffer,
|
||||
// then copy the reordered members back over the original members.
|
||||
@@ -277,14 +351,24 @@ func reorderObjects(d *Decoder, scratch *[]byte) {
|
||||
//
|
||||
// The following invariant must hold:
|
||||
// sum([m.after-m.before for m in members]) == afterBody-beforeBody
|
||||
commaAndWhitespacePrefix := func(b []byte) []byte {
|
||||
return b[:len(b)-len(bytes.TrimLeft(b, commaAndWhitespace))]
|
||||
}
|
||||
sorted := (*scratch)[:0]
|
||||
for i, member := range *members {
|
||||
if d.s.buf[member.before] == ',' {
|
||||
member.before++ // trim leading comma
|
||||
}
|
||||
sorted = append(sorted, d.s.buf[member.before:member.after]...)
|
||||
if i < len(*members)-1 {
|
||||
sorted = append(sorted, ',') // append trailing comma
|
||||
switch {
|
||||
case i == 0 && &member.buffer[0] != &firstBufferBeforeSorting[0]:
|
||||
// First member after sorting is not the first member before sorting,
|
||||
// so use the prefix of the first member before sorting.
|
||||
sorted = append(sorted, commaAndWhitespacePrefix(firstBufferBeforeSorting)...)
|
||||
sorted = append(sorted, bytes.TrimLeft(member.buffer, commaAndWhitespace)...)
|
||||
case i != 0 && &member.buffer[0] == &firstBufferBeforeSorting[0]:
|
||||
// Later member after sorting is the first member before sorting,
|
||||
// so use the prefix of the first member after sorting.
|
||||
sorted = append(sorted, commaAndWhitespacePrefix(firstBufferAfterSorting)...)
|
||||
sorted = append(sorted, bytes.TrimLeft(member.buffer, commaAndWhitespace)...)
|
||||
default:
|
||||
sorted = append(sorted, member.buffer...)
|
||||
}
|
||||
}
|
||||
if int(afterBody-beforeBody) != len(sorted) {
|
||||
@@ -298,8 +382,12 @@ func reorderObjects(d *Decoder, scratch *[]byte) {
|
||||
}
|
||||
case '[':
|
||||
for d.PeekKind() != ']' {
|
||||
reorderObjects(d, scratch)
|
||||
mustReorderObjectsFromDecoder(d, scratch)
|
||||
}
|
||||
d.ReadToken()
|
||||
default:
|
||||
if err != nil {
|
||||
panic("BUG: " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
243
vendor/github.com/go-json-experiment/json/migrate.sh
generated
vendored
Normal file
243
vendor/github.com/go-json-experiment/json/migrate.sh
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
GOROOT=${1:-../go}
|
||||
JSONROOT="."
|
||||
|
||||
# Check if the Go toolchain has a clean checkout.
|
||||
if [ -n "$(cd $GOROOT; git status --porcelain)" ]; then
|
||||
(cd $GOROOT; git status --porcelain)
|
||||
echo "Working directory is not clean."
|
||||
echo ""
|
||||
echo "To cleanup, run:"
|
||||
echo " (cd $GOROOT && git checkout . && git clean -fd)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
/bin/rm -rf $GOROOT/src/encoding/json/*
|
||||
cp $JSONROOT/v1/* $GOROOT/src/encoding/json/
|
||||
cp -r $JSONROOT/internal/ $GOROOT/src/encoding/json/internal/
|
||||
mkdir $GOROOT/src/encoding/json/v2/
|
||||
cp -r $JSONROOT/*.go $GOROOT/src/encoding/json/v2/
|
||||
mkdir $GOROOT/src/encoding/json/jsontext/
|
||||
cp -r $JSONROOT/jsontext/*.go $GOROOT/src/encoding/json/jsontext/
|
||||
find $GOROOT/src/encoding/json -type f -exec sed -i 's|github[.]com/go-json-experiment/json/v1|encoding/json|g' {} +
|
||||
find $GOROOT/src/encoding/json -type f -exec sed -i 's|github[.]com/go-json-experiment/json/|encoding/json/|g' {} +
|
||||
find $GOROOT/src/encoding/json -type f -exec sed -i 's|github[.]com/go-json-experiment/json|encoding/json/v2|g' {} +
|
||||
|
||||
# Adjust for changed package path.
|
||||
sed -i 's/json\.struct/v2.struct/g' $GOROOT/src/encoding/json/v2/errors_test.go
|
||||
|
||||
# Adjust tests that hardcode formatted error strings.
|
||||
sed -i 's/}`, "Time.UnmarshalJSON: input is not a JSON string/}`, "json: cannot unmarshal JSON object into Go type time.Time/g' $GOROOT/src/time/time_test.go
|
||||
sed -i 's/]`, "Time.UnmarshalJSON: input is not a JSON string/]`, "json: cannot unmarshal JSON array into Go type time.Time/g' $GOROOT/src/time/time_test.go
|
||||
|
||||
# Adjust for changed dependency tree.
|
||||
sed -i 's|encoding/json|encoding/json/v2|g' $GOROOT/src/cmd/go/internal/imports/scan_test.go
|
||||
sed -i 's|encoding/binary|internal/reflectlite|g' $GOROOT/src/cmd/go/internal/imports/scan_test.go
|
||||
LINE=$(sed -n '/encoding\/json, encoding\/pem, encoding\/xml, mime;/=' $GOROOT/src/go/build/deps_test.go)
|
||||
sed -i 's|encoding/json, encoding/pem, encoding/xml, mime|encoding/pem, encoding/xml, mime|g' $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+ 1)) i\\\\" $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+ 2)) i\\\tSTR, errors" $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+ 3)) i\\\t< encoding/json/internal" $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+ 4)) i\\\t< encoding/json/internal/jsonflags" $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+ 5)) i\\\t< encoding/json/internal/jsonopts" $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+ 6)) i\\\t< encoding/json/internal/jsonwire" $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+ 7)) i\\\t< encoding/json/jsontext;" $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+ 8)) i\\\\" $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+ 9)) i\\\tFMT," $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+10)) i\\\tencoding/hex," $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+11)) i\\\tencoding/base32," $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+12)) i\\\tencoding/base64," $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+13)) i\\\tencoding/binary," $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+14)) i\\\tencoding/json/jsontext," $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+15)) i\\\tencoding/json/internal," $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+16)) i\\\tencoding/json/internal/jsonflags," $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+17)) i\\\tencoding/json/internal/jsonopts," $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+18)) i\\\tencoding/json/internal/jsonwire" $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+19)) i\\\t< encoding/json/v2" $GOROOT/src/go/build/deps_test.go
|
||||
sed -i "$((LINE+20)) i\\\t< encoding/json;" $GOROOT/src/go/build/deps_test.go
|
||||
LINE=$(sed -n '/Test-only packages can have anything they want/=' $GOROOT/src/go/build/deps_test.go)
|
||||
sed -i "$((LINE+1)) i\\\tFMT, compress/gzip, embed, encoding/binary < encoding/json/internal/jsontest;" $GOROOT/src/go/build/deps_test.go
|
||||
|
||||
# Adjust for newly added API.
|
||||
ISSUE=71497
|
||||
FILE="next/$ISSUE.txt"
|
||||
NEXT="$GOROOT/doc/next/6-stdlib/99-minor"
|
||||
mkdir -p $NEXT/encoding/json
|
||||
echo "A new [Options] type with associated constructors provide individual options" >> $NEXT/encoding/json/$ISSUE.md
|
||||
echo "to configure \"encoding/json/v2\" to operate with certain historical v1 behavior." >> $NEXT/encoding/json/$ISSUE.md
|
||||
echo "The [DefaultOptionsV2] option represents the set of all options needed" >> $NEXT/encoding/json/$ISSUE.md
|
||||
echo "to configure \"encoding/json/v2\" to entirely operate with historical v1 behavior." >> $NEXT/encoding/json/$ISSUE.md
|
||||
mkdir -p $NEXT/encoding/json/v2
|
||||
echo "A new major version of \"encoding/json\" for processing JSON at a semantic level which is" >> $NEXT/encoding/json/v2/$ISSUE.md
|
||||
echo "functionality that determines the meaning of JSON values as Go values and vice-versa." >> $NEXT/encoding/json/v2/$ISSUE.md
|
||||
mkdir -p $NEXT/encoding/json/jsontext
|
||||
echo "A new package to process JSON at a syntactic level that" >> $NEXT/encoding/json/jsontext/$ISSUE.md
|
||||
echo "is concerned with processing JSON based on its grammar alone." >> $NEXT/encoding/json/jsontext/$ISSUE.md
|
||||
NEXT="$GOROOT/api/next/$ISSUE.txt"
|
||||
echo "pkg encoding/json, func CallMethodsWithLegacySemantics(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, func DefaultOptionsV1() jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, func EscapeInvalidUTF8(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, func FormatBytesWithLegacySemantics(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, func FormatTimeWithLegacySemantics(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, func MatchCaseSensitiveDelimiter(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, func MergeWithLegacySemantics(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, func OmitEmptyWithLegacyDefinition(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, func ReportErrorsWithLegacySemantics(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, func StringifyWithLegacySemantics(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, func UnmarshalArrayFromAnyLength(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, method (*Number) UnmarshalJSONFrom(*jsontext.Decoder, jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, method (*UnmarshalTypeError) Unwrap() error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, method (Number) MarshalJSONTo(*jsontext.Encoder, jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, type Marshaler = json.Marshaler #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, type Options = jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, type RawMessage = jsontext.Value #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, type UnmarshalTypeError struct, Err error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json, type Unmarshaler = json.Unmarshaler #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func AllowDuplicateNames(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func AllowInvalidUTF8(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func AppendFormat([]uint8, []uint8, ...jsonopts.Options) ([]uint8, error) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func AppendQuote[\$0 interface{ ~[]uint8 | ~string }]([]uint8, \$0) ([]uint8, error) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func AppendUnquote[\$0 interface{ ~[]uint8 | ~string }]([]uint8, \$0) ([]uint8, error) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func Bool(bool) Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func CanonicalizeRawFloats(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func CanonicalizeRawInts(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func EscapeForHTML(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func EscapeForJS(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func Float(float64) Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func Int(int64) Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func Multiline(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func NewDecoder(io.Reader, ...jsonopts.Options) *Decoder #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func NewEncoder(io.Writer, ...jsonopts.Options) *Encoder #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func PreserveRawStrings(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func ReorderRawObjects(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func SpaceAfterColon(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func SpaceAfterComma(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func String(string) Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func Uint(uint64) Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func WithIndent(string) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, func WithIndentPrefix(string) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Decoder) InputOffset() int64 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Decoder) PeekKind() Kind #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Decoder) ReadToken() (Token, error) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Decoder) ReadValue() (Value, error) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Decoder) Reset(io.Reader, ...jsonopts.Options) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Decoder) SkipValue() error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Decoder) StackDepth() int #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Decoder) StackIndex(int) (Kind, int64) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Decoder) StackPointer() Pointer #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Decoder) UnreadBuffer() []uint8 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Encoder) OutputOffset() int64 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Encoder) Reset(io.Writer, ...jsonopts.Options) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Encoder) StackDepth() int #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Encoder) StackIndex(int) (Kind, int64) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Encoder) StackPointer() Pointer #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Encoder) UnusedBuffer() []uint8 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Encoder) WriteToken(Token) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Encoder) WriteValue(Value) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*SyntacticError) Error() string #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*SyntacticError) Unwrap() error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Value) Canonicalize(...jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Value) Compact(...jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Value) Format(...jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Value) Indent(...jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (*Value) UnmarshalJSON([]uint8) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Kind) String() string #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Pointer) AppendToken(string) Pointer #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Pointer) Contains(Pointer) bool #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Pointer) IsValid() bool #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Pointer) LastToken() string #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Pointer) Parent() Pointer #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Pointer) Tokens() iter.Seq[string] #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Token) Bool() bool #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Token) Clone() Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Token) Float() float64 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Token) Int() int64 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Token) Kind() Kind #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Token) String() string #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Token) Uint() uint64 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Value) Clone() Value #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Value) IsValid(...jsonopts.Options) bool #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Value) Kind() Kind #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Value) MarshalJSON() ([]uint8, error) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, method (Value) String() string #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type Decoder struct #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type Encoder struct #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type Kind uint8 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type Options = jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type Pointer string #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type SyntacticError struct #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type SyntacticError struct, ByteOffset int64 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type SyntacticError struct, Err error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type SyntacticError struct, JSONPointer Pointer #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type Token struct #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, type Value []uint8 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, var BeginArray Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, var BeginObject Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, var EndArray Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, var EndObject Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, var ErrDuplicateName error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, var ErrNonStringName error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, var False Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, var Internal exporter #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, var Null Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/jsontext, var True Token #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func DefaultOptionsV2() jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func Deterministic(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func DiscardUnknownMembers(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func FormatNilMapAsNull(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func FormatNilSliceAsNull(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func GetOption[\$0 interface{}](jsonopts.Options, func(\$0) jsonopts.Options) (\$0, bool) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func JoinMarshalers(...*typedArshalers[jsontext.Encoder]) *typedArshalers[jsontext.Encoder] #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func JoinOptions(...jsonopts.Options) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func JoinUnmarshalers(...*typedArshalers[jsontext.Decoder]) *typedArshalers[jsontext.Decoder] #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func Marshal(interface{}, ...jsonopts.Options) ([]uint8, error) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func MarshalEncode(*jsontext.Encoder, interface{}, ...jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func MarshalFunc[\$0 interface{}](func(\$0) ([]uint8, error)) *typedArshalers[jsontext.Encoder] #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func MarshalToFunc[\$0 interface{}](func(*jsontext.Encoder, \$0, jsonopts.Options) error) *typedArshalers[jsontext.Encoder] #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func MarshalWrite(io.Writer, interface{}, ...jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func MatchCaseInsensitiveNames(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func OmitZeroStructFields(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func RejectUnknownMembers(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func StringifyNumbers(bool) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func Unmarshal([]uint8, interface{}, ...jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func UnmarshalDecode(*jsontext.Decoder, interface{}, ...jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func UnmarshalFromFunc[\$0 interface{}](func(*jsontext.Decoder, \$0, jsonopts.Options) error) *typedArshalers[jsontext.Decoder] #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func UnmarshalFunc[\$0 interface{}](func([]uint8, \$0) error) *typedArshalers[jsontext.Decoder] #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func UnmarshalRead(io.Reader, interface{}, ...jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func WithMarshalers(*typedArshalers[jsontext.Encoder]) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, func WithUnmarshalers(*typedArshalers[jsontext.Decoder]) jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, method (*SemanticError) Error() string #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, method (*SemanticError) Unwrap() error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type Marshaler interface { MarshalJSON } #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type Marshaler interface, MarshalJSON() ([]uint8, error) #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type MarshalerTo interface { MarshalJSONTo } #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type MarshalerTo interface, MarshalJSONTo(*jsontext.Encoder, jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type Marshalers = typedArshalers[jsontext.Encoder] #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type Options = jsonopts.Options #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type SemanticError struct #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type SemanticError struct, ByteOffset int64 #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type SemanticError struct, Err error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type SemanticError struct, GoType reflect.Type #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type SemanticError struct, JSONKind jsontext.Kind #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type SemanticError struct, JSONPointer jsontext.Pointer #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type SemanticError struct, JSONValue jsontext.Value #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type Unmarshaler interface { UnmarshalJSON } #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type Unmarshaler interface, UnmarshalJSON([]uint8) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type UnmarshalerFrom interface { UnmarshalJSONFrom } #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type UnmarshalerFrom interface, UnmarshalJSONFrom(*jsontext.Decoder, jsonopts.Options) error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, type Unmarshalers = typedArshalers[jsontext.Decoder] #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, var ErrUnknownName error #$ISSUE" >> $NEXT
|
||||
echo "pkg encoding/json/v2, var SkipFunc error #$ISSUE" >> $NEXT
|
||||
# The following declarations were moved to encoding/json/v2 or encoding/json/jsontext.
|
||||
EXCEPT="$GOROOT/api/except.txt"
|
||||
echo "pkg encoding/json, method (*RawMessage) UnmarshalJSON([]uint8) error" >> $EXCEPT
|
||||
echo "pkg encoding/json, method (RawMessage) MarshalJSON() ([]uint8, error)" >> $EXCEPT
|
||||
echo "pkg encoding/json, type Marshaler interface { MarshalJSON }" >> $EXCEPT
|
||||
echo "pkg encoding/json, type Marshaler interface, MarshalJSON() ([]uint8, error)" >> $EXCEPT
|
||||
echo "pkg encoding/json, type RawMessage []uint8" >> $EXCEPT
|
||||
echo "pkg encoding/json, type Unmarshaler interface { UnmarshalJSON }" >> $EXCEPT
|
||||
echo "pkg encoding/json, type Unmarshaler interface, UnmarshalJSON([]uint8) error" >> $EXCEPT
|
||||
|
||||
# Run the tests.
|
||||
(cd $GOROOT/src; ./all.bash)
|
||||
39
vendor/github.com/go-json-experiment/json/options.go
generated
vendored
39
vendor/github.com/go-json-experiment/json/options.go
generated
vendored
@@ -61,6 +61,7 @@ import (
|
||||
// - [Deterministic] affects marshaling only
|
||||
// - [FormatNilSliceAsNull] affects marshaling only
|
||||
// - [FormatNilMapAsNull] affects marshaling only
|
||||
// - [OmitZeroStructFields] affects marshaling only
|
||||
// - [MatchCaseInsensitiveNames] affects marshaling and unmarshaling
|
||||
// - [DiscardUnknownMembers] affects marshaling only
|
||||
// - [RejectUnknownMembers] affects unmarshaling only
|
||||
@@ -74,9 +75,7 @@ type Options = jsonopts.Options
|
||||
// Properties set in later options override the value of previously set properties.
|
||||
func JoinOptions(srcs ...Options) Options {
|
||||
var dst jsonopts.Struct
|
||||
for _, src := range srcs {
|
||||
dst.Join(src)
|
||||
}
|
||||
dst.Join(srcs...)
|
||||
return &dst
|
||||
}
|
||||
|
||||
@@ -88,27 +87,25 @@ func JoinOptions(srcs ...Options) Options {
|
||||
// v, ok := json.GetOption(opts, json.Deterministic)
|
||||
//
|
||||
// Options are most commonly introspected to alter the JSON representation of
|
||||
// [MarshalerV2.MarshalJSONV2] and [MarshalerV2.MarshalJSONV2] methods, and
|
||||
// [MarshalFuncV2] and [UnmarshalFuncV2] functions.
|
||||
// [MarshalerTo.MarshalJSONTo] and [UnmarshalerFrom.UnmarshalJSONFrom] methods, and
|
||||
// [MarshalToFunc] and [UnmarshalFromFunc] functions.
|
||||
// In such cases, the presence bit should generally be ignored.
|
||||
func GetOption[T any](opts Options, setter func(T) Options) (T, bool) {
|
||||
return jsonopts.GetOption(opts, setter)
|
||||
}
|
||||
|
||||
// DefaultOptionsV2 is the full set of all options that define v2 semantics.
|
||||
// It is equivalent to all boolean options under [Options],
|
||||
// [encoding/json.Options], and [encoding/json/jsontext.Options]
|
||||
// being set to false. All non-boolean options are set to the zero value,
|
||||
// except for [jsontext.WithIndent], which defaults to "\t".
|
||||
// It is equivalent to all options under [Options], [encoding/json.Options],
|
||||
// and [encoding/json/jsontext.Options] being set to false or the zero value,
|
||||
// except for the options related to whitespace formatting.
|
||||
func DefaultOptionsV2() Options {
|
||||
return &jsonopts.DefaultOptionsV2
|
||||
}
|
||||
|
||||
// StringifyNumbers specifies that numeric Go types should be marshaled
|
||||
// as a JSON string containing the equivalent JSON number value.
|
||||
// When unmarshaling, numeric Go types can be parsed from either
|
||||
// a JSON number or a JSON string containing the JSON number
|
||||
// without any surrounding whitespace.
|
||||
// When unmarshaling, numeric Go types are parsed from a JSON string
|
||||
// containing the JSON number without any surrounding whitespace.
|
||||
//
|
||||
// According to RFC 8259, section 6, a JSON implementation may choose to
|
||||
// limit the representation of a JSON number to an IEEE 754 binary64 value.
|
||||
@@ -168,9 +165,25 @@ func FormatNilMapAsNull(v bool) Options {
|
||||
}
|
||||
}
|
||||
|
||||
// OmitZeroStructFields specifies that a Go struct should marshal in such a way
|
||||
// that all struct fields that are zero are omitted from the marshaled output
|
||||
// if the value is zero as determined by the "IsZero() bool" method if present,
|
||||
// otherwise based on whether the field is the zero Go value.
|
||||
// This is semantically equivalent to specifying the `omitzero` tag option
|
||||
// on every field in a Go struct.
|
||||
//
|
||||
// This only affects marshaling and is ignored when unmarshaling.
|
||||
func OmitZeroStructFields(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.OmitZeroStructFields | 1
|
||||
} else {
|
||||
return jsonflags.OmitZeroStructFields | 0
|
||||
}
|
||||
}
|
||||
|
||||
// MatchCaseInsensitiveNames specifies that JSON object members are matched
|
||||
// against Go struct fields using a case-insensitive match of the name.
|
||||
// Go struct fields explicitly marked with `strictcase` or `nocase`
|
||||
// Go struct fields explicitly marked with `case:strict` or `case:ignore`
|
||||
// always use case-sensitive (or case-insensitive) name matching,
|
||||
// regardless of the value of this option.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user