vendor tsgo
This commit is contained in:
3
tools/tsgo/vendor/github.com/go-json-experiment/json/AUTHORS
generated
vendored
Normal file
3
tools/tsgo/vendor/github.com/go-json-experiment/json/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at https://tip.golang.org/AUTHORS.
|
||||
3
tools/tsgo/vendor/github.com/go-json-experiment/json/CONTRIBUTORS
generated
vendored
Normal file
3
tools/tsgo/vendor/github.com/go-json-experiment/json/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at https://tip.golang.org/CONTRIBUTORS.
|
||||
27
tools/tsgo/vendor/github.com/go-json-experiment/json/LICENSE
generated
vendored
Normal file
27
tools/tsgo/vendor/github.com/go-json-experiment/json/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
176
tools/tsgo/vendor/github.com/go-json-experiment/json/README.md
generated
vendored
Normal file
176
tools/tsgo/vendor/github.com/go-json-experiment/json/README.md
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
# JSON Serialization (v2)
|
||||
|
||||
[](https://pkg.go.dev/github.com/go-json-experiment/json)
|
||||
[](https://github.com/go-json-experiment/json/actions)
|
||||
|
||||
This module hosts an experimental implementation of v2 `encoding/json`.
|
||||
The API is unstable and breaking changes will regularly be made.
|
||||
Do not depend on this in publicly available modules.
|
||||
|
||||
Any commits that make breaking API or behavior changes will be marked
|
||||
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.
|
||||
|
||||
## Current status
|
||||
|
||||
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.
|
||||
At present, this module is available within the Go standard library as a Go experiment.
|
||||
See ["A new experimental Go API for JSON"](https://go.dev/blog/jsonv2-exp) for more details.
|
||||
|
||||
On 2025-11-20, a `json/v2` working group was established by the Go project
|
||||
consisting of @aclements, @neild, @prattmic, @ChrisHines, and @dsnet
|
||||
to review outstanding issues in preparing for formal adoption into the standard library.
|
||||
The working group meets weekly and notes are recorded on [issue #76406](https://github.com/golang/go/issues/76406).
|
||||
|
||||
See [the project dashboard](https://github.com/orgs/golang/projects/50/views/1)
|
||||
to get a sense of what issues remain for the meeting group to resolve.
|
||||
The "Under review" and "Needs review" categories need to be emptied
|
||||
before `json/v2` can be released.
|
||||
|
||||
This repository will continue to import and mirror the upstream Go project
|
||||
for the foreseeable future. Consequently, code changes should be made at
|
||||
the upstream [Go project](https://go.googlesource.com/go/) instead of here.
|
||||
Building this module with the `goexperiment.jsonv2` tag will cause the
|
||||
module to use the `encoding/json/v2` package under-the-hood,
|
||||
otherwise this will provide a different implementation of `json/v2`.
|
||||
|
||||
## Goals and objectives
|
||||
|
||||
* **Mostly backwards compatible:** If possible, v2 should aim to be _mostly_
|
||||
compatible with v1 in terms of both API and default behavior to ease migration.
|
||||
For example, the `Marshal` and `Unmarshal` functions are the most widely used
|
||||
declarations in the v1 package. It seems sensible for equivalent functionality
|
||||
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.
|
||||
Options exist that can bring the v2 implementation to 100% compatibility,
|
||||
but it will not be the default.
|
||||
|
||||
* **More flexible:** There is a
|
||||
[long list of feature requests](https://github.com/golang/go/issues?q=is%3Aissue+is%3Aopen+encoding%2Fjson+in%3Atitle).
|
||||
We should aim to provide the most flexible features that addresses most usages.
|
||||
We do not want to over fit the v2 API to handle every possible use case.
|
||||
Ideally, the features provided should be orthogonal in nature such that
|
||||
any combination of features results in as few surprising edge cases as possible.
|
||||
|
||||
* **More performant:** JSON serialization is widely used and any bit of extra
|
||||
performance gains will be greatly appreciated. Some rarely used behaviors of v1
|
||||
may be dropped in favor of better performance. For example,
|
||||
despite `Encoder` and `Decoder` operating on an `io.Writer` and `io.Reader`,
|
||||
they do not operate in a truly streaming manner,
|
||||
leading to a loss in performance. The v2 implementation should aim to be truly
|
||||
streaming by default (see [#33714](https://golang.org/issue/33714)).
|
||||
|
||||
* **Easy to use (hard to misuse):** The v2 API should aim to make
|
||||
the common case easy and the less common case at least possible.
|
||||
The API should avoid behavior that goes contrary to user expectation,
|
||||
which may result in subtle bugs (see [#36225](https://golang.org/issue/36225)).
|
||||
|
||||
* **v1 and v2 maintainability:** Since the v1 implementation must stay forever,
|
||||
it would be beneficial if v1 could be implemented under the hood with v2,
|
||||
allowing for less maintenance burden in the future. This probably implies that
|
||||
behavioral changes in v2 relative to v1 need to be exposed as options.
|
||||
|
||||
* **Avoid unsafe:** Standard library packages generally avoid the use of
|
||||
package `unsafe` even if it could provide a performance boost.
|
||||
We aim to preserve this property.
|
||||
|
||||
## Development
|
||||
|
||||
This module is primarily developed by
|
||||
[@dsnet](https://github.com/dsnet),
|
||||
[@mvdan](https://github.com/mvdan), and
|
||||
[@johanbrandhorst](https://github.com/johanbrandhorst)
|
||||
with feedback provided by
|
||||
[@rogpeppe](https://github.com/rogpeppe),
|
||||
[@ChrisHines](https://github.com/ChrisHines), and
|
||||
[@rsc](https://github.com/rsc).
|
||||
|
||||
Discussion about semantics occur semi-regularly, where a
|
||||
[record of past meetings can be found here](https://docs.google.com/document/d/1rovrOTd-wTawGMPPlPuKhwXaYBg9VszTXR9AQQL5LfI/edit?usp=sharing).
|
||||
|
||||
## Design overview
|
||||
|
||||
This package aims to provide a clean separation between syntax and semantics.
|
||||
Syntax deals with the structural representation of JSON (as specified in
|
||||
[RFC 4627](https://tools.ietf.org/html/rfc4627),
|
||||
[RFC 7159](https://tools.ietf.org/html/rfc7159),
|
||||
[RFC 7493](https://tools.ietf.org/html/rfc7493),
|
||||
[RFC 8259](https://tools.ietf.org/html/rfc8259), and
|
||||
[RFC 8785](https://tools.ietf.org/html/rfc8785)).
|
||||
Semantics deals with the meaning of syntactic data as usable application data.
|
||||
|
||||
The `Encoder` and `Decoder` types are streaming tokenizers concerned with the
|
||||
packing or parsing of JSON data. They operate on `Token` and `Value` types
|
||||
which represent the common data structures that are representable in JSON.
|
||||
`Encoder` and `Decoder` do not aim to provide any interpretation of the data.
|
||||
|
||||
Functions like `Marshal`, `MarshalWrite`, `MarshalEncode`, `Unmarshal`,
|
||||
`UnmarshalRead`, and `UnmarshalDecode` provide semantic meaning by correlating
|
||||
any arbitrary Go type with some JSON representation of that type (as stored in
|
||||
data types like `[]byte`, `io.Writer`, `io.Reader`, `Encoder`, or `Decoder`).
|
||||
|
||||

|
||||
|
||||
This diagram provides a high-level overview of the v2 `json` and `jsontext` packages.
|
||||
Purple blocks represent types, while blue blocks represent functions or methods.
|
||||
The arrows and their direction represent the approximate flow of data.
|
||||
The bottom half of the diagram contains functionality that is only concerned
|
||||
with syntax (implemented by the `jsontext` package),
|
||||
while the upper half contains functionality that assigns
|
||||
semantic meaning to syntactic data handled by the bottom half
|
||||
(as implemented by the v2 `json` package).
|
||||
|
||||
In contrast to v1 `encoding/json`, options are represented as separate types
|
||||
rather than being setter methods on the `Encoder` or `Decoder` types.
|
||||
Some options affects JSON serialization at the syntactic layer,
|
||||
while others affect it at the semantic layer.
|
||||
Some options only affect JSON when decoding,
|
||||
while others affect JSON while encoding.
|
||||
|
||||
## Behavior changes
|
||||
|
||||
The v2 `json` package changes the default behavior of `Marshal` and `Unmarshal`
|
||||
relative to the v1 `json` package to be more sensible.
|
||||
Some of these behavior changes have options and workarounds to opt into
|
||||
behavior similar to what v1 provided.
|
||||
|
||||
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](/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` has no default representation in v2 (see [#71631](https://golang.org/issue/71631)) and results in an error. | |
|
||||
| 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](/v1/diff_test.go) for details about every change.
|
||||
|
||||
## Performance
|
||||
|
||||
One of the goals of the v2 module is to be more performant than v1,
|
||||
but not at the expense of correctness.
|
||||
In general, v2 is at performance parity with v1 for marshaling,
|
||||
but dramatically faster for unmarshaling.
|
||||
|
||||
See https://github.com/go-json-experiment/jsonbench for benchmarks
|
||||
comparing v2 with v1 and a number of other popular JSON implementations.
|
||||
943
tools/tsgo/vendor/github.com/go-json-experiment/json/alias.go
generated
vendored
Normal file
943
tools/tsgo/vendor/github.com/go-json-experiment/json/alias.go
generated
vendored
Normal file
@@ -0,0 +1,943 @@
|
||||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by alias_gen.go; DO NOT EDIT.
|
||||
|
||||
//go:build goexperiment.jsonv2 && go1.25
|
||||
|
||||
// Package json implements semantic processing of JSON as specified in RFC 8259.
|
||||
// JSON is a simple data interchange format that can represent
|
||||
// primitive data types such as booleans, strings, and numbers,
|
||||
// in addition to structured data types such as objects and arrays.
|
||||
//
|
||||
// [Marshal] and [Unmarshal] encode and decode Go values
|
||||
// to/from JSON text contained within a []byte.
|
||||
// [MarshalWrite] and [UnmarshalRead] operate on JSON text
|
||||
// by writing to or reading from an [io.Writer] or [io.Reader].
|
||||
// [MarshalEncode] and [UnmarshalDecode] operate on JSON text
|
||||
// by encoding to or decoding from a [jsontext.Encoder] or [jsontext.Decoder].
|
||||
// [Options] may be passed to each of the marshal or unmarshal functions
|
||||
// to configure the semantic behavior of marshaling and unmarshaling
|
||||
// (i.e., alter how JSON data is understood as Go data and vice versa).
|
||||
// [jsontext.Options] may also be passed to the marshal or unmarshal functions
|
||||
// to configure the syntactic behavior of encoding or decoding.
|
||||
//
|
||||
// The data types of JSON are mapped to/from the data types of Go based on
|
||||
// the closest logical equivalent between the two type systems. For example,
|
||||
// a JSON boolean corresponds with a Go bool,
|
||||
// a JSON string corresponds with a Go string,
|
||||
// a JSON number corresponds with a Go int, uint or float,
|
||||
// a JSON array corresponds with a Go slice or array, and
|
||||
// a JSON object corresponds with a Go struct or map.
|
||||
// See the documentation on [Marshal] and [Unmarshal] for a comprehensive list
|
||||
// of how the JSON and Go type systems correspond.
|
||||
//
|
||||
// Arbitrary Go types can customize their JSON representation by implementing
|
||||
// [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
|
||||
// [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.
|
||||
//
|
||||
// # JSON Representation of Go structs
|
||||
//
|
||||
// A Go struct is naturally represented as a JSON object,
|
||||
// where each Go struct field corresponds with a JSON object member.
|
||||
// When marshaling, all Go struct fields are recursively encoded in depth-first
|
||||
// order as JSON object members except those that are ignored or omitted.
|
||||
// When unmarshaling, JSON object members are recursively decoded
|
||||
// into the corresponding Go struct fields.
|
||||
// Object members that do not match any struct fields,
|
||||
// also known as “unknown members”, are ignored by default or rejected
|
||||
// if [RejectUnknownMembers] is specified.
|
||||
//
|
||||
// The representation of each struct field can be customized in the
|
||||
// "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
|
||||
// is used as the JSON object name.
|
||||
// By default, unmarshaling uses case-sensitive matching to identify
|
||||
// the Go struct field associated with a JSON object name.
|
||||
//
|
||||
// After the name, the following tag options are supported:
|
||||
//
|
||||
// - omitzero: When marshaling, the "omitzero" option specifies that
|
||||
// the struct field should be omitted if the field value is zero
|
||||
// as determined by the "IsZero() bool" method if present,
|
||||
// otherwise based on whether the field is the zero Go value.
|
||||
// This option has no effect when unmarshaling.
|
||||
//
|
||||
// - omitempty: When marshaling, the "omitempty" option specifies that
|
||||
// the struct field should be omitted if the field value would have been
|
||||
// encoded as a JSON null, empty string, empty object, or empty array.
|
||||
// This option has no effect when unmarshaling.
|
||||
//
|
||||
// - string: The "string" option specifies that [StringifyNumbers] be set
|
||||
// when marshaling or unmarshaling a struct field value.
|
||||
// This causes types that would normally be encoded as a JSON number
|
||||
// to instead be encoded as a JSON number quoted within a JSON string,
|
||||
// and to be decoded from a JSON string containing the JSON number
|
||||
// without any surrounding whitespace.
|
||||
// The "string" option only applies to the top-level of the Go struct field value.
|
||||
// Specifically, for the default representation of composite Go data types
|
||||
// (e.g., array, slice, struct, or map), it will not stringify JSON numbers
|
||||
// within such types. Applying this option to invalid types causes a runtime error.
|
||||
// This extra level of encoding is often necessary since many JSON parsers
|
||||
// cannot precisely represent 64-bit integers.
|
||||
//
|
||||
// - 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,
|
||||
// then the field with an exact name match is selected, otherwise an error
|
||||
// is reported due to an ambiguous set of candidate fields to unmarshal into.
|
||||
// The 'strict' value specifies that matching is case-sensitive.
|
||||
// This takes precedence over the [MatchCaseInsensitiveNames] option.
|
||||
//
|
||||
// - embed: The "embed" option specifies that
|
||||
// the JSON representable content of this field type is to be promoted
|
||||
// as if they were specified in the parent struct.
|
||||
// It is the JSON equivalent of Go struct embedding.
|
||||
// A Go embedded field is implicitly JSON embedded unless
|
||||
// an explicit JSON name is specified. The embedded 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,
|
||||
// embedded fields from a pointer type are omitted if it is nil.
|
||||
// Embedded fields of type [jsontext.Value] and map[~string]T are called
|
||||
// “embedded fallbacks” as they can represent all possible
|
||||
// JSON object members not directly handled by the parent struct.
|
||||
// Only one embedded fallback field may be specified in a struct,
|
||||
// while many non-fallback fields may be specified. This option
|
||||
// must not be specified with any other option (including the JSON name).
|
||||
//
|
||||
// The "omitzero" and "omitempty" options are mostly semantically identical.
|
||||
// The former is defined in terms of the Go type system,
|
||||
// while the latter in terms of the JSON type system.
|
||||
// Consequently they behave differently in some circumstances.
|
||||
// For example, only a nil slice or map is omitted under "omitzero", while
|
||||
// an empty slice or map is omitted under "omitempty" regardless of nilness.
|
||||
// The "omitzero" option is useful for types with a well-defined zero value
|
||||
// (e.g., [net/netip.Addr]) or have an IsZero method (e.g., [time.Time.IsZero]).
|
||||
//
|
||||
// Every Go struct corresponds to a list of JSON representable fields
|
||||
// which is constructed by performing a breadth-first search over
|
||||
// all struct fields (excluding unexported or ignored fields),
|
||||
// where the search recursively descends into embedded structs.
|
||||
// The set of non-embedded fields in a struct must have unique JSON names.
|
||||
// If multiple fields all have the same JSON name, then the one
|
||||
// at shallowest depth takes precedence and the other fields at deeper depths
|
||||
// are excluded from the list of JSON representable fields.
|
||||
// If multiple fields at the shallowest depth have the same JSON name,
|
||||
// but exactly one is explicitly tagged with a JSON name,
|
||||
// then that field takes precedence and all others are excluded from the list.
|
||||
// This is analogous to Go visibility rules for struct field selection
|
||||
// with embedded struct types.
|
||||
//
|
||||
// Marshaling or unmarshaling a non-empty struct
|
||||
// without any JSON representable fields results in a [SemanticError].
|
||||
// Unexported fields must not have any `json` tags except for `json:"-"`.
|
||||
//
|
||||
// # Security Considerations
|
||||
//
|
||||
// JSON is frequently used as a data interchange format to communicate
|
||||
// between different systems, possibly implemented in different languages.
|
||||
// For interoperability and security reasons, it is important that
|
||||
// all implementations agree upon the semantic meaning of the data.
|
||||
//
|
||||
// [For example, suppose we have two micro-services.]
|
||||
// The first service is responsible for authenticating a JSON request,
|
||||
// while the second service is responsible for executing the request
|
||||
// (having assumed that the prior service authenticated the request).
|
||||
// If an attacker were able to maliciously craft a JSON request such that
|
||||
// both services believe that the same request is from different users,
|
||||
// it could bypass the authenticator with valid credentials for one user,
|
||||
// but maliciously perform an action on behalf of a different user.
|
||||
//
|
||||
// According to RFC 8259, there unfortunately exist many JSON texts
|
||||
// that are syntactically valid but semantically ambiguous.
|
||||
// For example, the standard does not define how to interpret duplicate
|
||||
// names within an object.
|
||||
//
|
||||
// The v1 [encoding/json] and [encoding/json/v2] packages
|
||||
// interpret some inputs in different ways. In particular:
|
||||
//
|
||||
// - The standard specifies that JSON must be encoded using UTF-8.
|
||||
// By default, v1 replaces invalid bytes of UTF-8 in JSON strings
|
||||
// with the Unicode replacement character,
|
||||
// while v2 rejects inputs with invalid UTF-8.
|
||||
// To change the default, specify the [jsontext.AllowInvalidUTF8] option.
|
||||
// The replacement of invalid UTF-8 is a form of data corruption
|
||||
// that alters the precise meaning of strings.
|
||||
//
|
||||
// - The standard does not specify a particular behavior when
|
||||
// duplicate names are encountered within a JSON object,
|
||||
// which means that different implementations may behave differently.
|
||||
// By default, v1 allows for the presence of duplicate names,
|
||||
// while v2 rejects duplicate names.
|
||||
// To change the default, specify the [jsontext.AllowDuplicateNames] option.
|
||||
// If allowed, object members are processed in the order they are observed,
|
||||
// meaning that later values will replace or be merged into prior values,
|
||||
// depending on the Go value type.
|
||||
//
|
||||
// - The standard defines a JSON object as an unordered collection of name/value pairs.
|
||||
// While ordering can be observed through the underlying [jsontext] API,
|
||||
// both v1 and v2 generally avoid exposing the ordering.
|
||||
// No application should semantically depend on the order of object members.
|
||||
// Allowing duplicate names is a vector through which ordering of members
|
||||
// can accidentally be observed and depended upon.
|
||||
//
|
||||
// - The standard suggests that JSON object names are typically compared
|
||||
// based on equality of the sequence of Unicode code points,
|
||||
// which implies that comparing names is often case-sensitive.
|
||||
// When unmarshaling a JSON object into a Go struct,
|
||||
// by default, v1 uses a (loose) case-insensitive match on the name,
|
||||
// while v2 uses a (strict) case-sensitive match on the name.
|
||||
// To change the default, specify the [MatchCaseInsensitiveNames] option.
|
||||
// The use of case-insensitive matching provides another vector through
|
||||
// which duplicate names can occur. Allowing case-insensitive matching
|
||||
// means that v1 or v2 might interpret JSON objects differently from most
|
||||
// other JSON implementations (which typically use a case-sensitive match).
|
||||
//
|
||||
// - The standard does not specify a particular behavior when
|
||||
// an unknown name in a JSON object is encountered.
|
||||
// When unmarshaling a JSON object into a Go struct, by default
|
||||
// both v1 and v2 ignore unknown names and their corresponding values.
|
||||
// To change the default, specify the [RejectUnknownMembers] option.
|
||||
//
|
||||
// - The standard suggests that implementations may use a float64
|
||||
// to represent a JSON number. Consequently, large JSON integers
|
||||
// may lose precision when stored as a floating-point type.
|
||||
// Both v1 and v2 correctly preserve precision when marshaling and
|
||||
// unmarshaling a concrete integer type. However, even if v1 and v2
|
||||
// preserve precision for concrete types, other JSON implementations
|
||||
// may not be able to preserve precision for outputs produced by v1 or v2.
|
||||
// The `string` tag option can be used to specify that an integer type
|
||||
// is to be quoted within a JSON string to avoid loss of precision.
|
||||
// Furthermore, v1 and v2 may still lose precision when unmarshaling
|
||||
// into an any interface value, where unmarshal uses a float64
|
||||
// by default to represent a JSON number.
|
||||
// To change the default, specify the [WithUnmarshalers] option
|
||||
// with a custom unmarshaler that pre-populates the interface value
|
||||
// with a concrete Go type that can preserve precision.
|
||||
//
|
||||
// RFC 8785 specifies a canonical form for any JSON text,
|
||||
// which explicitly defines specific behaviors that RFC 8259 leaves undefined.
|
||||
// In theory, if a text can successfully [jsontext.Value.Canonicalize]
|
||||
// without changing the semantic meaning of the data, then it provides a
|
||||
// greater degree of confidence that the data is more secure and interoperable.
|
||||
//
|
||||
// The v2 API generally chooses more secure defaults than v1,
|
||||
// but care should still be taken with large integers or unknown members.
|
||||
//
|
||||
// [For example, suppose we have two micro-services.]: https://www.youtube.com/watch?v=avilmOcHKHE&t=1057s
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json/jsontext"
|
||||
"encoding/json/v2"
|
||||
"io"
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Type-specific marshal functions and methods take precedence
|
||||
// over the default representation of a value.
|
||||
// Functions or methods that operate on *T are only called when encoding
|
||||
// a value of type T (by taking its address) or a non-nil value of *T.
|
||||
// Marshal ensures that a value is always addressable
|
||||
// (by copying the value if necessary) so that
|
||||
// these functions and methods can be consistently called. For performance,
|
||||
// it is recommended that Marshal be passed a non-nil pointer to the value.
|
||||
//
|
||||
// The input value is encoded as JSON according to the following rules:
|
||||
//
|
||||
// - If any type-specific functions in a [WithMarshalers] option match
|
||||
// the value type, then those functions are called to encode the value.
|
||||
// If all applicable functions return [errors.ErrUnsupported],
|
||||
// then the value is encoded according to subsequent rules.
|
||||
//
|
||||
// - If the value type implements [MarshalerTo],
|
||||
// then the MarshalJSONTo method is called to encode the value.
|
||||
// If the method returns [errors.ErrUnsupported],
|
||||
// then the input is encoded according to subsequent rules.
|
||||
//
|
||||
// - 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.
|
||||
//
|
||||
// - Otherwise, the value is encoded according to the value's type
|
||||
// as described in detail below.
|
||||
//
|
||||
// Most Go types have a default JSON representation as follows:
|
||||
//
|
||||
// - A Go boolean is encoded as a JSON boolean (e.g., true or false).
|
||||
//
|
||||
// - A Go string is encoded as a JSON string.
|
||||
//
|
||||
// - A Go []byte or [N]byte is encoded as a JSON string containing
|
||||
// a binary value using Base 64 Encoding per RFC 4648, section 4.
|
||||
//
|
||||
// - A Go integer is encoded as a JSON number without fractions or exponents.
|
||||
// If [StringifyNumbers] is specified or encoding a JSON object name,
|
||||
// then the JSON number is encoded within a JSON string.
|
||||
//
|
||||
// - A Go float is encoded as a JSON number.
|
||||
// If [StringifyNumbers] is specified or encoding a JSON object name,
|
||||
// then the JSON number is encoded within a JSON string.
|
||||
// Encoding a NaN or ±Inf results in a [SemanticError].
|
||||
//
|
||||
// - 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]. The Go map is traversed in a non-deterministic order.
|
||||
// For deterministic encoding, consider using the [Deterministic] option.
|
||||
// By default, a nil map is encoded as an empty JSON object,
|
||||
// unless the [FormatNilMapAsNull] option is specified.
|
||||
//
|
||||
// - A Go struct is encoded as a JSON object.
|
||||
// See the “JSON Representation of Go structs” section
|
||||
// in the package-level documentation for more details.
|
||||
//
|
||||
// - A Go slice is encoded as a JSON array, where each Go slice element
|
||||
// is recursively JSON-encoded as the elements of the JSON array.
|
||||
// By default, a nil slice is encoded as an empty JSON array,
|
||||
// unless the [FormatNilSliceAsNull] option is specified.
|
||||
//
|
||||
// - A Go array is encoded as a JSON array, where each Go array element
|
||||
// is recursively JSON-encoded as the elements of the JSON array.
|
||||
// The JSON array length is always identical to the Go array length.
|
||||
//
|
||||
// - A Go pointer is encoded as a JSON null if nil, otherwise it is
|
||||
// the recursively JSON-encoded representation of the underlying value.
|
||||
//
|
||||
// - A Go interface is encoded as a JSON null if nil, otherwise it is
|
||||
// the recursively JSON-encoded representation of the underlying value.
|
||||
//
|
||||
// - A Go [time.Time] is encoded as a JSON string containing the timestamp
|
||||
// formatted in RFC 3339 with nanosecond precision.
|
||||
//
|
||||
// - A Go [time.Duration] currently has no default representation and
|
||||
// results in a [SemanticError], unless the [encoding/json.FormatDurationAsNano]
|
||||
// option is specified, in which case it is encoded as a JSON number
|
||||
// without fractions or exponents, representing the duration in nanoseconds.
|
||||
//
|
||||
// - All other Go types (e.g., complex numbers, channels, and functions)
|
||||
// have no default representation and result in a [SemanticError].
|
||||
//
|
||||
// JSON cannot represent cyclic data structures and Marshal does not handle them.
|
||||
// Passing cyclic structures will result in an error.
|
||||
func Marshal(in any, opts ...Options) (out []byte, err error) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
return json.Marshal(in, opts...)
|
||||
}
|
||||
|
||||
// MarshalWrite serializes a Go value into an [io.Writer] according to the provided
|
||||
// marshal and encode options (while ignoring unmarshal or decode options).
|
||||
// It does not terminate the output with a newline.
|
||||
// See [Marshal] for details about the conversion of a Go value into JSON.
|
||||
func MarshalWrite(out io.Writer, in any, opts ...Options) (err error) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
return json.MarshalWrite(out, in, opts...)
|
||||
}
|
||||
|
||||
// MarshalEncode serializes a Go value into an [jsontext.Encoder] according to
|
||||
// the provided marshal or encode options (while ignoring unmarshal or decode options).
|
||||
// The options provided take precedence over options already applied on
|
||||
// the [jsontext.Encoder] and only apply for the duration of the marshal call.
|
||||
//
|
||||
// See [Marshal] for details about the conversion of a Go value into JSON.
|
||||
func MarshalEncode(out *jsontext.Encoder, in any, opts ...Options) (err error) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
return json.MarshalEncode(out, in, opts...)
|
||||
}
|
||||
|
||||
// Unmarshal decodes a []byte input into a Go value according to the provided
|
||||
// unmarshal and decode options (while ignoring marshal or encode options).
|
||||
// The input must be a single JSON value with optional whitespace interspersed.
|
||||
// The output must be a non-nil pointer.
|
||||
//
|
||||
// Type-specific unmarshal functions and methods take precedence
|
||||
// over the default representation of a value.
|
||||
// Functions or methods that operate on *T are only called when decoding
|
||||
// a value of type T (by taking its address) or a non-nil value of *T.
|
||||
// Unmarshal ensures that a value is always addressable
|
||||
// (by copying the value if necessary) so that
|
||||
// these functions and methods can be consistently called.
|
||||
// If a value must be shallow copied to call a pointer-receiver
|
||||
// [Unmarshaler], [UnmarshalerFrom], or [encoding.TextUnmarshaler] method,
|
||||
// then any mutations performed by the method are shallow copied back
|
||||
// into the destination value.
|
||||
//
|
||||
// The input is decoded into the output according to the following rules:
|
||||
//
|
||||
// - If any type-specific functions in a [WithUnmarshalers] option match
|
||||
// the value type, then those functions are called to decode the JSON
|
||||
// value. If all applicable functions return [errors.ErrUnsupported],
|
||||
// then the input is decoded according to subsequent rules.
|
||||
//
|
||||
// - If the value type implements [UnmarshalerFrom],
|
||||
// then the UnmarshalJSONFrom method is called to decode the JSON value.
|
||||
// If the method returns [errors.ErrUnsupported],
|
||||
// then the input is decoded according to subsequent rules.
|
||||
//
|
||||
// - If the value type implements [Unmarshaler],
|
||||
// then the UnmarshalJSON method is called to decode the JSON value.
|
||||
//
|
||||
// - If the value type implements [encoding.TextUnmarshaler],
|
||||
// then the input is decoded as a JSON string and
|
||||
// the UnmarshalText method is called with the decoded string value.
|
||||
// This fails with a [SemanticError] if the input is not a JSON string.
|
||||
//
|
||||
// - Otherwise, the JSON value is decoded according to the value's type
|
||||
// as described in detail below.
|
||||
//
|
||||
// Most Go types have a default JSON representation.
|
||||
// A JSON null may be decoded into every supported Go value where
|
||||
// it is equivalent to storing the zero value of the Go value.
|
||||
// If the input JSON kind is not handled by the current Go value type,
|
||||
// then this fails with a [SemanticError]. Unless otherwise specified,
|
||||
// the decoded value replaces any pre-existing value.
|
||||
//
|
||||
// The representation of each type is as follows:
|
||||
//
|
||||
// - A Go boolean is decoded from a JSON boolean (e.g., true or false).
|
||||
//
|
||||
// - A Go string is decoded from a JSON string.
|
||||
//
|
||||
// - A Go []byte or [N]byte is decoded from a JSON string containing
|
||||
// a binary value using Base 64 Encoding per RFC 4648, section 4.
|
||||
// When decoding into a non-nil []byte, the slice length is reset to zero
|
||||
// and the decoded input is appended to it.
|
||||
// When decoding into a [N]byte, the input must decode to exactly N bytes,
|
||||
// otherwise it fails with a [SemanticError].
|
||||
//
|
||||
// - A Go integer is decoded from a JSON number.
|
||||
// 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.
|
||||
//
|
||||
// - A Go float is decoded from a JSON number.
|
||||
// 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.
|
||||
// Since JSON lacks a native representation for a NaN or ±Inf,
|
||||
// such values cannot be the result of decoding.
|
||||
//
|
||||
// - 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. 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.
|
||||
//
|
||||
// - A Go struct is decoded from a JSON object.
|
||||
// See the “JSON Representation of Go structs” section
|
||||
// in the package-level documentation for more details.
|
||||
//
|
||||
// - A Go slice is decoded from a JSON array, where each JSON element
|
||||
// is recursively decoded and appended to the Go slice.
|
||||
// Before appending into a Go slice, a new slice is allocated if it is nil,
|
||||
// otherwise the slice length is reset to zero.
|
||||
//
|
||||
// - A Go array is decoded from a JSON array, where each JSON array element
|
||||
// is recursively decoded as each corresponding Go array element.
|
||||
// Each Go array element is zeroed before decoding into it.
|
||||
// It fails with a [SemanticError] if the JSON array does not contain
|
||||
// the exact same number of elements as the Go array.
|
||||
//
|
||||
// - A Go pointer is decoded based on the JSON kind and underlying Go type.
|
||||
// If the input is a JSON null, then this stores a nil pointer.
|
||||
// Otherwise, it allocates a new underlying value if the pointer is nil,
|
||||
// and recursively JSON decodes into the underlying value.
|
||||
//
|
||||
// - A Go interface is decoded based on the JSON kind and underlying Go type.
|
||||
// If the input is a JSON null, then this stores a nil interface value.
|
||||
// Otherwise, a nil interface value of an empty interface type is initialized
|
||||
// with a zero Go bool, string, float64, map[string]any, or []any if the
|
||||
// input is a JSON boolean, string, number, object, or array, respectively.
|
||||
// If the interface value is still nil, then this fails with a [SemanticError]
|
||||
// since decoding could not determine an appropriate Go type to decode into.
|
||||
// For example, unmarshaling into a nil io.Reader fails since
|
||||
// there is no concrete type to populate the interface value with.
|
||||
// Otherwise an underlying value exists and it recursively decodes
|
||||
// the JSON input into it.
|
||||
//
|
||||
// - A Go [time.Time] is decoded from a JSON string containing the time
|
||||
// formatted in RFC 3339 with nanosecond precision.
|
||||
//
|
||||
// - A Go [time.Duration] currently has no default representation and
|
||||
// results in a [SemanticError], unless the [encoding/json.FormatDurationAsNano]
|
||||
// option is specified, in which case it is decoded as a JSON number
|
||||
// without fractions or exponents, representing the duration in nanoseconds.
|
||||
//
|
||||
// - All other Go types (e.g., complex numbers, channels, and functions)
|
||||
// have no default representation and result in a [SemanticError].
|
||||
//
|
||||
// In general, unmarshaling follows merge semantics (similar to RFC 7396)
|
||||
// where the decoded Go value replaces the destination value
|
||||
// for any JSON kind other than an object.
|
||||
// For JSON objects, the input object is merged into the destination value
|
||||
// where matching object members recursively apply merge semantics.
|
||||
func Unmarshal(in []byte, out any, opts ...Options) (err error) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
return json.Unmarshal(in, out, opts...)
|
||||
}
|
||||
|
||||
// UnmarshalRead deserializes a Go value from an [io.Reader] according to the
|
||||
// provided unmarshal and decode options (while ignoring marshal or encode options).
|
||||
// The input must be a single JSON value with optional whitespace interspersed.
|
||||
// It consumes the entirety of [io.Reader] until [io.EOF] is encountered,
|
||||
// without reporting an error for EOF. The output must be a non-nil pointer.
|
||||
// See [Unmarshal] for details about the conversion of JSON into a Go value.
|
||||
func UnmarshalRead(in io.Reader, out any, opts ...Options) (err error) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
return json.UnmarshalRead(in, out, opts...)
|
||||
}
|
||||
|
||||
// UnmarshalDecode deserializes a Go value from a [jsontext.Decoder] according to
|
||||
// the provided unmarshal or decode options (while ignoring marshal or encode options).
|
||||
// The options provided take precedence over options already applied on
|
||||
// the [jsontext.Decoder] and only apply for the duration of the unmarshal call.
|
||||
//
|
||||
// The input may be a stream of zero or more JSON values,
|
||||
// where this only unmarshals the next JSON value in the stream.
|
||||
// If there are no more top-level JSON values, it reports [io.EOF].
|
||||
// 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) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
return json.UnmarshalDecode(in, out, opts...)
|
||||
}
|
||||
|
||||
// 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].
|
||||
// A nil *Marshalers is equivalent to an empty list.
|
||||
// There are no exported fields or methods on Marshalers.
|
||||
type Marshalers = json.Marshalers
|
||||
|
||||
// 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 [errors.ErrUnsupported],
|
||||
// then the next applicable function is called,
|
||||
// otherwise the default marshaling behavior is used.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// 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 json.JoinMarshalers(ms...)
|
||||
}
|
||||
|
||||
// Unmarshalers is a list of functions that may override the unmarshal behavior
|
||||
// of specific types. Populate [WithUnmarshalers] to use it with
|
||||
// [Unmarshal], [UnmarshalRead], or [UnmarshalDecode].
|
||||
// A nil *Unmarshalers is equivalent to an empty list.
|
||||
// There are no exported fields or methods on Unmarshalers.
|
||||
type Unmarshalers = json.Unmarshalers
|
||||
|
||||
// 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 [errors.ErrUnsupported],
|
||||
// then the next applicable function is called,
|
||||
// otherwise the default unmarshaling behavior is used.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// 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 json.JoinUnmarshalers(us...)
|
||||
}
|
||||
|
||||
// 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
|
||||
// if T is an interface or pointer type.
|
||||
//
|
||||
// The function must marshal exactly one JSON value.
|
||||
// The value of T must not be retained outside the function call.
|
||||
// It is recommended that fn return a []byte buffer that is safe
|
||||
// for the caller to retain and potentially mutate.
|
||||
// It may not return [errors.ErrUnsupported].
|
||||
func MarshalFunc[T any](fn func(T) ([]byte, error)) *Marshalers {
|
||||
return json.MarshalFunc[T](fn)
|
||||
}
|
||||
|
||||
// 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
|
||||
// if T is an interface or pointer type.
|
||||
//
|
||||
// The function must marshal exactly one JSON value by calling write methods
|
||||
// on the provided encoder. It may return [errors.ErrUnsupported] such that marshaling can
|
||||
// move on to the next marshal function. However, no mutable method calls may
|
||||
// be called on the encoder if [errors.ErrUnsupported] is returned.
|
||||
// The pointer to [jsontext.Encoder] and the value of T
|
||||
// must not be retained outside the function call.
|
||||
func MarshalToFunc[T any](fn func(*jsontext.Encoder, T) error) *Marshalers {
|
||||
return json.MarshalToFunc[T](fn)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// The function must unmarshal exactly one JSON value.
|
||||
// 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 [errors.ErrUnsupported].
|
||||
func UnmarshalFunc[T any](fn func([]byte, T) error) *Unmarshalers {
|
||||
return json.UnmarshalFunc[T](fn)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// The function must unmarshal exactly one JSON value by calling read methods
|
||||
// on the provided decoder. It may return [errors.ErrUnsupported] such that unmarshaling can
|
||||
// move on to the next unmarshal function. However, no mutable method calls may
|
||||
// be called on the decoder if [errors.ErrUnsupported] is returned.
|
||||
// The pointer to [jsontext.Decoder] and the value of T
|
||||
// must not be retained outside the function call.
|
||||
func UnmarshalFromFunc[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers {
|
||||
return json.UnmarshalFromFunc[T](fn)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// If the returned error is a [SemanticError], then unpopulated fields
|
||||
// of the error may be populated by [json] with additional context.
|
||||
// Errors of other types are wrapped within a [SemanticError].
|
||||
type Marshaler = json.Marshaler
|
||||
|
||||
// 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 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.
|
||||
// Alternatively, it may return [errors.ErrUnsupported] without mutating
|
||||
// the Encoder. The "json" package calling the method will
|
||||
// use the next available JSON representation for the receiver type.
|
||||
// Implementations must not retain the pointer to [jsontext.Encoder].
|
||||
//
|
||||
// If the returned error is a [SemanticError], then unpopulated fields
|
||||
// of the error may be populated by [json] with additional context.
|
||||
// Errors of other types are wrapped within a [SemanticError],
|
||||
// unless it is an IO error.
|
||||
//
|
||||
// The MarshalJSONTo method should not be directly called as it may
|
||||
// return sentinel errors that need special handling.
|
||||
// Users should instead call [MarshalEncode], which handles such cases.
|
||||
type MarshalerTo = json.MarshalerTo
|
||||
|
||||
// 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
|
||||
// if called from unmarshal functionality in this package.
|
||||
// UnmarshalJSON must copy the JSON data if it is retained after returning.
|
||||
// It is recommended that UnmarshalJSON implement merge semantics when
|
||||
// unmarshaling into a pre-populated value.
|
||||
//
|
||||
// Implementations must not retain or mutate the input []byte.
|
||||
//
|
||||
// If the returned error is a [SemanticError], then unpopulated fields
|
||||
// of the error may be populated by [json] with additional context.
|
||||
// Errors of other types are wrapped within a [SemanticError].
|
||||
type Unmarshaler = json.Unmarshaler
|
||||
|
||||
// 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 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 UnmarshalJSONFrom implement merge semantics when
|
||||
// unmarshaling into a pre-populated value.
|
||||
// Alternatively, it may return [errors.ErrUnsupported] without mutating
|
||||
// the Decoder. The "json" package calling the method will
|
||||
// use the next available JSON representation for the receiver type.
|
||||
// Implementations must not retain the pointer to [jsontext.Decoder].
|
||||
//
|
||||
// If the returned error is a [SemanticError], then unpopulated fields
|
||||
// of the error may be populated by [json] with additional context.
|
||||
// Errors of other types are wrapped within a [SemanticError],
|
||||
// unless it is a [jsontext.SyntacticError] or an IO error.
|
||||
//
|
||||
// The UnmarshalJSONFrom method should not be directly called as it may
|
||||
// return sentinel errors that need special handling.
|
||||
// Users should instead call [UnmarshalDecode], which handles such cases.
|
||||
type UnmarshalerFrom = json.UnmarshalerFrom
|
||||
|
||||
// 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 := ...
|
||||
// serr, ok := errors.AsType[*json.SemanticError](err)
|
||||
// if ok && 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 = json.ErrUnknownName
|
||||
|
||||
// SemanticError describes an error determining the meaning
|
||||
// of JSON data as Go data or vice-versa.
|
||||
//
|
||||
// If a [Marshaler], [MarshalerTo], [Unmarshaler], or [UnmarshalerFrom] method
|
||||
// returns a SemanticError when called by the [json] package,
|
||||
// then the ByteOffset, JSONPointer, and GoType fields are automatically
|
||||
// populated by the calling context if they are the zero value.
|
||||
//
|
||||
// The contents of this error as produced by this package may change over time.
|
||||
type SemanticError = json.SemanticError
|
||||
|
||||
// Options configure [Marshal], [MarshalWrite], [MarshalEncode],
|
||||
// [Unmarshal], [UnmarshalRead], and [UnmarshalDecode] with specific features.
|
||||
// Each function takes in a variadic list of options, where properties
|
||||
// set in later options override the value of previously set properties.
|
||||
//
|
||||
// The Options type is identical to [encoding/json.Options] and
|
||||
// [encoding/json/jsontext.Options]. Options from the other packages can
|
||||
// be used interchangeably with functionality in this package.
|
||||
//
|
||||
// Options represent either a singular option or a set of options.
|
||||
// It can be functionally thought of as a Go map of option properties
|
||||
// (even though the underlying implementation avoids Go maps for performance).
|
||||
//
|
||||
// The constructors (e.g., [Deterministic]) return a singular option value:
|
||||
//
|
||||
// opt := Deterministic(true)
|
||||
//
|
||||
// which is analogous to creating a single entry map:
|
||||
//
|
||||
// opt := Options{"Deterministic": true}
|
||||
//
|
||||
// [JoinOptions] composes multiple options values together:
|
||||
//
|
||||
// out := JoinOptions(opts...)
|
||||
//
|
||||
// which is analogous to making a new map and copying the options over:
|
||||
//
|
||||
// out := make(Options)
|
||||
// for _, m := range opts {
|
||||
// for k, v := range m {
|
||||
// out[k] = v
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// [GetOption] looks up the value of an options parameter:
|
||||
//
|
||||
// v, ok := GetOption(opts, Deterministic)
|
||||
//
|
||||
// which is analogous to a Go map lookup:
|
||||
//
|
||||
// v, ok := Options["Deterministic"]
|
||||
//
|
||||
// There is a single Options type, which is used with both marshal and unmarshal.
|
||||
// Some options affect both operations, while others only affect one operation:
|
||||
//
|
||||
// - [StringifyNumbers] affects marshaling and unmarshaling
|
||||
// - [Deterministic] affects marshaling only
|
||||
// - [FormatNilSliceAsNull] affects marshaling only
|
||||
// - [FormatNilMapAsNull] affects marshaling only
|
||||
// - [OmitZeroStructFields] affects marshaling only
|
||||
// - [MatchCaseInsensitiveNames] affects marshaling and unmarshaling
|
||||
// - [RejectUnknownMembers] affects unmarshaling only
|
||||
// - [WithMarshalers] affects marshaling only
|
||||
// - [WithUnmarshalers] affects unmarshaling only
|
||||
//
|
||||
// Options that do not affect a particular operation are ignored.
|
||||
type Options = json.Options
|
||||
|
||||
// JoinOptions coalesces the provided list of options into a single Options.
|
||||
// Properties set in later options override the value of previously set properties.
|
||||
func JoinOptions(srcs ...Options) Options {
|
||||
return json.JoinOptions(srcs...)
|
||||
}
|
||||
|
||||
// GetOption returns the value stored in opts with the provided setter,
|
||||
// reporting whether the value is present.
|
||||
// If not present, the returned value is the zero value for type T.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// v, ok := json.GetOption(opts, json.Deterministic)
|
||||
//
|
||||
// Options are most commonly introspected to alter the JSON representation of
|
||||
// [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 json.GetOption[T](opts, setter)
|
||||
}
|
||||
|
||||
// DefaultOptionsV2 is the full set of all options that define v2 semantics.
|
||||
// It is equivalent to the set of options in [encoding/json.DefaultOptionsV1]
|
||||
// all being set to false. All other options are not present.
|
||||
func DefaultOptionsV2() Options {
|
||||
return json.DefaultOptionsV2()
|
||||
}
|
||||
|
||||
// StringifyNumbers specifies that types that would normally be
|
||||
// encoded as a JSON number to instead be encoded as a JSON string
|
||||
// containing the equivalent JSON number value.
|
||||
// When unmarshaling, the value is parsed from a JSON string
|
||||
// containing the JSON number without any surrounding whitespace.
|
||||
//
|
||||
// When the `string` tag option is specified on a Go struct field,
|
||||
// this option is applied for the top-level JSON value for that field.
|
||||
// Unless StringifyNumbers was applied globally, the option does not
|
||||
// recursively apply to nested JSON numbers within a JSON object or array.
|
||||
// A Go type with custom marshal/unmarshal that represents a JSON number
|
||||
// should respect the StringifyNumbers option and if specified
|
||||
// serialize as a JSON number within a JSON string.
|
||||
//
|
||||
// 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.
|
||||
// This may cause decoders to lose precision for int64 and uint64 types.
|
||||
// Quoting JSON numbers as a JSON string preserves the exact precision.
|
||||
//
|
||||
// This affects either marshaling or unmarshaling.
|
||||
func StringifyNumbers(v bool) Options {
|
||||
return json.StringifyNumbers(v)
|
||||
}
|
||||
|
||||
// Deterministic specifies that the same input value will be serialized
|
||||
// as the exact same output bytes. Different processes of
|
||||
// the same program will serialize equal values to the same bytes,
|
||||
// but different versions of the same program are not guaranteed
|
||||
// to produce the exact same sequence of bytes.
|
||||
//
|
||||
// This only affects marshaling and is ignored when unmarshaling.
|
||||
func Deterministic(v bool) Options {
|
||||
return json.Deterministic(v)
|
||||
}
|
||||
|
||||
// FormatNilSliceAsNull specifies that a nil Go slice should marshal as a
|
||||
// JSON null instead of the default representation as an empty JSON array
|
||||
// (or an empty JSON string in the case of ~[]byte).
|
||||
//
|
||||
// This only affects marshaling and is ignored when unmarshaling.
|
||||
func FormatNilSliceAsNull(v bool) Options {
|
||||
return json.FormatNilSliceAsNull(v)
|
||||
}
|
||||
|
||||
// FormatNilMapAsNull specifies that a nil Go map should marshal as a
|
||||
// JSON null instead of the default representation as an empty JSON object.
|
||||
//
|
||||
// This only affects marshaling and is ignored when unmarshaling.
|
||||
func FormatNilMapAsNull(v bool) Options {
|
||||
return json.FormatNilMapAsNull(v)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return json.OmitZeroStructFields(v)
|
||||
}
|
||||
|
||||
// MatchCaseInsensitiveNames specifies that JSON object members are matched
|
||||
// against Go struct fields using a case-insensitive match of the name.
|
||||
// If a name matches multiple fields, it chooses the field with an exact
|
||||
// match of the name, otherwise it reports an error.
|
||||
// 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.
|
||||
//
|
||||
// This affects either marshaling or unmarshaling.
|
||||
//
|
||||
// By matching names in a case-insensitive manner, it also affects the detection
|
||||
// of duplicate names (assuming [jsontext.AllowDuplicateNames] is false) since
|
||||
// variations of the same name may match the same Go struct field.
|
||||
// For example, when unmarshaling, the names "foo" and "Foo" may both
|
||||
// match the same Go struct field and therefore be considered a duplicate name.
|
||||
// When marshaling, normally it is impossible for any two Go struct fields to
|
||||
// serialize in a way where they unmarshal into the same Go struct field
|
||||
// since they all have unique exact names.
|
||||
// However, with the use of an embedded fallback, it is possible for the
|
||||
// embedded fallback to contain a name that also matches the name for
|
||||
// a Go struct field, resulting in a duplicate name error.
|
||||
func MatchCaseInsensitiveNames(v bool) Options {
|
||||
return json.MatchCaseInsensitiveNames(v)
|
||||
}
|
||||
|
||||
// RejectUnknownMembers specifies that unknown members should be rejected
|
||||
// when unmarshaling a JSON object.
|
||||
//
|
||||
// This only affects unmarshaling and is ignored when marshaling.
|
||||
func RejectUnknownMembers(v bool) Options {
|
||||
return json.RejectUnknownMembers(v)
|
||||
}
|
||||
|
||||
// WithMarshalers specifies a list of type-specific marshalers to use,
|
||||
// which can be used to override the default marshal behavior for values
|
||||
// of particular types.
|
||||
//
|
||||
// This only affects marshaling and is ignored when unmarshaling.
|
||||
func WithMarshalers(v *Marshalers) Options {
|
||||
return json.WithMarshalers(v)
|
||||
}
|
||||
|
||||
// WithUnmarshalers specifies a list of type-specific unmarshalers to use,
|
||||
// which can be used to override the default unmarshal behavior for values
|
||||
// of particular types.
|
||||
//
|
||||
// This only affects unmarshaling and is ignored when marshaling.
|
||||
func WithUnmarshalers(v *Unmarshalers) Options {
|
||||
return json.WithUnmarshalers(v)
|
||||
}
|
||||
BIN
tools/tsgo/vendor/github.com/go-json-experiment/json/api.png
generated
vendored
Normal file
BIN
tools/tsgo/vendor/github.com/go-json-experiment/json/api.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 151 KiB |
569
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal.go
generated
vendored
Normal file
569
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal.go
generated
vendored
Normal file
@@ -0,0 +1,569 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"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/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
|
||||
)
|
||||
|
||||
var (
|
||||
// Once a JSON object has begun processing without duplicate name verification,
|
||||
// it does not track the history of names that have been seen so far.
|
||||
// Reject changing the setting for the current JSON object namespace,
|
||||
// otherwise we would be operating with inconsistent state.
|
||||
// Note that you can change the setting before processing the start
|
||||
// of a different JSON object.
|
||||
//
|
||||
// TODO: We could loosen this restriction in certain conditions.
|
||||
// If we are already checking for duplicate names,
|
||||
// we can momentarily disable it for the next JSON object member name.
|
||||
// However, if we are already NOT checking for duplicate names,
|
||||
// we cannot momentarily enable it for the next JSON object member name
|
||||
// since we already lack prior history of JSON object names.
|
||||
errChangingDuplicateNames = errors.New("cannot change duplicate name checks after a JSON object has already begun processing")
|
||||
|
||||
// The presence of invalid UTF-8 has an interesting intersection
|
||||
// with checking for duplicate names. Due to the semantic of mangling
|
||||
// invalid UTF-8 as the Unicode replacement character,
|
||||
// two string keys in a Go map (both with invalid UTF-8)
|
||||
// may encode as the same JSON string. Thus, we forbid changing of
|
||||
// invalid UTF-8 checks in the current JSON object namespace.
|
||||
errChangingInvalidUTF8 = errors.New("cannot change UTF-8 checks after a JSON object has already begun processing")
|
||||
|
||||
// TODO(https://go.dev/issue/79559): Changing whitespace currently
|
||||
// leads to strange effects and will need more careful adjustment.
|
||||
// For now, we just report an error.
|
||||
errChangingWhitespace = errors.New("cannot change whitespace formatting within a MarshalEncode call")
|
||||
)
|
||||
|
||||
// export exposes internal functionality of the "jsontext" package.
|
||||
var export = jsontext.Internal.Export(&internal.AllowInternalUse)
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Type-specific marshal functions and methods take precedence
|
||||
// over the default representation of a value.
|
||||
// Functions or methods that operate on *T are only called when encoding
|
||||
// a value of type T (by taking its address) or a non-nil value of *T.
|
||||
// Marshal ensures that a value is always addressable
|
||||
// (by copying the value if necessary) so that
|
||||
// these functions and methods can be consistently called. For performance,
|
||||
// it is recommended that Marshal be passed a non-nil pointer to the value.
|
||||
//
|
||||
// The input value is encoded as JSON according to the following rules:
|
||||
//
|
||||
// - If any type-specific functions in a [WithMarshalers] option match
|
||||
// the value type, then those functions are called to encode the value.
|
||||
// If all applicable functions return [errors.ErrUnsupported],
|
||||
// then the value is encoded according to subsequent rules.
|
||||
//
|
||||
// - If the value type implements [MarshalerTo],
|
||||
// then the MarshalJSONTo method is called to encode the value.
|
||||
// If the method returns [errors.ErrUnsupported],
|
||||
// then the input is encoded according to subsequent rules.
|
||||
//
|
||||
// - 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.
|
||||
//
|
||||
// - Otherwise, the value is encoded according to the value's type
|
||||
// as described in detail below.
|
||||
//
|
||||
// Most Go types have a default JSON representation as follows:
|
||||
//
|
||||
// - A Go boolean is encoded as a JSON boolean (e.g., true or false).
|
||||
//
|
||||
// - A Go string is encoded as a JSON string.
|
||||
//
|
||||
// - A Go []byte or [N]byte is encoded as a JSON string containing
|
||||
// a binary value using Base 64 Encoding per RFC 4648, section 4.
|
||||
//
|
||||
// - A Go integer is encoded as a JSON number without fractions or exponents.
|
||||
// If [StringifyNumbers] is specified or encoding a JSON object name,
|
||||
// then the JSON number is encoded within a JSON string.
|
||||
//
|
||||
// - A Go float is encoded as a JSON number.
|
||||
// If [StringifyNumbers] is specified or encoding a JSON object name,
|
||||
// then the JSON number is encoded within a JSON string.
|
||||
// Encoding a NaN or ±Inf results in a [SemanticError].
|
||||
//
|
||||
// - 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]. The Go map is traversed in a non-deterministic order.
|
||||
// For deterministic encoding, consider using the [Deterministic] option.
|
||||
// By default, a nil map is encoded as an empty JSON object,
|
||||
// unless the [FormatNilMapAsNull] option is specified.
|
||||
//
|
||||
// - A Go struct is encoded as a JSON object.
|
||||
// See the “JSON Representation of Go structs” section
|
||||
// in the package-level documentation for more details.
|
||||
//
|
||||
// - A Go slice is encoded as a JSON array, where each Go slice element
|
||||
// is recursively JSON-encoded as the elements of the JSON array.
|
||||
// By default, a nil slice is encoded as an empty JSON array,
|
||||
// unless the [FormatNilSliceAsNull] option is specified.
|
||||
//
|
||||
// - A Go array is encoded as a JSON array, where each Go array element
|
||||
// is recursively JSON-encoded as the elements of the JSON array.
|
||||
// The JSON array length is always identical to the Go array length.
|
||||
//
|
||||
// - A Go pointer is encoded as a JSON null if nil, otherwise it is
|
||||
// the recursively JSON-encoded representation of the underlying value.
|
||||
//
|
||||
// - A Go interface is encoded as a JSON null if nil, otherwise it is
|
||||
// the recursively JSON-encoded representation of the underlying value.
|
||||
//
|
||||
// - A Go [time.Time] is encoded as a JSON string containing the timestamp
|
||||
// formatted in RFC 3339 with nanosecond precision.
|
||||
//
|
||||
// - A Go [time.Duration] currently has no default representation and
|
||||
// results in a [SemanticError], unless the [encoding/json.FormatDurationAsNano]
|
||||
// option is specified, in which case it is encoded as a JSON number
|
||||
// without fractions or exponents, representing the duration in nanoseconds.
|
||||
//
|
||||
// - All other Go types (e.g., complex numbers, channels, and functions)
|
||||
// have no default representation and result in a [SemanticError].
|
||||
//
|
||||
// JSON cannot represent cyclic data structures and Marshal does not handle them.
|
||||
// Passing cyclic structures will result in an error.
|
||||
func Marshal(in any, opts ...Options) (out []byte, err error) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
enc := export.GetBufferedEncoder(opts...)
|
||||
defer export.PutBufferedEncoder(enc)
|
||||
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
|
||||
}
|
||||
|
||||
// MarshalWrite serializes a Go value into an [io.Writer] according to the provided
|
||||
// marshal and encode options (while ignoring unmarshal or decode options).
|
||||
// It does not terminate the output with a newline.
|
||||
// See [Marshal] for details about the conversion of a Go value into JSON.
|
||||
func MarshalWrite(out io.Writer, in any, opts ...Options) (err error) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
enc := export.GetStreamingEncoder(out, opts...)
|
||||
defer export.PutStreamingEncoder(enc)
|
||||
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 internal.TransformMarshalError(in, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalEncode serializes a Go value into an [jsontext.Encoder] according to
|
||||
// the provided marshal or encode options (while ignoring unmarshal or decode options).
|
||||
// The options provided take precedence over options already applied on
|
||||
// the [jsontext.Encoder] and only apply for the duration of the marshal call.
|
||||
//
|
||||
// See [Marshal] for details about the conversion of a Go value into JSON.
|
||||
func MarshalEncode(out *jsontext.Encoder, in any, opts ...Options) (err error) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
xe := export.Encoder(out)
|
||||
if len(opts) > 0 {
|
||||
optsOriginal := xe.Struct
|
||||
defer func() { xe.Struct = optsOriginal }()
|
||||
xe.Struct.Join(opts...)
|
||||
if xe.Tokens.Last.NeedObjectName() {
|
||||
if optsOriginal.Flags.Get(jsonflags.AllowDuplicateNames) != xe.Struct.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
return newMarshalErrorBefore(out, reflect.TypeOf(in), errChangingDuplicateNames)
|
||||
}
|
||||
if optsOriginal.Flags.Get(jsonflags.AllowInvalidUTF8) != xe.Struct.Flags.Get(jsonflags.AllowInvalidUTF8) {
|
||||
return newMarshalErrorBefore(out, reflect.TypeOf(in), errChangingInvalidUTF8)
|
||||
}
|
||||
}
|
||||
if xe.Struct.Flags.Has(jsonflags.AnyWhitespace) {
|
||||
if xe.Struct.Flags.Get(jsonflags.Multiline) {
|
||||
xe.Struct.InitializeMultiline()
|
||||
}
|
||||
if jsonopts.ChangedWhitespace(optsOriginal, xe.Struct) {
|
||||
return newMarshalErrorBefore(out, reflect.TypeOf(in), errChangingWhitespace)
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
v := reflect.ValueOf(in)
|
||||
if !v.IsValid() || (v.Kind() == reflect.Pointer && v.IsNil()) {
|
||||
return out.WriteToken(jsontext.Null)
|
||||
}
|
||||
// Shallow copy non-pointer values to obtain an addressable value.
|
||||
// It is beneficial to performance to always pass pointers to avoid this.
|
||||
forceAddr := v.Kind() != reflect.Pointer
|
||||
if forceAddr {
|
||||
v2 := reflect.New(v.Type())
|
||||
v2.Elem().Set(v)
|
||||
v = v2
|
||||
}
|
||||
va := addressableValue{v.Elem(), forceAddr} // dereferenced pointer is always addressable
|
||||
t := va.Type()
|
||||
|
||||
// Lookup and call the marshal function for this type.
|
||||
marshal := lookupArshaler(t).marshal
|
||||
if mo.Marshalers != nil {
|
||||
marshal, _ = mo.Marshalers.(*Marshalers).lookup(marshal, t)
|
||||
}
|
||||
if err := marshal(out, va, mo); err != nil {
|
||||
if !mo.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
export.Encoder(out).Tokens.InvalidateDisabledNamespaces()
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal decodes a []byte input into a Go value according to the provided
|
||||
// unmarshal and decode options (while ignoring marshal or encode options).
|
||||
// The input must be a single JSON value with optional whitespace interspersed.
|
||||
// The output must be a non-nil pointer.
|
||||
//
|
||||
// Type-specific unmarshal functions and methods take precedence
|
||||
// over the default representation of a value.
|
||||
// Functions or methods that operate on *T are only called when decoding
|
||||
// a value of type T (by taking its address) or a non-nil value of *T.
|
||||
// Unmarshal ensures that a value is always addressable
|
||||
// (by copying the value if necessary) so that
|
||||
// these functions and methods can be consistently called.
|
||||
// If a value must be shallow copied to call a pointer-receiver
|
||||
// [Unmarshaler], [UnmarshalerFrom], or [encoding.TextUnmarshaler] method,
|
||||
// then any mutations performed by the method are shallow copied back
|
||||
// into the destination value.
|
||||
//
|
||||
// The input is decoded into the output according to the following rules:
|
||||
//
|
||||
// - If any type-specific functions in a [WithUnmarshalers] option match
|
||||
// the value type, then those functions are called to decode the JSON
|
||||
// value. If all applicable functions return [errors.ErrUnsupported],
|
||||
// then the input is decoded according to subsequent rules.
|
||||
//
|
||||
// - If the value type implements [UnmarshalerFrom],
|
||||
// then the UnmarshalJSONFrom method is called to decode the JSON value.
|
||||
// If the method returns [errors.ErrUnsupported],
|
||||
// then the input is decoded according to subsequent rules.
|
||||
//
|
||||
// - If the value type implements [Unmarshaler],
|
||||
// then the UnmarshalJSON method is called to decode the JSON value.
|
||||
//
|
||||
// - If the value type implements [encoding.TextUnmarshaler],
|
||||
// then the input is decoded as a JSON string and
|
||||
// the UnmarshalText method is called with the decoded string value.
|
||||
// This fails with a [SemanticError] if the input is not a JSON string.
|
||||
//
|
||||
// - Otherwise, the JSON value is decoded according to the value's type
|
||||
// as described in detail below.
|
||||
//
|
||||
// Most Go types have a default JSON representation.
|
||||
// A JSON null may be decoded into every supported Go value where
|
||||
// it is equivalent to storing the zero value of the Go value.
|
||||
// If the input JSON kind is not handled by the current Go value type,
|
||||
// then this fails with a [SemanticError]. Unless otherwise specified,
|
||||
// the decoded value replaces any pre-existing value.
|
||||
//
|
||||
// The representation of each type is as follows:
|
||||
//
|
||||
// - A Go boolean is decoded from a JSON boolean (e.g., true or false).
|
||||
//
|
||||
// - A Go string is decoded from a JSON string.
|
||||
//
|
||||
// - A Go []byte or [N]byte is decoded from a JSON string containing
|
||||
// a binary value using Base 64 Encoding per RFC 4648, section 4.
|
||||
// When decoding into a non-nil []byte, the slice length is reset to zero
|
||||
// and the decoded input is appended to it.
|
||||
// When decoding into a [N]byte, the input must decode to exactly N bytes,
|
||||
// otherwise it fails with a [SemanticError].
|
||||
//
|
||||
// - A Go integer is decoded from a JSON number.
|
||||
// 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.
|
||||
//
|
||||
// - A Go float is decoded from a JSON number.
|
||||
// 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.
|
||||
// Since JSON lacks a native representation for a NaN or ±Inf,
|
||||
// such values cannot be the result of decoding.
|
||||
//
|
||||
// - 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. 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.
|
||||
//
|
||||
// - A Go struct is decoded from a JSON object.
|
||||
// See the “JSON Representation of Go structs” section
|
||||
// in the package-level documentation for more details.
|
||||
//
|
||||
// - A Go slice is decoded from a JSON array, where each JSON element
|
||||
// is recursively decoded and appended to the Go slice.
|
||||
// Before appending into a Go slice, a new slice is allocated if it is nil,
|
||||
// otherwise the slice length is reset to zero.
|
||||
//
|
||||
// - A Go array is decoded from a JSON array, where each JSON array element
|
||||
// is recursively decoded as each corresponding Go array element.
|
||||
// Each Go array element is zeroed before decoding into it.
|
||||
// It fails with a [SemanticError] if the JSON array does not contain
|
||||
// the exact same number of elements as the Go array.
|
||||
//
|
||||
// - A Go pointer is decoded based on the JSON kind and underlying Go type.
|
||||
// If the input is a JSON null, then this stores a nil pointer.
|
||||
// Otherwise, it allocates a new underlying value if the pointer is nil,
|
||||
// and recursively JSON decodes into the underlying value.
|
||||
//
|
||||
// - A Go interface is decoded based on the JSON kind and underlying Go type.
|
||||
// If the input is a JSON null, then this stores a nil interface value.
|
||||
// Otherwise, a nil interface value of an empty interface type is initialized
|
||||
// with a zero Go bool, string, float64, map[string]any, or []any if the
|
||||
// input is a JSON boolean, string, number, object, or array, respectively.
|
||||
// If the interface value is still nil, then this fails with a [SemanticError]
|
||||
// since decoding could not determine an appropriate Go type to decode into.
|
||||
// For example, unmarshaling into a nil io.Reader fails since
|
||||
// there is no concrete type to populate the interface value with.
|
||||
// Otherwise an underlying value exists and it recursively decodes
|
||||
// the JSON input into it.
|
||||
//
|
||||
// - A Go [time.Time] is decoded from a JSON string containing the time
|
||||
// formatted in RFC 3339 with nanosecond precision.
|
||||
//
|
||||
// - A Go [time.Duration] currently has no default representation and
|
||||
// results in a [SemanticError], unless the [encoding/json.FormatDurationAsNano]
|
||||
// option is specified, in which case it is decoded as a JSON number
|
||||
// without fractions or exponents, representing the duration in nanoseconds.
|
||||
//
|
||||
// - All other Go types (e.g., complex numbers, channels, and functions)
|
||||
// have no default representation and result in a [SemanticError].
|
||||
//
|
||||
// In general, unmarshaling follows merge semantics (similar to RFC 7396)
|
||||
// where the decoded Go value replaces the destination value
|
||||
// for any JSON kind other than an object.
|
||||
// For JSON objects, the input object is merged into the destination value
|
||||
// where matching object members recursively apply merge semantics.
|
||||
func Unmarshal(in []byte, out any, opts ...Options) (err error) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
dec := export.GetBufferedDecoder(in, opts...)
|
||||
defer export.PutBufferedDecoder(dec)
|
||||
xd := export.Decoder(dec)
|
||||
err = unmarshalDecode(dec, out, &xd.Struct, true)
|
||||
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
|
||||
// provided unmarshal and decode options (while ignoring marshal or encode options).
|
||||
// The input must be a single JSON value with optional whitespace interspersed.
|
||||
// It consumes the entirety of [io.Reader] until [io.EOF] is encountered,
|
||||
// without reporting an error for EOF. The output must be a non-nil pointer.
|
||||
// See [Unmarshal] for details about the conversion of JSON into a Go value.
|
||||
func UnmarshalRead(in io.Reader, out any, opts ...Options) (err error) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
dec := export.GetStreamingDecoder(in, opts...)
|
||||
defer export.PutStreamingDecoder(dec)
|
||||
xd := export.Decoder(dec)
|
||||
err = unmarshalDecode(dec, out, &xd.Struct, true)
|
||||
if err != nil && xd.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return internal.TransformUnmarshalError(out, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalDecode deserializes a Go value from a [jsontext.Decoder] according to
|
||||
// the provided unmarshal or decode options (while ignoring marshal or encode options).
|
||||
// The options provided take precedence over options already applied on
|
||||
// the [jsontext.Decoder] and only apply for the duration of the unmarshal call.
|
||||
//
|
||||
// The input may be a stream of zero or more JSON values,
|
||||
// where this only unmarshals the next JSON value in the stream.
|
||||
// If there are no more top-level JSON values, it reports [io.EOF].
|
||||
// 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) {
|
||||
opts = mayAppendSupportFormatTag(opts)
|
||||
xd := export.Decoder(in)
|
||||
if len(opts) > 0 {
|
||||
optsOriginal := xd.Struct
|
||||
defer func() { xd.Struct = optsOriginal }()
|
||||
xd.Struct.Join(opts...)
|
||||
if xd.Tokens.Last.NeedObjectName() {
|
||||
if optsOriginal.Flags.Get(jsonflags.AllowDuplicateNames) != xd.Struct.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
return newUnmarshalErrorBefore(in, reflect.TypeOf(out), errChangingDuplicateNames)
|
||||
}
|
||||
if optsOriginal.Flags.Get(jsonflags.AllowInvalidUTF8) != xd.Struct.Flags.Get(jsonflags.AllowInvalidUTF8) {
|
||||
return newUnmarshalErrorBefore(in, reflect.TypeOf(out), errChangingInvalidUTF8)
|
||||
}
|
||||
}
|
||||
}
|
||||
err = unmarshalDecode(in, out, &xd.Struct, false)
|
||||
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, last bool) (err error) {
|
||||
v := reflect.ValueOf(out)
|
||||
if v.Kind() != reflect.Pointer || v.IsNil() {
|
||||
return &SemanticError{action: "unmarshal", GoType: reflect.TypeOf(out), Err: internal.ErrNonNilReference}
|
||||
}
|
||||
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(last); err != nil {
|
||||
if err == io.EOF && last {
|
||||
offset := in.InputOffset() + int64(len(in.UnreadBuffer()))
|
||||
return &jsontext.SyntacticError{ByteOffset: offset, Err: io.ErrUnexpectedEOF}
|
||||
}
|
||||
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 {
|
||||
if !uo.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
export.Decoder(in).Tokens.InvalidateDisabledNamespaces()
|
||||
}
|
||||
if err == io.EOF && last {
|
||||
offset := in.InputOffset() + int64(len(in.UnreadBuffer()))
|
||||
return &jsontext.SyntacticError{ByteOffset: offset, Err: io.ErrUnexpectedEOF}
|
||||
}
|
||||
return err
|
||||
}
|
||||
if last {
|
||||
return export.Decoder(in).CheckEOF()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addressableValue is a reflect.Value that is guaranteed to be addressable
|
||||
// such that calling the Addr and Set methods do not panic.
|
||||
//
|
||||
// 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
|
||||
|
||||
// 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(), 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 be identical to or at least
|
||||
// a strict super-set of the options in Encoder.Struct or Decoder.Struct.
|
||||
// It is identical for Marshal, Unmarshal, MarshalWrite, and UnmarshalRead.
|
||||
// It is a super-set for MarshalEncode and UnmarshalDecode.
|
||||
type (
|
||||
marshaler = func(*jsontext.Encoder, addressableValue, *jsonopts.Struct) error
|
||||
unmarshaler = func(*jsontext.Decoder, addressableValue, *jsonopts.Struct) error
|
||||
)
|
||||
|
||||
type arshaler struct {
|
||||
marshal marshaler
|
||||
unmarshal unmarshaler
|
||||
nonDefault bool
|
||||
}
|
||||
|
||||
var lookupArshalerCache sync.Map // map[reflect.Type]*arshaler
|
||||
|
||||
func lookupArshaler(t reflect.Type) *arshaler {
|
||||
if v, ok := lookupArshalerCache.Load(t); ok {
|
||||
return v.(*arshaler)
|
||||
}
|
||||
|
||||
fncs := makeDefaultArshaler(t)
|
||||
fncs = makeMethodArshaler(fncs, t)
|
||||
fncs = makeTimeArshaler(fncs, t)
|
||||
|
||||
// Use the last stored so that duplicate arshalers can be garbage collected.
|
||||
v, _ := lookupArshalerCache.LoadOrStore(t, fncs)
|
||||
return v.(*arshaler)
|
||||
}
|
||||
|
||||
var stringsPools = &sync.Pool{New: func() any { return new(stringSlice) }}
|
||||
|
||||
type stringSlice []string
|
||||
|
||||
// getStrings returns a non-nil pointer to a slice with length n.
|
||||
func getStrings(n int) *stringSlice {
|
||||
s := stringsPools.Get().(*stringSlice)
|
||||
if cap(*s) < n {
|
||||
*s = make([]string, n)
|
||||
}
|
||||
*s = (*s)[:n]
|
||||
return s
|
||||
}
|
||||
|
||||
func putStrings(s *stringSlice) {
|
||||
if cap(*s) > 1<<10 {
|
||||
*s = nil // avoid pinning arbitrarily large amounts of memory
|
||||
}
|
||||
clear(*s) // avoid pinning a reference to each string
|
||||
stringsPools.Put(s)
|
||||
}
|
||||
289
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_any.go
generated
vendored
Normal file
289
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_any.go
generated
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"math"
|
||||
"reflect"
|
||||
"slices"
|
||||
"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"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
)
|
||||
|
||||
// This file contains an optimized marshal and unmarshal implementation
|
||||
// for the any type. This type is often used when the Go program has
|
||||
// no knowledge of the JSON schema. This is a common enough occurrence
|
||||
// to justify the complexity of adding logic for this.
|
||||
|
||||
// marshalValueAny marshals a Go any as a JSON value.
|
||||
// This assumes that there are no special formatting directives
|
||||
// for any possible nested value.
|
||||
func marshalValueAny(enc *jsontext.Encoder, val any, mo *jsonopts.Struct) error {
|
||||
switch val := val.(type) {
|
||||
case nil:
|
||||
return enc.WriteToken(jsontext.Null)
|
||||
case bool:
|
||||
return enc.WriteToken(jsontext.Bool(val))
|
||||
case string:
|
||||
return enc.WriteToken(jsontext.String(val))
|
||||
case float64:
|
||||
if math.IsNaN(val) || math.IsInf(val, 0) {
|
||||
break // use default logic below
|
||||
}
|
||||
return enc.WriteToken(jsontext.Float(val))
|
||||
case map[string]any:
|
||||
return marshalObjectAny(enc, val, mo)
|
||||
case []any:
|
||||
return marshalArrayAny(enc, val, mo)
|
||||
}
|
||||
|
||||
v := newAddressableValue(reflect.TypeOf(val))
|
||||
v.Set(reflect.ValueOf(val))
|
||||
marshal := lookupArshaler(v.Type()).marshal
|
||||
if mo.Marshalers != nil {
|
||||
marshal, _ = mo.Marshalers.(*Marshalers).lookup(marshal, v.Type())
|
||||
}
|
||||
return marshal(enc, v, mo)
|
||||
}
|
||||
|
||||
// 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 '{':
|
||||
return unmarshalObjectAny(dec, uo)
|
||||
case '[':
|
||||
return unmarshalArrayAny(dec, uo)
|
||||
default:
|
||||
xd := export.Decoder(dec)
|
||||
var flags jsonwire.ValueFlags
|
||||
val, err := xd.ReadValue(&flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch val.Kind() {
|
||||
case 'n':
|
||||
return nil, nil
|
||||
case 'f':
|
||||
return false, nil
|
||||
case 't':
|
||||
return true, nil
|
||||
case '"':
|
||||
val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
|
||||
if xd.StringCache == nil {
|
||||
xd.StringCache = new(stringCache)
|
||||
}
|
||||
return makeString(xd.StringCache, val), nil
|
||||
case '0':
|
||||
if uo.Flags.Get(jsonflags.UnmarshalAnyWithRawNumber) {
|
||||
return internal.RawNumberOf(val), nil
|
||||
}
|
||||
fv, err := strconv.ParseFloat(string(val), 64)
|
||||
if err != nil {
|
||||
return fv, newUnmarshalErrorAfterWithValue(dec, float64Type, errors.Unwrap(err))
|
||||
}
|
||||
return fv, nil
|
||||
default:
|
||||
panic("BUG: invalid kind: " + k.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 newMarshalErrorBefore(enc, mapStringAnyType, err)
|
||||
}
|
||||
defer leavePointer(&xe.SeenPointers, v)
|
||||
}
|
||||
|
||||
// Handle empty maps.
|
||||
if len(obj) == 0 {
|
||||
if mo.Flags.Get(jsonflags.FormatNilMapAsNull) && obj == nil {
|
||||
return enc.WriteToken(jsontext.Null)
|
||||
}
|
||||
// Optimize for marshaling an empty map without any preceding whitespace.
|
||||
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() {
|
||||
return xe.Flush()
|
||||
}
|
||||
return 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 !mo.Flags.Get(jsonflags.AllowInvalidUTF8) {
|
||||
xe.Tokens.Last.DisableNamespace()
|
||||
}
|
||||
if !mo.Flags.Get(jsonflags.Deterministic) || len(obj) <= 1 {
|
||||
for name, val := range obj {
|
||||
if err := enc.WriteToken(jsontext.String(name)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := marshalValueAny(enc, val, mo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
names := getStrings(len(obj))
|
||||
var i int
|
||||
for name := range obj {
|
||||
(*names)[i] = name
|
||||
i++
|
||||
}
|
||||
slices.Sort(*names)
|
||||
for _, name := range *names {
|
||||
if err := enc.WriteToken(jsontext.String(name)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := marshalValueAny(enc, obj[name], mo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
putStrings(names)
|
||||
}
|
||||
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) {
|
||||
switch tok, err := dec.ReadToken(); {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case tok.Kind() != '{':
|
||||
panic("BUG: invalid kind: " + tok.Kind().String())
|
||||
}
|
||||
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
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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 newMarshalErrorBefore(enc, sliceAnyType, err)
|
||||
}
|
||||
defer leavePointer(&xe.SeenPointers, v)
|
||||
}
|
||||
|
||||
// Handle empty slices.
|
||||
if len(arr) == 0 {
|
||||
if mo.Flags.Get(jsonflags.FormatNilSliceAsNull) && arr == nil {
|
||||
return enc.WriteToken(jsontext.Null)
|
||||
}
|
||||
// Optimize for marshaling an empty slice without any preceding whitespace.
|
||||
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() {
|
||||
return xe.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := enc.WriteToken(jsontext.BeginArray); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, val := range arr {
|
||||
if err := marshalValueAny(enc, val, mo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
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) {
|
||||
switch tok, err := dec.ReadToken(); {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case tok.Kind() != '[':
|
||||
panic("BUG: invalid kind: " + tok.Kind().String())
|
||||
}
|
||||
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, errUnmarshal
|
||||
}
|
||||
2043
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_default.go
generated
vendored
Normal file
2043
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_default.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
230
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_embedded.go
generated
vendored
Normal file
230
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_embedded.go
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"slices"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// This package supports "embedding" a Go struct field, where the contents
|
||||
// of the serialized field (which must be a JSON object) are treated as if
|
||||
// they are part of the parent Go struct (which represents a JSON object).
|
||||
//
|
||||
// Generally, embedded fields are of a Go struct type, where the fields of the
|
||||
// nested struct are virtually hoisted up to the parent struct using rules
|
||||
// similar to how Go embedding works (but operating within the JSON namespace).
|
||||
//
|
||||
// However, embedded fields may also be of a Go map type with a string key or
|
||||
// a jsontext.Value. Such embedded fields are called "fallback" fields since they
|
||||
// represent any arbitrary JSON object member. Explicitly named fields take
|
||||
// precedence over the embedded fallback. Only one embedded fallback is allowed.
|
||||
|
||||
var errRawEmbedNotObject = errors.New("embedded raw value must be a JSON object")
|
||||
|
||||
var jsontextValueType = reflect.TypeFor[jsontext.Value]()
|
||||
|
||||
// marshalEmbeddedFallbackAll marshals all the members in an embedded fallback.
|
||||
func marshalEmbeddedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct, f *structField, insertUnquotedName func([]byte) bool) error {
|
||||
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 embedded field
|
||||
}
|
||||
}
|
||||
v = v.indirect(false)
|
||||
if !v.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if v.Type() == jsontextValueType {
|
||||
b, _ := reflect.TypeAssert[jsontext.Value](v.Value)
|
||||
if len(b) == 0 { // TODO: Should this be nil? What if it were all whitespace?
|
||||
return nil
|
||||
}
|
||||
|
||||
dec := export.GetBufferedDecoder(b)
|
||||
defer export.PutBufferedDecoder(dec)
|
||||
xd := export.Decoder(dec)
|
||||
xd.Flags.Set(jsonflags.AllowDuplicateNames | jsonflags.AllowInvalidUTF8 | 1)
|
||||
|
||||
tok, err := dec.ReadToken()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return newMarshalErrorBefore(enc, v.Type(), err)
|
||||
}
|
||||
if tok.Kind() != '{' {
|
||||
return newMarshalErrorBefore(enc, v.Type(), errRawEmbedNotObject)
|
||||
}
|
||||
for dec.PeekKind() != '}' {
|
||||
// Parse the JSON object name.
|
||||
var flags jsonwire.ValueFlags
|
||||
val, err := xd.ReadValue(&flags)
|
||||
if err != nil {
|
||||
return newMarshalErrorBefore(enc, v.Type(), err)
|
||||
}
|
||||
if insertUnquotedName != nil {
|
||||
name := jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
|
||||
if !insertUnquotedName(name) {
|
||||
return newDuplicateNameError(enc.StackPointer().Parent(), val, enc.OutputOffset())
|
||||
}
|
||||
}
|
||||
if err := enc.WriteValue(val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse the JSON object value.
|
||||
val, err = xd.ReadValue(&flags)
|
||||
if err != nil {
|
||||
return newMarshalErrorBefore(enc, v.Type(), err)
|
||||
}
|
||||
if err := enc.WriteValue(val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := dec.ReadToken(); err != nil {
|
||||
return newMarshalErrorBefore(enc, v.Type(), err)
|
||||
}
|
||||
if err := xd.CheckEOF(); err != nil {
|
||||
return newMarshalErrorBefore(enc, v.Type(), err)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
m := v // must be a map[~string]V
|
||||
n := m.Len()
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
mk := newAddressableValue(m.Type().Key())
|
||||
mv := newAddressableValue(m.Type().Elem())
|
||||
marshalKey := func(mk addressableValue) error {
|
||||
b, err := jsonwire.AppendQuote(enc.AvailableBuffer(), []byte(mk.String()), &mo.Flags)
|
||||
if err != nil {
|
||||
return newMarshalErrorBefore(enc, m.Type().Key(), err)
|
||||
}
|
||||
if insertUnquotedName != nil {
|
||||
isVerbatim := bytes.IndexByte(b, '\\') < 0
|
||||
name := jsonwire.UnquoteMayCopy(b, isVerbatim)
|
||||
if !insertUnquotedName(name) {
|
||||
return newDuplicateNameError(enc.StackPointer().Parent(), b, enc.OutputOffset())
|
||||
}
|
||||
}
|
||||
return enc.WriteValue(b)
|
||||
}
|
||||
marshalVal := f.fncs.marshal
|
||||
if mo.Marshalers != nil {
|
||||
marshalVal, _ = mo.Marshalers.(*Marshalers).lookup(marshalVal, mv.Type())
|
||||
}
|
||||
if !mo.Flags.Get(jsonflags.Deterministic) || n <= 1 {
|
||||
for iter := m.MapRange(); iter.Next(); {
|
||||
mk.SetIterKey(iter)
|
||||
if err := marshalKey(mk); err != nil {
|
||||
return err
|
||||
}
|
||||
mv.Set(iter.Value())
|
||||
if err := marshalVal(enc, mv, mo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
names := getStrings(n)
|
||||
for i, iter := 0, m.Value.MapRange(); i < n && iter.Next(); i++ {
|
||||
mk.SetIterKey(iter)
|
||||
(*names)[i] = mk.String()
|
||||
}
|
||||
slices.Sort(*names)
|
||||
for _, name := range *names {
|
||||
mk.SetString(name)
|
||||
if err := marshalKey(mk); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(https://go.dev/issue/57061): Use mv.SetMapIndexOf.
|
||||
mv.Set(m.MapIndex(mk.Value))
|
||||
if err := marshalVal(enc, mv, mo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
putStrings(names)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// unmarshalEmbeddedFallbackNext unmarshals only the next member in an embedded fallback.
|
||||
func unmarshalEmbeddedFallbackNext(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct, f *structField, quotedName, unquotedName []byte) error {
|
||||
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)
|
||||
|
||||
if v.Type() == jsontextValueType {
|
||||
b, _ := reflect.TypeAssert[*jsontext.Value](v.Addr())
|
||||
if len(*b) == 0 { // TODO: Should this be nil? What if it were all whitespace?
|
||||
*b = append(*b, '{')
|
||||
} else {
|
||||
*b = jsonwire.TrimSuffixWhitespace(*b)
|
||||
if jsonwire.HasSuffixByte(*b, '}') {
|
||||
// TODO: When merging into an object for the first time,
|
||||
// should we verify that it is valid?
|
||||
*b = jsonwire.TrimSuffixByte(*b, '}')
|
||||
*b = jsonwire.TrimSuffixWhitespace(*b)
|
||||
if !jsonwire.HasSuffixByte(*b, ',') && !jsonwire.HasSuffixByte(*b, '{') {
|
||||
*b = append(*b, ',')
|
||||
}
|
||||
} else {
|
||||
return newUnmarshalErrorAfterWithSkipping(dec, v.Type(), errRawEmbedNotObject)
|
||||
}
|
||||
}
|
||||
*b = append(*b, quotedName...)
|
||||
*b = append(*b, ':')
|
||||
val, err := dec.ReadValue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*b = append(*b, val...)
|
||||
*b = append(*b, '}')
|
||||
return nil
|
||||
} else {
|
||||
name := string(unquotedName) // TODO: Intern this?
|
||||
|
||||
m := v // must be a map[~string]V
|
||||
if m.IsNil() {
|
||||
m.Set(reflect.MakeMap(m.Type()))
|
||||
}
|
||||
mk := reflect.ValueOf(name)
|
||||
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)
|
||||
}
|
||||
|
||||
unmarshal := f.fncs.unmarshal
|
||||
if uo.Unmarshalers != nil {
|
||||
unmarshal, _ = uo.Unmarshalers.(*Unmarshalers).lookup(unmarshal, mv.Type())
|
||||
}
|
||||
err := unmarshal(dec, mv, uo)
|
||||
m.SetMapIndex(mk, mv.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
428
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_funcs.go
generated
vendored
Normal file
428
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_funcs.go
generated
vendored
Normal file
@@ -0,0 +1,428 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"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"
|
||||
)
|
||||
|
||||
var errUnsupportedMutation = errors.New("unsupported calls must not read or write any tokens")
|
||||
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].
|
||||
// A nil *Marshalers is equivalent to an empty list.
|
||||
// There are no exported fields or methods on Marshalers.
|
||||
type Marshalers = typedMarshalers
|
||||
|
||||
// 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 [errors.ErrUnsupported],
|
||||
// then the next applicable function is called,
|
||||
// otherwise the default marshaling behavior is used.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
// Unmarshalers is a list of functions that may override the unmarshal behavior
|
||||
// of specific types. Populate [WithUnmarshalers] to use it with
|
||||
// [Unmarshal], [UnmarshalRead], or [UnmarshalDecode].
|
||||
// A nil *Unmarshalers is equivalent to an empty list.
|
||||
// There are no exported fields or methods on Unmarshalers.
|
||||
type Unmarshalers = typedUnmarshalers
|
||||
|
||||
// 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 [errors.ErrUnsupported],
|
||||
// then the next applicable function is called,
|
||||
// otherwise the default unmarshaling behavior is used.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
type typedMarshalers = typedArshalers[jsontext.Encoder]
|
||||
type typedUnmarshalers = typedArshalers[jsontext.Decoder]
|
||||
type typedArshalers[Coder any] struct {
|
||||
nonComparable
|
||||
|
||||
fncVals []typedArshaler[Coder]
|
||||
fncCache sync.Map // map[reflect.Type]arshaler
|
||||
|
||||
// fromAny reports whether any of Go types used to represent arbitrary JSON
|
||||
// (i.e., any, bool, string, float64, map[string]any, or []any) matches
|
||||
// any of the provided type-specific arshalers.
|
||||
//
|
||||
// This bit of information is needed in arshal_default.go to determine
|
||||
// whether to use the specialized logic in arshal_any.go to handle
|
||||
// the any interface type. The logic in arshal_any.go does not support
|
||||
// type-specific arshal functions, so we must avoid using that logic
|
||||
// if this is true.
|
||||
fromAny bool
|
||||
}
|
||||
type typedMarshaler = typedArshaler[jsontext.Encoder]
|
||||
type typedUnmarshaler = typedArshaler[jsontext.Decoder]
|
||||
type typedArshaler[Coder any] struct {
|
||||
typ reflect.Type
|
||||
fnc func(*Coder, addressableValue, *jsonopts.Struct) error
|
||||
maySkip bool
|
||||
}
|
||||
|
||||
func newMarshalers(ms ...*Marshalers) *Marshalers { return newTypedArshalers(ms...) }
|
||||
func newUnmarshalers(us ...*Unmarshalers) *Unmarshalers { return newTypedArshalers(us...) }
|
||||
func newTypedArshalers[Coder any](as ...*typedArshalers[Coder]) *typedArshalers[Coder] {
|
||||
var a typedArshalers[Coder]
|
||||
for _, a2 := range as {
|
||||
if a2 != nil {
|
||||
a.fncVals = append(a.fncVals, a2.fncVals...)
|
||||
a.fromAny = a.fromAny || a2.fromAny
|
||||
}
|
||||
}
|
||||
if len(a.fncVals) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a *typedArshalers[Coder]) lookup(fnc func(*Coder, addressableValue, *jsonopts.Struct) error, t reflect.Type) (func(*Coder, addressableValue, *jsonopts.Struct) error, bool) {
|
||||
if a == nil {
|
||||
return fnc, false
|
||||
}
|
||||
if v, ok := a.fncCache.Load(t); ok {
|
||||
if v == nil {
|
||||
return fnc, false
|
||||
}
|
||||
return v.(func(*Coder, addressableValue, *jsonopts.Struct) error), true
|
||||
}
|
||||
|
||||
// Collect a list of arshalers that can be called for this type.
|
||||
// This list may be longer than 1 since some arshalers can be skipped.
|
||||
var fncs []func(*Coder, addressableValue, *jsonopts.Struct) error
|
||||
for _, fncVal := range a.fncVals {
|
||||
if !castableTo(t, fncVal.typ) {
|
||||
continue
|
||||
}
|
||||
fncs = append(fncs, fncVal.fnc)
|
||||
if !fncVal.maySkip {
|
||||
break // subsequent arshalers will never be called
|
||||
}
|
||||
}
|
||||
|
||||
if len(fncs) == 0 {
|
||||
a.fncCache.Store(t, nil) // nil to indicate that no funcs found
|
||||
return fnc, false
|
||||
}
|
||||
|
||||
// Construct an arshaler that may call every applicable arshaler.
|
||||
fncDefault := fnc
|
||||
fnc = func(c *Coder, v addressableValue, o *jsonopts.Struct) error {
|
||||
for _, fnc := range fncs {
|
||||
if err := fnc(c, v, o); !errors.Is(err, errors.ErrUnsupported) {
|
||||
return err // may be nil or non-nil
|
||||
}
|
||||
}
|
||||
return fncDefault(c, v, o)
|
||||
}
|
||||
|
||||
// Use the first stored so duplicate work can be garbage collected.
|
||||
v, _ := a.fncCache.LoadOrStore(t, fnc)
|
||||
return v.(func(*Coder, addressableValue, *jsonopts.Struct) error), true
|
||||
}
|
||||
|
||||
// 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
|
||||
// if T is an interface or pointer type.
|
||||
//
|
||||
// The function must marshal exactly one JSON value.
|
||||
// The value of T must not be retained outside the function call.
|
||||
// It is recommended that fn return a []byte buffer that is safe
|
||||
// for the caller to retain and potentially mutate.
|
||||
// It may not return [errors.ErrUnsupported].
|
||||
func MarshalFunc[T any](fn func(T) ([]byte, error)) *Marshalers {
|
||||
t := reflect.TypeFor[T]()
|
||||
assertCastableTo(t, true)
|
||||
typFnc := typedMarshaler{
|
||||
typ: t,
|
||||
fnc: func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
||||
v, _ := reflect.TypeAssert[T](va.castTo(t))
|
||||
val, err := fn(v)
|
||||
if err != nil {
|
||||
err = wrapErrUnsupported(err, "marshal function of type func(T) ([]byte, error)")
|
||||
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 {
|
||||
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
|
||||
},
|
||||
}
|
||||
return &Marshalers{fncVals: []typedMarshaler{typFnc}, fromAny: castableToFromAny(t)}
|
||||
}
|
||||
|
||||
// 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
|
||||
// if T is an interface or pointer type.
|
||||
//
|
||||
// The function must marshal exactly one JSON value by calling write methods
|
||||
// on the provided encoder. It may return [errors.ErrUnsupported] such that marshaling can
|
||||
// move on to the next marshal function. However, no mutable method calls may
|
||||
// be called on the encoder if [errors.ErrUnsupported] is returned.
|
||||
// The pointer to [jsontext.Encoder] and the value of T
|
||||
// must not be retained outside the function call.
|
||||
func MarshalToFunc[T any](fn func(*jsontext.Encoder, T) error) *Marshalers {
|
||||
t := reflect.TypeFor[T]()
|
||||
assertCastableTo(t, true)
|
||||
typFnc := typedMarshaler{
|
||||
typ: t,
|
||||
fnc: 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)
|
||||
v, _ := reflect.TypeAssert[T](va.castTo(t))
|
||||
err := fn(enc, v)
|
||||
xe.Flags.Set(jsonflags.WithinArshalCall | 0)
|
||||
currDepth, currLength := xe.Tokens.DepthLength()
|
||||
if err == nil && (prevDepth != currDepth || prevLength+1 != currLength) {
|
||||
err = errNonSingularValue
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, errors.ErrUnsupported) {
|
||||
if prevDepth == currDepth && prevLength == currLength {
|
||||
return err // forward [errors.ErrUnsupported]
|
||||
}
|
||||
err = errUnsupportedMutation
|
||||
}
|
||||
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
|
||||
},
|
||||
maySkip: true,
|
||||
}
|
||||
return &Marshalers{fncVals: []typedMarshaler{typFnc}, fromAny: castableToFromAny(t)}
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// The function must unmarshal exactly one JSON value.
|
||||
// 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 [errors.ErrUnsupported].
|
||||
func UnmarshalFunc[T any](fn func([]byte, T) error) *Unmarshalers {
|
||||
t := reflect.TypeFor[T]()
|
||||
assertCastableTo(t, false)
|
||||
typFnc := typedUnmarshaler{
|
||||
typ: t,
|
||||
fnc: 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
|
||||
}
|
||||
v, _ := reflect.TypeAssert[T](va.castTo(t))
|
||||
err = fn(val, v)
|
||||
if err != nil {
|
||||
err = wrapErrUnsupported(err, "unmarshal function of type func([]byte, T) error")
|
||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return err // unlike marshal, never wrapped
|
||||
}
|
||||
err = newUnmarshalErrorAfter(dec, t, err)
|
||||
return collapseSemanticErrors(err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return &Unmarshalers{fncVals: []typedUnmarshaler{typFnc}, fromAny: castableToFromAny(t)}
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// The function must unmarshal exactly one JSON value by calling read methods
|
||||
// on the provided decoder. It may return [errors.ErrUnsupported] such that unmarshaling can
|
||||
// move on to the next unmarshal function. However, no mutable method calls may
|
||||
// be called on the decoder if [errors.ErrUnsupported] is returned.
|
||||
// The pointer to [jsontext.Decoder] and the value of T
|
||||
// must not be retained outside the function call.
|
||||
func UnmarshalFromFunc[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers {
|
||||
t := reflect.TypeFor[T]()
|
||||
assertCastableTo(t, false)
|
||||
typFnc := typedUnmarshaler{
|
||||
typ: t,
|
||||
fnc: func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
|
||||
xd := export.Decoder(dec)
|
||||
prevDepth, prevLength := xd.Tokens.DepthLength()
|
||||
if prevDepth == 1 && xd.AtEOF() {
|
||||
return io.EOF // check EOF early to avoid fn reporting an EOF
|
||||
}
|
||||
xd.Flags.Set(jsonflags.WithinArshalCall | 1)
|
||||
v, _ := reflect.TypeAssert[T](va.castTo(t))
|
||||
err := fn(dec, v)
|
||||
xd.Flags.Set(jsonflags.WithinArshalCall | 0)
|
||||
currDepth, currLength := xd.Tokens.DepthLength()
|
||||
if err == nil && (prevDepth != currDepth || prevLength+1 != currLength) {
|
||||
err = errNonSingularValue
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, errors.ErrUnsupported) {
|
||||
if prevDepth == currDepth && prevLength == currLength {
|
||||
return err // forward [errors.ErrUnsupported]
|
||||
}
|
||||
err = errUnsupportedMutation
|
||||
}
|
||||
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
|
||||
},
|
||||
maySkip: true,
|
||||
}
|
||||
return &Unmarshalers{fncVals: []typedUnmarshaler{typFnc}, fromAny: castableToFromAny(t)}
|
||||
}
|
||||
|
||||
// assertCastableTo asserts that "to" is a valid type to be casted to.
|
||||
// These are the Go types that type-specific arshalers may operate upon.
|
||||
//
|
||||
// Let AllTypes be the universal set of all possible Go types.
|
||||
// This function generally asserts that:
|
||||
//
|
||||
// len([from for from in AllTypes if castableTo(from, to)]) > 0
|
||||
//
|
||||
// otherwise it panics.
|
||||
//
|
||||
// As a special-case if marshal is false, then we forbid any non-pointer or
|
||||
// non-interface type since it is almost always a bug trying to unmarshal
|
||||
// into something where the end-user caller did not pass in an addressable value
|
||||
// since they will not observe the mutations.
|
||||
func assertCastableTo(to reflect.Type, marshal bool) {
|
||||
switch to.Kind() {
|
||||
case reflect.Interface:
|
||||
return
|
||||
case reflect.Pointer:
|
||||
// Only allow unnamed pointers to be consistent with the fact that
|
||||
// taking the address of a value produces an unnamed pointer type.
|
||||
if to.Name() == "" {
|
||||
return
|
||||
}
|
||||
default:
|
||||
// Technically, non-pointer types are permissible for unmarshal.
|
||||
// However, they are often a bug since the receiver would be immutable.
|
||||
// Thus, only allow them for marshaling.
|
||||
if marshal {
|
||||
return
|
||||
}
|
||||
}
|
||||
if marshal {
|
||||
panic(fmt.Sprintf("input type %v must be an interface type, an unnamed pointer type, or a non-pointer type", to))
|
||||
} else {
|
||||
panic(fmt.Sprintf("input type %v must be an interface type or an unnamed pointer type", to))
|
||||
}
|
||||
}
|
||||
|
||||
// castableTo checks whether values of type "from" can be casted to type "to".
|
||||
// Nil pointer or interface "from" values are never considered castable.
|
||||
//
|
||||
// This function must be kept in sync with addressableValue.castTo.
|
||||
func castableTo(from, to reflect.Type) bool {
|
||||
switch to.Kind() {
|
||||
case reflect.Interface:
|
||||
// TODO: This breaks when ordinary interfaces can have type sets
|
||||
// since interfaces now exist where only the value form of a type (T)
|
||||
// implements the interface, but not the pointer variant (*T).
|
||||
// See https://go.dev/issue/45346.
|
||||
return reflect.PointerTo(from).Implements(to)
|
||||
case reflect.Pointer:
|
||||
// Common case for unmarshaling.
|
||||
// From must be a concrete or interface type.
|
||||
return reflect.PointerTo(from) == to
|
||||
default:
|
||||
// Common case for marshaling.
|
||||
// From must be a concrete type.
|
||||
return from == to
|
||||
}
|
||||
}
|
||||
|
||||
// castTo casts va to the specified type.
|
||||
// If the type is an interface, then the underlying type will always
|
||||
// be a non-nil pointer to a concrete type.
|
||||
//
|
||||
// Requirement: castableTo(va.Type(), to) must hold.
|
||||
func (va addressableValue) castTo(to reflect.Type) reflect.Value {
|
||||
switch to.Kind() {
|
||||
case reflect.Interface:
|
||||
return va.Addr().Convert(to)
|
||||
case reflect.Pointer:
|
||||
return va.Addr()
|
||||
default:
|
||||
return va.Value
|
||||
}
|
||||
}
|
||||
|
||||
// castableToFromAny reports whether "to" can be casted to from any
|
||||
// of the dynamic types used to represent arbitrary JSON.
|
||||
func castableToFromAny(to reflect.Type) bool {
|
||||
for _, from := range []reflect.Type{anyType, boolType, stringType, float64Type, mapStringAnyType, sliceAnyType} {
|
||||
if castableTo(from, to) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
378
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_methods.go
generated
vendored
Normal file
378
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_methods.go
generated
vendored
Normal file
@@ -0,0 +1,378 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"io"
|
||||
"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 (
|
||||
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]()
|
||||
|
||||
allMarshalerTypes = []reflect.Type{jsonMarshalerToType, jsonMarshalerType, textAppenderType, textMarshalerType}
|
||||
allUnmarshalerTypes = []reflect.Type{jsonUnmarshalerFromType, jsonUnmarshalerType, textUnmarshalerType}
|
||||
allMethodTypes = append(allMarshalerTypes, allUnmarshalerTypes...)
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
// If the returned error is a [SemanticError], then unpopulated fields
|
||||
// of the error may be populated by [json] with additional context.
|
||||
// Errors of other types are wrapped within a [SemanticError].
|
||||
type Marshaler interface {
|
||||
MarshalJSON() ([]byte, error)
|
||||
}
|
||||
|
||||
// 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 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.
|
||||
// Alternatively, it may return [errors.ErrUnsupported] without mutating
|
||||
// the Encoder. The "json" package calling the method will
|
||||
// use the next available JSON representation for the receiver type.
|
||||
// Implementations must not retain the pointer to [jsontext.Encoder].
|
||||
//
|
||||
// If the returned error is a [SemanticError], then unpopulated fields
|
||||
// of the error may be populated by [json] with additional context.
|
||||
// Errors of other types are wrapped within a [SemanticError],
|
||||
// unless it is an IO error.
|
||||
//
|
||||
// The MarshalJSONTo method should not be directly called as it may
|
||||
// return sentinel errors that need special handling.
|
||||
// Users should instead call [MarshalEncode], which handles such cases.
|
||||
type MarshalerTo interface {
|
||||
MarshalJSONTo(*jsontext.Encoder) error
|
||||
}
|
||||
|
||||
// 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
|
||||
// if called from unmarshal functionality in this package.
|
||||
// UnmarshalJSON must copy the JSON data if it is retained after returning.
|
||||
// It is recommended that UnmarshalJSON implement merge semantics when
|
||||
// unmarshaling into a pre-populated value.
|
||||
//
|
||||
// Implementations must not retain or mutate the input []byte.
|
||||
//
|
||||
// If the returned error is a [SemanticError], then unpopulated fields
|
||||
// of the error may be populated by [json] with additional context.
|
||||
// Errors of other types are wrapped within a [SemanticError].
|
||||
type Unmarshaler interface {
|
||||
UnmarshalJSON([]byte) error
|
||||
}
|
||||
|
||||
// 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 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 UnmarshalJSONFrom implement merge semantics when
|
||||
// unmarshaling into a pre-populated value.
|
||||
// Alternatively, it may return [errors.ErrUnsupported] without mutating
|
||||
// the Decoder. The "json" package calling the method will
|
||||
// use the next available JSON representation for the receiver type.
|
||||
// Implementations must not retain the pointer to [jsontext.Decoder].
|
||||
//
|
||||
// If the returned error is a [SemanticError], then unpopulated fields
|
||||
// of the error may be populated by [json] with additional context.
|
||||
// Errors of other types are wrapped within a [SemanticError],
|
||||
// unless it is a [jsontext.SyntacticError] or an IO error.
|
||||
//
|
||||
// The UnmarshalJSONFrom method should not be directly called as it may
|
||||
// return sentinel errors that need special handling.
|
||||
// Users should instead call [UnmarshalDecode], which handles such cases.
|
||||
type UnmarshalerFrom interface {
|
||||
UnmarshalJSONFrom(*jsontext.Decoder) error
|
||||
}
|
||||
|
||||
func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
// Avoid injecting method arshaler on the pointer or interface version
|
||||
// to avoid ever calling the method on a nil pointer or interface receiver.
|
||||
// Let it be injected on the value receiver (which is always addressable).
|
||||
if t.Kind() == reflect.Pointer || t.Kind() == reflect.Interface {
|
||||
return fncs
|
||||
}
|
||||
|
||||
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 {
|
||||
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
||||
(needAddr && va.forcedAddr) {
|
||||
return prevMarshal(enc, va, mo)
|
||||
}
|
||||
marshaler, _ := reflect.TypeAssert[encoding.TextMarshaler](va.Addr())
|
||||
if err := export.Encoder(enc).AppendRaw('"', false, func(b []byte) ([]byte, error) {
|
||||
b2, err := marshaler.MarshalText()
|
||||
return append(b, b2...), err
|
||||
}); err != nil {
|
||||
err = wrapErrUnsupported(err, "MarshalText method")
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if needAddr, ok := implements(t, textAppenderType); ok {
|
||||
fncs.nonDefault = true
|
||||
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, _ := reflect.TypeAssert[encoding.TextAppender](va.Addr())
|
||||
if err := export.Encoder(enc).AppendRaw('"', false, appender.AppendText); err != nil {
|
||||
err = wrapErrUnsupported(err, "AppendText 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, _ := reflect.TypeAssert[Marshaler](va.Addr())
|
||||
val, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
err = wrapErrUnsupported(err, "MarshalJSON 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)
|
||||
marshaler, _ := reflect.TypeAssert[MarshalerTo](va.Addr())
|
||||
err := marshaler.MarshalJSONTo(enc)
|
||||
xe.Flags.Set(jsonflags.WithinArshalCall | 0)
|
||||
currDepth, currLength := xe.Tokens.DepthLength()
|
||||
if (prevDepth != currDepth || prevLength+1 != currLength) && err == nil {
|
||||
err = errNonSingularValue
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, errors.ErrUnsupported) {
|
||||
if prevDepth == currDepth && prevLength == currLength {
|
||||
return prevMarshal(enc, va, mo)
|
||||
}
|
||||
err = errUnsupportedMutation
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
var flags jsonwire.ValueFlags
|
||||
val, err := xd.ReadValue(&flags)
|
||||
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() != '"' {
|
||||
return newUnmarshalErrorAfter(dec, t, errNonStringValue)
|
||||
}
|
||||
s := jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
|
||||
unmarshaler, _ := reflect.TypeAssert[encoding.TextUnmarshaler](va.Addr())
|
||||
if err := unmarshaler.UnmarshalText(s); err != nil {
|
||||
err = wrapErrUnsupported(err, "UnmarshalText method")
|
||||
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, _ := reflect.TypeAssert[Unmarshaler](va.Addr())
|
||||
if err := unmarshaler.UnmarshalJSON(val); err != nil {
|
||||
err = wrapErrUnsupported(err, "UnmarshalJSON 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()
|
||||
if prevDepth == 1 && xd.AtEOF() {
|
||||
return io.EOF // check EOF early to avoid fn reporting an EOF
|
||||
}
|
||||
xd.Flags.Set(jsonflags.WithinArshalCall | 1)
|
||||
unmarshaler, _ := reflect.TypeAssert[UnmarshalerFrom](va.Addr())
|
||||
err := unmarshaler.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 {
|
||||
if errors.Is(err, errors.ErrUnsupported) {
|
||||
if prevDepth == currDepth && prevLength == currLength {
|
||||
return prevUnmarshal(dec, va, uo)
|
||||
}
|
||||
err = errUnsupportedMutation
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return fncs
|
||||
}
|
||||
|
||||
// implementsAny is like t.Implements(ifaceType) for a list of interfaces,
|
||||
// but checks whether either t or reflect.PointerTo(t) implements the interface.
|
||||
func implementsAny(t reflect.Type, ifaceTypes ...reflect.Type) bool {
|
||||
for _, ifaceType := range ifaceTypes {
|
||||
if _, ok := implements(t, ifaceType); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
795
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_time.go
generated
vendored
Normal file
795
tools/tsgo/vendor/github.com/go-json-experiment/json/arshal_time.go
generated
vendored
Normal file
@@ -0,0 +1,795 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/bits"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"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"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
)
|
||||
|
||||
var (
|
||||
timeDurationType = reflect.TypeFor[time.Duration]()
|
||||
timeTimeType = reflect.TypeFor[time.Time]()
|
||||
)
|
||||
|
||||
func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
||||
// 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.
|
||||
//
|
||||
// Injecting the arshaling functionality like this will not be identical
|
||||
// to actually declaring methods on the time types since embedding of the
|
||||
// time types will not be able to forward this functionality.
|
||||
switch t {
|
||||
case timeDurationType:
|
||||
fncs.nonDefault = true
|
||||
marshalNano := fncs.marshal
|
||||
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
||||
xe := export.Encoder(enc)
|
||||
var m durationArshaler
|
||||
if mo.Flags.Has(jsonflags.FormatTag) {
|
||||
if !m.initFormat(mo.Format) {
|
||||
return newInvalidFormatError(enc, t)
|
||||
}
|
||||
} else if mo.Flags.Get(jsonflags.FormatDurationAsNano) {
|
||||
return marshalNano(enc, va, mo)
|
||||
} else {
|
||||
// TODO(https://go.dev/issue/71631): Decide on default duration representation.
|
||||
var workaround string
|
||||
if mo.Flags.Get(jsonflags.FormatTagSupported) {
|
||||
workaround = "; specify an explicit format"
|
||||
}
|
||||
return newMarshalErrorBefore(enc, t, errors.New("no default representation"+workaround))
|
||||
}
|
||||
if mo.Flags.Get(jsonflags.StringTag) && !m.isNumeric() && !mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return newMarshalErrorBefore(enc, t, errInvalidStringTag)
|
||||
}
|
||||
|
||||
m.td, _ = reflect.TypeAssert[time.Duration](va.Value)
|
||||
k := stringOrNumberKind(!m.isNumeric() || xe.Tokens.Last.NeedObjectName() || mo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag))
|
||||
if err := xe.AppendRaw(k, true, m.appendMarshal); err != nil {
|
||||
if !isSyntacticError(err) && !export.IsIOError(err) {
|
||||
err = newMarshalErrorBefore(enc, t, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
unmarshalNano := fncs.unmarshal
|
||||
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
|
||||
xd := export.Decoder(dec)
|
||||
var u durationArshaler
|
||||
if uo.Flags.Has(jsonflags.FormatTag) {
|
||||
if !u.initFormat(uo.Format) {
|
||||
return newInvalidFormatError(dec, t)
|
||||
}
|
||||
} else if uo.Flags.Get(jsonflags.FormatDurationAsNano) {
|
||||
return unmarshalNano(dec, va, uo)
|
||||
} else {
|
||||
// TODO(https://go.dev/issue/71631): Decide on default duration representation.
|
||||
var workaround string
|
||||
if uo.Flags.Get(jsonflags.FormatTagSupported) {
|
||||
workaround = "; specify an explicit format"
|
||||
}
|
||||
return newUnmarshalErrorBeforeWithSkipping(dec, t, errors.New("no default representation"+workaround))
|
||||
}
|
||||
if uo.Flags.Get(jsonflags.StringTag) && !u.isNumeric() && !uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return newUnmarshalErrorBeforeWithSkipping(dec, t, errInvalidStringTag)
|
||||
}
|
||||
|
||||
stringify := !u.isNumeric() || xd.Tokens.Last.NeedObjectName() || uo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag)
|
||||
var flags jsonwire.ValueFlags
|
||||
td, _ := reflect.TypeAssert[*time.Duration](va.Addr())
|
||||
val, err := xd.ReadValue(&flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch k := val.Kind(); k {
|
||||
case 'n':
|
||||
if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
|
||||
*td = time.Duration(0)
|
||||
}
|
||||
return nil
|
||||
case '"':
|
||||
if !stringify {
|
||||
break
|
||||
}
|
||||
val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
|
||||
if err := u.unmarshal(val); err != nil {
|
||||
return newUnmarshalErrorAfter(dec, t, err)
|
||||
}
|
||||
*td = u.td
|
||||
return nil
|
||||
case '0':
|
||||
if stringify {
|
||||
break
|
||||
}
|
||||
if err := u.unmarshal(val); err != nil {
|
||||
return newUnmarshalErrorAfter(dec, t, err)
|
||||
}
|
||||
*td = u.td
|
||||
return nil
|
||||
}
|
||||
return newUnmarshalErrorAfter(dec, t, nil)
|
||||
}
|
||||
case timeTimeType:
|
||||
fncs.nonDefault = true
|
||||
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) (err error) {
|
||||
xe := export.Encoder(enc)
|
||||
var m timeArshaler
|
||||
if mo.Flags.Has(jsonflags.FormatTag) {
|
||||
if !m.initFormat(mo.Format) {
|
||||
return newInvalidFormatError(enc, t)
|
||||
}
|
||||
}
|
||||
if mo.Flags.Get(jsonflags.StringTag) && !m.isNumeric() && !mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return newMarshalErrorBefore(enc, t, errInvalidStringTag)
|
||||
}
|
||||
|
||||
m.tt, _ = reflect.TypeAssert[time.Time](va.Value)
|
||||
k := stringOrNumberKind(!m.isNumeric() || xe.Tokens.Last.NeedObjectName() || mo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag))
|
||||
if err := xe.AppendRaw(k, !m.hasCustomFormat(), m.appendMarshal); err != nil {
|
||||
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
|
||||
}
|
||||
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) (err error) {
|
||||
xd := export.Decoder(dec)
|
||||
var u timeArshaler
|
||||
if uo.Flags.Has(jsonflags.FormatTag) {
|
||||
if !u.initFormat(uo.Format) {
|
||||
return newInvalidFormatError(dec, t)
|
||||
}
|
||||
} else if uo.Flags.Get(jsonflags.ParseTimeWithLooseRFC3339) {
|
||||
u.looseRFC3339 = true
|
||||
}
|
||||
if uo.Flags.Get(jsonflags.StringTag) && !u.isNumeric() && !uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return newUnmarshalErrorBeforeWithSkipping(dec, t, errInvalidStringTag)
|
||||
}
|
||||
|
||||
stringify := !u.isNumeric() || xd.Tokens.Last.NeedObjectName() || uo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag)
|
||||
var flags jsonwire.ValueFlags
|
||||
tt, _ := reflect.TypeAssert[*time.Time](va.Addr())
|
||||
val, err := xd.ReadValue(&flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch k := val.Kind(); k {
|
||||
case 'n':
|
||||
if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
|
||||
*tt = time.Time{}
|
||||
}
|
||||
return nil
|
||||
case '"':
|
||||
if !stringify {
|
||||
break
|
||||
}
|
||||
val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
|
||||
if err := u.unmarshal(val); err != nil {
|
||||
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 stringify {
|
||||
break
|
||||
}
|
||||
if err := u.unmarshal(val); err != nil {
|
||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
return err // unlike marshal, never wrapped
|
||||
}
|
||||
return newUnmarshalErrorAfter(dec, t, err)
|
||||
}
|
||||
*tt = u.tt
|
||||
return nil
|
||||
}
|
||||
return newUnmarshalErrorAfter(dec, t, nil)
|
||||
}
|
||||
}
|
||||
return fncs
|
||||
}
|
||||
|
||||
type durationArshaler struct {
|
||||
td time.Duration
|
||||
|
||||
// base records the representation where:
|
||||
// - 0 uses time.Duration.String
|
||||
// - 1e0, 1e3, 1e6, or 1e9 use a decimal encoding of the duration as
|
||||
// nanoseconds, microseconds, milliseconds, or seconds.
|
||||
// - 8601 uses ISO 8601
|
||||
base uint64
|
||||
}
|
||||
|
||||
func (a *durationArshaler) initFormat(format string) (ok bool) {
|
||||
switch format {
|
||||
case "units":
|
||||
a.base = 0
|
||||
case "sec":
|
||||
a.base = 1e9
|
||||
case "milli":
|
||||
a.base = 1e6
|
||||
case "micro":
|
||||
a.base = 1e3
|
||||
case "nano":
|
||||
a.base = 1e0
|
||||
case "iso8601":
|
||||
a.base = 8601
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *durationArshaler) isNumeric() bool {
|
||||
return a.base != 0 && a.base != 8601
|
||||
}
|
||||
|
||||
func (a *durationArshaler) appendMarshal(b []byte) ([]byte, error) {
|
||||
switch a.base {
|
||||
case 0:
|
||||
return append(b, a.td.String()...), nil
|
||||
case 8601:
|
||||
return appendDurationISO8601(b, a.td), nil
|
||||
default:
|
||||
return appendDurationBase10(b, a.td, a.base), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *durationArshaler) unmarshal(b []byte) (err error) {
|
||||
switch a.base {
|
||||
case 0:
|
||||
a.td, err = time.ParseDuration(string(b))
|
||||
case 8601:
|
||||
a.td, err = parseDurationISO8601(b)
|
||||
default:
|
||||
a.td, err = parseDurationBase10(b, a.base)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type timeArshaler struct {
|
||||
tt time.Time
|
||||
|
||||
// base records the representation where:
|
||||
// - 0 uses RFC 3339 encoding of the timestamp
|
||||
// - 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 uint64
|
||||
format string // time format passed to time.Parse
|
||||
|
||||
looseRFC3339 bool
|
||||
}
|
||||
|
||||
func (a *timeArshaler) initFormat(format string) bool {
|
||||
// We assume that an exported constant in the time package will
|
||||
// always start with an uppercase ASCII letter.
|
||||
if len(format) == 0 {
|
||||
return false
|
||||
}
|
||||
a.base = math.MaxUint // implies custom format
|
||||
if c := format[0]; !('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z') {
|
||||
a.format = format
|
||||
return true
|
||||
}
|
||||
switch format {
|
||||
case "ANSIC":
|
||||
a.format = time.ANSIC
|
||||
case "UnixDate":
|
||||
a.format = time.UnixDate
|
||||
case "RubyDate":
|
||||
a.format = time.RubyDate
|
||||
case "RFC822":
|
||||
a.format = time.RFC822
|
||||
case "RFC822Z":
|
||||
a.format = time.RFC822Z
|
||||
case "RFC850":
|
||||
a.format = time.RFC850
|
||||
case "RFC1123":
|
||||
a.format = time.RFC1123
|
||||
case "RFC1123Z":
|
||||
a.format = time.RFC1123Z
|
||||
case "RFC3339":
|
||||
a.base = 0
|
||||
a.format = time.RFC3339
|
||||
case "RFC3339Nano":
|
||||
a.base = 0
|
||||
a.format = time.RFC3339Nano
|
||||
case "Kitchen":
|
||||
a.format = time.Kitchen
|
||||
case "Stamp":
|
||||
a.format = time.Stamp
|
||||
case "StampMilli":
|
||||
a.format = time.StampMilli
|
||||
case "StampMicro":
|
||||
a.format = time.StampMicro
|
||||
case "StampNano":
|
||||
a.format = time.StampNano
|
||||
case "DateTime":
|
||||
a.format = time.DateTime
|
||||
case "DateOnly":
|
||||
a.format = time.DateOnly
|
||||
case "TimeOnly":
|
||||
a.format = time.TimeOnly
|
||||
case "unix":
|
||||
a.base = 1e0
|
||||
case "unixmilli":
|
||||
a.base = 1e3
|
||||
case "unixmicro":
|
||||
a.base = 1e6
|
||||
case "unixnano":
|
||||
a.base = 1e9
|
||||
default:
|
||||
// Reject any Go identifier in case new constants are supported.
|
||||
if strings.TrimFunc(format, isLetterOrDigit) == "" {
|
||||
return false
|
||||
}
|
||||
a.format = format
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *timeArshaler) isNumeric() bool {
|
||||
return int(a.base) > 0
|
||||
}
|
||||
|
||||
func (a *timeArshaler) hasCustomFormat() bool {
|
||||
return a.base == math.MaxUint
|
||||
}
|
||||
|
||||
func (a *timeArshaler) appendMarshal(b []byte) ([]byte, error) {
|
||||
switch a.base {
|
||||
case 0:
|
||||
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.
|
||||
// Explicitly check for these edge cases.
|
||||
// See https://go.dev/issue/4556 and https://go.dev/issue/54580.
|
||||
switch b := b[n0:]; {
|
||||
case b[len("9999")] != '-': // year must be exactly 4 digits wide
|
||||
return b, errors.New("year outside of range [0,9999]")
|
||||
case b[len(b)-1] != 'Z':
|
||||
c := b[len(b)-len("Z07:00")]
|
||||
if ('0' <= c && c <= '9') || parseDec2(b[len(b)-len("07:00"):]) >= 24 {
|
||||
return b, errors.New("timezone hour outside of range [0,23]")
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
case math.MaxUint:
|
||||
return a.tt.AppendFormat(b, a.format), nil
|
||||
default:
|
||||
return appendTimeUnix(b, a.tt, a.base), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *timeArshaler) unmarshal(b []byte) (err error) {
|
||||
switch a.base {
|
||||
case 0:
|
||||
// Use time.Time.UnmarshalText to avoid possible string allocation.
|
||||
if err := a.tt.UnmarshalText(b); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(https://go.dev/issue/57912):
|
||||
// RFC 3339 specifies the grammar for a valid timestamp.
|
||||
// However, the parsing functionality in "time" is too loose and
|
||||
// incorrectly accepts invalid timestamps as valid.
|
||||
// Remove these manual checks when "time" checks it for us.
|
||||
newParseError := func(layout, value, layoutElem, valueElem, message string) 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
|
||||
return newParseError(time.RFC3339, string(b), ".", ",", "")
|
||||
case b[len(b)-1] != 'Z':
|
||||
switch {
|
||||
case parseDec2(b[len(b)-len("07:00"):]) >= 24: // timezone hour must be in range
|
||||
return newParseError(time.RFC3339, string(b), "Z07:00", string(b[len(b)-len("Z07:00"):]), ": timezone hour out of range")
|
||||
case parseDec2(b[len(b)-len("00"):]) >= 60: // timezone minute must be in range
|
||||
return newParseError(time.RFC3339, string(b), "Z07:00", string(b[len(b)-len("Z07:00"):]), ": timezone minute out of range")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case math.MaxUint:
|
||||
a.tt, err = time.Parse(a.format, string(b))
|
||||
return err
|
||||
default:
|
||||
a.tt, err = parseTimeUnix(b, a.base)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 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 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, 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 uint64) (time.Duration, error) {
|
||||
suffix, neg := consumeSign(b, false) // consume sign
|
||||
wholeBytes, fracBytes := bytesCutByte(suffix, '.', true) // consume whole and frac fields
|
||||
whole, okWhole := jsonwire.ParseUint(wholeBytes) // parse whole field; may overflow
|
||||
frac, okFrac := parseFracBase10(fracBytes, pow10) // parse frac field
|
||||
hi, lo := bits.Mul64(whole, uint64(pow10)) // overflow if hi > 0
|
||||
sum, co := bits.Add64(lo, uint64(frac), 0) // overflow if co > 0
|
||||
switch d := mayApplyDurationSign(sum, neg); { // overflow if neg != (d < 0)
|
||||
case (!okWhole && whole != math.MaxUint64) || !okFrac:
|
||||
return 0, fmt.Errorf("invalid duration %q: %w", b, strconv.ErrSyntax)
|
||||
case !okWhole || hi > 0 || co > 0 || neg != (d < 0):
|
||||
return 0, fmt.Errorf("invalid duration %q: %w", b, strconv.ErrRange)
|
||||
default:
|
||||
return d, nil
|
||||
}
|
||||
}
|
||||
|
||||
// appendDurationISO8601 appends an ISO 8601 duration with a restricted grammar,
|
||||
// where leading and trailing zeroes and zero-value designators are omitted.
|
||||
// It only uses hour, minute, and second designators since ISO 8601 defines
|
||||
// those as being "accurate", while year, month, week, and day are "nominal".
|
||||
func appendDurationISO8601(b []byte, d time.Duration) []byte {
|
||||
if d == 0 {
|
||||
return append(b, "PT0S"...)
|
||||
}
|
||||
b, n := mayAppendDurationSign(b, d)
|
||||
b = append(b, "PT"...)
|
||||
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
|
||||
if hour > 0 {
|
||||
b = append(strconv.AppendUint(b, hour, 10), 'H')
|
||||
}
|
||||
if min > 0 {
|
||||
b = append(strconv.AppendUint(b, min, 10), 'M')
|
||||
}
|
||||
if sec > 0 || nsec > 0 {
|
||||
b = append(appendFracBase10(strconv.AppendUint(b, sec, 10), nsec, 1e9), 'S')
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// daysPerYear is the exact average number of days in a year according to
|
||||
// the Gregorian calendar, which has an extra day each year that is
|
||||
// a multiple of 4, unless it is evenly divisible by 100 but not by 400.
|
||||
// This does not take into account leap seconds, which are not deterministic.
|
||||
const daysPerYear = 365.2425
|
||||
|
||||
var errInaccurateDateUnits = errors.New("inaccurate year, month, week, or day units")
|
||||
|
||||
// parseDurationISO8601 parses a duration according to ISO 8601-1:2019,
|
||||
// section 5.5.2.2 and 5.5.2.3 with the following restrictions or extensions:
|
||||
//
|
||||
// - A leading minus sign is permitted for negative duration according
|
||||
// to ISO 8601-2:2019, section 4.4.1.9. We do not permit negative values
|
||||
// for each "time scale component", which is permitted by section 4.4.1.1,
|
||||
// but rarely supported by parsers.
|
||||
//
|
||||
// - A leading plus sign is permitted (and ignored).
|
||||
// This is not required by ISO 8601, but not forbidden either.
|
||||
// There is some precedent for this as it is supported by the principle of
|
||||
// duration arithmetic as specified in ISO 8601-2-2019, section 14.1.
|
||||
// Of note, the JavaScript grammar for ISO 8601 permits a leading plus sign.
|
||||
//
|
||||
// - A fractional value is only permitted for accurate units
|
||||
// (i.e., hour, minute, and seconds) in the last time component,
|
||||
// which is permissible by ISO 8601-1:2019, section 5.5.2.3.
|
||||
//
|
||||
// - Both periods ('.') and commas (',') are supported as the separator
|
||||
// between the integer part and fraction part of a number,
|
||||
// as specified in ISO 8601-1:2019, section 3.2.6.
|
||||
// While ISO 8601 recommends comma as the default separator,
|
||||
// most formatters use a period.
|
||||
//
|
||||
// - Leading zeros are ignored. This is not required by ISO 8601,
|
||||
// but also not forbidden by the standard. Many parsers support this.
|
||||
//
|
||||
// - Lowercase designators are supported. This is not required by ISO 8601,
|
||||
// but also not forbidden by the standard. Many parsers support this.
|
||||
//
|
||||
// If the nominal units of year, month, week, or day are present,
|
||||
// this produces a best-effort value and also reports [errInaccurateDateUnits].
|
||||
//
|
||||
// The accepted grammar is identical to JavaScript's Duration:
|
||||
//
|
||||
// https://tc39.es/proposal-temporal/#prod-Duration
|
||||
//
|
||||
// We follow JavaScript's grammar as JSON itself is derived from JavaScript.
|
||||
// The Temporal.Duration.toJSON method is guaranteed to produce an output
|
||||
// that can be parsed by this function so long as arithmetic in JavaScript
|
||||
// does not use a largestUnit value higher than "hours" (which is the default).
|
||||
// Even if it does, this will do a best-effort parsing with inaccurate units,
|
||||
// but report [errInaccurateDateUnits].
|
||||
func parseDurationISO8601(b []byte) (time.Duration, error) {
|
||||
var invalid, overflow, inaccurate, sawFrac bool
|
||||
var sumNanos, n, co uint64
|
||||
|
||||
// cutBytes is like [bytes.Cut], but uses either c0 or c1 as the separator.
|
||||
cutBytes := func(b []byte, c0, c1 byte) (prefix, suffix []byte, ok bool) {
|
||||
for i, c := range b {
|
||||
if c == c0 || c == c1 {
|
||||
return b[:i], b[i+1:], true
|
||||
}
|
||||
}
|
||||
return b, nil, false
|
||||
}
|
||||
|
||||
// mayParseUnit attempts to parse another date or time number
|
||||
// identified by the desHi and desLo unit characters.
|
||||
// If the part is absent for current unit, it returns b as is.
|
||||
mayParseUnit := func(b []byte, desHi, desLo byte, unit time.Duration) []byte {
|
||||
number, suffix, ok := cutBytes(b, desHi, desLo)
|
||||
if !ok || sawFrac {
|
||||
return b // designator is not present or already saw fraction, which can only be in the last component
|
||||
}
|
||||
|
||||
// Parse the number.
|
||||
// A fraction is allowed for the accurate units in the last part.
|
||||
whole, frac, ok := cutBytes(number, '.', ',')
|
||||
if ok {
|
||||
sawFrac = true
|
||||
invalid = invalid || len(frac) == len("") || unit > time.Hour
|
||||
if unit == time.Second {
|
||||
n, ok = parsePaddedBase10(frac, uint64(time.Second))
|
||||
invalid = invalid || !ok
|
||||
} else {
|
||||
f, err := strconv.ParseFloat("0."+string(frac), 64)
|
||||
invalid = invalid || err != nil || len(bytes.Trim(frac[len("."):], "0123456789")) > 0
|
||||
n = uint64(math.Round(f * float64(unit))) // never overflows since f is within [0..1]
|
||||
}
|
||||
sumNanos, co = bits.Add64(sumNanos, n, 0) // overflow if co > 0
|
||||
overflow = overflow || co > 0
|
||||
}
|
||||
for len(whole) > 1 && whole[0] == '0' {
|
||||
whole = whole[len("0"):] // trim leading zeros
|
||||
}
|
||||
n, ok := jsonwire.ParseUint(whole) // overflow if !ok && MaxUint64
|
||||
hi, lo := bits.Mul64(n, uint64(unit)) // overflow if hi > 0
|
||||
sumNanos, co = bits.Add64(sumNanos, lo, 0) // overflow if co > 0
|
||||
invalid = invalid || (!ok && n != math.MaxUint64)
|
||||
overflow = overflow || (!ok && n == math.MaxUint64) || hi > 0 || co > 0
|
||||
inaccurate = inaccurate || unit > time.Hour
|
||||
return suffix
|
||||
}
|
||||
|
||||
suffix, neg := consumeSign(b, true)
|
||||
prefix, suffix, okP := cutBytes(suffix, 'P', 'p')
|
||||
durDate, durTime, okT := cutBytes(suffix, 'T', 't')
|
||||
invalid = invalid || len(prefix) > 0 || !okP || (okT && len(durTime) == 0) || len(durDate)+len(durTime) == 0
|
||||
if len(durDate) > 0 { // nominal portion of the duration
|
||||
durDate = mayParseUnit(durDate, 'Y', 'y', time.Duration(daysPerYear*24*60*60*1e9))
|
||||
durDate = mayParseUnit(durDate, 'M', 'm', time.Duration(daysPerYear/12*24*60*60*1e9))
|
||||
durDate = mayParseUnit(durDate, 'W', 'w', time.Duration(7*24*60*60*1e9))
|
||||
durDate = mayParseUnit(durDate, 'D', 'd', time.Duration(24*60*60*1e9))
|
||||
invalid = invalid || len(durDate) > 0 // unknown elements
|
||||
}
|
||||
if len(durTime) > 0 { // accurate portion of the duration
|
||||
durTime = mayParseUnit(durTime, 'H', 'h', time.Duration(60*60*1e9))
|
||||
durTime = mayParseUnit(durTime, 'M', 'm', time.Duration(60*1e9))
|
||||
durTime = mayParseUnit(durTime, 'S', 's', time.Duration(1e9))
|
||||
invalid = invalid || len(durTime) > 0 // unknown elements
|
||||
}
|
||||
d := mayApplyDurationSign(sumNanos, neg)
|
||||
overflow = overflow || (neg != (d < 0) && d != 0) // overflows signed duration
|
||||
|
||||
switch {
|
||||
case invalid:
|
||||
return 0, fmt.Errorf("invalid ISO 8601 duration %q: %w", b, strconv.ErrSyntax)
|
||||
case overflow:
|
||||
return 0, fmt.Errorf("invalid ISO 8601 duration %q: %w", b, strconv.ErrRange)
|
||||
case inaccurate:
|
||||
return d, fmt.Errorf("invalid ISO 8601 duration %q: %w", b, errInaccurateDateUnits)
|
||||
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 {
|
||||
b = append(b, '-')
|
||||
d *= -1
|
||||
}
|
||||
return b, uint64(d)
|
||||
}
|
||||
|
||||
// mayApplyDurationSign inverts n if neg is specified.
|
||||
func mayApplyDurationSign(n uint64, neg bool) time.Duration {
|
||||
if neg {
|
||||
return -1 * time.Duration(n)
|
||||
} else {
|
||||
return +1 * time.Duration(n)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 uint64) []byte {
|
||||
sec, nsec := t.Unix(), int64(t.Nanosecond())
|
||||
if sec < 0 {
|
||||
b = append(b, '-')
|
||||
sec, nsec = negateSecNano(sec, nsec)
|
||||
}
|
||||
switch {
|
||||
case pow10 == 1e0: // fast case where units is in seconds
|
||||
b = strconv.AppendUint(b, uint64(sec), 10)
|
||||
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(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, 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 uint64) (time.Time, error) {
|
||||
suffix, neg := consumeSign(b, false) // consume sign
|
||||
wholeBytes, fracBytes := bytesCutByte(suffix, '.', true) // consume whole and frac fields
|
||||
whole, okWhole := jsonwire.ParseUint(wholeBytes) // parse whole field; may overflow
|
||||
frac, okFrac := parseFracBase10(fracBytes, 1e9/pow10) // parse frac field
|
||||
var sec, nsec int64
|
||||
switch {
|
||||
case pow10 == 1e0: // fast case where units is in seconds
|
||||
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 / 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(mid*(1e9/pow10) + frac) // cannot overflow
|
||||
}
|
||||
if neg {
|
||||
sec, nsec = negateSecNano(sec, nsec)
|
||||
}
|
||||
switch t := time.Unix(sec, nsec).UTC(); {
|
||||
case (!okWhole && whole != math.MaxUint64) || !okFrac:
|
||||
return time.Time{}, fmt.Errorf("invalid time %q: %w", b, strconv.ErrSyntax)
|
||||
case !okWhole || neg != (t.Unix() < 0):
|
||||
return time.Time{}, fmt.Errorf("invalid time %q: %w", b, strconv.ErrRange)
|
||||
default:
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
|
||||
// negateSecNano negates a Unix timestamp, where nsec must be within [0, 1e9).
|
||||
func negateSecNano(sec, nsec int64) (int64, int64) {
|
||||
sec = ^sec // twos-complement negation (i.e., -1*sec + 1)
|
||||
nsec = -nsec + 1e9 // negate nsec and add 1e9 (which is the extra +1 from sec negation)
|
||||
sec += int64(nsec / 1e9) // handle possible overflow of nsec if it started as zero
|
||||
nsec %= 1e9 // ensure nsec stays within [0, 1e9)
|
||||
return sec, nsec
|
||||
}
|
||||
|
||||
// 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 uint64) []byte {
|
||||
if n == 0 {
|
||||
return b
|
||||
}
|
||||
return bytes.TrimRight(appendPaddedBase10(append(b, '.'), n, max10), "0")
|
||||
}
|
||||
|
||||
// parseFracBase10 parses the fraction of n/max10,
|
||||
// where max10 is a power-of-10 that is larger than n.
|
||||
func parseFracBase10(b []byte, max10 uint64) (n uint64, ok bool) {
|
||||
switch {
|
||||
case len(b) == 0:
|
||||
return 0, true
|
||||
case len(b) < len(".0") || b[0] != '.':
|
||||
return 0, false
|
||||
}
|
||||
return parsePaddedBase10(b[len("."):], max10)
|
||||
}
|
||||
|
||||
// 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 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, n+max10/10, 10)
|
||||
b[i]-- // subtract the addition of max10/10
|
||||
return b
|
||||
}
|
||||
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 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 += uint64(b[0] - '0')
|
||||
b = b[1:]
|
||||
}
|
||||
pow10 *= 10
|
||||
}
|
||||
if len(b) > 0 && len(bytes.TrimRight(b, "0123456789")) > 0 {
|
||||
return n, false // trailing characters are not digits
|
||||
}
|
||||
return n, true
|
||||
}
|
||||
|
||||
// consumeSign consumes an optional leading negative or positive sign.
|
||||
func consumeSign(b []byte, allowPlus bool) ([]byte, bool) {
|
||||
if len(b) > 0 {
|
||||
if b[0] == '-' {
|
||||
return b[len("-"):], true
|
||||
} else if b[0] == '+' && allowPlus {
|
||||
return b[len("+"):], false
|
||||
}
|
||||
}
|
||||
return b, false
|
||||
}
|
||||
|
||||
// bytesCutByte is similar to bytes.Cut(b, []byte{c}),
|
||||
// except c may optionally be included as part of the suffix.
|
||||
func bytesCutByte(b []byte, c byte, include bool) ([]byte, []byte) {
|
||||
if i := bytes.IndexByte(b, c); i >= 0 {
|
||||
if include {
|
||||
return b[:i], b[i:]
|
||||
}
|
||||
return b[:i], b[i+1:]
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// parseDec2 parses b as an unsigned, base-10, 2-digit number.
|
||||
// The result is undefined if digits are not base-10.
|
||||
func parseDec2(b []byte) byte {
|
||||
if len(b) < 2 {
|
||||
return 0
|
||||
}
|
||||
return 10*(b[0]-'0') + (b[1] - '0')
|
||||
}
|
||||
247
tools/tsgo/vendor/github.com/go-json-experiment/json/doc.go
generated
vendored
Normal file
247
tools/tsgo/vendor/github.com/go-json-experiment/json/doc.go
generated
vendored
Normal file
@@ -0,0 +1,247 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
// Package json implements semantic processing of JSON as specified in RFC 8259.
|
||||
// JSON is a simple data interchange format that can represent
|
||||
// primitive data types such as booleans, strings, and numbers,
|
||||
// in addition to structured data types such as objects and arrays.
|
||||
//
|
||||
// [Marshal] and [Unmarshal] encode and decode Go values
|
||||
// to/from JSON text contained within a []byte.
|
||||
// [MarshalWrite] and [UnmarshalRead] operate on JSON text
|
||||
// by writing to or reading from an [io.Writer] or [io.Reader].
|
||||
// [MarshalEncode] and [UnmarshalDecode] operate on JSON text
|
||||
// by encoding to or decoding from a [jsontext.Encoder] or [jsontext.Decoder].
|
||||
// [Options] may be passed to each of the marshal or unmarshal functions
|
||||
// to configure the semantic behavior of marshaling and unmarshaling
|
||||
// (i.e., alter how JSON data is understood as Go data and vice versa).
|
||||
// [jsontext.Options] may also be passed to the marshal or unmarshal functions
|
||||
// to configure the syntactic behavior of encoding or decoding.
|
||||
//
|
||||
// The data types of JSON are mapped to/from the data types of Go based on
|
||||
// the closest logical equivalent between the two type systems. For example,
|
||||
// a JSON boolean corresponds with a Go bool,
|
||||
// a JSON string corresponds with a Go string,
|
||||
// a JSON number corresponds with a Go int, uint or float,
|
||||
// a JSON array corresponds with a Go slice or array, and
|
||||
// a JSON object corresponds with a Go struct or map.
|
||||
// See the documentation on [Marshal] and [Unmarshal] for a comprehensive list
|
||||
// of how the JSON and Go type systems correspond.
|
||||
//
|
||||
// Arbitrary Go types can customize their JSON representation by implementing
|
||||
// [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
|
||||
// [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.
|
||||
//
|
||||
// # JSON Representation of Go structs
|
||||
//
|
||||
// A Go struct is naturally represented as a JSON object,
|
||||
// where each Go struct field corresponds with a JSON object member.
|
||||
// When marshaling, all Go struct fields are recursively encoded in depth-first
|
||||
// order as JSON object members except those that are ignored or omitted.
|
||||
// When unmarshaling, JSON object members are recursively decoded
|
||||
// into the corresponding Go struct fields.
|
||||
// Object members that do not match any struct fields,
|
||||
// also known as “unknown members”, are ignored by default or rejected
|
||||
// if [RejectUnknownMembers] is specified.
|
||||
//
|
||||
// The representation of each struct field can be customized in the
|
||||
// "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
|
||||
// is used as the JSON object name.
|
||||
// By default, unmarshaling uses case-sensitive matching to identify
|
||||
// the Go struct field associated with a JSON object name.
|
||||
//
|
||||
// After the name, the following tag options are supported:
|
||||
//
|
||||
// - omitzero: When marshaling, the "omitzero" option specifies that
|
||||
// the struct field should be omitted if the field value is zero
|
||||
// as determined by the "IsZero() bool" method if present,
|
||||
// otherwise based on whether the field is the zero Go value.
|
||||
// This option has no effect when unmarshaling.
|
||||
//
|
||||
// - omitempty: When marshaling, the "omitempty" option specifies that
|
||||
// the struct field should be omitted if the field value would have been
|
||||
// encoded as a JSON null, empty string, empty object, or empty array.
|
||||
// This option has no effect when unmarshaling.
|
||||
//
|
||||
// - string: The "string" option specifies that [StringifyNumbers] be set
|
||||
// when marshaling or unmarshaling a struct field value.
|
||||
// This causes types that would normally be encoded as a JSON number
|
||||
// to instead be encoded as a JSON number quoted within a JSON string,
|
||||
// and to be decoded from a JSON string containing the JSON number
|
||||
// without any surrounding whitespace.
|
||||
// The "string" option only applies to the top-level of the Go struct field value.
|
||||
// Specifically, for the default representation of composite Go data types
|
||||
// (e.g., array, slice, struct, or map), it will not stringify JSON numbers
|
||||
// within such types. Applying this option to invalid types causes a runtime error.
|
||||
// This extra level of encoding is often necessary since many JSON parsers
|
||||
// cannot precisely represent 64-bit integers.
|
||||
//
|
||||
// - 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,
|
||||
// then the field with an exact name match is selected, otherwise an error
|
||||
// is reported due to an ambiguous set of candidate fields to unmarshal into.
|
||||
// The 'strict' value specifies that matching is case-sensitive.
|
||||
// This takes precedence over the [MatchCaseInsensitiveNames] option.
|
||||
//
|
||||
// - embed: The "embed" option specifies that
|
||||
// the JSON representable content of this field type is to be promoted
|
||||
// as if they were specified in the parent struct.
|
||||
// It is the JSON equivalent of Go struct embedding.
|
||||
// A Go embedded field is implicitly JSON embedded unless
|
||||
// an explicit JSON name is specified. The embedded 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,
|
||||
// embedded fields from a pointer type are omitted if it is nil.
|
||||
// Embedded fields of type [jsontext.Value] and map[~string]T are called
|
||||
// “embedded fallbacks” as they can represent all possible
|
||||
// JSON object members not directly handled by the parent struct.
|
||||
// Only one embedded fallback field may be specified in a struct,
|
||||
// while many non-fallback fields may be specified. This option
|
||||
// must not be specified with any other option (including the JSON name).
|
||||
//
|
||||
// The "omitzero" and "omitempty" options are mostly semantically identical.
|
||||
// The former is defined in terms of the Go type system,
|
||||
// while the latter in terms of the JSON type system.
|
||||
// Consequently they behave differently in some circumstances.
|
||||
// For example, only a nil slice or map is omitted under "omitzero", while
|
||||
// an empty slice or map is omitted under "omitempty" regardless of nilness.
|
||||
// The "omitzero" option is useful for types with a well-defined zero value
|
||||
// (e.g., [net/netip.Addr]) or have an IsZero method (e.g., [time.Time.IsZero]).
|
||||
//
|
||||
// Every Go struct corresponds to a list of JSON representable fields
|
||||
// which is constructed by performing a breadth-first search over
|
||||
// all struct fields (excluding unexported or ignored fields),
|
||||
// where the search recursively descends into embedded structs.
|
||||
// The set of non-embedded fields in a struct must have unique JSON names.
|
||||
// If multiple fields all have the same JSON name, then the one
|
||||
// at shallowest depth takes precedence and the other fields at deeper depths
|
||||
// are excluded from the list of JSON representable fields.
|
||||
// If multiple fields at the shallowest depth have the same JSON name,
|
||||
// but exactly one is explicitly tagged with a JSON name,
|
||||
// then that field takes precedence and all others are excluded from the list.
|
||||
// This is analogous to Go visibility rules for struct field selection
|
||||
// with embedded struct types.
|
||||
//
|
||||
// Marshaling or unmarshaling a non-empty struct
|
||||
// without any JSON representable fields results in a [SemanticError].
|
||||
// Unexported fields must not have any `json` tags except for `json:"-"`.
|
||||
//
|
||||
// # Security Considerations
|
||||
//
|
||||
// JSON is frequently used as a data interchange format to communicate
|
||||
// between different systems, possibly implemented in different languages.
|
||||
// For interoperability and security reasons, it is important that
|
||||
// all implementations agree upon the semantic meaning of the data.
|
||||
//
|
||||
// [For example, suppose we have two micro-services.]
|
||||
// The first service is responsible for authenticating a JSON request,
|
||||
// while the second service is responsible for executing the request
|
||||
// (having assumed that the prior service authenticated the request).
|
||||
// If an attacker were able to maliciously craft a JSON request such that
|
||||
// both services believe that the same request is from different users,
|
||||
// it could bypass the authenticator with valid credentials for one user,
|
||||
// but maliciously perform an action on behalf of a different user.
|
||||
//
|
||||
// According to RFC 8259, there unfortunately exist many JSON texts
|
||||
// that are syntactically valid but semantically ambiguous.
|
||||
// For example, the standard does not define how to interpret duplicate
|
||||
// names within an object.
|
||||
//
|
||||
// The v1 [encoding/json] and [encoding/json/v2] packages
|
||||
// interpret some inputs in different ways. In particular:
|
||||
//
|
||||
// - The standard specifies that JSON must be encoded using UTF-8.
|
||||
// By default, v1 replaces invalid bytes of UTF-8 in JSON strings
|
||||
// with the Unicode replacement character,
|
||||
// while v2 rejects inputs with invalid UTF-8.
|
||||
// To change the default, specify the [jsontext.AllowInvalidUTF8] option.
|
||||
// The replacement of invalid UTF-8 is a form of data corruption
|
||||
// that alters the precise meaning of strings.
|
||||
//
|
||||
// - The standard does not specify a particular behavior when
|
||||
// duplicate names are encountered within a JSON object,
|
||||
// which means that different implementations may behave differently.
|
||||
// By default, v1 allows for the presence of duplicate names,
|
||||
// while v2 rejects duplicate names.
|
||||
// To change the default, specify the [jsontext.AllowDuplicateNames] option.
|
||||
// If allowed, object members are processed in the order they are observed,
|
||||
// meaning that later values will replace or be merged into prior values,
|
||||
// depending on the Go value type.
|
||||
//
|
||||
// - The standard defines a JSON object as an unordered collection of name/value pairs.
|
||||
// While ordering can be observed through the underlying [jsontext] API,
|
||||
// both v1 and v2 generally avoid exposing the ordering.
|
||||
// No application should semantically depend on the order of object members.
|
||||
// Allowing duplicate names is a vector through which ordering of members
|
||||
// can accidentally be observed and depended upon.
|
||||
//
|
||||
// - The standard suggests that JSON object names are typically compared
|
||||
// based on equality of the sequence of Unicode code points,
|
||||
// which implies that comparing names is often case-sensitive.
|
||||
// When unmarshaling a JSON object into a Go struct,
|
||||
// by default, v1 uses a (loose) case-insensitive match on the name,
|
||||
// while v2 uses a (strict) case-sensitive match on the name.
|
||||
// To change the default, specify the [MatchCaseInsensitiveNames] option.
|
||||
// The use of case-insensitive matching provides another vector through
|
||||
// which duplicate names can occur. Allowing case-insensitive matching
|
||||
// means that v1 or v2 might interpret JSON objects differently from most
|
||||
// other JSON implementations (which typically use a case-sensitive match).
|
||||
//
|
||||
// - The standard does not specify a particular behavior when
|
||||
// an unknown name in a JSON object is encountered.
|
||||
// When unmarshaling a JSON object into a Go struct, by default
|
||||
// both v1 and v2 ignore unknown names and their corresponding values.
|
||||
// To change the default, specify the [RejectUnknownMembers] option.
|
||||
//
|
||||
// - The standard suggests that implementations may use a float64
|
||||
// to represent a JSON number. Consequently, large JSON integers
|
||||
// may lose precision when stored as a floating-point type.
|
||||
// Both v1 and v2 correctly preserve precision when marshaling and
|
||||
// unmarshaling a concrete integer type. However, even if v1 and v2
|
||||
// preserve precision for concrete types, other JSON implementations
|
||||
// may not be able to preserve precision for outputs produced by v1 or v2.
|
||||
// The `string` tag option can be used to specify that an integer type
|
||||
// is to be quoted within a JSON string to avoid loss of precision.
|
||||
// Furthermore, v1 and v2 may still lose precision when unmarshaling
|
||||
// into an any interface value, where unmarshal uses a float64
|
||||
// by default to represent a JSON number.
|
||||
// To change the default, specify the [WithUnmarshalers] option
|
||||
// with a custom unmarshaler that pre-populates the interface value
|
||||
// with a concrete Go type that can preserve precision.
|
||||
//
|
||||
// RFC 8785 specifies a canonical form for any JSON text,
|
||||
// which explicitly defines specific behaviors that RFC 8259 leaves undefined.
|
||||
// In theory, if a text can successfully [jsontext.Value.Canonicalize]
|
||||
// without changing the semantic meaning of the data, then it provides a
|
||||
// greater degree of confidence that the data is more secure and interoperable.
|
||||
//
|
||||
// The v2 API generally chooses more secure defaults than v1,
|
||||
// but care should still be taken with large integers or unknown members.
|
||||
//
|
||||
// [For example, suppose we have two micro-services.]: https://www.youtube.com/watch?v=avilmOcHKHE&t=1057s
|
||||
package json
|
||||
|
||||
// requireKeyedLiterals can be embedded in a struct to require keyed literals.
|
||||
type requireKeyedLiterals struct{}
|
||||
|
||||
// nonComparable can be embedded in a struct to prevent comparability.
|
||||
type nonComparable [0]func()
|
||||
465
tools/tsgo/vendor/github.com/go-json-experiment/json/errors.go
generated
vendored
Normal file
465
tools/tsgo/vendor/github.com/go-json-experiment/json/errors.go
generated
vendored
Normal file
@@ -0,0 +1,465 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"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 := ...
|
||||
// serr, ok := errors.AsType[*json.SemanticError](err)
|
||||
// if ok && 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")
|
||||
|
||||
// errAmbiguousName indicates a JSON object member could be unmarshal
|
||||
// into multiple candidate Go struct fields.
|
||||
var errAmbiguousName = errors.New("ambiguous 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 arshaling.
|
||||
// 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.
|
||||
//
|
||||
// If a [Marshaler], [MarshalerTo], [Unmarshaler], or [UnmarshalerFrom] method
|
||||
// returns a SemanticError when called by the [json] package,
|
||||
// then the ByteOffset, JSONPointer, and GoType fields are automatically
|
||||
// populated by the calling context if they are the zero value.
|
||||
//
|
||||
// The contents of this error as produced by this package may change over time.
|
||||
type SemanticError struct {
|
||||
requireKeyedLiterals
|
||||
nonComparable
|
||||
|
||||
action string // either "marshal" or "unmarshal"
|
||||
|
||||
// ByteOffset indicates that an error occurred at or after this byte offset.
|
||||
ByteOffset int64
|
||||
// JSONPointer indicates that an error occurred within this JSON value
|
||||
// as indicated using the JSON Pointer notation (see RFC 6901).
|
||||
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
|
||||
|
||||
// Err is the underlying error.
|
||||
Err error // may be nil
|
||||
}
|
||||
|
||||
// coder is implemented by [jsontext.Encoder] or [jsontext.Decoder].
|
||||
type coder interface {
|
||||
StackPointer() jsontext.Pointer
|
||||
Options() Options
|
||||
}
|
||||
|
||||
// newInvalidFormatError constructs a SemanticError because
|
||||
// the current type t cannot handle the provided options format.
|
||||
// This function 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) error {
|
||||
err := fmt.Errorf("invalid format flag %q", c.Options().(*jsonopts.Struct).Format)
|
||||
switch c := c.(type) {
|
||||
case *jsontext.Encoder:
|
||||
err = newMarshalErrorBefore(c, t, err)
|
||||
case *jsontext.Decoder:
|
||||
err = newUnmarshalErrorBeforeWithSkipping(c, 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: toUnexpectedEOF(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).
|
||||
// However, if [jsonflags.ReportErrorsWithLegacySemantics] is specified,
|
||||
// then it does record the next JSON kind for historical reporting reasons.
|
||||
func newUnmarshalErrorBefore(d *jsontext.Decoder, t reflect.Type, err error) error {
|
||||
var k jsontext.Kind
|
||||
if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
k = d.PeekKind()
|
||||
}
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: toUnexpectedEOF(err),
|
||||
ByteOffset: d.InputOffset() + int64(export.Decoder(d).CountNextDelimWhitespace()),
|
||||
JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, +1)),
|
||||
JSONKind: k}
|
||||
}
|
||||
|
||||
// newUnmarshalErrorBeforeWithSkipping is like [newUnmarshalErrorBefore],
|
||||
// but automatically skips the next value if
|
||||
// [jsonflags.ReportErrorsWithLegacySemantics] is specified.
|
||||
func newUnmarshalErrorBeforeWithSkipping(d *jsontext.Decoder, t reflect.Type, err error) error {
|
||||
err = newUnmarshalErrorBefore(d, t, err)
|
||||
if export.Decoder(d).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()
|
||||
byteOffset := d.InputOffset() - int64(len(tokOrVal))
|
||||
if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
// NOTE: In v1, the offset pointed to the end of the bad token or value.
|
||||
if k := jsontext.Value(tokOrVal).Kind(); k == '[' || k == '{' {
|
||||
byteOffset++ // add just the '[' or '{'
|
||||
} else {
|
||||
byteOffset += int64(len(tokOrVal))
|
||||
}
|
||||
}
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: toUnexpectedEOF(err),
|
||||
ByteOffset: byteOffset,
|
||||
JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, -1)),
|
||||
JSONKind: jsontext.Value(tokOrVal).Kind()}
|
||||
}
|
||||
|
||||
// newUnmarshalErrorAfterWithValue 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, t reflect.Type, err error) error {
|
||||
err = newUnmarshalErrorAfter(d, t, err)
|
||||
if export.Decoder(d).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}
|
||||
}
|
||||
serr.Err = toUnexpectedEOF(serr.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
|
||||
}
|
||||
|
||||
func wrapErrUnsupported(err error, what string) error {
|
||||
if errors.Is(err, errors.ErrUnsupported) {
|
||||
return errors.New(what + " may not return errors.ErrUnsupported")
|
||||
}
|
||||
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)
|
||||
sb.WriteString(errorModalVerb())
|
||||
|
||||
// Format action.
|
||||
var preposition string
|
||||
switch e.action {
|
||||
case "marshal":
|
||||
sb.WriteString(" marshal")
|
||||
preposition = " from"
|
||||
case "unmarshal":
|
||||
sb.WriteString(" unmarshal")
|
||||
preposition = " into"
|
||||
default:
|
||||
sb.WriteString(" handle")
|
||||
preposition = " with"
|
||||
}
|
||||
|
||||
// Format JSON kind.
|
||||
switch e.JSONKind {
|
||||
case 'n':
|
||||
sb.WriteString(" JSON null")
|
||||
case 'f', 't':
|
||||
sb.WriteString(" JSON boolean")
|
||||
case '"':
|
||||
sb.WriteString(" JSON string")
|
||||
case '0':
|
||||
sb.WriteString(" JSON number")
|
||||
case '{', '}':
|
||||
sb.WriteString(" JSON object")
|
||||
case '[', ']':
|
||||
sb.WriteString(" JSON array")
|
||||
default:
|
||||
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 {
|
||||
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(preposition)
|
||||
sb.WriteString(" Go ")
|
||||
sb.WriteString(typeString)
|
||||
}
|
||||
|
||||
// Special handling for unknown names.
|
||||
if e.Err == ErrUnknownName || e.Err == errAmbiguousName {
|
||||
sb.WriteString(": ")
|
||||
sb.WriteString(e.Err.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.
|
||||
// Avoid printing if it overlaps with a wrapped SyntacticError.
|
||||
switch serr, _ := e.Err.(*jsontext.SyntacticError); {
|
||||
case 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:
|
||||
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(errString)
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (e *SemanticError) Unwrap() error {
|
||||
return e.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,
|
||||
}
|
||||
}
|
||||
|
||||
// toUnexpectedEOF converts [io.EOF] to [io.ErrUnexpectedEOF].
|
||||
func toUnexpectedEOF(err error) error {
|
||||
if err == io.EOF {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return err
|
||||
}
|
||||
656
tools/tsgo/vendor/github.com/go-json-experiment/json/fields.go
generated
vendored
Normal file
656
tools/tsgo/vendor/github.com/go-json-experiment/json/fields.go
generated
vendored
Normal file
@@ -0,0 +1,656 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
type isZeroer interface {
|
||||
IsZero() bool
|
||||
}
|
||||
|
||||
var isZeroerType = reflect.TypeFor[isZeroer]()
|
||||
|
||||
type structFields struct {
|
||||
flattened []structField // listed in depth-first ordering
|
||||
byActualName map[string]*structField
|
||||
byFoldedName map[string][]*structField
|
||||
embeddedFallback *structField
|
||||
|
||||
errUnsupportedFormat *SemanticError
|
||||
}
|
||||
|
||||
// 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.embeddedFallback != nil {
|
||||
reindex(sf.embeddedFallback)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return fs.byFoldedName[string(foldName(name))]
|
||||
}
|
||||
|
||||
type structField struct {
|
||||
id int // unique numeric ID in breadth-first ordering
|
||||
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
|
||||
isEmpty func(addressableValue) bool
|
||||
fieldOptions
|
||||
}
|
||||
|
||||
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 breadth-first search.
|
||||
var queueIndex int
|
||||
type queueEntry struct {
|
||||
typ reflect.Type
|
||||
index []int
|
||||
visitChildren bool // whether to recursively visit embedded field in this struct
|
||||
}
|
||||
queue := []queueEntry{{root, nil, true}}
|
||||
seen := map[reflect.Type]bool{root: true}
|
||||
|
||||
// Perform a breadth-first search over all reachable fields.
|
||||
// This ensures that len(f.index) will be monotonically increasing.
|
||||
var allFields, embeddedFallbacks []structField
|
||||
for queueIndex < len(queue) {
|
||||
qe := queue[queueIndex]
|
||||
queueIndex++
|
||||
|
||||
t := qe.typ
|
||||
embeddedFallbackIndex := -1 // index of last embedded fallback field in current struct
|
||||
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 := range t.NumField() {
|
||||
sf := t.Field(i)
|
||||
_, hasTag := sf.Tag.Lookup("json")
|
||||
hasAnyJSONTag = hasAnyJSONTag || hasTag
|
||||
options, ignored, err := parseFieldOptions(sf)
|
||||
if err != nil {
|
||||
serr = cmp.Or(serr, &SemanticError{GoType: t, Err: err})
|
||||
}
|
||||
if ignored {
|
||||
continue
|
||||
}
|
||||
hasAnyJSONField = true
|
||||
f := structField{
|
||||
// Allocate a new slice (len=N+1) to hold both
|
||||
// the parent index (len=N) and the current index (len=1).
|
||||
// Do this to avoid clobbering the memory of the parent index.
|
||||
index: append(append(make([]int, 0, len(qe.index)+1), qe.index...), i),
|
||||
typ: sf.Type,
|
||||
fieldOptions: options,
|
||||
}
|
||||
if sf.Anonymous && !f.hasName {
|
||||
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.embed = true // implied by use of Go embedding without an explicit name
|
||||
}
|
||||
}
|
||||
|
||||
var handleEmbed, handleField func()
|
||||
handleEmbed = func() {
|
||||
// Handle an embedded field that serializes to/from
|
||||
// zero or more JSON object members.
|
||||
|
||||
if f.fieldOptions != (fieldOptions{name: f.name, quotedName: f.quotedName, embed: true}) {
|
||||
serr = orErrorf(serr, t, "Go struct field %s cannot have any options other than `embed` specified", sf.Name)
|
||||
if f.hasName {
|
||||
handleField()
|
||||
return // invalid embedded field; treat as regular field
|
||||
}
|
||||
f.fieldOptions = fieldOptions{name: f.name, quotedName: f.quotedName, embed: f.embed}
|
||||
}
|
||||
|
||||
// Reject any types with custom serialization otherwise
|
||||
// it becomes impossible to know what sub-fields to embed.
|
||||
tf := indirectType(f.typ)
|
||||
if implementsAny(tf, allMethodTypes...) && tf != jsontextValueType {
|
||||
serr = orErrorf(serr, t, "embedded Go struct field %s of type %s must not implement marshal or unmarshal methods", sf.Name, tf)
|
||||
}
|
||||
|
||||
// Handle an embedded field that serializes to/from
|
||||
// a finite number of JSON object members backed by a Go struct.
|
||||
if tf.Kind() == reflect.Struct {
|
||||
if qe.visitChildren {
|
||||
queue = append(queue, queueEntry{tf, f.index, !seen[tf]})
|
||||
}
|
||||
seen[tf] = true
|
||||
return
|
||||
} else if !sf.IsExported() {
|
||||
serr = orErrorf(serr, t, "embedded Go struct field %s is not exported", sf.Name)
|
||||
return // invalid embedded field; treat as ignored
|
||||
}
|
||||
|
||||
// Handle an embedded field that serializes to/from any number of
|
||||
// JSON object members back by a Go map or jsontext.Value.
|
||||
switch {
|
||||
case tf == jsontextValueType:
|
||||
f.fncs = nil // specially handled in arshal_embedded.go
|
||||
case tf.Kind() == reflect.Map && tf.Key().Kind() == reflect.String:
|
||||
if implementsAny(tf.Key(), allMethodTypes...) {
|
||||
serr = orErrorf(serr, t, "embedded map field %s of type %s must have a string key that does not implement marshal or unmarshal methods", sf.Name, tf)
|
||||
handleField()
|
||||
return // invalid embedded field; treat as regular field
|
||||
}
|
||||
f.fncs = lookupArshaler(tf.Elem())
|
||||
default:
|
||||
serr = orErrorf(serr, t, "embedded Go struct field %s of type %s must be a Go struct, Go map of string key, or jsontext.Value", sf.Name, tf)
|
||||
handleField()
|
||||
return // invalid embedded field; treat as regular field
|
||||
}
|
||||
|
||||
// Reject multiple embedded fallback fields within the same struct.
|
||||
if embeddedFallbackIndex >= 0 {
|
||||
serr = orErrorf(serr, t, "embedded Go struct fields %s and %s cannot both be a Go map or jsontext.Value", t.Field(embeddedFallbackIndex).Name, sf.Name)
|
||||
// Still append f to embeddedFallbacks as there is still a
|
||||
// check for a dominant embedded fallback before returning.
|
||||
}
|
||||
embeddedFallbackIndex = i
|
||||
|
||||
embeddedFallbacks = append(embeddedFallbacks, f)
|
||||
}
|
||||
handleField = func() {
|
||||
// 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)
|
||||
return
|
||||
}
|
||||
// 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)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Provide a function that uses a type's IsZero method.
|
||||
switch {
|
||||
case sf.Type.Kind() == reflect.Interface && sf.Type.Implements(isZeroerType):
|
||||
f.isZero = func(va addressableValue) bool {
|
||||
// Avoid panics calling IsZero on a nil interface or
|
||||
// non-nil interface with nil pointer.
|
||||
return va.IsNil() || (va.Elem().Kind() == reflect.Pointer && va.Elem().IsNil()) || va.Interface().(isZeroer).IsZero()
|
||||
}
|
||||
case sf.Type.Kind() == reflect.Pointer && sf.Type.Implements(isZeroerType):
|
||||
f.isZero = func(va addressableValue) bool {
|
||||
// Avoid panics calling IsZero on nil pointer.
|
||||
return va.IsNil() || va.Interface().(isZeroer).IsZero()
|
||||
}
|
||||
case sf.Type.Implements(isZeroerType):
|
||||
f.isZero = func(va addressableValue) bool { return va.Interface().(isZeroer).IsZero() }
|
||||
case reflect.PointerTo(sf.Type).Implements(isZeroerType):
|
||||
f.isZero = func(va addressableValue) bool { return va.Addr().Interface().(isZeroer).IsZero() }
|
||||
}
|
||||
|
||||
// Provide a function that can determine whether the value would
|
||||
// serialize as an empty JSON value.
|
||||
switch sf.Type.Kind() {
|
||||
case reflect.String, reflect.Map, reflect.Array, reflect.Slice:
|
||||
f.isEmpty = func(va addressableValue) bool { return va.Len() == 0 }
|
||||
case reflect.Pointer, reflect.Interface:
|
||||
f.isEmpty = func(va addressableValue) bool { return va.IsNil() }
|
||||
}
|
||||
|
||||
// Reject multiple fields with same name within the same struct.
|
||||
if j, ok := namesIndex[f.name]; ok {
|
||||
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
|
||||
|
||||
f.id = len(allFields)
|
||||
f.fncs = lookupArshaler(sf.Type)
|
||||
allFields = append(allFields, f)
|
||||
if f.format != "" && fs.errUnsupportedFormat == nil {
|
||||
fs.errUnsupportedFormat = &SemanticError{GoType: t, Err: fmt.Errorf("Go struct field %s has unsupported `format` tag option", sf.Name)}
|
||||
}
|
||||
}
|
||||
|
||||
if f.embed {
|
||||
handleEmbed()
|
||||
} else {
|
||||
handleField()
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: New users to the json package are occasionally surprised that
|
||||
// unexported fields are ignored. This occurs by necessity due to our
|
||||
// inability to directly introspect such fields with Go reflection
|
||||
// without the use of unsafe.
|
||||
//
|
||||
// To reduce friction here, refuse to serialize any Go struct that
|
||||
// has no JSON serializable fields, has at least one Go struct field,
|
||||
// and does not have any `json` tags present. For example,
|
||||
// errors returned by errors.New would fail to serialize.
|
||||
isEmptyStruct := t.NumField() == 0
|
||||
if !isEmptyStruct && !hasAnyJSONTag && !hasAnyJSONField {
|
||||
serr = cmp.Or(serr, &SemanticError{GoType: t, Err: errNoExportedFields})
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the fields by exact name (breaking ties by depth and
|
||||
// then by presence of an explicitly provided JSON name).
|
||||
// Select the dominant field from each set of fields with the same name.
|
||||
// If multiple fields have the same name, then the dominant field
|
||||
// is the one that exists alone at the shallowest depth,
|
||||
// or the one that is uniquely tagged with a JSON name.
|
||||
// Otherwise, no dominant field exists for the set.
|
||||
flattened := allFields[:0]
|
||||
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
|
||||
for n < len(allFields) && allFields[n-1].name == allFields[n].name {
|
||||
n++
|
||||
}
|
||||
if n == 1 || len(allFields[0].index) != len(allFields[1].index) || allFields[0].hasName != allFields[1].hasName {
|
||||
flattened = append(flattened, allFields[0]) // only keep field if there is a dominant field
|
||||
}
|
||||
allFields = allFields[n:]
|
||||
}
|
||||
|
||||
// Sort the fields according to a breadth-first ordering
|
||||
// so that we can re-number IDs with the smallest possible values.
|
||||
// This optimizes use of uintSet such that it fits in the 64-entry bit set.
|
||||
slices.SortFunc(flattened, func(x, y structField) int {
|
||||
return cmp.Compare(x.id, y.id)
|
||||
})
|
||||
for i := range flattened {
|
||||
flattened[i].id = i
|
||||
}
|
||||
|
||||
// Sort the fields according to a depth-first ordering
|
||||
// as the typical order that fields are marshaled.
|
||||
slices.SortFunc(flattened, func(x, y structField) int {
|
||||
return slices.Compare(x.index, y.index)
|
||||
})
|
||||
|
||||
// Compute the mapping of fields in the byActualName map.
|
||||
// Pre-fold all names so that we can lookup folded names quickly.
|
||||
fs = structFields{
|
||||
flattened: flattened,
|
||||
byActualName: make(map[string]*structField, len(flattened)),
|
||||
byFoldedName: make(map[string][]*structField, len(flattened)),
|
||||
|
||||
errUnsupportedFormat: fs.errUnsupportedFormat,
|
||||
}
|
||||
for i, f := range fs.flattened {
|
||||
foldedName := string(foldName([]byte(f.name)))
|
||||
fs.byActualName[f.name] = &fs.flattened[i]
|
||||
fs.byFoldedName[foldedName] = append(fs.byFoldedName[foldedName], &fs.flattened[i])
|
||||
}
|
||||
for foldedName, fields := range fs.byFoldedName {
|
||||
if len(fields) > 1 {
|
||||
// The precedence order for conflicting case-insensitive 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)
|
||||
})
|
||||
fs.byFoldedName[foldedName] = fields
|
||||
}
|
||||
}
|
||||
if n := len(embeddedFallbacks); n == 1 || (n > 1 && len(embeddedFallbacks[0].index) != len(embeddedFallbacks[1].index)) {
|
||||
fs.embeddedFallback = &embeddedFallbacks[0] // dominant embedded fallback field
|
||||
}
|
||||
fs.reindex()
|
||||
return fs, serr
|
||||
}
|
||||
|
||||
// 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 `case:ignore` tag option is specified
|
||||
// or the MatchCaseInsensitiveNames call option is specified
|
||||
// (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,
|
||||
// while the v2 definition would ignore the presence of dashes and underscores.
|
||||
// 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 == caseIgnore || (flags.Get(jsonflags.MatchCaseInsensitiveNames) && f.casing != caseStrict) {
|
||||
if !flags.Get(jsonflags.MatchCaseSensitiveDelimiter) || strings.EqualFold(string(name), f.name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
caseIgnore = 1
|
||||
caseStrict = 2
|
||||
)
|
||||
|
||||
type fieldOptions struct {
|
||||
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
|
||||
embed bool
|
||||
omitzero bool
|
||||
omitempty bool
|
||||
string bool
|
||||
format string
|
||||
}
|
||||
|
||||
// parseFieldOptions parses the `json` tag in a Go struct field as
|
||||
// a structured set of options configuring parameters such as
|
||||
// the JSON member name and other features.
|
||||
func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool, err error) {
|
||||
tag, hasTag := sf.Tag.Lookup("json")
|
||||
|
||||
// Check whether this field is explicitly ignored.
|
||||
if tag == "-" {
|
||||
return fieldOptions{}, true, nil
|
||||
}
|
||||
|
||||
// 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 = cmp.Or(err, fmt.Errorf("unexported Go struct field %s cannot have non-ignored `json:%q` tag", sf.Name, tag))
|
||||
}
|
||||
return fieldOptions{}, true, err
|
||||
}
|
||||
|
||||
// Determine the JSON member name for this Go field.
|
||||
out.name = sf.Name // always starts with an uppercase character
|
||||
if len(tag) > 0 && !strings.HasPrefix(tag, ",") {
|
||||
// For better compatibility with v1, accept almost any unescaped name.
|
||||
n := len(tag) - len(strings.TrimLeftFunc(tag, func(r rune) bool {
|
||||
return !strings.ContainsRune(",\\'\"`", r) // reserve comma, backslash, and quotes
|
||||
}))
|
||||
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, false)
|
||||
if err2 != nil {
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has malformed `json` tag: %v", sf.Name, err2))
|
||||
}
|
||||
}
|
||||
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, []byte(out.name), &jsonflags.Flags{})
|
||||
out.quotedName = string(b)
|
||||
out.nameNeedEscape = jsonwire.NeedEscape([]byte(out.name))
|
||||
|
||||
// Handle any additional tag options (if any).
|
||||
var wasFormat bool
|
||||
seenOpts := make(map[string]bool)
|
||||
for len(tag) > 0 {
|
||||
// Consume comma delimiter.
|
||||
if 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 = cmp.Or(err, fmt.Errorf("Go struct field %s has malformed `json` tag: invalid trailing ',' character", sf.Name))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Consume and process the tag option.
|
||||
opt, n, err2 := consumeTagOption(tag, false)
|
||||
if err2 != nil {
|
||||
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 = 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 = 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 "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, false)
|
||||
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 "embed":
|
||||
out.embed = true
|
||||
case "omitzero":
|
||||
out.omitzero = true
|
||||
case "omitempty":
|
||||
out.omitempty = true
|
||||
case "string":
|
||||
out.string = true
|
||||
case "format":
|
||||
if !strings.HasPrefix(tag, ":") {
|
||||
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, true)
|
||||
if err2 != nil {
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s has malformed value for `format` tag option: %v", sf.Name, err2))
|
||||
break
|
||||
} else if opt == "" {
|
||||
err = cmp.Or(err, fmt.Errorf("Go struct field %s cannot have empty value for `format` tag option", sf.Name))
|
||||
break
|
||||
}
|
||||
tag = tag[n:]
|
||||
out.format = opt
|
||||
wasFormat = true
|
||||
default:
|
||||
// Reject keys that resemble one of the supported options.
|
||||
// This catches invalid mutants such as "omitEmpty" or "omit_empty".
|
||||
normOpt := strings.ReplaceAll(strings.ToLower(opt), "_", "")
|
||||
switch normOpt {
|
||||
case "case", "embed", "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
|
||||
// forward compatible to insert arbitrary tag options since
|
||||
// a future version of this package may understand that tag.
|
||||
}
|
||||
|
||||
// Reject duplicates.
|
||||
switch {
|
||||
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 = 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, allowQuoted bool) (string, int, error) {
|
||||
// For legacy compatibility with v1, assume options are comma-separated.
|
||||
i := strings.IndexByte(in, ',')
|
||||
if i < 0 {
|
||||
i = len(in)
|
||||
}
|
||||
|
||||
switch r, _ := utf8.DecodeRuneInString(in); {
|
||||
// Option as a Go identifier.
|
||||
case r == '_' || unicode.IsLetter(r):
|
||||
n := len(in) - len(strings.TrimLeftFunc(in, isLetterOrDigit))
|
||||
return in[:n], n, nil
|
||||
// Option as a single-quoted string.
|
||||
case r == '\'':
|
||||
if !allowQuoted {
|
||||
return in[:i], i, fmt.Errorf("invalid character %q at start of option (expecting Unicode letter)", r)
|
||||
}
|
||||
|
||||
// The grammar is nearly identical to a double-quoted Go string literal,
|
||||
// but uses single quotes as the terminators. The reason for a custom
|
||||
// grammar is because both backtick and double quotes cannot be used
|
||||
// verbatim in a struct tag.
|
||||
//
|
||||
// Convert a single-quoted string to a double-quote string and rely on
|
||||
// strconv.Unquote to handle the rest.
|
||||
var inEscape bool
|
||||
b := []byte{'"'}
|
||||
n := len(`'`)
|
||||
for len(in) > n {
|
||||
r, rn := utf8.DecodeRuneInString(in[n:])
|
||||
switch {
|
||||
case inEscape:
|
||||
if r == '\'' {
|
||||
b = b[:len(b)-1] // remove escape character: `\'` => `'`
|
||||
}
|
||||
inEscape = false
|
||||
case r == '\\':
|
||||
inEscape = true
|
||||
case r == '"':
|
||||
b = append(b, '\\') // insert escape character: `"` => `\"`
|
||||
case r == '\'':
|
||||
b = append(b, '"')
|
||||
n += len(`'`)
|
||||
out, err := strconv.Unquote(string(b))
|
||||
if err != nil {
|
||||
return in[:i], i, fmt.Errorf("invalid single-quoted string: %s", in[:n])
|
||||
}
|
||||
return out, n, nil
|
||||
}
|
||||
b = append(b, in[n:][:rn]...)
|
||||
n += rn
|
||||
}
|
||||
if n > 10 {
|
||||
n = 10 // limit the amount of context printed in the error
|
||||
}
|
||||
return in[:i], i, fmt.Errorf("single-quoted string not terminated: %s...", in[:n])
|
||||
case len(in) == 0:
|
||||
return in[:i], i, io.ErrUnexpectedEOF
|
||||
default:
|
||||
if !allowQuoted {
|
||||
return in[:i], i, fmt.Errorf("invalid character %q at start of option (expecting Unicode letter)", r)
|
||||
}
|
||||
return in[:i], i, fmt.Errorf("invalid character %q at start of option (expecting Unicode letter or single quote)", r)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
58
tools/tsgo/vendor/github.com/go-json-experiment/json/fold.go
generated
vendored
Normal file
58
tools/tsgo/vendor/github.com/go-json-experiment/json/fold.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// foldName returns a folded string such that foldName(x) == foldName(y)
|
||||
// is similar to strings.EqualFold(x, y), but ignores underscore and dashes.
|
||||
// This allows foldName to match common naming conventions.
|
||||
func foldName(in []byte) []byte {
|
||||
// This is inlinable to take advantage of "function outlining".
|
||||
// See https://blog.filippo.io/efficient-go-apis-with-the-inliner/
|
||||
var arr [32]byte // large enough for most JSON names
|
||||
return appendFoldedName(arr[:0], in)
|
||||
}
|
||||
func appendFoldedName(out, in []byte) []byte {
|
||||
for i := 0; i < len(in); {
|
||||
// Handle single-byte ASCII.
|
||||
if c := in[i]; c < utf8.RuneSelf {
|
||||
if c != '_' && c != '-' {
|
||||
if 'a' <= c && c <= 'z' {
|
||||
c -= 'a' - 'A'
|
||||
}
|
||||
out = append(out, c)
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
// Handle multi-byte Unicode.
|
||||
r, n := utf8.DecodeRune(in[i:])
|
||||
out = utf8.AppendRune(out, foldRune(r))
|
||||
i += n
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// foldRune is a variation on unicode.SimpleFold that returns the same rune
|
||||
// for all runes in the same fold set.
|
||||
//
|
||||
// Invariant:
|
||||
//
|
||||
// foldRune(x) == foldRune(y) ⇔ strings.EqualFold(string(x), string(y))
|
||||
func foldRune(r rune) rune {
|
||||
for {
|
||||
r2 := unicode.SimpleFold(r)
|
||||
if r2 <= r {
|
||||
return r2 // smallest character in the fold set
|
||||
}
|
||||
r = r2
|
||||
}
|
||||
}
|
||||
88
tools/tsgo/vendor/github.com/go-json-experiment/json/intern.go
generated
vendored
Normal file
88
tools/tsgo/vendor/github.com/go-json-experiment/json/intern.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// stringCache is a cache for strings converted from a []byte.
|
||||
type stringCache = [256]string // 256*unsafe.Sizeof(string("")) => 4KiB
|
||||
|
||||
// makeString returns the string form of b.
|
||||
// It returns a pre-allocated string from c if present, otherwise
|
||||
// it allocates a new string, inserts it into the cache, and returns it.
|
||||
func makeString(c *stringCache, b []byte) string {
|
||||
const (
|
||||
minCachedLen = 2 // single byte strings are already interned by the runtime
|
||||
maxCachedLen = 256 // large enough for UUIDs, IPv6 addresses, SHA-256 checksums, etc.
|
||||
)
|
||||
if c == nil || len(b) < minCachedLen || len(b) > maxCachedLen {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Compute a hash from the fixed-width prefix and suffix of the string.
|
||||
// This ensures hashing a string is a constant time operation.
|
||||
var h uint32
|
||||
switch {
|
||||
case len(b) >= 8:
|
||||
lo := binary.LittleEndian.Uint64(b[:8])
|
||||
hi := binary.LittleEndian.Uint64(b[len(b)-8:])
|
||||
h = hash64(uint32(lo), uint32(lo>>32)) ^ hash64(uint32(hi), uint32(hi>>32))
|
||||
case len(b) >= 4:
|
||||
lo := binary.LittleEndian.Uint32(b[:4])
|
||||
hi := binary.LittleEndian.Uint32(b[len(b)-4:])
|
||||
h = hash64(lo, hi)
|
||||
case len(b) >= 2:
|
||||
lo := binary.LittleEndian.Uint16(b[:2])
|
||||
hi := binary.LittleEndian.Uint16(b[len(b)-2:])
|
||||
h = hash64(uint32(lo), uint32(hi))
|
||||
}
|
||||
|
||||
// Check the cache for the string.
|
||||
i := h % uint32(len(*c))
|
||||
if s := (*c)[i]; s == string(b) {
|
||||
return s
|
||||
}
|
||||
s := string(b)
|
||||
(*c)[i] = s
|
||||
return s
|
||||
}
|
||||
|
||||
// hash64 returns the hash of two uint32s as a single uint32.
|
||||
func hash64(lo, hi uint32) uint32 {
|
||||
// If avalanche=true, this is identical to XXH32 hash on an 8B string:
|
||||
// var b [8]byte
|
||||
// binary.LittleEndian.PutUint32(b[:4], lo)
|
||||
// binary.LittleEndian.PutUint32(b[4:], hi)
|
||||
// return xxhash.Sum32(b[:])
|
||||
const (
|
||||
prime1 = 0x9e3779b1
|
||||
prime2 = 0x85ebca77
|
||||
prime3 = 0xc2b2ae3d
|
||||
prime4 = 0x27d4eb2f
|
||||
prime5 = 0x165667b1
|
||||
)
|
||||
h := prime5 + uint32(8)
|
||||
h += lo * prime3
|
||||
h = bits.RotateLeft32(h, 17) * prime4
|
||||
h += hi * prime3
|
||||
h = bits.RotateLeft32(h, 17) * prime4
|
||||
// Skip final mix (avalanche) step of XXH32 for performance reasons.
|
||||
// Empirical testing shows that the improvements in unbiased distribution
|
||||
// do not outweigh the extra cost in computational complexity.
|
||||
const avalanche = false
|
||||
if avalanche {
|
||||
h ^= h >> 15
|
||||
h *= prime2
|
||||
h ^= h >> 13
|
||||
h *= prime3
|
||||
h ^= h >> 16
|
||||
}
|
||||
return h
|
||||
}
|
||||
42
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/internal.go
generated
vendored
Normal file
42
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/internal.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
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.
|
||||
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")
|
||||
ErrNilInterface = errors.New("cannot derive concrete type for nil interface with finite type set")
|
||||
)
|
||||
|
||||
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 an 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
|
||||
)
|
||||
223
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonflags/flags.go
generated
vendored
Normal file
223
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonflags/flags.go
generated
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
// jsonflags implements all the optional boolean flags.
|
||||
// These flags are shared across both "json", "jsontext", and "jsonopts".
|
||||
package jsonflags
|
||||
|
||||
import "github.com/go-json-experiment/json/internal"
|
||||
|
||||
// Bools represents zero or more boolean flags, all set to true or false.
|
||||
// The least-significant bit is the boolean value of all flags in the set.
|
||||
// The remaining bits identify which particular flags.
|
||||
//
|
||||
// In common usage, this is OR'd with 0 or 1. For example:
|
||||
// - (AllowInvalidUTF8 | 0) means "AllowInvalidUTF8 is false"
|
||||
// - (Multiline | Indent | 1) means "Multiline and Indent are true"
|
||||
type Bools uint64
|
||||
|
||||
func (Bools) JSONOptions(internal.NotForPublicUse) {}
|
||||
|
||||
const (
|
||||
// AllFlags is the set of all flags.
|
||||
AllFlags = AllCoderFlags | AllArshalV2Flags | AllArshalV1Flags
|
||||
|
||||
// AllCoderFlags is the set of all encoder/decoder flags.
|
||||
AllCoderFlags = (maxCoderFlag - 1) - initFlag
|
||||
|
||||
// AllArshalV2Flags is the set of all v2 marshal/unmarshal flags.
|
||||
AllArshalV2Flags = (maxArshalV2Flag - 1) - (maxCoderFlag - 1)
|
||||
|
||||
// AllArshalV1Flags is the set of all v1 marshal/unmarshal flags.
|
||||
AllArshalV1Flags = (maxArshalV1Flag - 1) - (maxArshalV2Flag - 1)
|
||||
|
||||
// NonBooleanFlags is the set of non-boolean flags,
|
||||
// where the value is some other concrete Go type.
|
||||
// The value of the flag is stored within jsonopts.Struct.
|
||||
NonBooleanFlags = 0 |
|
||||
Indent |
|
||||
IndentPrefix |
|
||||
ByteLimit |
|
||||
DepthLimit |
|
||||
Marshalers |
|
||||
Unmarshalers |
|
||||
FormatTag
|
||||
|
||||
// DefaultV1Flags is the set of boolean flags that default to true under
|
||||
// v1 semantics. None of the non-boolean flags differ between v1 and v2.
|
||||
DefaultV1Flags = 0 |
|
||||
AllowDuplicateNames |
|
||||
AllowInvalidUTF8 |
|
||||
EscapeForHTML |
|
||||
EscapeForJS |
|
||||
PreserveRawStrings |
|
||||
Deterministic |
|
||||
FormatNilMapAsNull |
|
||||
FormatNilSliceAsNull |
|
||||
MatchCaseInsensitiveNames |
|
||||
CallMethodsWithLegacySemantics |
|
||||
FormatByteArrayAsArray |
|
||||
FormatBytesWithLegacySemantics |
|
||||
FormatDurationAsNano |
|
||||
MatchCaseSensitiveDelimiter |
|
||||
MergeWithLegacySemantics |
|
||||
OmitEmptyWithLegacySemantics |
|
||||
ParseBytesWithLooseRFC4648 |
|
||||
ParseTimeWithLooseRFC3339 |
|
||||
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
|
||||
|
||||
// CanonicalizeNumbers is the set of flags related to raw number canonicalization.
|
||||
CanonicalizeNumbers = CanonicalizeRawInts | CanonicalizeRawFloats
|
||||
|
||||
// TagFlags is the set of flags related to the presence of struct field tags.
|
||||
// Tags have non-recursive effects, where the tag only applies to
|
||||
// the top-level of the field value itself.
|
||||
// Whenever descending into a JSON object or array, these flags are cleared.
|
||||
TagFlags = StringTag | FormatTag
|
||||
)
|
||||
|
||||
// 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
|
||||
CanonicalizeRawInts // encode only
|
||||
CanonicalizeRawFloats // encode only
|
||||
ReorderRawObjects // encode only
|
||||
EscapeForHTML // encode only
|
||||
EscapeForJS // encode only
|
||||
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
|
||||
)
|
||||
|
||||
// Marshal and Unmarshal flags (for v2).
|
||||
const (
|
||||
_ Bools = (maxCoderFlag >> 1) << iota
|
||||
|
||||
StringifyNumbers // marshal or unmarshal
|
||||
Deterministic // marshal only
|
||||
FormatNilMapAsNull // marshal only
|
||||
FormatNilSliceAsNull // marshal only
|
||||
OmitZeroStructFields // marshal only
|
||||
MatchCaseInsensitiveNames // marshal or unmarshal
|
||||
RejectUnknownMembers // unmarshal only
|
||||
Marshalers // marshal only; non-boolean flag
|
||||
Unmarshalers // unmarshal only; non-boolean flag
|
||||
StringTag // marshal or unmarshal
|
||||
FormatTag // marshal or unmarshal; non-boolean flag
|
||||
FormatTagSupported // marshal or unmarshal
|
||||
|
||||
maxArshalV2Flag
|
||||
)
|
||||
|
||||
// Marshal and Unmarshal flags (for v1).
|
||||
const (
|
||||
_ Bools = (maxArshalV2Flag >> 1) << iota
|
||||
|
||||
CallMethodsWithLegacySemantics // marshal or unmarshal
|
||||
FormatByteArrayAsArray // marshal or unmarshal
|
||||
FormatBytesWithLegacySemantics // marshal or unmarshal
|
||||
FormatDurationAsNano // marshal or unmarshal
|
||||
MatchCaseSensitiveDelimiter // marshal or unmarshal
|
||||
MergeWithLegacySemantics // unmarshal
|
||||
OmitEmptyWithLegacySemantics // marshal
|
||||
ParseBytesWithLooseRFC4648 // unmarshal
|
||||
ParseTimeWithLooseRFC3339 // unmarshal
|
||||
ReportErrorsWithLegacySemantics // marshal or unmarshal
|
||||
StringifyWithLegacySemantics // marshal or unmarshal
|
||||
UnmarshalAnyWithRawNumber // unmarshal; for internal use by jsonv1.Decoder.UseNumber
|
||||
UnmarshalArrayFromAnyLength // unmarshal
|
||||
|
||||
maxArshalV1Flag
|
||||
)
|
||||
|
||||
// bitsUsed is the number of bits used in the 64-bit boolean flags
|
||||
const bitsUsed = 43
|
||||
|
||||
// Static compile check that bitsUsed and maxArshalV1Flag are in sync.
|
||||
const _ = uint64((1<<bitsUsed)-maxArshalV1Flag) + uint64(maxArshalV1Flag-(1<<bitsUsed))
|
||||
|
||||
// Flags is a set of boolean flags.
|
||||
// If the presence bit is zero, then the value bit must also be zero.
|
||||
// The least-significant bit of both fields is always zero.
|
||||
//
|
||||
// Unlike Bools, which can represent a set of bools that are all true or false,
|
||||
// Flags represents a set of bools, each individually may be true or false.
|
||||
type Flags struct{ Presence, Values uint64 }
|
||||
|
||||
// Join joins two sets of flags such that the latter takes precedence.
|
||||
func (dst *Flags) Join(src Flags) {
|
||||
// Copy over all source presence bits over to the destination (using OR),
|
||||
// then invert the source presence bits to clear out source value (using AND-NOT),
|
||||
// then copy over source value bits over to the destination (using OR).
|
||||
// e.g., dst := Flags{Presence: 0b_1100_0011, Values: 0b_1000_0011}
|
||||
// e.g., src := Flags{Presence: 0b_0101_1010, Values: 0b_1001_0010}
|
||||
dst.Presence |= src.Presence // e.g., 0b_1100_0011 | 0b_0101_1010 -> 0b_1101_1011
|
||||
dst.Values &= ^src.Presence // e.g., 0b_1000_0011 & 0b_1010_0101 -> 0b_1000_0001
|
||||
dst.Values |= src.Values // e.g., 0b_1000_0001 | 0b_1001_0010 -> 0b_1001_0011
|
||||
}
|
||||
|
||||
// Set sets both the presence and value for the provided bool (or set of bools).
|
||||
func (fs *Flags) Set(f Bools) {
|
||||
// Select out the bits for the flag identifiers (everything except LSB),
|
||||
// then set the presence for all the identifier bits (using OR),
|
||||
// then invert the identifier bits to clear out the values (using AND-NOT),
|
||||
// then copy over all the identifier bits to the value if LSB is 1.
|
||||
// e.g., fs := Flags{Presence: 0b_0101_0010, Values: 0b_0001_0010}
|
||||
// e.g., f := 0b_1001_0001
|
||||
id := uint64(f) &^ uint64(1) // e.g., 0b_1001_0001 & 0b_1111_1110 -> 0b_1001_0000
|
||||
fs.Presence |= id // e.g., 0b_0101_0010 | 0b_1001_0000 -> 0b_1101_0010
|
||||
fs.Values &= ^id // e.g., 0b_0001_0010 & 0b_0110_1111 -> 0b_0000_0010
|
||||
fs.Values |= uint64(f&1) * id // e.g., 0b_0000_0010 | 0b_1001_0000 -> 0b_1001_0010
|
||||
}
|
||||
|
||||
// Get reports whether the bool (or any of the bools) is true.
|
||||
// This is generally only used with a singular bool.
|
||||
// The value bit of f (i.e., the LSB) is ignored.
|
||||
func (fs Flags) Get(f Bools) bool {
|
||||
return fs.Values&uint64(f) > 0
|
||||
}
|
||||
|
||||
// Has reports whether the bool (or any of the bools) is set.
|
||||
// The value bit of f (i.e., the LSB) is ignored.
|
||||
func (fs Flags) Has(f Bools) bool {
|
||||
return fs.Presence&uint64(f) > 0
|
||||
}
|
||||
|
||||
// Clear clears both the presence and value for the provided bool or bools.
|
||||
// The value bit of f (i.e., the LSB) is ignored.
|
||||
func (fs *Flags) Clear(f Bools) {
|
||||
// Invert f to produce a mask to clear all bits in f (using AND).
|
||||
// e.g., fs := Flags{Presence: 0b_0101_0010, Values: 0b_0001_0010}
|
||||
// e.g., f := 0b_0001_1000
|
||||
mask := uint64(^f) // e.g., 0b_0001_1000 -> 0b_1110_0111
|
||||
fs.Presence &= mask // e.g., 0b_0101_0010 & 0b_1110_0111 -> 0b_0100_0010
|
||||
fs.Values &= mask // e.g., 0b_0001_0010 & 0b_1110_0111 -> 0b_0000_0010
|
||||
}
|
||||
217
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonopts/options.go
generated
vendored
Normal file
217
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonopts/options.go
generated
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsonopts
|
||||
|
||||
import (
|
||||
"github.com/go-json-experiment/json/internal"
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
)
|
||||
|
||||
// Options is the common options type shared across json packages.
|
||||
type Options interface {
|
||||
// JSONOptions is exported so related json packages can implement Options.
|
||||
JSONOptions(internal.NotForPublicUse)
|
||||
}
|
||||
|
||||
// experimentalFormatTagSupporter is a special option method that allows
|
||||
// [github.com/go-json-experiment/json.ExperimentalSupportFormatTag]
|
||||
// to enable experimental `format` tag functionality at runtime.
|
||||
type experimentalFormatTagSupporter interface{ ExperimentalSupportFormatTag() bool }
|
||||
|
||||
// Struct is the combination of all options in struct form.
|
||||
// This is efficient to pass down the call stack and to query.
|
||||
type Struct struct {
|
||||
Flags jsonflags.Flags
|
||||
|
||||
CoderValues
|
||||
ArshalValues
|
||||
}
|
||||
|
||||
type CoderValues struct {
|
||||
Indent string // jsonflags.Indent
|
||||
IndentPrefix string // jsonflags.IndentPrefix
|
||||
ByteLimit int64 // jsonflags.ByteLimit
|
||||
DepthLimit int // jsonflags.DepthLimit
|
||||
}
|
||||
|
||||
type ArshalValues struct {
|
||||
// The Marshalers and Unmarshalers fields use the any type to avoid a
|
||||
// concrete dependency on *json.Marshalers and *json.Unmarshalers,
|
||||
// which would in turn create a dependency on the "reflect" package.
|
||||
|
||||
Marshalers any // jsonflags.Marshalers
|
||||
Unmarshalers any // jsonflags.Unmarshalers
|
||||
|
||||
Format string // valid if jsonflags.FormatTag is set
|
||||
}
|
||||
|
||||
// DefaultOptionsV2 is the set of all options that define default v2 behavior.
|
||||
var DefaultOptionsV2 = Struct{
|
||||
Flags: jsonflags.Flags{
|
||||
Presence: uint64(jsonflags.DefaultV1Flags),
|
||||
Values: uint64(0), // all flags in DefaultV1Flags are false
|
||||
},
|
||||
}
|
||||
|
||||
// DefaultOptionsV1 is the set of all options that define default v1 behavior.
|
||||
var DefaultOptionsV1 = Struct{
|
||||
Flags: jsonflags.Flags{
|
||||
Presence: uint64(jsonflags.DefaultV1Flags),
|
||||
Values: uint64(jsonflags.DefaultV1Flags), // all flags in DefaultV1Flags are true
|
||||
},
|
||||
}
|
||||
|
||||
func (*Struct) JSONOptions(internal.NotForPublicUse) {}
|
||||
|
||||
// GetUnknownOption is injected by the "json" package to handle Options
|
||||
// declared in that package so that "jsonopts" can handle them.
|
||||
var GetUnknownOption = func(Struct, Options) (any, bool) { panic("unknown option") }
|
||||
|
||||
func GetOption[T any](opts Options, setter func(T) Options) (T, bool) {
|
||||
// Collapse the options to *Struct to simplify lookup.
|
||||
structOpts, ok := opts.(*Struct)
|
||||
if !ok {
|
||||
var structOpts2 Struct
|
||||
structOpts2.Join(opts)
|
||||
structOpts = &structOpts2
|
||||
}
|
||||
|
||||
// Lookup the option based on the return value of the setter.
|
||||
var zero T
|
||||
switch opt := setter(zero).(type) {
|
||||
case jsonflags.Bools:
|
||||
v := structOpts.Flags.Get(opt)
|
||||
ok := structOpts.Flags.Has(opt)
|
||||
if !ok && opt == jsonflags.StringifyNumbers && structOpts.Flags.Get(jsonflags.StringTag) {
|
||||
return any(true).(T), true // check also whether the option is specified via a `string` tag
|
||||
}
|
||||
return any(v).(T), ok
|
||||
case experimentalFormatTagSupporter:
|
||||
v := structOpts.Flags.Get(jsonflags.FormatTagSupported)
|
||||
ok := structOpts.Flags.Has(jsonflags.FormatTagSupported)
|
||||
return any(v).(T), ok
|
||||
case Indent:
|
||||
if !structOpts.Flags.Has(jsonflags.Indent) {
|
||||
return zero, false
|
||||
}
|
||||
return any(structOpts.Indent).(T), true
|
||||
case IndentPrefix:
|
||||
if !structOpts.Flags.Has(jsonflags.IndentPrefix) {
|
||||
return zero, false
|
||||
}
|
||||
return any(structOpts.IndentPrefix).(T), true
|
||||
case ByteLimit:
|
||||
if !structOpts.Flags.Has(jsonflags.ByteLimit) {
|
||||
return zero, false
|
||||
}
|
||||
return any(structOpts.ByteLimit).(T), true
|
||||
case DepthLimit:
|
||||
if !structOpts.Flags.Has(jsonflags.DepthLimit) {
|
||||
return zero, false
|
||||
}
|
||||
return any(structOpts.DepthLimit).(T), true
|
||||
default:
|
||||
v, ok := GetUnknownOption(*structOpts, opt)
|
||||
return v.(T), ok
|
||||
}
|
||||
}
|
||||
|
||||
// JoinUnknownOption is injected by the "json" package to handle Options
|
||||
// declared in that package so that "jsonopts" can handle them.
|
||||
var JoinUnknownOption = func(Struct, Options) Struct { panic("unknown option") }
|
||||
|
||||
func (dst *Struct) Join(srcs ...Options) {
|
||||
for _, src := range srcs {
|
||||
switch src := src.(type) {
|
||||
case nil:
|
||||
continue
|
||||
case jsonflags.Bools:
|
||||
dst.Flags.Set(src)
|
||||
case experimentalFormatTagSupporter:
|
||||
if src.ExperimentalSupportFormatTag() {
|
||||
dst.Flags.Set(jsonflags.FormatTagSupported | 1)
|
||||
} else {
|
||||
dst.Flags.Set(jsonflags.FormatTagSupported | 0)
|
||||
}
|
||||
case Indent:
|
||||
dst.Flags.Set(jsonflags.Multiline | jsonflags.Indent | 1)
|
||||
dst.Indent = string(src)
|
||||
case IndentPrefix:
|
||||
dst.Flags.Set(jsonflags.Multiline | jsonflags.IndentPrefix | 1)
|
||||
dst.IndentPrefix = string(src)
|
||||
case ByteLimit:
|
||||
dst.Flags.Set(jsonflags.ByteLimit | 1)
|
||||
dst.ByteLimit = int64(src)
|
||||
case DepthLimit:
|
||||
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) {
|
||||
dst.Indent = src.Indent
|
||||
}
|
||||
if src.Flags.Has(jsonflags.IndentPrefix) {
|
||||
dst.IndentPrefix = src.IndentPrefix
|
||||
}
|
||||
if src.Flags.Has(jsonflags.ByteLimit) {
|
||||
dst.ByteLimit = src.ByteLimit
|
||||
}
|
||||
if src.Flags.Has(jsonflags.DepthLimit) {
|
||||
dst.DepthLimit = src.DepthLimit
|
||||
}
|
||||
if src.Flags.Has(jsonflags.Marshalers) {
|
||||
dst.Marshalers = src.Marshalers
|
||||
}
|
||||
if src.Flags.Has(jsonflags.Unmarshalers) {
|
||||
dst.Unmarshalers = src.Unmarshalers
|
||||
}
|
||||
if src.Flags.Has(jsonflags.FormatTag) {
|
||||
dst.Format = src.Format
|
||||
}
|
||||
}
|
||||
default:
|
||||
*dst = JoinUnknownOption(*dst, src)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InitializeMultiline sets default options implied by Multiline.
|
||||
func (s *Struct) InitializeMultiline() {
|
||||
if !s.Flags.Has(jsonflags.SpaceAfterColon) {
|
||||
s.Flags.Set(jsonflags.SpaceAfterColon | 1)
|
||||
}
|
||||
if !s.Flags.Has(jsonflags.SpaceAfterComma) {
|
||||
s.Flags.Set(jsonflags.SpaceAfterComma | 0)
|
||||
}
|
||||
if !s.Flags.Has(jsonflags.Indent) {
|
||||
s.Flags.Set(jsonflags.Indent | 1)
|
||||
s.Indent = "\t"
|
||||
}
|
||||
}
|
||||
|
||||
// ChangedWhitespace reports whether whitespace values have changed.
|
||||
func ChangedWhitespace(s1, s2 Struct) bool {
|
||||
return s1.Flags.Get(jsonflags.Multiline) != s2.Flags.Get(jsonflags.Multiline) ||
|
||||
s1.Flags.Get(jsonflags.SpaceAfterColon) != s2.Flags.Get(jsonflags.SpaceAfterColon) ||
|
||||
s1.Flags.Get(jsonflags.SpaceAfterComma) != s2.Flags.Get(jsonflags.SpaceAfterComma) ||
|
||||
(s2.Flags.Get(jsonflags.Multiline) && (s1.Indent != s2.Indent || s1.IndentPrefix != s2.IndentPrefix))
|
||||
}
|
||||
|
||||
type (
|
||||
Indent string // jsontext.WithIndent
|
||||
IndentPrefix string // jsontext.WithIndentPrefix
|
||||
ByteLimit int64 // jsontext.WithByteLimit
|
||||
DepthLimit int // jsontext.WithDepthLimit
|
||||
// type for jsonflags.Marshalers declared in "json" package
|
||||
// type for jsonflags.Unmarshalers declared in "json" package
|
||||
)
|
||||
|
||||
func (Indent) JSONOptions(internal.NotForPublicUse) {}
|
||||
func (IndentPrefix) JSONOptions(internal.NotForPublicUse) {}
|
||||
func (ByteLimit) JSONOptions(internal.NotForPublicUse) {}
|
||||
func (DepthLimit) JSONOptions(internal.NotForPublicUse) {}
|
||||
124
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonopts/options_format.go
generated
vendored
Normal file
124
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonopts/options_format.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright 2026 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsonopts
|
||||
|
||||
// NOTE: While [ExperimentalSupportFormatTag] is exported,
|
||||
// it is in an internal package and thus inaccessible for public use.
|
||||
//
|
||||
// The [github.com/go-json-experiment/json] module is kept in sync
|
||||
// with the Go standard standard library and will expose this option
|
||||
// in a way that public code can now directly reference.
|
||||
|
||||
type supportFormatTag struct {
|
||||
Options
|
||||
v bool
|
||||
}
|
||||
|
||||
var (
|
||||
enableFormatTag = supportFormatTag{v: true}
|
||||
disableFormatTag = supportFormatTag{v: false}
|
||||
)
|
||||
|
||||
// ExperimentalSupportFormatTag is a marker method that is specially
|
||||
// recognized by the "github.com/go-json-experiment/json" runtime.
|
||||
func (o *supportFormatTag) ExperimentalSupportFormatTag() bool {
|
||||
return o.v
|
||||
}
|
||||
|
||||
// ExperimentalSupportFormatTag enables support for the `format` tag.
|
||||
//
|
||||
// WARNING: This is an experimental feature and will be removed in the future
|
||||
// as either a failed experiment or be formally included in "github.com/go-json-experiment/json"
|
||||
// in some semantically similar form, in which case, users of this option
|
||||
// must migrate to the officially supported feature.
|
||||
//
|
||||
// The `format` tag was originally part of the "github.com/go-json-experiment/json" experiment
|
||||
// but support for it was removed (see https://go.dev/issue/79071) for the
|
||||
// initial release of "github.com/go-json-experiment/json" in light of the anticipation that
|
||||
// the Go language might support typed struct tags (https://go.dev/issue/74472).
|
||||
//
|
||||
// Typed struct tags are a more expressive and type-safe way to express format
|
||||
// attributes than the bespoke `format` tag option that implements a miniature
|
||||
// domain-specific language (DSL) within the "json" package itself.
|
||||
//
|
||||
// While "github.com/go-json-experiment/json" was in the experimental phase,
|
||||
// some users were already depending on the `format` tag option.
|
||||
// This experimental option exists to provide a temporary workaround
|
||||
// until the (hopeful) inclusion of typed struct tags and support for
|
||||
// formatting directives in "github.com/go-json-experiment/json" using that language mechanism.
|
||||
//
|
||||
// This option enables support for the `format` tag option, which specifies
|
||||
// a format flag used to specialize the formatting of the field value.
|
||||
// The option is a key-value pair specified as "format:value" where
|
||||
// the value must be either a literal consisting of letters and numbers
|
||||
// (e.g., `format:RFC3339`) or a single-quoted string literal
|
||||
// (e.g., `format:'2006-01-02'`). The interpretation of the format flag
|
||||
// is determined by the struct field type.
|
||||
//
|
||||
// Go types with alternative representations are as follows:
|
||||
//
|
||||
// - A Go []byte or [N]byte is usually represented as a JSON string
|
||||
// containing the binary value encoded using RFC 4648.
|
||||
// If the format is "base64" or unspecified, then this uses RFC 4648, section 4.
|
||||
// If the format is "base64url", then this uses RFC 4648, section 5.
|
||||
// If the format is "base32", then this uses RFC 4648, section 6.
|
||||
// If the format is "base32hex", then this uses RFC 4648, section 7.
|
||||
// If the format is "base16" or "hex", then this uses RFC 4648, section 8.
|
||||
// If the format is "array", then the bytes value is represented as a JSON array
|
||||
// where each element recursively uses the JSON representation of each byte.
|
||||
//
|
||||
// - A Go float is usually represented as a JSON number.
|
||||
// If the format is "nonfinite", then NaN, +Inf, and -Inf are represented as
|
||||
// the JSON strings "NaN", "Infinity", and "-Infinity", respectively.
|
||||
// Without the use of this format, such string values result in a [SemanticError].
|
||||
//
|
||||
// - A nil Go map is usually encoded using an empty JSON object.
|
||||
// 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.
|
||||
//
|
||||
// - A nil Go slice is usually encoded using an empty JSON array.
|
||||
// If the format is "emitnull", then a nil slice is encoded as a JSON null.
|
||||
// If the format is "emitempty", then a nil slice is encoded as an empty JSON array,
|
||||
// regardless of whether [FormatNilSliceAsNull] is specified.
|
||||
//
|
||||
// - A Go pointer usually uses the JSON representation of the underlying value.
|
||||
// The format is forwarded to the marshaling and unmarshaling of the underlying type.
|
||||
//
|
||||
// - A Go [time.Time] is usually represented as a JSON string containing
|
||||
// the timestamp formatted in RFC 3339 with nanosecond precision.
|
||||
// If the format matches one of the format constants declared
|
||||
// in the time package (e.g., RFC1123), then that format is used.
|
||||
// If the format is "unix", "unixmilli", "unixmicro", or "unixnano",
|
||||
// then the timestamp is represented as a possibly fractional JSON number
|
||||
// of the number of seconds (or milliseconds, microseconds, or nanoseconds)
|
||||
// since the Unix epoch, which is January 1st, 1970 at 00:00:00 UTC.
|
||||
// To avoid a fractional component when encoding,
|
||||
// round the timestamp to the relevant unit.
|
||||
// Otherwise if non-empty, the format is used as-is and
|
||||
// encoded using [time.Time.Format] and
|
||||
// decoded using [time.Time.Parse].
|
||||
//
|
||||
// - A Go [time.Duration] usually has no default representation.
|
||||
// If the format is "sec", "milli", "micro", or "nano",
|
||||
// then the duration is represented as a possibly fractional JSON number
|
||||
// of the number of seconds (or milliseconds, microseconds, or nanoseconds).
|
||||
// To avoid a fractional component when encoding,
|
||||
// round the duration to the relevant unit.
|
||||
// If the format is "units", it is represented as a JSON string
|
||||
// encoded using [time.Duration.String] and decoded using [time.ParseDuration]
|
||||
// (e.g., "1h30m" for 1 hour 30 minutes).
|
||||
// If the format is "iso8601", it is represented as a JSON string using the
|
||||
// ISO 8601 standard for durations (e.g., "PT1H30M" for 1 hour 30 minutes)
|
||||
// using only accurate units of hours, minutes, and seconds.
|
||||
func ExperimentalSupportFormatTag(v bool) Options {
|
||||
if v {
|
||||
return &enableFormatTag
|
||||
} else {
|
||||
return &disableFormatTag
|
||||
}
|
||||
}
|
||||
606
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonwire/decode.go
generated
vendored
Normal file
606
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonwire/decode.go
generated
vendored
Normal file
@@ -0,0 +1,606 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsonwire
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math"
|
||||
"slices"
|
||||
"strconv"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type ValueFlags uint
|
||||
|
||||
const (
|
||||
_ ValueFlags = (1 << iota) / 2 // powers of two starting with zero
|
||||
|
||||
stringNonVerbatim // string cannot be naively treated as valid UTF-8
|
||||
stringNonCanonical // string not formatted according to RFC 8785, section 3.2.2.2.
|
||||
// TODO: Track whether a number is a non-integer?
|
||||
)
|
||||
|
||||
func (f *ValueFlags) Join(f2 ValueFlags) { *f |= f2 }
|
||||
func (f ValueFlags) IsVerbatim() bool { return f&stringNonVerbatim == 0 }
|
||||
func (f ValueFlags) IsCanonical() bool { return f&stringNonCanonical == 0 }
|
||||
|
||||
// ConsumeWhitespace consumes leading JSON whitespace per RFC 7159, section 2.
|
||||
func ConsumeWhitespace(b []byte) (n int) {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
for len(b) > n && (b[n] == ' ' || b[n] == '\t' || b[n] == '\r' || b[n] == '\n') {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// ConsumeNull consumes the next JSON null literal per RFC 7159, section 3.
|
||||
// It returns 0 if it is invalid, in which case ConsumeLiteral should be used.
|
||||
func ConsumeNull(b []byte) int {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
const literal = "null"
|
||||
if len(b) >= len(literal) && string(b[:len(literal)]) == literal {
|
||||
return len(literal)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ConsumeFalse consumes the next JSON false literal per RFC 7159, section 3.
|
||||
// It returns 0 if it is invalid, in which case ConsumeLiteral should be used.
|
||||
func ConsumeFalse(b []byte) int {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
const literal = "false"
|
||||
if len(b) >= len(literal) && string(b[:len(literal)]) == literal {
|
||||
return len(literal)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ConsumeTrue consumes the next JSON true literal per RFC 7159, section 3.
|
||||
// It returns 0 if it is invalid, in which case ConsumeLiteral should be used.
|
||||
func ConsumeTrue(b []byte) int {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
const literal = "true"
|
||||
if len(b) >= len(literal) && string(b[:len(literal)]) == literal {
|
||||
return len(literal)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ConsumeLiteral consumes the next JSON literal per RFC 7159, section 3.
|
||||
// If the input appears truncated, it returns io.ErrUnexpectedEOF.
|
||||
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:], "in literal "+lit+" (expecting "+strconv.QuoteRune(rune(lit[i]))+")")
|
||||
}
|
||||
}
|
||||
if len(b) < len(lit) {
|
||||
return len(b), io.ErrUnexpectedEOF
|
||||
}
|
||||
return len(lit), nil
|
||||
}
|
||||
|
||||
// ConsumeSimpleString consumes the next JSON string per RFC 7159, section 7
|
||||
// but is limited to the grammar for an ASCII string without escape sequences.
|
||||
// It returns 0 if it is invalid or more complicated than a simple string,
|
||||
// in which case [ConsumeString] should be called.
|
||||
//
|
||||
// It rejects '<', '>', and '&' for compatibility reasons since these were
|
||||
// always escaped in the v1 implementation. Thus, if this function reports
|
||||
// non-zero then we know that the string would be encoded the same way
|
||||
// under both v1 or v2 escape semantics.
|
||||
func ConsumeSimpleString(b []byte) (n int) {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
if len(b) > 0 && b[0] == '"' {
|
||||
n++
|
||||
for len(b) > n && b[n] < utf8.RuneSelf && escapeASCII[b[n]] == 0 {
|
||||
n++
|
||||
}
|
||||
if uint(len(b)) > uint(n) && b[n] == '"' {
|
||||
n++
|
||||
return n
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ConsumeString consumes the next JSON string per RFC 7159, section 7.
|
||||
// If validateUTF8 is false, then this allows the presence of invalid UTF-8
|
||||
// characters within the string itself.
|
||||
// It reports the number of bytes consumed and whether an error was encountered.
|
||||
// If the input appears truncated, it returns io.ErrUnexpectedEOF.
|
||||
func ConsumeString(flags *ValueFlags, b []byte, validateUTF8 bool) (n int, err error) {
|
||||
return ConsumeStringResumable(flags, b, 0, validateUTF8)
|
||||
}
|
||||
|
||||
// ConsumeStringResumable is identical to [ConsumeString] but supports resuming
|
||||
// from a previous call that returned io.ErrUnexpectedEOF.
|
||||
func ConsumeStringResumable(flags *ValueFlags, b []byte, resumeOffset int, validateUTF8 bool) (n int, err error) {
|
||||
// Consume the leading double quote.
|
||||
switch {
|
||||
case resumeOffset > 0:
|
||||
n = resumeOffset // already handled the leading quote
|
||||
case uint(len(b)) == 0:
|
||||
return n, io.ErrUnexpectedEOF
|
||||
case b[0] == '"':
|
||||
n++
|
||||
default:
|
||||
return n, NewInvalidCharacterError(b[n:], `at start of string (expecting '"')`)
|
||||
}
|
||||
|
||||
// Consume every character in the string.
|
||||
for uint(len(b)) > uint(n) {
|
||||
// Optimize for long sequences of unescaped characters.
|
||||
noEscape := func(c byte) bool {
|
||||
return c < utf8.RuneSelf && ' ' <= c && c != '\\' && c != '"'
|
||||
}
|
||||
for uint(len(b)) > uint(n) && noEscape(b[n]) {
|
||||
n++
|
||||
}
|
||||
if uint(len(b)) <= uint(n) {
|
||||
return n, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// Check for terminating double quote.
|
||||
if b[n] == '"' {
|
||||
n++
|
||||
return n, nil
|
||||
}
|
||||
|
||||
switch r, rn := utf8.DecodeRune(b[n:]); {
|
||||
// Handle UTF-8 encoded byte sequence.
|
||||
// Due to specialized handling of ASCII above, we know that
|
||||
// all normal sequences at this point must be 2 bytes or larger.
|
||||
case rn > 1:
|
||||
n += rn
|
||||
// Handle escape sequence.
|
||||
case r == '\\':
|
||||
flags.Join(stringNonVerbatim)
|
||||
resumeOffset = n
|
||||
if uint(len(b)) < uint(n+2) {
|
||||
return resumeOffset, io.ErrUnexpectedEOF
|
||||
}
|
||||
switch r := b[n+1]; r {
|
||||
case '/':
|
||||
// Forward slash is the only character with 3 representations.
|
||||
// Per RFC 8785, section 3.2.2.2., this must not be escaped.
|
||||
flags.Join(stringNonCanonical)
|
||||
n += 2
|
||||
case '"', '\\', 'b', 'f', 'n', 'r', 't':
|
||||
n += 2
|
||||
case 'u':
|
||||
if uint(len(b)) < uint(n+6) {
|
||||
if hasEscapedUTF16Prefix(b[n:], false) {
|
||||
return resumeOffset, io.ErrUnexpectedEOF
|
||||
}
|
||||
flags.Join(stringNonCanonical)
|
||||
return n, NewInvalidEscapeSequenceError(b[n:])
|
||||
}
|
||||
v1, ok := parseHexUint16(b[n+2 : n+6])
|
||||
if !ok {
|
||||
flags.Join(stringNonCanonical)
|
||||
return n, NewInvalidEscapeSequenceError(b[n : n+6])
|
||||
}
|
||||
// Only certain control characters can use the \uFFFF notation
|
||||
// for canonical formatting (per RFC 8785, section 3.2.2.2.).
|
||||
switch v1 {
|
||||
// \uFFFF notation not permitted for these characters.
|
||||
case '\b', '\f', '\n', '\r', '\t':
|
||||
flags.Join(stringNonCanonical)
|
||||
default:
|
||||
// \uFFFF notation only permitted for control characters.
|
||||
if v1 >= ' ' {
|
||||
flags.Join(stringNonCanonical)
|
||||
} else {
|
||||
// \uFFFF notation must be lower case.
|
||||
for _, c := range b[n+2 : n+6] {
|
||||
if 'A' <= c && c <= 'F' {
|
||||
flags.Join(stringNonCanonical)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
n += 6
|
||||
|
||||
r := rune(v1)
|
||||
if validateUTF8 && utf16.IsSurrogate(r) {
|
||||
if uint(len(b)) < uint(n+6) {
|
||||
if hasEscapedUTF16Prefix(b[n:], true) {
|
||||
return resumeOffset, io.ErrUnexpectedEOF
|
||||
}
|
||||
flags.Join(stringNonCanonical)
|
||||
return n - 6, NewInvalidEscapeSequenceError(b[n-6:])
|
||||
} else if v2, ok := parseHexUint16(b[n+2 : n+6]); b[n] != '\\' || b[n+1] != 'u' || !ok {
|
||||
flags.Join(stringNonCanonical)
|
||||
return n - 6, NewInvalidEscapeSequenceError(b[n-6 : n+6])
|
||||
} else if r = utf16.DecodeRune(rune(v1), rune(v2)); r == utf8.RuneError {
|
||||
flags.Join(stringNonCanonical)
|
||||
return n - 6, NewInvalidEscapeSequenceError(b[n-6 : n+6])
|
||||
} else {
|
||||
n += 6
|
||||
}
|
||||
}
|
||||
default:
|
||||
flags.Join(stringNonCanonical)
|
||||
return n, NewInvalidEscapeSequenceError(b[n : n+2])
|
||||
}
|
||||
// Handle invalid UTF-8.
|
||||
case r == utf8.RuneError:
|
||||
if !utf8.FullRune(b[n:]) {
|
||||
return n, io.ErrUnexpectedEOF
|
||||
}
|
||||
flags.Join(stringNonVerbatim | stringNonCanonical)
|
||||
if validateUTF8 {
|
||||
return n, ErrInvalidUTF8
|
||||
}
|
||||
n++
|
||||
// Handle invalid control characters.
|
||||
case r < ' ':
|
||||
flags.Join(stringNonVerbatim | stringNonCanonical)
|
||||
return n, NewInvalidCharacterError(b[n:], "in string (expecting non-control character)")
|
||||
default:
|
||||
panic("BUG: unhandled character " + QuoteRune(b[n:]))
|
||||
}
|
||||
}
|
||||
return n, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// AppendUnquote appends the unescaped form of a JSON string in src to dst.
|
||||
// Any invalid UTF-8 within the string will be replaced with utf8.RuneError,
|
||||
// but the error will be specified as having encountered such an error.
|
||||
// The input must be an entire JSON string with no surrounding whitespace.
|
||||
func AppendUnquote(dst, src []byte) (v []byte, err error) {
|
||||
dst = slices.Grow(dst, len(src))
|
||||
|
||||
// Consume the leading double quote.
|
||||
var i, n int
|
||||
switch {
|
||||
case uint(len(src)) == 0:
|
||||
return dst, io.ErrUnexpectedEOF
|
||||
case src[0] == '"':
|
||||
i, n = 1, 1
|
||||
default:
|
||||
return dst, NewInvalidCharacterError(src, `at start of string (expecting '"')`)
|
||||
}
|
||||
|
||||
// Consume every character in the string.
|
||||
for uint(len(src)) > uint(n) {
|
||||
// Optimize for long sequences of unescaped characters.
|
||||
noEscape := func(c byte) bool {
|
||||
return c < utf8.RuneSelf && ' ' <= c && c != '\\' && c != '"'
|
||||
}
|
||||
for uint(len(src)) > uint(n) && noEscape(src[n]) {
|
||||
n++
|
||||
}
|
||||
if uint(len(src)) <= uint(n) {
|
||||
dst = append(dst, src[i:n]...)
|
||||
return dst, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// Check for terminating double quote.
|
||||
if src[n] == '"' {
|
||||
dst = append(dst, src[i:n]...)
|
||||
n++
|
||||
if n < len(src) {
|
||||
err = NewInvalidCharacterError(src[n:], "after string value")
|
||||
}
|
||||
return dst, err
|
||||
}
|
||||
|
||||
switch r, rn := utf8.DecodeRune(src[n:]); {
|
||||
// Handle UTF-8 encoded byte sequence.
|
||||
// Due to specialized handling of ASCII above, we know that
|
||||
// all normal sequences at this point must be 2 bytes or larger.
|
||||
case rn > 1:
|
||||
n += rn
|
||||
// Handle escape sequence.
|
||||
case r == '\\':
|
||||
dst = append(dst, src[i:n]...)
|
||||
|
||||
// Handle escape sequence.
|
||||
if uint(len(src)) < uint(n+2) {
|
||||
return dst, io.ErrUnexpectedEOF
|
||||
}
|
||||
switch r := src[n+1]; r {
|
||||
case '"', '\\', '/':
|
||||
dst = append(dst, r)
|
||||
n += 2
|
||||
case 'b':
|
||||
dst = append(dst, '\b')
|
||||
n += 2
|
||||
case 'f':
|
||||
dst = append(dst, '\f')
|
||||
n += 2
|
||||
case 'n':
|
||||
dst = append(dst, '\n')
|
||||
n += 2
|
||||
case 'r':
|
||||
dst = append(dst, '\r')
|
||||
n += 2
|
||||
case 't':
|
||||
dst = append(dst, '\t')
|
||||
n += 2
|
||||
case 'u':
|
||||
if uint(len(src)) < uint(n+6) {
|
||||
if hasEscapedUTF16Prefix(src[n:], false) {
|
||||
return dst, io.ErrUnexpectedEOF
|
||||
}
|
||||
return dst, NewInvalidEscapeSequenceError(src[n:])
|
||||
}
|
||||
v1, ok := parseHexUint16(src[n+2 : n+6])
|
||||
if !ok {
|
||||
return dst, NewInvalidEscapeSequenceError(src[n : n+6])
|
||||
}
|
||||
n += 6
|
||||
|
||||
// Check whether this is a surrogate half.
|
||||
r := rune(v1)
|
||||
if utf16.IsSurrogate(r) {
|
||||
r = utf8.RuneError // assume failure unless the following succeeds
|
||||
if uint(len(src)) < uint(n+6) {
|
||||
if hasEscapedUTF16Prefix(src[n:], true) {
|
||||
return utf8.AppendRune(dst, r), io.ErrUnexpectedEOF
|
||||
}
|
||||
err = NewInvalidEscapeSequenceError(src[n-6:])
|
||||
} else if v2, ok := parseHexUint16(src[n+2 : n+6]); src[n] != '\\' || src[n+1] != 'u' || !ok {
|
||||
err = NewInvalidEscapeSequenceError(src[n-6 : n+6])
|
||||
} else if r = utf16.DecodeRune(rune(v1), rune(v2)); r == utf8.RuneError {
|
||||
err = NewInvalidEscapeSequenceError(src[n-6 : n+6])
|
||||
} else {
|
||||
n += 6
|
||||
}
|
||||
}
|
||||
|
||||
dst = utf8.AppendRune(dst, r)
|
||||
default:
|
||||
return dst, NewInvalidEscapeSequenceError(src[n : n+2])
|
||||
}
|
||||
i = n
|
||||
// Handle invalid UTF-8.
|
||||
case r == utf8.RuneError:
|
||||
dst = append(dst, src[i:n]...)
|
||||
if !utf8.FullRune(src[n:]) {
|
||||
return dst, io.ErrUnexpectedEOF
|
||||
}
|
||||
// NOTE: An unescaped string may be longer than the escaped string
|
||||
// because invalid UTF-8 bytes are being replaced.
|
||||
dst = append(dst, "\uFFFD"...)
|
||||
n += rn
|
||||
i = n
|
||||
err = ErrInvalidUTF8
|
||||
// Handle invalid control characters.
|
||||
case r < ' ':
|
||||
dst = append(dst, src[i:n]...)
|
||||
return dst, NewInvalidCharacterError(src[n:], "in string (expecting non-control character)")
|
||||
default:
|
||||
panic("BUG: unhandled character " + QuoteRune(src[n:]))
|
||||
}
|
||||
}
|
||||
dst = append(dst, src[i:n]...)
|
||||
return dst, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// hasEscapedUTF16Prefix reports whether b is possibly
|
||||
// the truncated prefix of a \uFFFF escape sequence.
|
||||
func hasEscapedUTF16Prefix(b []byte, lowerSurrogateHalf bool) bool {
|
||||
for i := range len(b) {
|
||||
switch c := b[i]; {
|
||||
case i == 0 && c != '\\':
|
||||
return false
|
||||
case i == 1 && c != 'u':
|
||||
return false
|
||||
case i == 2 && lowerSurrogateHalf && c != 'd' && c != 'D':
|
||||
return false // not within ['\uDC00':'\uDFFF']
|
||||
case i == 3 && lowerSurrogateHalf && !('c' <= c && c <= 'f') && !('C' <= c && c <= 'F'):
|
||||
return false // not within ['\uDC00':'\uDFFF']
|
||||
case i >= 2 && i < 6 && !('0' <= c && c <= '9') && !('a' <= c && c <= 'f') && !('A' <= c && c <= 'F'):
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UnquoteMayCopy returns the unescaped form of b.
|
||||
// If there are no escaped characters, the output is simply a subslice of
|
||||
// the input with the surrounding quotes removed.
|
||||
// Otherwise, a new buffer is allocated for the output.
|
||||
// It assumes the input is valid.
|
||||
func UnquoteMayCopy(b []byte, isVerbatim bool) []byte {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
if isVerbatim {
|
||||
return b[len(`"`) : len(b)-len(`"`)]
|
||||
}
|
||||
b, _ = AppendUnquote(nil, b)
|
||||
return b
|
||||
}
|
||||
|
||||
// ConsumeSimpleNumber consumes the next JSON number per RFC 7159, section 6
|
||||
// but is limited to the grammar for a non-negative integer.
|
||||
// It returns 0 if it is invalid or more complicated than a simple integer,
|
||||
// in which case ConsumeNumber should be called.
|
||||
func ConsumeSimpleNumber(b []byte) (n int) {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
if len(b) > 0 {
|
||||
if b[0] == '0' {
|
||||
n++
|
||||
} else if '1' <= b[0] && b[0] <= '9' {
|
||||
n++
|
||||
for len(b) > n && ('0' <= b[n] && b[n] <= '9') {
|
||||
n++
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
if uint(len(b)) <= uint(n) || (b[n] != '.' && b[n] != 'e' && b[n] != 'E') {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ConsumeNumberState uint
|
||||
|
||||
const (
|
||||
consumeNumberInit ConsumeNumberState = iota
|
||||
beforeIntegerDigits
|
||||
withinIntegerDigits
|
||||
beforeFractionalDigits
|
||||
withinFractionalDigits
|
||||
beforeExponentDigits
|
||||
withinExponentDigits
|
||||
)
|
||||
|
||||
// ConsumeNumber consumes the next JSON number per RFC 7159, section 6.
|
||||
// It reports the number of bytes consumed and whether an error was encountered.
|
||||
// If the input appears truncated, it returns io.ErrUnexpectedEOF.
|
||||
//
|
||||
// Note that JSON numbers are not self-terminating.
|
||||
// If the entire input is consumed, then the caller needs to consider whether
|
||||
// there may be subsequent unread data that may still be part of this number.
|
||||
func ConsumeNumber(b []byte) (n int, err error) {
|
||||
n, _, err = ConsumeNumberResumable(b, 0, consumeNumberInit)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ConsumeNumberResumable is identical to ConsumeNumber but supports resuming
|
||||
// from a previous call that returned io.ErrUnexpectedEOF.
|
||||
func ConsumeNumberResumable(b []byte, resumeOffset int, state ConsumeNumberState) (n int, _ ConsumeNumberState, err error) {
|
||||
// Jump to the right state when resuming from a partial consumption.
|
||||
n = resumeOffset
|
||||
if state > consumeNumberInit {
|
||||
switch state {
|
||||
case withinIntegerDigits, withinFractionalDigits, withinExponentDigits:
|
||||
// Consume leading digits.
|
||||
for uint(len(b)) > uint(n) && ('0' <= b[n] && b[n] <= '9') {
|
||||
n++
|
||||
}
|
||||
if uint(len(b)) <= uint(n) {
|
||||
return n, state, nil // still within the same state
|
||||
}
|
||||
state++ // switches "withinX" to "beforeY" where Y is the state after X
|
||||
}
|
||||
switch state {
|
||||
case beforeIntegerDigits:
|
||||
goto beforeInteger
|
||||
case beforeFractionalDigits:
|
||||
goto beforeFractional
|
||||
case beforeExponentDigits:
|
||||
goto beforeExponent
|
||||
default:
|
||||
return n, state, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Consume required integer component (with optional minus sign).
|
||||
beforeInteger:
|
||||
resumeOffset = n
|
||||
if uint(len(b)) > 0 && b[0] == '-' {
|
||||
n++
|
||||
}
|
||||
switch {
|
||||
case uint(len(b)) <= uint(n):
|
||||
return resumeOffset, beforeIntegerDigits, io.ErrUnexpectedEOF
|
||||
case b[n] == '0':
|
||||
n++
|
||||
state = beforeFractionalDigits
|
||||
case '1' <= b[n] && b[n] <= '9':
|
||||
n++
|
||||
for uint(len(b)) > uint(n) && ('0' <= b[n] && b[n] <= '9') {
|
||||
n++
|
||||
}
|
||||
state = withinIntegerDigits
|
||||
default:
|
||||
return n, state, NewInvalidCharacterError(b[n:], "in number (expecting digit)")
|
||||
}
|
||||
|
||||
// Consume optional fractional component.
|
||||
beforeFractional:
|
||||
if uint(len(b)) > uint(n) && b[n] == '.' {
|
||||
resumeOffset = n
|
||||
n++
|
||||
switch {
|
||||
case uint(len(b)) <= uint(n):
|
||||
return resumeOffset, beforeFractionalDigits, io.ErrUnexpectedEOF
|
||||
case '0' <= b[n] && b[n] <= '9':
|
||||
n++
|
||||
default:
|
||||
return n, state, NewInvalidCharacterError(b[n:], "in number (expecting digit)")
|
||||
}
|
||||
for uint(len(b)) > uint(n) && ('0' <= b[n] && b[n] <= '9') {
|
||||
n++
|
||||
}
|
||||
state = withinFractionalDigits
|
||||
}
|
||||
|
||||
// Consume optional exponent component.
|
||||
beforeExponent:
|
||||
if uint(len(b)) > uint(n) && (b[n] == 'e' || b[n] == 'E') {
|
||||
resumeOffset = n
|
||||
n++
|
||||
if uint(len(b)) > uint(n) && (b[n] == '-' || b[n] == '+') {
|
||||
n++
|
||||
}
|
||||
switch {
|
||||
case uint(len(b)) <= uint(n):
|
||||
return resumeOffset, beforeExponentDigits, io.ErrUnexpectedEOF
|
||||
case '0' <= b[n] && b[n] <= '9':
|
||||
n++
|
||||
default:
|
||||
return n, state, NewInvalidCharacterError(b[n:], "in number (expecting digit)")
|
||||
}
|
||||
for uint(len(b)) > uint(n) && ('0' <= b[n] && b[n] <= '9') {
|
||||
n++
|
||||
}
|
||||
state = withinExponentDigits
|
||||
}
|
||||
|
||||
return n, state, nil
|
||||
}
|
||||
|
||||
// parseHexUint16 is similar to strconv.ParseUint,
|
||||
// but operates directly on []byte and is optimized for base-16.
|
||||
// See https://go.dev/issue/42429.
|
||||
func parseHexUint16(b []byte) (v uint16, ok bool) {
|
||||
if len(b) != 4 {
|
||||
return 0, false
|
||||
}
|
||||
for i := range 4 {
|
||||
c := b[i]
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
c = c - '0'
|
||||
case 'a' <= c && c <= 'f':
|
||||
c = 10 + c - 'a'
|
||||
case 'A' <= c && c <= 'F':
|
||||
c = 10 + c - 'A'
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
v = v*16 + uint16(c)
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
// ParseUint parses b as a decimal unsigned integer according to
|
||||
// a strict subset of the JSON number grammar, returning the value if valid.
|
||||
// It returns (0, false) if there is a syntax error and
|
||||
// returns (math.MaxUint64, false) if there is an overflow.
|
||||
func ParseUint(b []byte) (v uint64, ok bool) {
|
||||
const unsafeWidth = 20 // len(fmt.Sprint(uint64(math.MaxUint64)))
|
||||
var n int
|
||||
for ; len(b) > n && ('0' <= b[n] && b[n] <= '9'); n++ {
|
||||
v = 10*v + uint64(b[n]-'0')
|
||||
}
|
||||
switch {
|
||||
case n == 0 || len(b) != n || (b[0] == '0' && string(b) != "0"):
|
||||
return 0, false
|
||||
case n >= unsafeWidth && (b[0] != '1' || v < 1e19 || n > unsafeWidth):
|
||||
return math.MaxUint64, false
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
290
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonwire/encode.go
generated
vendored
Normal file
290
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonwire/encode.go
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsonwire
|
||||
|
||||
import (
|
||||
"math"
|
||||
"slices"
|
||||
"strconv"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
)
|
||||
|
||||
// escapeASCII reports whether the ASCII character needs to be escaped.
|
||||
// It conservatively assumes EscapeForHTML.
|
||||
var escapeASCII = [...]uint8{
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // escape control characters
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // escape control characters
|
||||
0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // escape '"' and '&'
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, // escape '<' and '>'
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // escape '\\'
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
|
||||
// NeedEscape reports whether src needs escaping of any characters.
|
||||
// It conservatively assumes EscapeForHTML and EscapeForJS.
|
||||
// It reports true for inputs with invalid UTF-8.
|
||||
func NeedEscape(src []byte) bool {
|
||||
var i int
|
||||
for uint(len(src)) > uint(i) {
|
||||
if c := src[i]; c < utf8.RuneSelf {
|
||||
if escapeASCII[c] > 0 {
|
||||
return true
|
||||
}
|
||||
i++
|
||||
} else {
|
||||
r, rn := utf8.DecodeRune(src[i:])
|
||||
if r == utf8.RuneError || r == '\u2028' || r == '\u2029' {
|
||||
return true
|
||||
}
|
||||
i += rn
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AppendQuote appends src to dst as a JSON string per RFC 7159, section 7.
|
||||
//
|
||||
// It takes in flags and respects the following:
|
||||
// - EscapeForHTML escapes '<', '>', and '&'.
|
||||
// - EscapeForJS escapes '\u2028' and '\u2029'.
|
||||
// - AllowInvalidUTF8 avoids reporting an error for invalid UTF-8.
|
||||
//
|
||||
// Regardless of whether AllowInvalidUTF8 is specified,
|
||||
// invalid bytes are replaced with the Unicode replacement character ('\ufffd').
|
||||
// If no escape flags are set, then the shortest representable form is used,
|
||||
// which is also the canonical form for strings (RFC 8785, section 3.2.2.2).
|
||||
func AppendQuote(dst, src []byte, flags *jsonflags.Flags) ([]byte, error) {
|
||||
var i, n int
|
||||
var hasInvalidUTF8 bool
|
||||
dst = slices.Grow(dst, len(`"`)+len(src)+len(`"`))
|
||||
dst = append(dst, '"')
|
||||
for uint(len(src)) > uint(n) {
|
||||
if c := src[n]; c < utf8.RuneSelf {
|
||||
// Handle single-byte ASCII.
|
||||
n++
|
||||
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
|
||||
}
|
||||
} else {
|
||||
// Handle multi-byte Unicode.
|
||||
r, rn := utf8.DecodeRune(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]...)
|
||||
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]...)
|
||||
dst = append(dst, '"')
|
||||
if hasInvalidUTF8 && !flags.Get(jsonflags.AllowInvalidUTF8) {
|
||||
return dst, ErrInvalidUTF8
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
func appendEscapedASCII(dst []byte, c byte) []byte {
|
||||
switch c {
|
||||
case '"', '\\':
|
||||
dst = append(dst, '\\', c)
|
||||
case '\b':
|
||||
dst = append(dst, "\\b"...)
|
||||
case '\f':
|
||||
dst = append(dst, "\\f"...)
|
||||
case '\n':
|
||||
dst = append(dst, "\\n"...)
|
||||
case '\r':
|
||||
dst = append(dst, "\\r"...)
|
||||
case '\t':
|
||||
dst = append(dst, "\\t"...)
|
||||
default:
|
||||
dst = appendEscapedUTF16(dst, uint16(c))
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func appendEscapedUnicode(dst []byte, r rune) []byte {
|
||||
if r1, r2 := utf16.EncodeRune(r); r1 != '\ufffd' && r2 != '\ufffd' {
|
||||
dst = appendEscapedUTF16(dst, uint16(r1))
|
||||
dst = appendEscapedUTF16(dst, uint16(r2))
|
||||
} else {
|
||||
dst = appendEscapedUTF16(dst, uint16(r))
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func appendEscapedUTF16(dst []byte, x uint16) []byte {
|
||||
const hex = "0123456789abcdef"
|
||||
return append(dst, '\\', 'u', hex[(x>>12)&0xf], hex[(x>>8)&0xf], hex[(x>>4)&0xf], hex[(x>>0)&0xf])
|
||||
}
|
||||
|
||||
// ReformatString consumes a JSON string from src and appends it to dst,
|
||||
// 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?
|
||||
var valFlags ValueFlags
|
||||
n, err := ConsumeString(&valFlags, src, !flags.Get(jsonflags.AllowInvalidUTF8))
|
||||
if err != nil {
|
||||
return dst, n, err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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(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 to the current flags.
|
||||
b, _ := AppendUnquote(nil, src[:n])
|
||||
dst, _ = AppendQuote(dst, b, flags)
|
||||
return dst, n, nil
|
||||
}
|
||||
|
||||
// AppendFloat appends src to dst as a JSON number per RFC 7159, section 6.
|
||||
// It formats numbers similar to the ES6 number-to-string conversion.
|
||||
// See https://go.dev/issue/14135.
|
||||
//
|
||||
// The output is identical to ECMA-262, 6th edition, section 7.1.12.1 and with
|
||||
// RFC 8785, section 3.2.2.3 for 64-bit floating-point numbers except for -0,
|
||||
// which is formatted as -0 instead of just 0.
|
||||
//
|
||||
// For 32-bit floating-point numbers,
|
||||
// the output is a 32-bit equivalent of the algorithm.
|
||||
// Note that ECMA-262 specifies no algorithm for 32-bit numbers.
|
||||
func AppendFloat(dst []byte, src float64, bits int) []byte {
|
||||
if bits == 32 {
|
||||
src = float64(float32(src))
|
||||
}
|
||||
|
||||
abs := math.Abs(src)
|
||||
fmt := byte('f')
|
||||
if abs != 0 {
|
||||
if bits == 64 && (float64(abs) < 1e-6 || float64(abs) >= 1e21) ||
|
||||
bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
|
||||
fmt = 'e'
|
||||
}
|
||||
}
|
||||
dst = strconv.AppendFloat(dst, src, fmt, -1, bits)
|
||||
if fmt == 'e' {
|
||||
// Clean up e-09 to e-9.
|
||||
n := len(dst)
|
||||
if n >= 4 && dst[n-4] == 'e' && dst[n-3] == '-' && dst[n-2] == '0' {
|
||||
dst[n-2] = dst[n-1]
|
||||
dst = dst[:n-1]
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// ReformatNumber consumes a JSON number 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, flags *jsonflags.Flags) ([]byte, int, error) {
|
||||
n, err := ConsumeNumber(src)
|
||||
if err != nil {
|
||||
return dst, n, err
|
||||
}
|
||||
if !flags.Get(jsonflags.CanonicalizeNumbers) {
|
||||
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:
|
||||
fv = 0 // normalize negative zero as just zero
|
||||
case math.IsInf(fv, +1):
|
||||
fv = +math.MaxFloat64
|
||||
case math.IsInf(fv, -1):
|
||||
fv = -math.MaxFloat64
|
||||
}
|
||||
return AppendFloat(dst, fv, 64), n, nil
|
||||
}
|
||||
209
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonwire/wire.go
generated
vendored
Normal file
209
tools/tsgo/vendor/github.com/go-json-experiment/json/internal/jsonwire/wire.go
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
// Package jsonwire implements stateless functionality for handling JSON text.
|
||||
package jsonwire
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// TrimSuffixWhitespace trims JSON whitespace from the end of b.
|
||||
func TrimSuffixWhitespace(b []byte) []byte {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
n := len(b) - 1
|
||||
for n >= 0 && (b[n] == ' ' || b[n] == '\t' || b[n] == '\r' || b[n] == '\n') {
|
||||
n--
|
||||
}
|
||||
return b[:n+1]
|
||||
}
|
||||
|
||||
// TrimSuffixString trims a valid JSON string at the end of b.
|
||||
// The behavior is undefined if there is not a valid JSON string present.
|
||||
func TrimSuffixString(b []byte) []byte {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
if len(b) > 0 && b[len(b)-1] == '"' {
|
||||
b = b[:len(b)-1]
|
||||
}
|
||||
for len(b) >= 2 && !(b[len(b)-1] == '"' && b[len(b)-2] != '\\') {
|
||||
b = b[:len(b)-1] // trim all characters except an unescaped quote
|
||||
}
|
||||
if len(b) > 0 && b[len(b)-1] == '"' {
|
||||
b = b[:len(b)-1]
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// HasSuffixByte reports whether b ends with c.
|
||||
func HasSuffixByte(b []byte, c byte) bool {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
return len(b) > 0 && b[len(b)-1] == c
|
||||
}
|
||||
|
||||
// TrimSuffixByte removes c from the end of b if it is present.
|
||||
func TrimSuffixByte(b []byte, c byte) []byte {
|
||||
// NOTE: The arguments and logic are kept simple to keep this inlinable.
|
||||
if len(b) > 0 && b[len(b)-1] == c {
|
||||
return b[:len(b)-1]
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// QuoteRune quotes the first rune in the input.
|
||||
func QuoteRune(b []byte) string {
|
||||
r, n := utf8.DecodeRune(b)
|
||||
if r == utf8.RuneError && n == 1 {
|
||||
return `'\x` + strconv.FormatUint(uint64(b[0]), 16) + `'`
|
||||
}
|
||||
return strconv.QuoteRune(r)
|
||||
}
|
||||
|
||||
// CompareUTF16 lexicographically compares x to y according
|
||||
// to the UTF-16 codepoints of the UTF-8 encoded input strings.
|
||||
// This implements the ordering specified in RFC 8785, section 3.2.3.
|
||||
func CompareUTF16(x, y []byte) int {
|
||||
// NOTE: This is an optimized, mostly allocation-free implementation
|
||||
// of CompareUTF16Simple in wire_test.go. FuzzCompareUTF16 verifies that the
|
||||
// two implementations agree on the result of comparing any two strings.
|
||||
isUTF16Self := func(r rune) bool {
|
||||
return ('\u0000' <= r && r <= '\uD7FF') || ('\uE000' <= r && r <= '\uFFFF')
|
||||
}
|
||||
|
||||
for {
|
||||
if len(x) == 0 || len(y) == 0 {
|
||||
return cmp.Compare(len(x), len(y))
|
||||
}
|
||||
|
||||
// ASCII fast-path.
|
||||
if x[0] < utf8.RuneSelf || y[0] < utf8.RuneSelf {
|
||||
if x[0] != y[0] {
|
||||
return cmp.Compare(x[0], y[0])
|
||||
}
|
||||
x, y = x[1:], y[1:]
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode next pair of runes as UTF-8.
|
||||
rx, nx := utf8.DecodeRune(x)
|
||||
ry, ny := utf8.DecodeRune(y)
|
||||
|
||||
selfx := isUTF16Self(rx)
|
||||
selfy := isUTF16Self(ry)
|
||||
switch {
|
||||
// The x rune is a single UTF-16 codepoint, while
|
||||
// the y rune is a surrogate pair of UTF-16 codepoints.
|
||||
case selfx && !selfy:
|
||||
ry, _ = utf16.EncodeRune(ry)
|
||||
// The y rune is a single UTF-16 codepoint, while
|
||||
// the x rune is a surrogate pair of UTF-16 codepoints.
|
||||
case selfy && !selfx:
|
||||
rx, _ = utf16.EncodeRune(rx)
|
||||
}
|
||||
if rx != ry {
|
||||
return cmp.Compare(rx, ry)
|
||||
}
|
||||
|
||||
// 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:]
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
_, n := utf8.DecodeRune([]byte(prefix))
|
||||
return &InvalidTextError{"character", string(prefix[:n]), where}
|
||||
}
|
||||
|
||||
func NewInvalidEscapeSequenceError[Bytes ~[]byte | ~string](what Bytes) error {
|
||||
if len(what) > 6 {
|
||||
return &InvalidTextError{"surrogate pair", string(what), "in string"}
|
||||
}
|
||||
return &InvalidTextError{"escape sequence", string(what), "in string"}
|
||||
}
|
||||
|
||||
type InvalidTextError struct {
|
||||
Label string // e.g., "character" | "escape sequence" | "surrogate pair"
|
||||
What string // raw invalid text
|
||||
Where string // e.g., "in string" | "at start of value"
|
||||
}
|
||||
|
||||
func (e *InvalidTextError) Error() string {
|
||||
var what string
|
||||
needEscape := strings.ContainsFunc(e.What, func(r rune) bool {
|
||||
return r == '`' || r == utf8.RuneError || unicode.IsSpace(r) || !unicode.IsPrint(r)
|
||||
})
|
||||
switch {
|
||||
case utf8.RuneCount([]byte(e.What)) == 1:
|
||||
what = QuoteRune([]byte(e.What))
|
||||
case needEscape:
|
||||
what = strconv.Quote(e.What)
|
||||
default:
|
||||
what = "`" + e.What + "`"
|
||||
}
|
||||
return strings.TrimSuffix("invalid "+e.Label+" "+what+" "+e.Where, " ")
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
555
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/alias.go
generated
vendored
Normal file
555
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/alias.go
generated
vendored
Normal file
@@ -0,0 +1,555 @@
|
||||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by alias_gen.go; DO NOT EDIT.
|
||||
|
||||
//go:build goexperiment.jsonv2 && go1.25
|
||||
|
||||
// Package jsontext implements syntactic processing of JSON
|
||||
// as specified in RFC 4627, RFC 7159, RFC 7493, RFC 8259, and RFC 8785.
|
||||
// JSON is a simple data interchange format that can represent
|
||||
// primitive data types such as booleans, strings, and numbers,
|
||||
// in addition to structured data types such as objects and arrays.
|
||||
//
|
||||
// The [Encoder] and [Decoder] types are used to encode or decode
|
||||
// a stream of JSON tokens or values.
|
||||
//
|
||||
// # Tokens and Values
|
||||
//
|
||||
// A JSON token refers to the basic structural elements of JSON:
|
||||
//
|
||||
// - a JSON literal (i.e., null, true, or false)
|
||||
// - a JSON string (e.g., "hello, world!")
|
||||
// - a JSON number (e.g., 123.456)
|
||||
// - a begin or end delimiter for a JSON object (i.e., '{' or '}')
|
||||
// - a begin or end delimiter for a JSON array (i.e., '[' or ']')
|
||||
//
|
||||
// A JSON token is represented by the [Token] type in Go. Technically,
|
||||
// there are two additional structural characters (i.e., ':' and ','),
|
||||
// but there is no [Token] representation for them since their presence
|
||||
// can be inferred by the structure of the JSON grammar itself.
|
||||
// For example, there must always be an implicit colon between
|
||||
// the name and value of a JSON object member.
|
||||
//
|
||||
// A JSON value refers to a complete unit of JSON data:
|
||||
//
|
||||
// - a JSON literal, string, or number
|
||||
// - a JSON object (e.g., `{"name":"value"}`)
|
||||
// - a JSON array (e.g., `[1,2,3]`)
|
||||
//
|
||||
// A JSON value is represented by the [Value] type in Go and is a []byte
|
||||
// containing the raw textual representation of the value. There is some overlap
|
||||
// between tokens and values as both contain literals, strings, and numbers.
|
||||
// However, only a value can represent the entirety of a JSON object or array.
|
||||
//
|
||||
// The [Encoder] and [Decoder] types contain methods to read or write the next
|
||||
// [Token] or [Value] in a sequence. They maintain a state machine to validate
|
||||
// whether the sequence of JSON tokens and/or values produces a valid JSON.
|
||||
// [Options] may be passed to the [NewEncoder] or [NewDecoder] constructors
|
||||
// to configure the syntactic behavior of encoding and decoding.
|
||||
//
|
||||
// # Terminology
|
||||
//
|
||||
// The terms "encode" and "decode" are used for syntactic functionality
|
||||
// that is concerned with processing JSON based on its grammar, and
|
||||
// the terms "marshal" and "unmarshal" are used for semantic functionality
|
||||
// that determines the meaning of JSON values as Go values and vice-versa.
|
||||
// This package (i.e., [jsontext]) deals with JSON at a syntactic layer,
|
||||
// while [encoding/json/v2] deals with JSON at a semantic layer.
|
||||
// The goal is to provide a clear distinction between functionality that
|
||||
// is purely concerned with encoding versus that of marshaling.
|
||||
// For example, one can directly encode a stream of JSON tokens without
|
||||
// needing to marshal a concrete Go value representing them.
|
||||
// Similarly, one can decode a stream of JSON tokens without
|
||||
// needing to unmarshal them into a concrete Go value.
|
||||
//
|
||||
// This package uses JSON terminology when discussing JSON, which may differ
|
||||
// from related concepts in Go or elsewhere in computing literature.
|
||||
//
|
||||
// - a JSON "object" refers to an unordered collection of name/value members.
|
||||
// - a JSON "array" refers to an ordered sequence of elements.
|
||||
// - a JSON "value" refers to either a literal (i.e., null, false, or true),
|
||||
// string, number, object, or array.
|
||||
//
|
||||
// See RFC 8259 for more information.
|
||||
//
|
||||
// # Specifications
|
||||
//
|
||||
// Relevant specifications include RFC 4627, RFC 7159, RFC 7493, RFC 8259,
|
||||
// and RFC 8785. Each RFC is generally a stricter subset of another RFC.
|
||||
// In increasing order of strictness:
|
||||
//
|
||||
// - RFC 4627 and RFC 7159 do not require (but recommend) the use of UTF-8
|
||||
// and also do not require (but recommend) that object names be unique.
|
||||
// - RFC 8259 requires the use of UTF-8,
|
||||
// but does not require (but recommends) that object names be unique.
|
||||
// - RFC 7493 requires the use of UTF-8
|
||||
// and also requires that object names be unique.
|
||||
// - RFC 8785 defines a canonical representation. It requires the use of UTF-8
|
||||
// and also requires that object names be unique and in a specific ordering.
|
||||
// It specifies exactly how strings and numbers must be formatted.
|
||||
//
|
||||
// The primary difference between RFC 4627 and RFC 7159 is that the former
|
||||
// restricted top-level values to only JSON objects and arrays, while
|
||||
// RFC 7159 and subsequent RFCs permit top-level values to additionally be
|
||||
// JSON nulls, booleans, strings, or numbers.
|
||||
//
|
||||
// By default, this package operates on RFC 7493, but can be configured
|
||||
// to operate according to the other RFC specifications.
|
||||
// RFC 7493 is a stricter subset of RFC 8259 and fully compliant with it.
|
||||
// In particular, it makes specific choices about behavior that RFC 8259
|
||||
// leaves as undefined in order to ensure greater interoperability.
|
||||
//
|
||||
// # Security Considerations
|
||||
//
|
||||
// See the "Security Considerations" section in [encoding/json/v2].
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"encoding/json/jsontext"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Decoder is a streaming decoder for raw JSON tokens and values.
|
||||
// It is used to read a stream of top-level JSON values,
|
||||
// each separated by optional whitespace characters.
|
||||
//
|
||||
// [Decoder.ReadToken] and [Decoder.ReadValue] calls may be interleaved.
|
||||
// For example, the following JSON value:
|
||||
//
|
||||
// {"name":"value","array":[null,false,true,3.14159],"object":{"k":"v"}}
|
||||
//
|
||||
// can be parsed with the following calls (ignoring errors for brevity):
|
||||
//
|
||||
// d.ReadToken() // {
|
||||
// d.ReadToken() // "name"
|
||||
// d.ReadToken() // "value"
|
||||
// d.ReadValue() // "array"
|
||||
// d.ReadToken() // [
|
||||
// d.ReadToken() // null
|
||||
// d.ReadToken() // false
|
||||
// d.ReadValue() // true
|
||||
// d.ReadToken() // 3.14159
|
||||
// d.ReadToken() // ]
|
||||
// d.ReadValue() // "object"
|
||||
// d.ReadValue() // {"k":"v"}
|
||||
// d.ReadToken() // }
|
||||
//
|
||||
// The above is one of many possible sequences of calls and
|
||||
// may not represent the most sensible method to call for any given token/value.
|
||||
// For example, it is probably more common to call [Decoder.ReadToken] to obtain a
|
||||
// string token for object names.
|
||||
type Decoder = jsontext.Decoder
|
||||
|
||||
// NewDecoder constructs a new streaming decoder reading from r.
|
||||
//
|
||||
// If r is a [bytes.Buffer], then the decoder parses directly from the buffer
|
||||
// without first copying the contents to an intermediate buffer.
|
||||
// Additional writes to the buffer must not occur while the decoder is in use.
|
||||
func NewDecoder(r io.Reader, opts ...Options) *Decoder {
|
||||
return jsontext.NewDecoder(r, opts...)
|
||||
}
|
||||
|
||||
// Encoder is a streaming encoder from raw JSON tokens and values.
|
||||
// It is used to write a stream of top-level JSON values,
|
||||
// each terminated with a newline character.
|
||||
//
|
||||
// [Encoder.WriteToken] and [Encoder.WriteValue] calls may be interleaved.
|
||||
// For example, the following JSON value:
|
||||
//
|
||||
// {"name":"value","array":[null,false,true,3.14159],"object":{"k":"v"}}
|
||||
//
|
||||
// can be composed with the following calls (ignoring errors for brevity):
|
||||
//
|
||||
// e.WriteToken(BeginObject) // {
|
||||
// e.WriteToken(String("name")) // "name"
|
||||
// e.WriteToken(String("value")) // "value"
|
||||
// e.WriteValue(Value(`"array"`)) // "array"
|
||||
// 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(EndArray) // ]
|
||||
// e.WriteValue(Value(`"object"`)) // "object"
|
||||
// e.WriteValue(Value(`{"k":"v"}`)) // {"k":"v"}
|
||||
// e.WriteToken(EndObject) // }
|
||||
//
|
||||
// The above is one of many possible sequences of calls and
|
||||
// may not represent the most sensible method to call for any given token/value.
|
||||
// For example, it is probably more common to call [Encoder.WriteToken] with a string
|
||||
// for object names.
|
||||
type Encoder = jsontext.Encoder
|
||||
|
||||
// NewEncoder constructs a new streaming encoder writing to w
|
||||
// configured with the provided options.
|
||||
// It flushes the internal buffer when the buffer is sufficiently full or
|
||||
// when a top-level value has been written.
|
||||
//
|
||||
// If w is a [bytes.Buffer], then the encoder appends directly into the buffer
|
||||
// without copying the contents from an intermediate buffer.
|
||||
func NewEncoder(w io.Writer, opts ...Options) *Encoder {
|
||||
return jsontext.NewEncoder(w, opts...)
|
||||
}
|
||||
|
||||
// SyntacticError is a description of a syntactic error that occurred when
|
||||
// encoding or decoding JSON according to the grammar.
|
||||
//
|
||||
// The contents of this error as produced by this package may change over time.
|
||||
type SyntacticError = jsontext.SyntacticError
|
||||
|
||||
// Options configures [NewEncoder], [Encoder.Reset], [NewDecoder],
|
||||
// and [Decoder.Reset] with specific features.
|
||||
// Each function takes in a variadic list of options, where properties
|
||||
// set in later 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.
|
||||
// Options from this package may be used with the other packages.
|
||||
type Options = jsontext.Options
|
||||
|
||||
// AllowDuplicateNames specifies that JSON objects may contain
|
||||
// duplicate member names. Disabling the duplicate name check may provide
|
||||
// performance benefits, but breaks compliance with RFC 7493, section 2.3.
|
||||
// The input or output will still be compliant with RFC 8259,
|
||||
// which leaves the handling of duplicate names as unspecified behavior.
|
||||
//
|
||||
// This affects either encoding or decoding.
|
||||
func AllowDuplicateNames(v bool) Options {
|
||||
return jsontext.AllowDuplicateNames(v)
|
||||
}
|
||||
|
||||
// AllowInvalidUTF8 specifies that JSON strings may contain invalid UTF-8,
|
||||
// which will be mangled as the Unicode replacement character, U+FFFD.
|
||||
// This causes the encoder or decoder to break compliance with
|
||||
// RFC 7493, section 2.1, and RFC 8259, section 8.1.
|
||||
//
|
||||
// This affects either encoding or decoding.
|
||||
func AllowInvalidUTF8(v bool) Options {
|
||||
return jsontext.AllowInvalidUTF8(v)
|
||||
}
|
||||
|
||||
// EscapeForHTML specifies that '<', '>', and '&' characters within JSON strings
|
||||
// should be escaped as a hexadecimal Unicode codepoint (e.g., \u003c) so that
|
||||
// the output is safe to embed within HTML.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func EscapeForHTML(v bool) Options {
|
||||
return jsontext.EscapeForHTML(v)
|
||||
}
|
||||
|
||||
// EscapeForJS specifies that U+2028 and U+2029 characters within JSON strings
|
||||
// should be escaped as a hexadecimal Unicode codepoint (e.g., \u2028) so that
|
||||
// the output is valid to embed within JavaScript. See RFC 8259, section 12.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func EscapeForJS(v bool) Options {
|
||||
return jsontext.EscapeForJS(v)
|
||||
}
|
||||
|
||||
// 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 PreserveRawStrings(v bool) Options {
|
||||
return jsontext.PreserveRawStrings(v)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return jsontext.CanonicalizeRawInts(v)
|
||||
}
|
||||
|
||||
// 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 is saturated at that value and formatted as such.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func CanonicalizeRawFloats(v bool) Options {
|
||||
return jsontext.CanonicalizeRawFloats(v)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return jsontext.ReorderRawObjects(v)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return jsontext.SpaceAfterColon(v)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return jsontext.SpaceAfterComma(v)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return jsontext.Multiline(v)
|
||||
}
|
||||
|
||||
// WithIndent specifies that the encoder should emit multiline output
|
||||
// where each element in a JSON object or array begins on a new, indented line
|
||||
// beginning with the indent prefix (see [WithIndentPrefix])
|
||||
// followed by one or more copies of indent according to the nesting depth.
|
||||
// The indent must only be composed of space or tab characters.
|
||||
//
|
||||
// If the intent is to emit indented output without a preference for
|
||||
// the particular indent string, then use [Multiline] instead.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
// Use of this option implies [Multiline] being set to true.
|
||||
func WithIndent(indent string) Options {
|
||||
return jsontext.WithIndent(indent)
|
||||
}
|
||||
|
||||
// WithIndentPrefix specifies that the encoder should emit multiline output
|
||||
// where each element in a JSON object or array begins on a new, indented line
|
||||
// beginning with the indent prefix followed by one or more copies of indent
|
||||
// (see [WithIndent]) according to the nesting depth.
|
||||
// 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 [Multiline] being set to true.
|
||||
func WithIndentPrefix(prefix string) Options {
|
||||
return jsontext.WithIndentPrefix(prefix)
|
||||
}
|
||||
|
||||
// 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 jsontext.AppendQuote[Bytes](dst, src)
|
||||
}
|
||||
|
||||
// AppendUnquote appends the decoded interpretation of src as a
|
||||
// double-quoted JSON string literal to dst and returns the extended buffer.
|
||||
// The input src must be a JSON string without any surrounding whitespace.
|
||||
// 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 jsontext.AppendUnquote[Bytes](dst, src)
|
||||
}
|
||||
|
||||
// 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 := ...
|
||||
// serr, ok := errors.AsType[*jsontext.SyntacticError](err)
|
||||
// if ok && 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 = jsontext.ErrDuplicateName
|
||||
|
||||
// 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 = jsontext.ErrNonStringName
|
||||
|
||||
// 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 a 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 = jsontext.Pointer
|
||||
|
||||
// Token represents a lexical JSON token, which may be one of the following:
|
||||
// - a JSON literal (i.e., null, true, or false)
|
||||
// - a JSON string (e.g., "hello, world!")
|
||||
// - a JSON number (e.g., 123.456)
|
||||
// - a begin or end delimiter for a JSON object (i.e., { or } )
|
||||
// - a begin or end delimiter for a JSON array (i.e., [ or ] )
|
||||
//
|
||||
// A Token cannot represent entire array or object values, while a [Value] can.
|
||||
// There is no Token to represent commas and colons since
|
||||
// these structural tokens can be inferred from the surrounding context.
|
||||
//
|
||||
// A Token stores data in one of two forms:
|
||||
//
|
||||
// - As raw JSON text: backed by the internal buffer of the [Decoder]
|
||||
// and only ever produced by [Decoder.ReadToken].
|
||||
// Such a token is only valid until the next call to any method on that
|
||||
// [Decoder] (e.g., [Decoder.PeekKind], [Decoder.ReadToken],
|
||||
// [Decoder.ReadValue], or [Decoder.SkipValue]).
|
||||
// Call [Token.Clone] to copy the raw text into an independent allocation
|
||||
// that persists beyond subsequent [Decoder] calls.
|
||||
//
|
||||
// - As a typed Go value: a self-contained representation produced by
|
||||
// the constructor functions (e.g., [String], [Int], [Uint], [Float]).
|
||||
// Such tokens are valid indefinitely and do not need to be cloned.
|
||||
type Token = jsontext.Token
|
||||
|
||||
var (
|
||||
Null = jsontext.Null
|
||||
False = jsontext.False
|
||||
True = jsontext.True
|
||||
BeginObject = jsontext.BeginObject
|
||||
EndObject = jsontext.EndObject
|
||||
BeginArray = jsontext.BeginArray
|
||||
EndArray = jsontext.EndArray
|
||||
)
|
||||
|
||||
// Bool constructs a Token representing a JSON boolean.
|
||||
func Bool(b bool) Token {
|
||||
return jsontext.Bool(b)
|
||||
}
|
||||
|
||||
// String constructs a Token representing a JSON string.
|
||||
// The provided string should contain valid UTF-8, otherwise invalid characters
|
||||
// may be mangled as the Unicode replacement character.
|
||||
func String(s string) Token {
|
||||
return jsontext.String(s)
|
||||
}
|
||||
|
||||
// Float constructs a Token representing a JSON number as
|
||||
// a 64-bit floating-point number formatted according to
|
||||
// ECMA-262, 10th edition, section 7.1.12.1 and RFC 8785, section 3.2.2.3.
|
||||
// with the exception that -0 is still formatted as -0.
|
||||
// The values NaN, +Inf, and -Inf will be represented
|
||||
// as a JSON string with the values "NaN", "Infinity", and "-Infinity".
|
||||
func Float(n float64) Token {
|
||||
return jsontext.Float(n)
|
||||
}
|
||||
|
||||
// Int constructs a Token representing a JSON number from an int64.
|
||||
func Int(n int64) Token {
|
||||
return jsontext.Int(n)
|
||||
}
|
||||
|
||||
// Uint constructs a Token representing a JSON number from a uint64.
|
||||
func Uint(n uint64) Token {
|
||||
return jsontext.Uint(n)
|
||||
}
|
||||
|
||||
// A Kind represents the kind of a JSON token.
|
||||
//
|
||||
// Kind represents each possible JSON token kind with a single byte,
|
||||
// which is conveniently the first byte of that kind's grammar
|
||||
// with the restriction that numbers always be represented with '0'.
|
||||
type Kind = jsontext.Kind
|
||||
|
||||
const (
|
||||
KindInvalid = jsontext.KindInvalid
|
||||
KindNull = jsontext.KindNull
|
||||
KindFalse = jsontext.KindFalse
|
||||
KindTrue = jsontext.KindTrue
|
||||
KindString = jsontext.KindString
|
||||
KindNumber = jsontext.KindNumber
|
||||
KindBeginObject = jsontext.KindBeginObject
|
||||
KindEndObject = jsontext.KindEndObject
|
||||
KindBeginArray = jsontext.KindBeginArray
|
||||
KindEndArray = jsontext.KindEndArray
|
||||
)
|
||||
|
||||
// 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[Bytes ~[]byte | ~string](dst []byte, src Bytes, opts ...Options) ([]byte, error) {
|
||||
return jsontext.AppendFormat(dst, []byte(src), opts...)
|
||||
}
|
||||
|
||||
// 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!")
|
||||
// - a JSON number (e.g., 123.456)
|
||||
// - an entire JSON object (e.g., {"fizz":"buzz"} )
|
||||
// - an entire JSON array (e.g., [1,2,3] )
|
||||
//
|
||||
// Value can represent entire array or object values, while [Token] cannot.
|
||||
// Value may contain leading and/or trailing whitespace.
|
||||
type Value = jsontext.Value
|
||||
1218
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/decode.go
generated
vendored
Normal file
1218
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/decode.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
111
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/doc.go
generated
vendored
Normal file
111
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/doc.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
// Package jsontext implements syntactic processing of JSON
|
||||
// as specified in RFC 4627, RFC 7159, RFC 7493, RFC 8259, and RFC 8785.
|
||||
// JSON is a simple data interchange format that can represent
|
||||
// primitive data types such as booleans, strings, and numbers,
|
||||
// in addition to structured data types such as objects and arrays.
|
||||
//
|
||||
// The [Encoder] and [Decoder] types are used to encode or decode
|
||||
// a stream of JSON tokens or values.
|
||||
//
|
||||
// # Tokens and Values
|
||||
//
|
||||
// A JSON token refers to the basic structural elements of JSON:
|
||||
//
|
||||
// - a JSON literal (i.e., null, true, or false)
|
||||
// - a JSON string (e.g., "hello, world!")
|
||||
// - a JSON number (e.g., 123.456)
|
||||
// - a begin or end delimiter for a JSON object (i.e., '{' or '}')
|
||||
// - a begin or end delimiter for a JSON array (i.e., '[' or ']')
|
||||
//
|
||||
// A JSON token is represented by the [Token] type in Go. Technically,
|
||||
// there are two additional structural characters (i.e., ':' and ','),
|
||||
// but there is no [Token] representation for them since their presence
|
||||
// can be inferred by the structure of the JSON grammar itself.
|
||||
// For example, there must always be an implicit colon between
|
||||
// the name and value of a JSON object member.
|
||||
//
|
||||
// A JSON value refers to a complete unit of JSON data:
|
||||
//
|
||||
// - a JSON literal, string, or number
|
||||
// - a JSON object (e.g., `{"name":"value"}`)
|
||||
// - a JSON array (e.g., `[1,2,3]`)
|
||||
//
|
||||
// A JSON value is represented by the [Value] type in Go and is a []byte
|
||||
// containing the raw textual representation of the value. There is some overlap
|
||||
// between tokens and values as both contain literals, strings, and numbers.
|
||||
// However, only a value can represent the entirety of a JSON object or array.
|
||||
//
|
||||
// The [Encoder] and [Decoder] types contain methods to read or write the next
|
||||
// [Token] or [Value] in a sequence. They maintain a state machine to validate
|
||||
// whether the sequence of JSON tokens and/or values produces a valid JSON.
|
||||
// [Options] may be passed to the [NewEncoder] or [NewDecoder] constructors
|
||||
// to configure the syntactic behavior of encoding and decoding.
|
||||
//
|
||||
// # Terminology
|
||||
//
|
||||
// The terms "encode" and "decode" are used for syntactic functionality
|
||||
// that is concerned with processing JSON based on its grammar, and
|
||||
// the terms "marshal" and "unmarshal" are used for semantic functionality
|
||||
// that determines the meaning of JSON values as Go values and vice-versa.
|
||||
// This package (i.e., [jsontext]) deals with JSON at a syntactic layer,
|
||||
// while [encoding/json/v2] deals with JSON at a semantic layer.
|
||||
// The goal is to provide a clear distinction between functionality that
|
||||
// is purely concerned with encoding versus that of marshaling.
|
||||
// For example, one can directly encode a stream of JSON tokens without
|
||||
// needing to marshal a concrete Go value representing them.
|
||||
// Similarly, one can decode a stream of JSON tokens without
|
||||
// needing to unmarshal them into a concrete Go value.
|
||||
//
|
||||
// This package uses JSON terminology when discussing JSON, which may differ
|
||||
// from related concepts in Go or elsewhere in computing literature.
|
||||
//
|
||||
// - a JSON "object" refers to an unordered collection of name/value members.
|
||||
// - a JSON "array" refers to an ordered sequence of elements.
|
||||
// - a JSON "value" refers to either a literal (i.e., null, false, or true),
|
||||
// string, number, object, or array.
|
||||
//
|
||||
// See RFC 8259 for more information.
|
||||
//
|
||||
// # Specifications
|
||||
//
|
||||
// Relevant specifications include RFC 4627, RFC 7159, RFC 7493, RFC 8259,
|
||||
// and RFC 8785. Each RFC is generally a stricter subset of another RFC.
|
||||
// In increasing order of strictness:
|
||||
//
|
||||
// - RFC 4627 and RFC 7159 do not require (but recommend) the use of UTF-8
|
||||
// and also do not require (but recommend) that object names be unique.
|
||||
// - RFC 8259 requires the use of UTF-8,
|
||||
// but does not require (but recommends) that object names be unique.
|
||||
// - RFC 7493 requires the use of UTF-8
|
||||
// and also requires that object names be unique.
|
||||
// - RFC 8785 defines a canonical representation. It requires the use of UTF-8
|
||||
// and also requires that object names be unique and in a specific ordering.
|
||||
// It specifies exactly how strings and numbers must be formatted.
|
||||
//
|
||||
// The primary difference between RFC 4627 and RFC 7159 is that the former
|
||||
// restricted top-level values to only JSON objects and arrays, while
|
||||
// RFC 7159 and subsequent RFCs permit top-level values to additionally be
|
||||
// JSON nulls, booleans, strings, or numbers.
|
||||
//
|
||||
// By default, this package operates on RFC 7493, but can be configured
|
||||
// to operate according to the other RFC specifications.
|
||||
// RFC 7493 is a stricter subset of RFC 8259 and fully compliant with it.
|
||||
// In particular, it makes specific choices about behavior that RFC 8259
|
||||
// leaves as undefined in order to ensure greater interoperability.
|
||||
//
|
||||
// # Security Considerations
|
||||
//
|
||||
// See the "Security Considerations" section in [encoding/json/v2].
|
||||
package jsontext
|
||||
|
||||
// requireKeyedLiterals can be embedded in a struct to require keyed literals.
|
||||
type requireKeyedLiterals struct{}
|
||||
|
||||
// nonComparable can be embedded in a struct to prevent comparability.
|
||||
type nonComparable [0]func()
|
||||
993
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/encode.go
generated
vendored
Normal file
993
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/encode.go
generated
vendored
Normal file
@@ -0,0 +1,993 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"math/bits"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonopts"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
// Encoder is a streaming encoder from raw JSON tokens and values.
|
||||
// It is used to write a stream of top-level JSON values,
|
||||
// each terminated with a newline character.
|
||||
//
|
||||
// [Encoder.WriteToken] and [Encoder.WriteValue] calls may be interleaved.
|
||||
// For example, the following JSON value:
|
||||
//
|
||||
// {"name":"value","array":[null,false,true,3.14159],"object":{"k":"v"}}
|
||||
//
|
||||
// can be composed with the following calls (ignoring errors for brevity):
|
||||
//
|
||||
// e.WriteToken(BeginObject) // {
|
||||
// e.WriteToken(String("name")) // "name"
|
||||
// e.WriteToken(String("value")) // "value"
|
||||
// e.WriteValue(Value(`"array"`)) // "array"
|
||||
// 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(EndArray) // ]
|
||||
// e.WriteValue(Value(`"object"`)) // "object"
|
||||
// e.WriteValue(Value(`{"k":"v"}`)) // {"k":"v"}
|
||||
// e.WriteToken(EndObject) // }
|
||||
//
|
||||
// The above is one of many possible sequences of calls and
|
||||
// may not represent the most sensible method to call for any given token/value.
|
||||
// For example, it is probably more common to call [Encoder.WriteToken] with a string
|
||||
// for object names.
|
||||
type Encoder struct {
|
||||
s encoderState
|
||||
}
|
||||
|
||||
// encoderState is the low-level state of Encoder.
|
||||
// It has exported fields and methods for use by the "json" package.
|
||||
type encoderState struct {
|
||||
state
|
||||
encodeBuffer
|
||||
jsonopts.Struct
|
||||
|
||||
SeenPointers map[any]struct{} // only used when marshaling; identical to json.seenPointers
|
||||
}
|
||||
|
||||
// encodeBuffer is a buffer split into 2 segments:
|
||||
//
|
||||
// - buf[0:len(buf)] // written (but unflushed) portion of the buffer
|
||||
// - buf[len(buf):cap(buf)] // unused portion of the buffer
|
||||
type encodeBuffer struct {
|
||||
Buf []byte // may alias wr if it is a bytes.Buffer
|
||||
|
||||
// baseOffset is added to len(buf) to obtain the absolute offset
|
||||
// relative to the start of io.Writer stream.
|
||||
baseOffset int64
|
||||
|
||||
wr io.Writer
|
||||
|
||||
// maxValue is the approximate maximum Value size passed to WriteValue.
|
||||
maxValue int
|
||||
// availBuffer is the buffer returned by the AvailableBuffer method.
|
||||
availBuffer []byte // always has zero length
|
||||
// bufStats is statistics about buffer utilization.
|
||||
// It is only used with pooled encoders in pools.go.
|
||||
bufStats bufferStatistics
|
||||
}
|
||||
|
||||
// NewEncoder constructs a new streaming encoder writing to w
|
||||
// configured with the provided options.
|
||||
// It flushes the internal buffer when the buffer is sufficiently full or
|
||||
// when a top-level value has been written.
|
||||
//
|
||||
// If w is a [bytes.Buffer], then the encoder appends directly into the buffer
|
||||
// without copying the contents from an intermediate buffer.
|
||||
func NewEncoder(w io.Writer, opts ...Options) *Encoder {
|
||||
e := new(Encoder)
|
||||
e.Reset(w, opts...)
|
||||
return e
|
||||
}
|
||||
|
||||
// Reset resets an encoder such that it is writing afresh to w and
|
||||
// configured with the provided options. Reset must not be called on
|
||||
// an 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:
|
||||
panic("jsontext: invalid nil Encoder")
|
||||
case w == nil:
|
||||
panic("jsontext: invalid nil io.Writer")
|
||||
case e.s.Flags.Get(jsonflags.WithinArshalCall):
|
||||
panic("jsontext: cannot reset Encoder passed to json.MarshalerTo")
|
||||
}
|
||||
// Reuse the buffer if it does not alias a previous [bytes.Buffer].
|
||||
b := e.s.Buf[:0]
|
||||
if _, ok := e.s.wr.(*bytes.Buffer); ok {
|
||||
b = nil
|
||||
}
|
||||
e.s.reset(b, w, opts...)
|
||||
}
|
||||
|
||||
func (e *encoderState) reset(b []byte, w io.Writer, opts ...Options) {
|
||||
e.state.reset()
|
||||
e.encodeBuffer = encodeBuffer{Buf: b, wr: w, availBuffer: e.availBuffer, bufStats: e.bufStats}
|
||||
if bb, ok := w.(*bytes.Buffer); ok && bb != nil {
|
||||
e.Buf = bb.AvailableBuffer() // alias the unused buffer of bb
|
||||
}
|
||||
opts2 := jsonopts.Struct{} // avoid mutating e.Struct in case it is part of opts
|
||||
opts2.Join(opts...)
|
||||
e.Struct = opts2
|
||||
if e.Struct.Flags.Get(jsonflags.Multiline) {
|
||||
e.Struct.InitializeMultiline()
|
||||
}
|
||||
}
|
||||
|
||||
// Options returns the options used to construct the encoder 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
|
||||
}
|
||||
|
||||
func (e *encoderState) options() *jsonopts.Struct { return &e.Struct }
|
||||
|
||||
// NeedFlush determines whether to flush at this point.
|
||||
func (e *encoderState) NeedFlush() bool {
|
||||
// NOTE: This function is carefully written to be inlinable.
|
||||
|
||||
// Avoid flushing if e.wr is nil since there is no underlying writer.
|
||||
// Flush if less than 25% of the capacity remains.
|
||||
// Flushing at some constant fraction ensures that the buffer stops growing
|
||||
// so long as the largest Token or Value fits within that unused capacity.
|
||||
return e.wr != nil && (e.Tokens.Depth() == 1 || len(e.Buf) > 3*cap(e.Buf)/4)
|
||||
}
|
||||
|
||||
// Flush flushes the buffer to the underlying io.Writer.
|
||||
// It may append a trailing newline after the top-level value.
|
||||
func (e *encoderState) Flush() error {
|
||||
if e.wr == nil || e.avoidFlush() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// In streaming mode, always emit a newline after the top-level value.
|
||||
if e.Tokens.Depth() == 1 && !e.Flags.Get(jsonflags.OmitTopLevelNewline) {
|
||||
e.Buf = append(e.Buf, '\n')
|
||||
}
|
||||
|
||||
// Inform objectNameStack that we are about to flush the buffer content.
|
||||
e.Names.copyQuotedBuffer(e.Buf)
|
||||
|
||||
// Specialize bytes.Buffer for better performance.
|
||||
if bb, ok := e.wr.(*bytes.Buffer); ok {
|
||||
// If e.Buf already aliases the internal buffer of bb,
|
||||
// then the Write call simply increments the internal offset,
|
||||
// otherwise Write operates as expected.
|
||||
// See https://go.dev/issue/42986.
|
||||
n, _ := bb.Write(e.Buf) // never fails unless bb is nil
|
||||
e.baseOffset += int64(n)
|
||||
|
||||
// If the internal buffer of bytes.Buffer is too small,
|
||||
// append operations elsewhere in the Encoder may grow the buffer.
|
||||
// This would be semantically correct, but hurts performance.
|
||||
// As such, ensure 25% of the current length is always available
|
||||
// to reduce the probability that other appends must allocate.
|
||||
if avail := bb.Available(); avail < bb.Len()/4 {
|
||||
bb.Grow(avail + 1)
|
||||
}
|
||||
|
||||
e.Buf = bb.AvailableBuffer()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush the internal buffer to the underlying io.Writer.
|
||||
n, err := e.wr.Write(e.Buf)
|
||||
e.baseOffset += int64(n)
|
||||
if err != nil {
|
||||
// In the event of an error, preserve the unflushed portion.
|
||||
// Thus, write errors aren't fatal so long as the io.Writer
|
||||
// maintains consistent state after errors.
|
||||
if n > 0 {
|
||||
e.Buf = e.Buf[:copy(e.Buf, e.Buf[n:])]
|
||||
}
|
||||
return &ioError{action: "write", err: err}
|
||||
}
|
||||
e.Buf = e.Buf[:0]
|
||||
|
||||
// Check whether to grow the buffer.
|
||||
// Note that cap(e.Buf) may already exceed maxBufferSize since
|
||||
// an append elsewhere already grew it to store a large token.
|
||||
const maxBufferSize = 4 << 10
|
||||
const growthSizeFactor = 2 // higher value is faster
|
||||
const growthRateFactor = 2 // higher value is slower
|
||||
// By default, grow if below the maximum buffer size.
|
||||
grow := cap(e.Buf) <= maxBufferSize/growthSizeFactor
|
||||
// Growing can be expensive, so only grow
|
||||
// if a sufficient number of bytes have been processed.
|
||||
grow = grow && int64(cap(e.Buf)) < e.previousOffsetEnd()/growthRateFactor
|
||||
if grow {
|
||||
e.Buf = make([]byte, 0, cap(e.Buf)*growthSizeFactor)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (e *encodeBuffer) offsetAt(pos int) int64 { return e.baseOffset + int64(pos) }
|
||||
func (e *encodeBuffer) previousOffsetEnd() int64 { return e.baseOffset + int64(len(e.Buf)) }
|
||||
func (e *encodeBuffer) unflushedBuffer() []byte { return e.Buf }
|
||||
|
||||
// avoidFlush indicates whether to avoid flushing to ensure there is always
|
||||
// enough in the buffer to unwrite the last object member if it were empty.
|
||||
func (e *encoderState) avoidFlush() bool {
|
||||
switch {
|
||||
case e.Tokens.Last.Length() == 0:
|
||||
// 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():
|
||||
// Never flush before the object value since we don't know yet
|
||||
// if the object value will end up being empty.
|
||||
return true
|
||||
case e.Tokens.Last.NeedObjectName() && len(e.Buf) >= 2:
|
||||
// Never flush after the object value if it does turn out to be empty.
|
||||
switch string(e.Buf[len(e.Buf)-2:]) {
|
||||
case `ll`, `""`, `{}`, `[]`: // last two bytes of every empty value
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// UnwriteEmptyObjectMember unwrites the last object member if it is empty
|
||||
// and reports whether it performed an unwrite operation.
|
||||
func (e *encoderState) UnwriteEmptyObjectMember(prevName *string) bool {
|
||||
if last := e.Tokens.Last; !last.isObject() || !last.NeedObjectName() || last.Length() == 0 {
|
||||
panic("BUG: must be called on an object after writing a value")
|
||||
}
|
||||
|
||||
// The flushing logic is modified to never flush a trailing empty value.
|
||||
// The encoder never writes trailing whitespace eagerly.
|
||||
b := e.unflushedBuffer()
|
||||
|
||||
// Detect whether the last value was empty.
|
||||
var n int
|
||||
if len(b) >= 3 {
|
||||
switch string(b[len(b)-2:]) {
|
||||
case "ll": // last two bytes of `null`
|
||||
n = len(`null`)
|
||||
case `""`:
|
||||
// It is possible for a non-empty string to have `""` as a suffix
|
||||
// if the second to the last quote was escaped.
|
||||
if b[len(b)-3] == '\\' {
|
||||
return false // e.g., `"\""` is not empty
|
||||
}
|
||||
n = len(`""`)
|
||||
case `{}`:
|
||||
n = len(`{}`)
|
||||
case `[]`:
|
||||
n = len(`[]`)
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Unwrite the value, whitespace, colon, name, whitespace, and comma.
|
||||
b = b[:len(b)-n]
|
||||
b = jsonwire.TrimSuffixWhitespace(b)
|
||||
b = jsonwire.TrimSuffixByte(b, ':')
|
||||
b = jsonwire.TrimSuffixString(b)
|
||||
b = jsonwire.TrimSuffixWhitespace(b)
|
||||
b = jsonwire.TrimSuffixByte(b, ',')
|
||||
e.Buf = b // store back truncated unflushed buffer
|
||||
|
||||
// Undo state changes.
|
||||
e.Tokens.Last.decrement() // for object member value
|
||||
e.Tokens.Last.decrement() // for object member name
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
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)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UnwriteOnlyObjectMemberName unwrites the only object member name
|
||||
// and returns the unquoted name.
|
||||
func (e *encoderState) UnwriteOnlyObjectMemberName() string {
|
||||
if last := e.Tokens.Last; !last.isObject() || last.Length() != 1 {
|
||||
panic("BUG: must be called on an object after writing first name")
|
||||
}
|
||||
|
||||
// Unwrite the name and whitespace.
|
||||
b := jsonwire.TrimSuffixString(e.Buf)
|
||||
isVerbatim := bytes.IndexByte(e.Buf[len(b):], '\\') < 0
|
||||
name := string(jsonwire.UnquoteMayCopy(e.Buf[len(b):], isVerbatim))
|
||||
e.Buf = jsonwire.TrimSuffixWhitespace(b)
|
||||
|
||||
// Undo state changes.
|
||||
e.Tokens.Last.decrement()
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if e.Tokens.Last.isActiveNamespace() {
|
||||
e.Namespaces.Last().removeLast()
|
||||
}
|
||||
}
|
||||
e.Names.clearLast()
|
||||
return name
|
||||
}
|
||||
|
||||
// WriteToken writes the next token and advances the internal write offset.
|
||||
//
|
||||
// The provided token kind must be consistent with the JSON grammar.
|
||||
// For example, it is an error to provide a number when the encoder
|
||||
// is expecting an object name (which is always a string), or
|
||||
// to provide an end object delimiter when the encoder is finishing an array.
|
||||
// If the provided token is invalid, then it reports a [SyntacticError] and
|
||||
// the internal state remains unchanged. The offset reported
|
||||
// in [SyntacticError] will be the [Encoder.OutputOffset] plus any delimiter
|
||||
// or whitespace characters that would have preceded the provided token.
|
||||
func (e *Encoder) WriteToken(t Token) error {
|
||||
return e.s.WriteToken(t)
|
||||
}
|
||||
func (e *encoderState) WriteToken(t Token) error {
|
||||
k := t.Kind()
|
||||
b := e.Buf // use local variable to avoid mutating e in case of error
|
||||
|
||||
// Append any delimiters or optional whitespace.
|
||||
b = e.Tokens.MayAppendDelim(b, k)
|
||||
if e.Flags.Get(jsonflags.AnyWhitespace) {
|
||||
b = e.appendWhitespace(b, k)
|
||||
}
|
||||
pos := len(b) // offset before the token
|
||||
|
||||
// Append the token to the output and to the state machine.
|
||||
var err error
|
||||
switch k {
|
||||
case 'n':
|
||||
b = append(b, "null"...)
|
||||
err = e.Tokens.appendLiteral()
|
||||
case 'f':
|
||||
b = append(b, "false"...)
|
||||
err = e.Tokens.appendLiteral()
|
||||
case 't':
|
||||
b = append(b, "true"...)
|
||||
err = e.Tokens.appendLiteral()
|
||||
case '"':
|
||||
if b, err = t.appendString(b, &e.Flags); err != nil {
|
||||
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); err != nil {
|
||||
break
|
||||
}
|
||||
err = e.Tokens.appendNumber()
|
||||
case '{':
|
||||
b = append(b, '{')
|
||||
if err = e.Tokens.pushObject(); err != nil {
|
||||
break
|
||||
}
|
||||
e.Names.push()
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
e.Namespaces.push()
|
||||
}
|
||||
e.Flags.Clear(jsonflags.TagFlags) // tags only apply to current depth
|
||||
case '}':
|
||||
b = append(b, '}')
|
||||
if err = e.Tokens.popObject(); err != nil {
|
||||
break
|
||||
}
|
||||
e.Names.pop()
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
e.Namespaces.pop()
|
||||
}
|
||||
case '[':
|
||||
b = append(b, '[')
|
||||
err = e.Tokens.pushArray()
|
||||
e.Flags.Clear(jsonflags.TagFlags) // tags only apply to current depth
|
||||
case ']':
|
||||
b = append(b, ']')
|
||||
err = e.Tokens.popArray()
|
||||
default:
|
||||
err = errInvalidToken
|
||||
}
|
||||
if err != nil {
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
|
||||
// Finish off the buffer and store it back into e.
|
||||
e.Buf = b
|
||||
if e.NeedFlush() {
|
||||
return e.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AppendRaw appends either a raw string (without double quotes) or number.
|
||||
// Specify safeASCII if the string output is guaranteed to be ASCII
|
||||
// without any characters (including '<', '>', and '&') that need escaping,
|
||||
// otherwise this will validate whether the string needs escaping.
|
||||
// The appended bytes for a JSON number must be valid.
|
||||
//
|
||||
// This is a specialized implementation of Encoder.WriteValue
|
||||
// that allows appending directly into the buffer.
|
||||
// It is only called from marshal logic in the "json" package.
|
||||
func (e *encoderState) AppendRaw(k Kind, safeASCII bool, appendFn func([]byte) ([]byte, error)) error {
|
||||
b := e.Buf // use local variable to avoid mutating e in case of error
|
||||
|
||||
// Append any delimiters or optional whitespace.
|
||||
b = e.Tokens.MayAppendDelim(b, k)
|
||||
if e.Flags.Get(jsonflags.AnyWhitespace) {
|
||||
b = e.appendWhitespace(b, k)
|
||||
}
|
||||
pos := len(b) // offset before the token
|
||||
|
||||
var err error
|
||||
switch k {
|
||||
case '"':
|
||||
// Append directly into the encoder buffer by assuming that
|
||||
// most of the time none of the characters need escaping.
|
||||
b = append(b, '"')
|
||||
if b, err = appendFn(b); err != nil {
|
||||
return err
|
||||
}
|
||||
b = append(b, '"')
|
||||
|
||||
// Check whether we need to escape the string and if necessary
|
||||
// copy it to a scratch buffer and then escape it back.
|
||||
isVerbatim := safeASCII || !jsonwire.NeedEscape(b[pos+len(`"`):len(b)-len(`"`)])
|
||||
if !isVerbatim {
|
||||
var err error
|
||||
b2 := append(e.availBuffer, b[pos+len(`"`):len(b)-len(`"`)]...)
|
||||
b, err = jsonwire.AppendQuote(b[:pos], b2, &e.Flags)
|
||||
e.availBuffer = b2[:0]
|
||||
if err != nil {
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the state machine.
|
||||
if e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
return wrapSyntacticError(e, errInvalidNamespace, 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 wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
case '0':
|
||||
if b, err = appendFn(b); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := e.Tokens.appendNumber(); err != nil {
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
default:
|
||||
panic("BUG: invalid kind")
|
||||
}
|
||||
|
||||
// Finish off the buffer and store it back into e.
|
||||
e.Buf = b
|
||||
if e.NeedFlush() {
|
||||
return e.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteValue writes the next raw value and advances the internal write offset.
|
||||
// The Encoder does not simply copy the provided value verbatim, but
|
||||
// parses it to ensure that it is syntactically valid and reformats it
|
||||
// according to how the Encoder is configured to format whitespace and strings.
|
||||
// If [AllowInvalidUTF8] is specified, then any invalid UTF-8 is mangled
|
||||
// as the Unicode replacement character, U+FFFD.
|
||||
//
|
||||
// The provided value kind must be consistent with the JSON grammar
|
||||
// (see examples on [Encoder.WriteToken]). If the provided value is invalid,
|
||||
// then it reports a [SyntacticError] and the internal state remains unchanged.
|
||||
// The offset reported in [SyntacticError] will be the [Encoder.OutputOffset]
|
||||
// plus the offset into v of any encountered syntax error.
|
||||
func (e *Encoder) WriteValue(v Value) error {
|
||||
return e.s.WriteValue(v)
|
||||
}
|
||||
func (e *encoderState) WriteValue(v Value) error {
|
||||
e.maxValue |= len(v) // bitwise OR is a fast approximation of max
|
||||
|
||||
k := v.Kind()
|
||||
b := e.Buf // use local variable to avoid mutating e in case of error
|
||||
|
||||
// Append any delimiters or optional whitespace.
|
||||
b = e.Tokens.MayAppendDelim(b, k)
|
||||
if e.Flags.Get(jsonflags.AnyWhitespace) {
|
||||
b = e.appendWhitespace(b, k)
|
||||
}
|
||||
pos := len(b) // offset before the value
|
||||
|
||||
// Append the value to the output.
|
||||
var n int
|
||||
n += jsonwire.ConsumeWhitespace(v[n:])
|
||||
b, m, err := e.reformatValue(b, v[n:], e.Tokens.Depth())
|
||||
if err != nil {
|
||||
return wrapSyntacticError(e, err, pos+n+m, +1)
|
||||
}
|
||||
n += m
|
||||
n += jsonwire.ConsumeWhitespace(v[n:])
|
||||
if len(v) > n {
|
||||
err = jsonwire.NewInvalidCharacterError(v[n:], "after top-level value")
|
||||
return wrapSyntacticError(e, err, pos+n, 0)
|
||||
}
|
||||
|
||||
// Append the kind to the state machine.
|
||||
switch k {
|
||||
case 'n', 'f', 't':
|
||||
err = e.Tokens.appendLiteral()
|
||||
case '"':
|
||||
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':
|
||||
err = e.Tokens.appendNumber()
|
||||
case '{':
|
||||
if err = e.Tokens.pushObject(); err != nil {
|
||||
break
|
||||
}
|
||||
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
|
||||
}
|
||||
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 wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
|
||||
// Finish off the buffer and store it back into e.
|
||||
e.Buf = b
|
||||
if e.NeedFlush() {
|
||||
return e.Flush()
|
||||
}
|
||||
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 delim := e.Tokens.needDelim(next); delim == ':' {
|
||||
if e.Flags.Get(jsonflags.SpaceAfterColon) {
|
||||
b = append(b, ' ')
|
||||
}
|
||||
} else {
|
||||
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
|
||||
// for the current nested level, n.
|
||||
func (e *encoderState) AppendIndent(b []byte, n int) []byte {
|
||||
if n == 0 {
|
||||
return b
|
||||
}
|
||||
b = append(b, '\n')
|
||||
b = append(b, e.IndentPrefix...)
|
||||
for ; n > 1; n-- {
|
||||
b = append(b, e.Indent...)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// reformatValue parses a JSON value from the start of src and
|
||||
// appends it to the end of dst, reformatting whitespace and strings as needed.
|
||||
// It returns the extended dst buffer and the number of consumed input bytes.
|
||||
func (e *encoderState) reformatValue(dst []byte, src Value, depth int) ([]byte, int, error) {
|
||||
// TODO: Should this update ValueFlags as input?
|
||||
if len(src) == 0 {
|
||||
return dst, 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
switch k := Kind(src[0]).normalize(); k {
|
||||
case 'n':
|
||||
if jsonwire.ConsumeNull(src) == 0 {
|
||||
n, err := jsonwire.ConsumeLiteral(src, "null")
|
||||
return dst, n, err
|
||||
}
|
||||
return append(dst, "null"...), len("null"), nil
|
||||
case 'f':
|
||||
if jsonwire.ConsumeFalse(src) == 0 {
|
||||
n, err := jsonwire.ConsumeLiteral(src, "false")
|
||||
return dst, n, err
|
||||
}
|
||||
return append(dst, "false"...), len("false"), nil
|
||||
case 't':
|
||||
if jsonwire.ConsumeTrue(src) == 0 {
|
||||
n, err := jsonwire.ConsumeLiteral(src, "true")
|
||||
return dst, n, err
|
||||
}
|
||||
return append(dst, "true"...), len("true"), nil
|
||||
case '"':
|
||||
if n := jsonwire.ConsumeSimpleString(src); n > 0 {
|
||||
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 = append(dst, src[:n]...) // copy simple numbers verbatim
|
||||
return dst, n, nil
|
||||
}
|
||||
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, jsonwire.NewInvalidCharacterError(src, "at start of value")
|
||||
}
|
||||
}
|
||||
|
||||
// reformatObject parses a JSON object from the start of src and
|
||||
// appends it to the end of dst, reformatting whitespace and strings as needed.
|
||||
// It returns the extended dst buffer and the number of consumed input bytes.
|
||||
func (e *encoderState) reformatObject(dst []byte, src Value, depth int) ([]byte, int, error) {
|
||||
// Append object begin.
|
||||
if len(src) == 0 || src[0] != '{' {
|
||||
panic("BUG: reformatObject must be called with a buffer that starts with '{'")
|
||||
} else if depth == maxNestingDepth+1 {
|
||||
return dst, 0, errMaxDepth
|
||||
}
|
||||
dst = append(dst, '{')
|
||||
n := len("{")
|
||||
|
||||
// Append (possible) object end.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
}
|
||||
if src[n] == '}' {
|
||||
dst = append(dst, '}')
|
||||
n += len("}")
|
||||
return dst, n, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
var names *objectNamespace
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
e.Namespaces.push()
|
||||
defer e.Namespaces.pop()
|
||||
names = e.Namespaces.Last()
|
||||
}
|
||||
depth++
|
||||
for {
|
||||
// Append optional newline and indentation.
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth)
|
||||
}
|
||||
|
||||
// Append object name.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
}
|
||||
m := jsonwire.ConsumeSimpleString(src[n:])
|
||||
isVerbatim := m > 0
|
||||
if isVerbatim {
|
||||
dst = append(dst, src[n:n+m]...)
|
||||
} else {
|
||||
dst, m, err = jsonwire.ReformatString(dst, src[n:], &e.Flags)
|
||||
if err != nil {
|
||||
return dst, n + m, err
|
||||
}
|
||||
}
|
||||
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, wrapWithObjectName(io.ErrUnexpectedEOF, quotedName)
|
||||
}
|
||||
if src[n] != ':' {
|
||||
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.SpaceAfterColon) {
|
||||
dst = append(dst, ' ')
|
||||
}
|
||||
|
||||
// Append object value.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, wrapWithObjectName(io.ErrUnexpectedEOF, quotedName)
|
||||
}
|
||||
dst, m, err = e.reformatValue(dst, src[n:], depth)
|
||||
if err != nil {
|
||||
return dst, n + m, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
n += m
|
||||
|
||||
// Append comma or object end.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
}
|
||||
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.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth-1)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
n += len("}")
|
||||
return dst, n, nil
|
||||
default:
|
||||
return dst, n, jsonwire.NewInvalidCharacterError(src[n:], "after object value (expecting ',' or '}')")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reformatArray parses a JSON array from the start of src and
|
||||
// appends it to the end of dst, reformatting whitespace and strings as needed.
|
||||
// It returns the extended dst buffer and the number of consumed input bytes.
|
||||
func (e *encoderState) reformatArray(dst []byte, src Value, depth int) ([]byte, int, error) {
|
||||
// Append array begin.
|
||||
if len(src) == 0 || src[0] != '[' {
|
||||
panic("BUG: reformatArray must be called with a buffer that starts with '['")
|
||||
} else if depth == maxNestingDepth+1 {
|
||||
return dst, 0, errMaxDepth
|
||||
}
|
||||
dst = append(dst, '[')
|
||||
n := len("[")
|
||||
|
||||
// Append (possible) array end.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
}
|
||||
if src[n] == ']' {
|
||||
dst = append(dst, ']')
|
||||
n += len("]")
|
||||
return dst, n, nil
|
||||
}
|
||||
|
||||
var idx int64
|
||||
var err error
|
||||
depth++
|
||||
for {
|
||||
// Append optional newline and indentation.
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth)
|
||||
}
|
||||
|
||||
// Append array value.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
}
|
||||
var m int
|
||||
dst, m, err = e.reformatValue(dst, src[n:], depth)
|
||||
if err != nil {
|
||||
return dst, n + m, wrapWithArrayIndex(err, idx)
|
||||
}
|
||||
n += m
|
||||
|
||||
// Append comma or array end.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
}
|
||||
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.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth-1)
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
n += len("]")
|
||||
return dst, n, nil
|
||||
default:
|
||||
return dst, n, jsonwire.NewInvalidCharacterError(src[n:], "after array value (expecting ',' or ']')")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OutputOffset returns the current output byte offset. It gives the location
|
||||
// of the next byte immediately after the most recently written token or value.
|
||||
// The number of bytes actually written to the underlying [io.Writer] may be less
|
||||
// than this offset due to internal buffering effects.
|
||||
func (e *Encoder) OutputOffset() int64 {
|
||||
return e.s.previousOffsetEnd()
|
||||
}
|
||||
|
||||
// AvailableBuffer returns a zero-length buffer with a possible non-zero capacity.
|
||||
// This buffer is intended to be used to populate a [Value]
|
||||
// being passed to an immediately succeeding [Encoder.WriteValue] call.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// b := e.AvailableBuffer()
|
||||
// b = append(b, '"')
|
||||
// b = appendString(b, v) // append the string formatting of v
|
||||
// b = append(b, '"')
|
||||
// ... := e.WriteValue(b)
|
||||
//
|
||||
// WriteValue expects a JSON value. Using AvailableBuffer to manually construct
|
||||
// a value requires caution to avoid producing an invalid JSON value that would
|
||||
// then cause WriteValue to fail.
|
||||
func (e *Encoder) AvailableBuffer() []byte {
|
||||
// NOTE: We don't return e.Buf[len(e.Buf):cap(e.Buf)] since WriteValue would
|
||||
// need to take special care to avoid mangling the data while reformatting.
|
||||
// WriteValue can't easily identify whether the input Value aliases e.Buf
|
||||
// without using unsafe.Pointer. Thus, we just return a different buffer.
|
||||
// Should this ever alias e.Buf, we need to consider how it operates with
|
||||
// the specialized performance optimization for bytes.Buffer.
|
||||
n := 1 << bits.Len(uint(e.s.maxValue|63)) // fast approximation for max length
|
||||
if cap(e.s.availBuffer) < n {
|
||||
e.s.availBuffer = make([]byte, 0, n)
|
||||
}
|
||||
return e.s.availBuffer
|
||||
}
|
||||
|
||||
// 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 a [BeginObject] or [BeginArray] token is encountered
|
||||
// and decremented whenever an [EndObject] or [EndArray] token is encountered.
|
||||
//
|
||||
// StackDepth returns 0 when not inside any object or array.
|
||||
// In particular, it returns 0 before any tokens have been written,
|
||||
// after any top-level value has been written, and between values
|
||||
// when encoding a stream of top-level values (e.g., NDJSON).
|
||||
// StackDepth returns 1 inside a top-level object or array,
|
||||
// 2 inside a nested object or array, and so on.
|
||||
//
|
||||
// For example, consider encoding the following JSON:
|
||||
//
|
||||
// {"a": [1, 2], "b": {"c": 3}}
|
||||
//
|
||||
// While encoding, StackDepth would report the following:
|
||||
//
|
||||
// - At the start, StackDepth reports 0.
|
||||
// - After encoding the outer '{', StackDepth reports 1.
|
||||
// - After encoding the inner '[', StackDepth reports 2.
|
||||
// - After encoding the inner ']', StackDepth reports 1.
|
||||
// - After encoding the outer '}', StackDepth reports 0.
|
||||
func (e *Encoder) StackDepth() int {
|
||||
// NOTE: Keep in sync with Decoder.StackDepth.
|
||||
return e.s.Tokens.Depth() - 1
|
||||
}
|
||||
|
||||
// StackIndex returns information about the specified stack level.
|
||||
// It must be a number between 0 and [Encoder.StackDepth], inclusive.
|
||||
// For each level, it reports the kind:
|
||||
//
|
||||
// - [KindInvalid] for a level of zero,
|
||||
// - [KindBeginObject] for a level representing a JSON object, and
|
||||
// - [KindBeginArray] for a level representing a JSON array.
|
||||
//
|
||||
// It also reports the length of that JSON object or array encoded so far.
|
||||
// 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, int64) {
|
||||
// NOTE: Keep in sync with Decoder.StackIndex.
|
||||
switch s := e.s.Tokens.index(i); {
|
||||
case i > 0 && s.isObject():
|
||||
return '{', s.Length()
|
||||
case i > 0 && s.isArray():
|
||||
return '[', s.Length()
|
||||
default:
|
||||
return 0, s.Length()
|
||||
}
|
||||
}
|
||||
|
||||
// StackPointer returns a JSON Pointer (RFC 6901) to the most recently written value.
|
||||
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)
|
||||
}
|
||||
213
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/errors.go
generated
vendored
Normal file
213
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/errors.go
generated
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonopts"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
const errorPrefix = "jsontext: "
|
||||
|
||||
type ioError struct {
|
||||
action string // either "read" or "write"
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *ioError) Error() string {
|
||||
return errorPrefix + e.action + " error: " + e.err.Error()
|
||||
}
|
||||
func (e *ioError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
type numError struct {
|
||||
accessor string // either "Int", "Uint", or "Float"
|
||||
value string // e.g., "1e1000"
|
||||
err error // either [strconv.ErrSyntax] or [strconv.ErrRange]
|
||||
}
|
||||
|
||||
func (e *numError) Error() string {
|
||||
return "jsontext.Token(" + e.value + ")." + e.accessor + " error: " + e.err.Error()
|
||||
}
|
||||
func (e *numError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// SyntacticError is a description of a syntactic error that occurred when
|
||||
// encoding or decoding JSON according to the grammar.
|
||||
//
|
||||
// The contents of this error as produced by this package may change over time.
|
||||
type SyntacticError struct {
|
||||
requireKeyedLiterals
|
||||
nonComparable
|
||||
|
||||
// ByteOffset indicates that an error occurred at or after this byte offset.
|
||||
ByteOffset int64
|
||||
// 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 specifies 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 {
|
||||
options() *jsonopts.Struct
|
||||
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)
|
||||
}
|
||||
if state.options().Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) && err != io.ErrUnexpectedEOF {
|
||||
// The v1 error offset more consistently reported
|
||||
// the number of bytes read that lead to the error.
|
||||
// This implies that the offset is after the invalid text.
|
||||
if werr, ok := err.(*jsonwire.InvalidTextError); ok && werr != nil {
|
||||
offset += int64(len(werr.What))
|
||||
} else {
|
||||
// This implies a structural error (e.g., parsing a JSON number
|
||||
// when the grammar is expecting a JSON string for the object name).
|
||||
// In such a case, simply increment by one, since the first byte
|
||||
// is usually sufficient to indicate that the incoming token
|
||||
// cannot possibly be correct.
|
||||
offset++
|
||||
}
|
||||
}
|
||||
return &SyntacticError{ByteOffset: offset, JSONPointer: Pointer(ptr), Err: err}
|
||||
}
|
||||
|
||||
func (e *SyntacticError) Error() string {
|
||||
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 (e *SyntacticError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
91
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/export.go
generated
vendored
Normal file
91
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/export.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/go-json-experiment/json/internal"
|
||||
)
|
||||
|
||||
// TODO(https://go.dev/issue/73435): Remove the Internal symbol.
|
||||
//
|
||||
// The Go language lacks a 3rd category of visibility where
|
||||
// certain symbols can only be referenced from within the same module.
|
||||
// One solution to this is to put the entirety of a package with
|
||||
// both public and module-only symbols exposed as an internal package.
|
||||
// A separate, public package can re-export all of the public symbols
|
||||
// via type aliases and thus ensuring module-only symbols cannot be
|
||||
// referenced by the end-user. While this works, it unfortunately
|
||||
// leads to a poor user experience since the Go pkgsite is unable to
|
||||
// forward the documentation for symbols like methods and fields.
|
||||
// We need to improve the pkgsite experience before we can delete
|
||||
// the Internal symbol.
|
||||
|
||||
// Internal is for internal use only.
|
||||
// This is exempt from the Go compatibility agreement.
|
||||
var Internal exporter
|
||||
|
||||
type exporter struct{}
|
||||
|
||||
// Export exposes internal functionality from "jsontext" to "json".
|
||||
// This cannot be dynamically called by other packages since
|
||||
// they cannot obtain a reference to the internal.AllowInternalUse value.
|
||||
func (exporter) Export(p *internal.NotForPublicUse) export {
|
||||
if p != &internal.AllowInternalUse {
|
||||
panic("unauthorized call to Export")
|
||||
}
|
||||
return export{}
|
||||
}
|
||||
|
||||
// The export type exposes functionality to packages with visibility to
|
||||
// the internal.AllowInternalUse variable. The "json" package uses this
|
||||
// to modify low-level state in the Encoder and Decoder types.
|
||||
// It mutates the state directly instead of calling ReadToken or WriteToken
|
||||
// since this is more performant. The public APIs need to track state to ensure
|
||||
// that users are constructing a valid JSON value, but the "json" implementation
|
||||
// guarantees that it emits valid JSON by the structure of the code itself.
|
||||
type export struct{}
|
||||
|
||||
// Encoder returns a pointer to the underlying encoderState.
|
||||
func (export) Encoder(e *Encoder) *encoderState { return &e.s }
|
||||
|
||||
// Decoder returns a pointer to the underlying decoderState.
|
||||
func (export) Decoder(d *Decoder) *decoderState { return &d.s }
|
||||
|
||||
func (export) GetBufferedEncoder(o ...Options) *Encoder {
|
||||
return getBufferedEncoder(o...)
|
||||
}
|
||||
func (export) PutBufferedEncoder(e *Encoder) {
|
||||
putBufferedEncoder(e)
|
||||
}
|
||||
|
||||
func (export) GetStreamingEncoder(w io.Writer, o ...Options) *Encoder {
|
||||
return getStreamingEncoder(w, o...)
|
||||
}
|
||||
func (export) PutStreamingEncoder(e *Encoder) {
|
||||
putStreamingEncoder(e)
|
||||
}
|
||||
|
||||
func (export) GetBufferedDecoder(b []byte, o ...Options) *Decoder {
|
||||
return getBufferedDecoder(b, o...)
|
||||
}
|
||||
func (export) PutBufferedDecoder(d *Decoder) {
|
||||
putBufferedDecoder(d)
|
||||
}
|
||||
|
||||
func (export) GetStreamingDecoder(r io.Reader, o ...Options) *Decoder {
|
||||
return getStreamingDecoder(r, o...)
|
||||
}
|
||||
func (export) PutStreamingDecoder(d *Decoder) {
|
||||
putStreamingDecoder(d)
|
||||
}
|
||||
|
||||
func (export) IsIOError(err error) bool {
|
||||
_, ok := err.(*ioError)
|
||||
return ok
|
||||
}
|
||||
304
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/options.go
generated
vendored
Normal file
304
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/options.go
generated
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonopts"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
// Options configures [NewEncoder], [Encoder.Reset], [NewDecoder],
|
||||
// and [Decoder.Reset] with specific features.
|
||||
// Each function takes in a variadic list of options, where properties
|
||||
// set in later 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.
|
||||
// Options from this package may be used with the other packages.
|
||||
type Options = jsonopts.Options
|
||||
|
||||
// AllowDuplicateNames specifies that JSON objects may contain
|
||||
// duplicate member names. Disabling the duplicate name check may provide
|
||||
// performance benefits, but breaks compliance with RFC 7493, section 2.3.
|
||||
// The input or output will still be compliant with RFC 8259,
|
||||
// which leaves the handling of duplicate names as unspecified behavior.
|
||||
//
|
||||
// This affects either encoding or decoding.
|
||||
func AllowDuplicateNames(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.AllowDuplicateNames | 1
|
||||
} else {
|
||||
return jsonflags.AllowDuplicateNames | 0
|
||||
}
|
||||
}
|
||||
|
||||
// AllowInvalidUTF8 specifies that JSON strings may contain invalid UTF-8,
|
||||
// which will be mangled as the Unicode replacement character, U+FFFD.
|
||||
// This causes the encoder or decoder to break compliance with
|
||||
// RFC 7493, section 2.1, and RFC 8259, section 8.1.
|
||||
//
|
||||
// This affects either encoding or decoding.
|
||||
func AllowInvalidUTF8(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.AllowInvalidUTF8 | 1
|
||||
} else {
|
||||
return jsonflags.AllowInvalidUTF8 | 0
|
||||
}
|
||||
}
|
||||
|
||||
// EscapeForHTML specifies that '<', '>', and '&' characters within JSON strings
|
||||
// should be escaped as a hexadecimal Unicode codepoint (e.g., \u003c) so that
|
||||
// the output is safe to embed within HTML.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func EscapeForHTML(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.EscapeForHTML | 1
|
||||
} else {
|
||||
return jsonflags.EscapeForHTML | 0
|
||||
}
|
||||
}
|
||||
|
||||
// EscapeForJS specifies that U+2028 and U+2029 characters within JSON strings
|
||||
// should be escaped as a hexadecimal Unicode codepoint (e.g., \u2028) so that
|
||||
// the output is valid to embed within JavaScript. See RFC 8259, section 12.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func EscapeForJS(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.EscapeForJS | 1
|
||||
} else {
|
||||
return jsonflags.EscapeForJS | 0
|
||||
}
|
||||
}
|
||||
|
||||
// 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 PreserveRawStrings(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.PreserveRawStrings | 1
|
||||
} else {
|
||||
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 is 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
|
||||
}
|
||||
}
|
||||
|
||||
// WithIndent specifies that the encoder should emit multiline output
|
||||
// where each element in a JSON object or array begins on a new, indented line
|
||||
// beginning with the indent prefix (see [WithIndentPrefix])
|
||||
// followed by one or more copies of indent according to the nesting depth.
|
||||
// The indent must only be composed of space or tab characters.
|
||||
//
|
||||
// If the intent is to emit indented output without a preference for
|
||||
// the particular indent string, then use [Multiline] instead.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
// 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.
|
||||
switch indent {
|
||||
case "\t":
|
||||
return jsonopts.Indent("\t") // ~14k usages
|
||||
case " ":
|
||||
return jsonopts.Indent(" ") // ~18k usages
|
||||
case " ":
|
||||
return jsonopts.Indent(" ") // ~1.7k usages
|
||||
case " ":
|
||||
return jsonopts.Indent(" ") // ~52k usages
|
||||
case " ":
|
||||
return jsonopts.Indent(" ") // ~12k usages
|
||||
case "":
|
||||
return jsonopts.Indent("") // ~1.5k usages
|
||||
}
|
||||
|
||||
// Otherwise, allocate for this unique value.
|
||||
if s := strings.Trim(indent, " \t"); len(s) > 0 {
|
||||
panic("json: invalid character " + jsonwire.QuoteRune([]byte(s)) + " in indent")
|
||||
}
|
||||
return jsonopts.Indent(indent)
|
||||
}
|
||||
|
||||
// WithIndentPrefix specifies that the encoder should emit multiline output
|
||||
// where each element in a JSON object or array begins on a new, indented line
|
||||
// beginning with the indent prefix followed by one or more copies of indent
|
||||
// (see [WithIndent]) according to the nesting depth.
|
||||
// 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 [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([]byte(s)) + " in indent prefix")
|
||||
}
|
||||
return jsonopts.IndentPrefix(prefix)
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO(https://go.dev/issue/56733): Implement WithByteLimit and WithDepthLimit.
|
||||
// Remember to also update the "Security Considerations" section.
|
||||
|
||||
// WithByteLimit sets a limit on the number of bytes of input or output bytes
|
||||
// that may be consumed or produced for each top-level JSON value.
|
||||
// If a [Decoder] or [Encoder] method call would need to consume/produce
|
||||
// more than a total of n bytes to make progress on the top-level JSON value,
|
||||
// then the call will report an error.
|
||||
// Whitespace before and within the top-level value are counted against the limit.
|
||||
// Whitespace after a top-level value are counted against the limit
|
||||
// for the next top-level value.
|
||||
//
|
||||
// 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))
|
||||
}
|
||||
|
||||
// WithDepthLimit sets a limit on the maximum depth of JSON nesting
|
||||
// that may be consumed or produced for each top-level JSON value.
|
||||
// If a [Decoder] or [Encoder] method call would need to consume or produce
|
||||
// a depth greater than n to make progress on the top-level JSON value,
|
||||
// then the call will report an error.
|
||||
//
|
||||
// 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))
|
||||
}
|
||||
*/
|
||||
164
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/pools.go
generated
vendored
Normal file
164
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/pools.go
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"math/bits"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// TODO(https://go.dev/issue/47657): Use sync.PoolOf.
|
||||
|
||||
var (
|
||||
// This owns the internal buffer since there is no io.Writer to output to.
|
||||
// Since the buffer can get arbitrarily large in normal usage,
|
||||
// there is statistical tracking logic to determine whether to recycle
|
||||
// the internal buffer or not based on a history of utilization.
|
||||
bufferedEncoderPool = &sync.Pool{New: func() any { return new(Encoder) }}
|
||||
|
||||
// This owns the internal buffer, but it is only used to temporarily store
|
||||
// buffered JSON before flushing it to the underlying io.Writer.
|
||||
// In a sufficiently efficient streaming mode, we do not expect the buffer
|
||||
// to grow arbitrarily large. Thus, we avoid recycling large buffers.
|
||||
streamingEncoderPool = &sync.Pool{New: func() any { return new(Encoder) }}
|
||||
|
||||
// This does not own the internal buffer since
|
||||
// it is taken directly from the provided bytes.Buffer.
|
||||
bytesBufferEncoderPool = &sync.Pool{New: func() any { return new(Encoder) }}
|
||||
)
|
||||
|
||||
// bufferStatistics is statistics to track buffer utilization.
|
||||
// It is used to determine whether to recycle a buffer or not
|
||||
// to avoid https://go.dev/issue/23199.
|
||||
type bufferStatistics struct {
|
||||
strikes int // number of times the buffer was under-utilized
|
||||
prevLen int // length of previous buffer
|
||||
}
|
||||
|
||||
func getBufferedEncoder(opts ...Options) *Encoder {
|
||||
e := bufferedEncoderPool.Get().(*Encoder)
|
||||
if e.s.Buf == nil {
|
||||
// Round up to nearest 2ⁿ to make best use of malloc size classes.
|
||||
// See runtime/sizeclasses.go on Go1.15.
|
||||
// Logical OR with 63 to ensure 64 as the minimum buffer size.
|
||||
n := 1 << bits.Len(uint(e.s.bufStats.prevLen|63))
|
||||
e.s.Buf = make([]byte, 0, n)
|
||||
}
|
||||
e.s.reset(e.s.Buf[:0], nil, opts...)
|
||||
return e
|
||||
}
|
||||
func putBufferedEncoder(e *Encoder) {
|
||||
if cap(e.s.availBuffer) > 64<<10 {
|
||||
e.s.availBuffer = nil // avoid pinning arbitrarily large amounts of memory
|
||||
}
|
||||
|
||||
// Recycle large buffers only if sufficiently utilized.
|
||||
// If a buffer is under-utilized enough times sequentially,
|
||||
// then it is discarded, ensuring that a single large buffer
|
||||
// won't be kept alive by a continuous stream of small usages.
|
||||
//
|
||||
// The worst case utilization is computed as:
|
||||
// MIN_UTILIZATION_THRESHOLD / (1 + MAX_NUM_STRIKES)
|
||||
//
|
||||
// For the constants chosen below, this is (25%)/(1+4) ⇒ 5%.
|
||||
// This may seem low, but it ensures a lower bound on
|
||||
// the absolute worst-case utilization. Without this check,
|
||||
// this would be theoretically 0%, which is infinitely worse.
|
||||
//
|
||||
// See https://go.dev/issue/27735.
|
||||
switch {
|
||||
case cap(e.s.Buf) <= 4<<10: // always recycle buffers smaller than 4KiB
|
||||
e.s.bufStats.strikes = 0
|
||||
case cap(e.s.Buf)/4 <= len(e.s.Buf): // at least 25% utilization
|
||||
e.s.bufStats.strikes = 0
|
||||
case e.s.bufStats.strikes < 4: // at most 4 strikes
|
||||
e.s.bufStats.strikes++
|
||||
default: // discard the buffer; too large and too often under-utilized
|
||||
e.s.bufStats.strikes = 0
|
||||
e.s.bufStats.prevLen = len(e.s.Buf) // heuristic for size to allocate next time
|
||||
e.s.Buf = nil
|
||||
}
|
||||
bufferedEncoderPool.Put(e)
|
||||
}
|
||||
|
||||
func getStreamingEncoder(w io.Writer, opts ...Options) *Encoder {
|
||||
if _, ok := w.(*bytes.Buffer); ok {
|
||||
e := bytesBufferEncoderPool.Get().(*Encoder)
|
||||
e.s.reset(nil, w, opts...) // buffer taken from bytes.Buffer
|
||||
return e
|
||||
} else {
|
||||
e := streamingEncoderPool.Get().(*Encoder)
|
||||
e.s.reset(e.s.Buf[:0], w, opts...) // preserve existing buffer
|
||||
return e
|
||||
}
|
||||
}
|
||||
func putStreamingEncoder(e *Encoder) {
|
||||
if cap(e.s.availBuffer) > 64<<10 {
|
||||
e.s.availBuffer = nil // avoid pinning arbitrarily large amounts of memory
|
||||
}
|
||||
if _, ok := e.s.wr.(*bytes.Buffer); ok {
|
||||
e.s.wr, e.s.Buf = nil, nil // avoid pinning the provided bytes.Buffer
|
||||
bytesBufferEncoderPool.Put(e)
|
||||
} else {
|
||||
e.s.wr = nil // avoid pinning the provided io.Writer
|
||||
if cap(e.s.Buf) > 64<<10 {
|
||||
e.s.Buf = nil // avoid pinning arbitrarily large amounts of memory
|
||||
}
|
||||
streamingEncoderPool.Put(e)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// This does not own the internal buffer since it is externally provided.
|
||||
bufferedDecoderPool = &sync.Pool{New: func() any { return new(Decoder) }}
|
||||
|
||||
// This owns the internal buffer, but it is only used to temporarily store
|
||||
// buffered JSON fetched from the underlying io.Reader.
|
||||
// In a sufficiently efficient streaming mode, we do not expect the buffer
|
||||
// to grow arbitrarily large. Thus, we avoid recycling large buffers.
|
||||
streamingDecoderPool = &sync.Pool{New: func() any { return new(Decoder) }}
|
||||
|
||||
// This does not own the internal buffer since
|
||||
// it is taken directly from the provided bytes.Buffer.
|
||||
bytesBufferDecoderPool = bufferedDecoderPool
|
||||
)
|
||||
|
||||
func getBufferedDecoder(b []byte, opts ...Options) *Decoder {
|
||||
d := bufferedDecoderPool.Get().(*Decoder)
|
||||
d.s.reset(b, nil, opts...)
|
||||
return d
|
||||
}
|
||||
func putBufferedDecoder(d *Decoder) {
|
||||
d.s.buf = nil // avoid pinning the provided buffer
|
||||
bufferedDecoderPool.Put(d)
|
||||
}
|
||||
|
||||
func getStreamingDecoder(r io.Reader, opts ...Options) *Decoder {
|
||||
if _, ok := r.(*bytes.Buffer); ok {
|
||||
d := bytesBufferDecoderPool.Get().(*Decoder)
|
||||
d.s.reset(nil, r, opts...) // buffer taken from bytes.Buffer
|
||||
return d
|
||||
} else {
|
||||
d := streamingDecoderPool.Get().(*Decoder)
|
||||
d.s.reset(d.s.buf[:0], r, opts...) // preserve existing buffer
|
||||
return d
|
||||
}
|
||||
}
|
||||
func putStreamingDecoder(d *Decoder) {
|
||||
if _, ok := d.s.rd.(*bytes.Buffer); ok {
|
||||
d.s.rd, d.s.buf = nil, nil // avoid pinning the provided bytes.Buffer
|
||||
bytesBufferDecoderPool.Put(d)
|
||||
} else {
|
||||
d.s.rd = nil // avoid pinning the provided io.Reader
|
||||
if cap(d.s.buf) > 64<<10 {
|
||||
d.s.buf = nil // avoid pinning arbitrarily large amounts of memory
|
||||
}
|
||||
streamingDecoderPool.Put(d)
|
||||
}
|
||||
}
|
||||
41
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/quote.go
generated
vendored
Normal file
41
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/quote.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
dst, err := jsonwire.AppendQuote(dst, []byte(src), &jsonflags.Flags{})
|
||||
if err != nil {
|
||||
err = &SyntacticError{Err: err}
|
||||
}
|
||||
return dst, err
|
||||
}
|
||||
|
||||
// AppendUnquote appends the decoded interpretation of src as a
|
||||
// double-quoted JSON string literal to dst and returns the extended buffer.
|
||||
// The input src must be a JSON string without any surrounding whitespace.
|
||||
// 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) {
|
||||
dst, err := jsonwire.AppendUnquote(dst, []byte(src))
|
||||
if err != nil {
|
||||
err = &SyntacticError{Err: err}
|
||||
}
|
||||
return dst, err
|
||||
}
|
||||
828
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/state.go
generated
vendored
Normal file
828
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/state.go
generated
vendored
Normal file
@@ -0,0 +1,828 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"iter"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
// 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 := ...
|
||||
// serr, ok := errors.AsType[*jsontext.SyntacticError](err)
|
||||
// if ok && 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")
|
||||
|
||||
// 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.
|
||||
// Such a limit is necessary to prevent stack overflows.
|
||||
const maxNestingDepth = 10000
|
||||
|
||||
type state struct {
|
||||
// Tokens validates whether the next token kind is valid.
|
||||
Tokens stateMachine
|
||||
|
||||
// Names is a stack of object names.
|
||||
Names objectNameStack
|
||||
|
||||
// Namespaces is a stack of object namespaces.
|
||||
// For performance reasons, Encoder or Decoder may not update this
|
||||
// if Marshal or Unmarshal is able to track names in a more efficient way.
|
||||
// See makeMapArshaler and makeStructArshaler.
|
||||
// Not used if AllowDuplicateNames is true.
|
||||
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 a 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+"/"), []byte(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.
|
||||
//
|
||||
// - 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, where int) []byte {
|
||||
var objectDepth int
|
||||
for i := 1; i < s.Tokens.Depth(); i++ {
|
||||
e := s.Tokens.index(i)
|
||||
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
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case e.isObject():
|
||||
b = appendEscapePointerName(append(b, '/'), s.Names.getUnquoted(objectDepth))
|
||||
objectDepth++
|
||||
case e.isArray():
|
||||
b = strconv.AppendUint(append(b, '/'), uint64(e.Length()+int64(arrayDelta)), 10)
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func appendEscapePointerName(b, name []byte) []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
|
||||
}
|
||||
|
||||
// stateMachine is a push-down automaton that validates whether
|
||||
// a sequence of tokens is valid or not according to the JSON grammar.
|
||||
// It is useful for both encoding and decoding.
|
||||
//
|
||||
// It is a stack where each entry represents a nested JSON object or array.
|
||||
// The stack has a minimum depth of 1 where the first level is a
|
||||
// virtual JSON array to handle a stream of top-level JSON values.
|
||||
// The top-level virtual JSON array is special in that it doesn't require commas
|
||||
// between each JSON value.
|
||||
//
|
||||
// For performance, most methods are carefully written to be inlinable.
|
||||
// The zero value is a valid state machine ready for use.
|
||||
type stateMachine struct {
|
||||
Stack []stateEntry
|
||||
Last stateEntry
|
||||
}
|
||||
|
||||
// reset resets the state machine.
|
||||
// The machine always starts with a minimum depth of 1.
|
||||
func (m *stateMachine) reset() {
|
||||
m.Stack = m.Stack[:0]
|
||||
if cap(m.Stack) > 1<<10 {
|
||||
m.Stack = nil
|
||||
}
|
||||
m.Last = stateTypeArray
|
||||
}
|
||||
|
||||
// Depth is the current nested depth of JSON objects and arrays.
|
||||
// It is one-indexed (i.e., top-level values have a depth of 1).
|
||||
func (m stateMachine) Depth() int {
|
||||
return len(m.Stack) + 1
|
||||
}
|
||||
|
||||
// index returns a reference to the ith entry.
|
||||
// It is only valid until the next push method call.
|
||||
func (m *stateMachine) index(i int) *stateEntry {
|
||||
if i == len(m.Stack) {
|
||||
return &m.Last
|
||||
}
|
||||
return &m.Stack[i]
|
||||
}
|
||||
|
||||
// DepthLength reports the current nested depth and
|
||||
// the length of the last JSON object or array.
|
||||
func (m stateMachine) DepthLength() (int, int64) {
|
||||
return m.Depth(), m.Last.Length()
|
||||
}
|
||||
|
||||
// appendLiteral appends a JSON literal as the next token in the sequence.
|
||||
// If an error is returned, the state is not mutated.
|
||||
func (m *stateMachine) appendLiteral() error {
|
||||
switch {
|
||||
case m.Last.NeedObjectName():
|
||||
return ErrNonStringName
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
default:
|
||||
m.Last.Increment()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// appendString appends a JSON string as the next token in the sequence.
|
||||
// If an error is returned, the state is not mutated.
|
||||
func (m *stateMachine) appendString() error {
|
||||
switch {
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
default:
|
||||
m.Last.Increment()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// appendNumber appends a JSON number as the next token in the sequence.
|
||||
// If an error is returned, the state is not mutated.
|
||||
func (m *stateMachine) appendNumber() error {
|
||||
return m.appendLiteral()
|
||||
}
|
||||
|
||||
// pushObject appends a JSON begin object token as next in the sequence.
|
||||
// If an error is returned, the state is not mutated.
|
||||
func (m *stateMachine) pushObject() error {
|
||||
switch {
|
||||
case m.Last.NeedObjectName():
|
||||
return ErrNonStringName
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
case len(m.Stack) == maxNestingDepth:
|
||||
return errMaxDepth
|
||||
default:
|
||||
m.Last.Increment()
|
||||
m.Stack = append(m.Stack, m.Last)
|
||||
m.Last = stateTypeObject
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// popObject appends a JSON end object token as next in the sequence.
|
||||
// If an error is returned, the state is not mutated.
|
||||
func (m *stateMachine) popObject() error {
|
||||
switch {
|
||||
case !m.Last.isObject():
|
||||
return errMismatchDelim
|
||||
case m.Last.needObjectValue():
|
||||
return errMissingValue
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
default:
|
||||
m.Last = m.Stack[len(m.Stack)-1]
|
||||
m.Stack = m.Stack[:len(m.Stack)-1]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// pushArray appends a JSON begin array token as next in the sequence.
|
||||
// If an error is returned, the state is not mutated.
|
||||
func (m *stateMachine) pushArray() error {
|
||||
switch {
|
||||
case m.Last.NeedObjectName():
|
||||
return ErrNonStringName
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
case len(m.Stack) == maxNestingDepth:
|
||||
return errMaxDepth
|
||||
default:
|
||||
m.Last.Increment()
|
||||
m.Stack = append(m.Stack, m.Last)
|
||||
m.Last = stateTypeArray
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// popArray appends a JSON end array token as next in the sequence.
|
||||
// If an error is returned, the state is not mutated.
|
||||
func (m *stateMachine) popArray() error {
|
||||
switch {
|
||||
case !m.Last.isArray() || len(m.Stack) == 0: // forbid popping top-level virtual JSON array
|
||||
return errMismatchDelim
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
default:
|
||||
m.Last = m.Stack[len(m.Stack)-1]
|
||||
m.Stack = m.Stack[:len(m.Stack)-1]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NeedIndent reports whether indent whitespace should be injected.
|
||||
// A zero value means that no whitespace should be injected.
|
||||
// A positive value means '\n', indentPrefix, and (n-1) copies of indentBody
|
||||
// should be appended to the output immediately before the next token.
|
||||
func (m stateMachine) NeedIndent(next Kind) (n int) {
|
||||
willEnd := next == '}' || next == ']'
|
||||
switch {
|
||||
case m.Depth() == 1:
|
||||
return 0 // top-level values are never indented
|
||||
case m.Last.Length() == 0 && willEnd:
|
||||
return 0 // an empty object or array is never indented
|
||||
case m.Last.Length() == 0 || m.Last.needImplicitComma(next):
|
||||
return m.Depth()
|
||||
case willEnd:
|
||||
return m.Depth() - 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// MayAppendDelim appends a colon or comma that may precede the next token.
|
||||
func (m stateMachine) MayAppendDelim(b []byte, next Kind) []byte {
|
||||
switch {
|
||||
case m.Last.needImplicitColon():
|
||||
return append(b, ':')
|
||||
case m.Last.needImplicitComma(next) && len(m.Stack) != 0: // comma not needed for top-level values
|
||||
return append(b, ',')
|
||||
default:
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
// needDelim reports whether a colon or comma token should be implicitly emitted
|
||||
// before the next token of the specified kind.
|
||||
// A zero value means no delimiter should be emitted.
|
||||
func (m stateMachine) needDelim(next Kind) (delim byte) {
|
||||
switch {
|
||||
case m.Last.needImplicitColon():
|
||||
return ':'
|
||||
case m.Last.needImplicitComma(next) && len(m.Stack) != 0: // comma not needed for top-level values
|
||||
return ','
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidateDisabledNamespaces marks all disabled namespaces as invalid.
|
||||
//
|
||||
// For efficiency, Marshal and Unmarshal may disable namespaces since there are
|
||||
// more efficient ways to track duplicate names. However, if an error occurs,
|
||||
// the namespaces in Encoder or Decoder will be left in an inconsistent state.
|
||||
// Mark the namespaces as invalid so that future method calls on
|
||||
// Encoder or Decoder will return an error.
|
||||
func (m *stateMachine) InvalidateDisabledNamespaces() {
|
||||
for i := range m.Depth() {
|
||||
e := m.index(i)
|
||||
if !e.isActiveNamespace() {
|
||||
e.invalidateNamespace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stateEntry encodes several artifacts within a single unsigned integer:
|
||||
// - whether this represents a JSON object or array,
|
||||
// - whether this object should check for duplicate names, and
|
||||
// - how many elements are in this JSON object or array.
|
||||
type stateEntry uint64
|
||||
|
||||
const (
|
||||
// The type mask (1 bit) records whether this is a JSON object or array.
|
||||
stateTypeMask stateEntry = 0x8000_0000_0000_0000
|
||||
stateTypeObject stateEntry = 0x8000_0000_0000_0000
|
||||
stateTypeArray stateEntry = 0x0000_0000_0000_0000
|
||||
|
||||
// The name check mask (2 bits) records whether to update
|
||||
// the namespaces for the current JSON object and
|
||||
// whether the namespace is valid.
|
||||
stateNamespaceMask stateEntry = 0x6000_0000_0000_0000
|
||||
stateDisableNamespace stateEntry = 0x4000_0000_0000_0000
|
||||
stateInvalidNamespace stateEntry = 0x2000_0000_0000_0000
|
||||
|
||||
// The count mask (61 bits) records the number of elements.
|
||||
stateCountMask stateEntry = 0x1fff_ffff_ffff_ffff
|
||||
stateCountLSBMask stateEntry = 0x0000_0000_0000_0001
|
||||
stateCountOdd stateEntry = 0x0000_0000_0000_0001
|
||||
stateCountEven stateEntry = 0x0000_0000_0000_0000
|
||||
)
|
||||
|
||||
// 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() int64 {
|
||||
return int64(e & stateCountMask)
|
||||
}
|
||||
|
||||
// isObject reports whether this is a JSON object.
|
||||
func (e stateEntry) isObject() bool {
|
||||
return e&stateTypeMask == stateTypeObject
|
||||
}
|
||||
|
||||
// isArray reports whether this is a JSON array.
|
||||
func (e stateEntry) isArray() bool {
|
||||
return e&stateTypeMask == stateTypeArray
|
||||
}
|
||||
|
||||
// NeedObjectName reports whether the next token must be a JSON string,
|
||||
// which is necessary for JSON object names.
|
||||
func (e stateEntry) NeedObjectName() bool {
|
||||
return e&(stateTypeMask|stateCountLSBMask) == stateTypeObject|stateCountEven
|
||||
}
|
||||
|
||||
// needImplicitColon reports whether a colon should occur next,
|
||||
// which always occurs after JSON object names.
|
||||
func (e stateEntry) needImplicitColon() bool {
|
||||
return e.needObjectValue()
|
||||
}
|
||||
|
||||
// needObjectValue reports whether the next token must be a JSON value,
|
||||
// which is necessary after every JSON object name.
|
||||
func (e stateEntry) needObjectValue() bool {
|
||||
return e&(stateTypeMask|stateCountLSBMask) == stateTypeObject|stateCountOdd
|
||||
}
|
||||
|
||||
// needImplicitComma reports whether a comma should occur next,
|
||||
// which always occurs after a value in a JSON object or array
|
||||
// before the next value (or name).
|
||||
func (e stateEntry) needImplicitComma(next Kind) bool {
|
||||
return !e.needObjectValue() && e.Length() > 0 && next != '}' && next != ']'
|
||||
}
|
||||
|
||||
// Increment increments the number of elements for the current object or array.
|
||||
// This assumes that overflow won't practically be an issue since
|
||||
// 1<<bits.OnesCount(stateCountMask) is sufficiently large.
|
||||
func (e *stateEntry) Increment() {
|
||||
(*e)++
|
||||
}
|
||||
|
||||
// decrement decrements the number of elements for the current object or array.
|
||||
// It is the caller's responsibility to ensure that e.length > 0.
|
||||
func (e *stateEntry) decrement() {
|
||||
(*e)--
|
||||
}
|
||||
|
||||
// DisableNamespace disables the JSON object namespace such that the
|
||||
// Encoder or Decoder no longer updates the namespace.
|
||||
func (e *stateEntry) DisableNamespace() {
|
||||
*e |= stateDisableNamespace
|
||||
}
|
||||
|
||||
// isActiveNamespace reports whether the JSON object namespace is actively
|
||||
// being updated and used for duplicate name checks.
|
||||
func (e stateEntry) isActiveNamespace() bool {
|
||||
return e&(stateDisableNamespace) == 0
|
||||
}
|
||||
|
||||
// invalidateNamespace marks the JSON object namespace as being invalid.
|
||||
func (e *stateEntry) invalidateNamespace() {
|
||||
*e |= stateInvalidNamespace
|
||||
}
|
||||
|
||||
// isValidNamespace reports whether the JSON object namespace is valid.
|
||||
func (e stateEntry) isValidNamespace() bool {
|
||||
return e&(stateInvalidNamespace) == 0
|
||||
}
|
||||
|
||||
// objectNameStack is a stack of names when descending into a JSON object.
|
||||
// In contrast to objectNamespaceStack, this only has to remember a single name
|
||||
// per JSON object.
|
||||
//
|
||||
// This data structure may contain offsets to encodeBuffer or decodeBuffer.
|
||||
// It violates clean abstraction of layers, but is significantly more efficient.
|
||||
// This ensures that popping and pushing in the common case is a trivial
|
||||
// push/pop of an offset integer.
|
||||
//
|
||||
// The zero value is an empty names stack ready for use.
|
||||
type objectNameStack struct {
|
||||
// offsets is a stack of offsets for each name.
|
||||
// A non-negative offset is the ending offset into the local names buffer.
|
||||
// A negative offset is the bit-wise inverse of a starting offset into
|
||||
// a remote buffer (e.g., encodeBuffer or decodeBuffer).
|
||||
// A math.MinInt offset at the end implies that the last object is empty.
|
||||
// Invariant: Positive offsets always occur before negative offsets.
|
||||
offsets []int
|
||||
// unquotedNames is a back-to-back concatenation of names.
|
||||
unquotedNames []byte
|
||||
}
|
||||
|
||||
func (ns *objectNameStack) reset() {
|
||||
ns.offsets = ns.offsets[:0]
|
||||
ns.unquotedNames = ns.unquotedNames[:0]
|
||||
if cap(ns.offsets) > 1<<6 {
|
||||
ns.offsets = nil // avoid pinning arbitrarily large amounts of memory
|
||||
}
|
||||
if cap(ns.unquotedNames) > 1<<10 {
|
||||
ns.unquotedNames = nil // avoid pinning arbitrarily large amounts of memory
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *objectNameStack) length() int {
|
||||
return len(ns.offsets)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (ns *objectNameStack) getUnquoted(i int) []byte {
|
||||
ns.ensureCopiedBuffer()
|
||||
if i == 0 {
|
||||
return ns.unquotedNames[:ns.offsets[0]]
|
||||
} else {
|
||||
return ns.unquotedNames[ns.offsets[i-1]:ns.offsets[i]]
|
||||
}
|
||||
}
|
||||
|
||||
// invalidOffset indicates that the last JSON object currently has no name.
|
||||
const invalidOffset = math.MinInt
|
||||
|
||||
// push descends into a nested JSON object.
|
||||
func (ns *objectNameStack) push() {
|
||||
ns.offsets = append(ns.offsets, invalidOffset)
|
||||
}
|
||||
|
||||
// ReplaceLastQuotedOffset replaces the last name with the starting offset
|
||||
// to the quoted name in some remote buffer. All offsets provided must be
|
||||
// relative to the same buffer until copyQuotedBuffer is called.
|
||||
func (ns *objectNameStack) ReplaceLastQuotedOffset(i int) {
|
||||
// Use bit-wise inversion instead of naive multiplication by -1 to avoid
|
||||
// ambiguity regarding zero (which is a valid offset into the names field).
|
||||
// Bit-wise inversion is mathematically equivalent to -i-1,
|
||||
// such that 0 becomes -1, 1 becomes -2, and so forth.
|
||||
// This ensures that remote offsets are always negative.
|
||||
ns.offsets[len(ns.offsets)-1] = ^i
|
||||
}
|
||||
|
||||
// replaceLastUnquotedName replaces the last name with the provided name.
|
||||
//
|
||||
// Invariant: Must call copyQuotedBuffer beforehand.
|
||||
func (ns *objectNameStack) replaceLastUnquotedName(s string) {
|
||||
ns.ensureCopiedBuffer()
|
||||
var startOffset int
|
||||
if len(ns.offsets) > 1 {
|
||||
startOffset = ns.offsets[len(ns.offsets)-2]
|
||||
}
|
||||
ns.unquotedNames = append(ns.unquotedNames[:startOffset], s...)
|
||||
ns.offsets[len(ns.offsets)-1] = len(ns.unquotedNames)
|
||||
}
|
||||
|
||||
// clearLast removes any name in the last JSON object.
|
||||
// It is semantically equivalent to ns.push followed by ns.pop.
|
||||
func (ns *objectNameStack) clearLast() {
|
||||
ns.offsets[len(ns.offsets)-1] = invalidOffset
|
||||
}
|
||||
|
||||
// pop ascends out of a nested JSON object.
|
||||
func (ns *objectNameStack) pop() {
|
||||
ns.offsets = ns.offsets[:len(ns.offsets)-1]
|
||||
}
|
||||
|
||||
// copyQuotedBuffer copies names from the remote buffer into the local names
|
||||
// buffer so that there are no more offset references into the remote buffer.
|
||||
// This allows the remote buffer to change contents without affecting
|
||||
// the names that this data structure is trying to remember.
|
||||
func (ns *objectNameStack) copyQuotedBuffer(b []byte) {
|
||||
// Find the first negative offset.
|
||||
var i int
|
||||
for i = len(ns.offsets) - 1; i >= 0 && ns.offsets[i] < 0; i-- {
|
||||
continue
|
||||
}
|
||||
|
||||
// Copy each name from the remote buffer into the local buffer.
|
||||
for i = i + 1; i < len(ns.offsets); i++ {
|
||||
if i == len(ns.offsets)-1 && ns.offsets[i] == invalidOffset {
|
||||
if i == 0 {
|
||||
ns.offsets[i] = 0
|
||||
} else {
|
||||
ns.offsets[i] = ns.offsets[i-1]
|
||||
}
|
||||
break // last JSON object had a push without any names
|
||||
}
|
||||
|
||||
// As a form of Hyrum proofing, we write an invalid character into the
|
||||
// buffer to make misuse of Decoder.ReadToken more obvious.
|
||||
// We need to undo that mutation here.
|
||||
quotedName := b[^ns.offsets[i]:]
|
||||
if quotedName[0] == invalidateBufferByte {
|
||||
quotedName[0] = '"'
|
||||
}
|
||||
|
||||
// Append the unquoted name to the local buffer.
|
||||
var startOffset int
|
||||
if i > 0 {
|
||||
startOffset = ns.offsets[i-1]
|
||||
}
|
||||
if n := jsonwire.ConsumeSimpleString(quotedName); n > 0 {
|
||||
ns.unquotedNames = append(ns.unquotedNames[:startOffset], quotedName[len(`"`):n-len(`"`)]...)
|
||||
} else {
|
||||
ns.unquotedNames, _ = jsonwire.AppendUnquote(ns.unquotedNames[:startOffset], quotedName)
|
||||
}
|
||||
ns.offsets[i] = len(ns.unquotedNames)
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *objectNameStack) ensureCopiedBuffer() {
|
||||
if len(ns.offsets) > 0 && ns.offsets[len(ns.offsets)-1] < 0 {
|
||||
panic("BUG: copyQuotedBuffer not called beforehand")
|
||||
}
|
||||
}
|
||||
|
||||
// objectNamespaceStack is a stack of object namespaces.
|
||||
// This data structure assists in detecting duplicate names.
|
||||
type objectNamespaceStack []objectNamespace
|
||||
|
||||
// reset resets the object namespace stack.
|
||||
func (nss *objectNamespaceStack) reset() {
|
||||
if cap(*nss) > 1<<10 {
|
||||
*nss = nil
|
||||
}
|
||||
*nss = (*nss)[:0]
|
||||
}
|
||||
|
||||
// push starts a new namespace for a nested JSON object.
|
||||
func (nss *objectNamespaceStack) push() {
|
||||
if cap(*nss) > len(*nss) {
|
||||
*nss = (*nss)[:len(*nss)+1]
|
||||
nss.Last().reset()
|
||||
} else {
|
||||
*nss = append(*nss, objectNamespace{})
|
||||
}
|
||||
}
|
||||
|
||||
// Last returns a pointer to the last JSON object namespace.
|
||||
func (nss objectNamespaceStack) Last() *objectNamespace {
|
||||
return &nss[len(nss)-1]
|
||||
}
|
||||
|
||||
// pop terminates the namespace for a nested JSON object.
|
||||
func (nss *objectNamespaceStack) pop() {
|
||||
*nss = (*nss)[:len(*nss)-1]
|
||||
}
|
||||
|
||||
// objectNamespace is the namespace for a JSON object.
|
||||
// In contrast to objectNameStack, this needs to remember all names
|
||||
// per JSON object.
|
||||
//
|
||||
// The zero value is an empty namespace ready for use.
|
||||
type objectNamespace struct {
|
||||
// It relies on a linear search over all the names before switching
|
||||
// to use a Go map for direct lookup.
|
||||
|
||||
// endOffsets is a list of offsets to the end of each name in buffers.
|
||||
// The length of offsets is the number of names in the namespace.
|
||||
endOffsets []uint
|
||||
// allUnquotedNames is a back-to-back concatenation of every name in the namespace.
|
||||
allUnquotedNames []byte
|
||||
// mapNames is a Go map containing every name in the namespace.
|
||||
// Only valid if non-nil.
|
||||
mapNames map[string]struct{}
|
||||
}
|
||||
|
||||
// reset resets the namespace to be empty.
|
||||
func (ns *objectNamespace) reset() {
|
||||
ns.endOffsets = ns.endOffsets[:0]
|
||||
ns.allUnquotedNames = ns.allUnquotedNames[:0]
|
||||
ns.mapNames = nil
|
||||
if cap(ns.endOffsets) > 1<<6 {
|
||||
ns.endOffsets = nil // avoid pinning arbitrarily large amounts of memory
|
||||
}
|
||||
if cap(ns.allUnquotedNames) > 1<<10 {
|
||||
ns.allUnquotedNames = nil // avoid pinning arbitrarily large amounts of memory
|
||||
}
|
||||
}
|
||||
|
||||
// length reports the number of names in the namespace.
|
||||
func (ns *objectNamespace) length() int {
|
||||
return len(ns.endOffsets)
|
||||
}
|
||||
|
||||
// getUnquoted retrieves the ith unquoted name in the namespace.
|
||||
func (ns *objectNamespace) getUnquoted(i int) []byte {
|
||||
if i == 0 {
|
||||
return ns.allUnquotedNames[:ns.endOffsets[0]]
|
||||
} else {
|
||||
return ns.allUnquotedNames[ns.endOffsets[i-1]:ns.endOffsets[i]]
|
||||
}
|
||||
}
|
||||
|
||||
// lastUnquoted retrieves the last name in the namespace.
|
||||
func (ns *objectNamespace) lastUnquoted() []byte {
|
||||
return ns.getUnquoted(ns.length() - 1)
|
||||
}
|
||||
|
||||
// insertQuoted inserts a name and reports whether it was inserted,
|
||||
// which only occurs if name is not already in the namespace.
|
||||
// The provided name must be a valid JSON string.
|
||||
func (ns *objectNamespace) insertQuoted(name []byte, isVerbatim bool) bool {
|
||||
if isVerbatim {
|
||||
name = name[len(`"`) : len(name)-len(`"`)]
|
||||
}
|
||||
return ns.insert(name, !isVerbatim)
|
||||
}
|
||||
func (ns *objectNamespace) InsertUnquoted(name []byte) bool {
|
||||
return ns.insert(name, false)
|
||||
}
|
||||
func (ns *objectNamespace) insert(name []byte, quoted bool) bool {
|
||||
var allNames []byte
|
||||
if quoted {
|
||||
allNames, _ = jsonwire.AppendUnquote(ns.allUnquotedNames, name)
|
||||
} else {
|
||||
allNames = append(ns.allUnquotedNames, name...)
|
||||
}
|
||||
name = allNames[len(ns.allUnquotedNames):]
|
||||
|
||||
// Switch to a map if the buffer is too large for linear search.
|
||||
// This does not add the current name to the map.
|
||||
if ns.mapNames == nil && (ns.length() > 64 || len(ns.allUnquotedNames) > 1024) {
|
||||
ns.mapNames = make(map[string]struct{})
|
||||
var startOffset uint
|
||||
for _, endOffset := range ns.endOffsets {
|
||||
name := ns.allUnquotedNames[startOffset:endOffset]
|
||||
ns.mapNames[string(name)] = struct{}{} // allocates a new string
|
||||
startOffset = endOffset
|
||||
}
|
||||
}
|
||||
|
||||
if ns.mapNames == nil {
|
||||
// Perform linear search over the buffer to find matching names.
|
||||
// It provides O(n) lookup, but does not require any allocations.
|
||||
var startOffset uint
|
||||
for _, endOffset := range ns.endOffsets {
|
||||
if string(ns.allUnquotedNames[startOffset:endOffset]) == string(name) {
|
||||
return false
|
||||
}
|
||||
startOffset = endOffset
|
||||
}
|
||||
} else {
|
||||
// Use the map if it is populated.
|
||||
// It provides O(1) lookup, but requires a string allocation per name.
|
||||
if _, ok := ns.mapNames[string(name)]; ok {
|
||||
return false
|
||||
}
|
||||
ns.mapNames[string(name)] = struct{}{} // allocates a new string
|
||||
}
|
||||
|
||||
ns.allUnquotedNames = allNames
|
||||
ns.endOffsets = append(ns.endOffsets, uint(len(ns.allUnquotedNames)))
|
||||
return true
|
||||
}
|
||||
|
||||
// removeLast removes the last name in the namespace.
|
||||
func (ns *objectNamespace) removeLast() {
|
||||
if ns.mapNames != nil {
|
||||
delete(ns.mapNames, string(ns.lastUnquoted()))
|
||||
}
|
||||
if ns.length()-1 == 0 {
|
||||
ns.endOffsets = ns.endOffsets[:0]
|
||||
ns.allUnquotedNames = ns.allUnquotedNames[:0]
|
||||
} else {
|
||||
ns.endOffsets = ns.endOffsets[:ns.length()-1]
|
||||
ns.allUnquotedNames = ns.allUnquotedNames[:ns.endOffsets[ns.length()-1]]
|
||||
}
|
||||
}
|
||||
697
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/token.go
generated
vendored
Normal file
697
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/token.go
generated
vendored
Normal file
@@ -0,0 +1,697 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
// NOTE: Token is analogous to v1 json.Token.
|
||||
|
||||
const (
|
||||
maxInt64 = math.MaxInt64
|
||||
minInt64 = math.MinInt64
|
||||
maxUint64 = math.MaxUint64
|
||||
minUint64 = 0 // for consistency and readability purposes
|
||||
|
||||
invalidTokenPanic = "invalid jsontext.Token; it has been voided by a subsequent json.Decoder call"
|
||||
)
|
||||
|
||||
var errInvalidToken = errors.New("invalid jsontext.Token")
|
||||
|
||||
// Token represents a lexical JSON token, which may be one of the following:
|
||||
// - a JSON literal (i.e., null, true, or false)
|
||||
// - a JSON string (e.g., "hello, world!")
|
||||
// - a JSON number (e.g., 123.456)
|
||||
// - a begin or end delimiter for a JSON object (i.e., { or } )
|
||||
// - a begin or end delimiter for a JSON array (i.e., [ or ] )
|
||||
//
|
||||
// A Token cannot represent entire array or object values, while a [Value] can.
|
||||
// There is no Token to represent commas and colons since
|
||||
// these structural tokens can be inferred from the surrounding context.
|
||||
//
|
||||
// A Token stores data in one of two forms:
|
||||
//
|
||||
// - As raw JSON text: backed by the internal buffer of the [Decoder]
|
||||
// and only ever produced by [Decoder.ReadToken].
|
||||
// Such a token is only valid until the next call to any method on that
|
||||
// [Decoder] (e.g., [Decoder.PeekKind], [Decoder.ReadToken],
|
||||
// [Decoder.ReadValue], or [Decoder.SkipValue]).
|
||||
// Call [Token.Clone] to copy the raw text into an independent allocation
|
||||
// that persists beyond subsequent [Decoder] calls.
|
||||
//
|
||||
// - As a typed Go value: a self-contained representation produced by
|
||||
// the constructor functions (e.g., [String], [Int], [Uint], [Float]).
|
||||
// Such tokens are valid indefinitely and do not need to be cloned.
|
||||
type Token struct {
|
||||
nonComparable
|
||||
|
||||
// Tokens can exist in either a "raw" or an "exact" form.
|
||||
// Tokens produced by the Decoder are in the "raw" form.
|
||||
// Tokens returned by constructors are usually in the "exact" form.
|
||||
// The Encoder accepts Tokens in either the "raw" or "exact" form.
|
||||
//
|
||||
// The following chart shows the possible values for each Token type:
|
||||
// ╔══════════════════╦════════════╤════════════╤════════════╗
|
||||
// ║ Token type ║ raw field │ str field │ num field ║
|
||||
// ╠══════════════════╬════════════╪════════════╪════════════╣
|
||||
// ║ null (raw) ║ "null" │ "" │ 0 ║
|
||||
// ║ false (raw) ║ "false" │ "" │ 0 ║
|
||||
// ║ true (raw) ║ "true" │ "" │ 0 ║
|
||||
// ║ string (raw) ║ non-empty │ "" │ offset ║
|
||||
// ║ string (string) ║ nil │ non-empty │ 0 ║
|
||||
// ║ number (raw) ║ non-empty │ "" │ offset ║
|
||||
// ║ number (float32) ║ nil │ "F" │ non-zero ║
|
||||
// ║ number (float64) ║ nil │ "f" │ non-zero ║
|
||||
// ║ number (int64) ║ nil │ "i" │ non-zero ║
|
||||
// ║ number (uint64) ║ nil │ "u" │ non-zero ║
|
||||
// ║ object (delim) ║ "{" or "}" │ "" │ 0 ║
|
||||
// ║ array (delim) ║ "[" or "]" │ "" │ 0 ║
|
||||
// ╚══════════════════╩════════════╧════════════╧════════════╝
|
||||
//
|
||||
// Notes:
|
||||
// - For tokens stored in "raw" form, the num field contains the
|
||||
// absolute offset determined by raw.previousOffsetStart().
|
||||
// The buffer itself is stored in raw.previousBuffer().
|
||||
// - JSON literals and structural characters are always in the "raw" form.
|
||||
// - JSON strings and numbers can be in either "raw" or "exact" forms.
|
||||
// - The exact zero value of JSON strings and numbers in the "exact" forms
|
||||
// have ambiguous representation. Thus, they are always represented
|
||||
// in the "raw" form.
|
||||
|
||||
// raw contains a reference to the raw decode buffer.
|
||||
// If non-nil, then its value takes precedence over str and num.
|
||||
// It is only valid if num == raw.previousOffsetStart().
|
||||
raw *decodeBuffer
|
||||
|
||||
// str is the unescaped JSON string if num is zero.
|
||||
// Otherwise, it is "F", "f", "i", or "u" if num should be interpreted
|
||||
// as a float32, float64, int64, or uint64, respectively.
|
||||
str string
|
||||
|
||||
// num is a float32, float64, int64, or uint64 stored as a uint64 value.
|
||||
// For floating-point values, it stores the raw IEEE-754 bit-pattern.
|
||||
// It is non-zero for any JSON number in the "exact" form.
|
||||
num uint64
|
||||
}
|
||||
|
||||
// TODO: Does representing 1-byte delimiters as *decodeBuffer cause performance issues?
|
||||
|
||||
var (
|
||||
Null Token = rawToken("null")
|
||||
False Token = rawToken("false")
|
||||
True Token = rawToken("true")
|
||||
|
||||
BeginObject Token = rawToken("{")
|
||||
EndObject Token = rawToken("}")
|
||||
BeginArray Token = rawToken("[")
|
||||
EndArray Token = rawToken("]")
|
||||
|
||||
zeroString Token = rawToken(`""`)
|
||||
zeroNumber Token = rawToken(`0`)
|
||||
|
||||
nanString Token = String("NaN")
|
||||
pinfString Token = String("Infinity")
|
||||
ninfString Token = String("-Infinity")
|
||||
)
|
||||
|
||||
func rawToken(s string) Token {
|
||||
return Token{raw: &decodeBuffer{buf: []byte(s), prevStart: 0, prevEnd: len(s)}}
|
||||
}
|
||||
|
||||
// Bool constructs a Token representing a JSON boolean.
|
||||
func Bool(b bool) Token {
|
||||
if b {
|
||||
return True
|
||||
}
|
||||
return False
|
||||
}
|
||||
|
||||
// String constructs a Token representing a JSON string.
|
||||
// The provided string should contain valid UTF-8, otherwise invalid characters
|
||||
// may be mangled as the Unicode replacement character.
|
||||
func String(s string) Token {
|
||||
if len(s) == 0 {
|
||||
return zeroString
|
||||
}
|
||||
return Token{str: s}
|
||||
}
|
||||
|
||||
// Float32 constructs a Token representing a JSON number as
|
||||
// a 32-bit floating-point number formatted according to
|
||||
// ECMA-262, 10th edition, section 7.1.12.1,
|
||||
// with the exception that -0 is still formatted as -0.
|
||||
// The values NaN, +Inf, and -Inf will be represented
|
||||
// as a JSON string with the values "NaN", "Infinity", and "-Infinity".
|
||||
//
|
||||
// Note that most JSON libraries and standards assume that JSON numbers
|
||||
// are 64-bit floating-point numbers. Use of 32-bit precision should
|
||||
// only be used if the corresponding decoder knows that
|
||||
// this JSON number token is expected to only have 32-bit precision.
|
||||
// For all other situations, prefer using the [Float] constructor instead.
|
||||
func Float32(n float32) Token {
|
||||
if n != 0 && !math.IsNaN(float64(n)) && !math.IsInf(float64(n), 0) {
|
||||
return Token{str: "F", num: uint64(math.Float32bits(n))}
|
||||
}
|
||||
return Float(float64(n)) // handles ±0, NaN, and ±Inf
|
||||
}
|
||||
|
||||
// Float constructs a Token representing a JSON number as
|
||||
// a 64-bit floating-point number formatted according to
|
||||
// ECMA-262, 10th edition, section 7.1.12.1 and RFC 8785, section 3.2.2.3.
|
||||
// with the exception that -0 is still formatted as -0.
|
||||
// The values NaN, +Inf, and -Inf will be represented
|
||||
// as a JSON string with the values "NaN", "Infinity", and "-Infinity".
|
||||
func Float(n float64) Token {
|
||||
switch {
|
||||
case math.Float64bits(n) == 0:
|
||||
return zeroNumber
|
||||
case math.IsNaN(n):
|
||||
return nanString
|
||||
case math.IsInf(n, +1):
|
||||
return pinfString
|
||||
case math.IsInf(n, -1):
|
||||
return ninfString
|
||||
}
|
||||
return Token{str: "f", num: math.Float64bits(n)}
|
||||
}
|
||||
|
||||
// Int constructs a Token representing a JSON number from an int64.
|
||||
func Int(n int64) Token {
|
||||
if n == 0 {
|
||||
return zeroNumber
|
||||
}
|
||||
return Token{str: "i", num: uint64(n)}
|
||||
}
|
||||
|
||||
// Uint constructs a Token representing a JSON number from a uint64.
|
||||
func Uint(n uint64) Token {
|
||||
if n == 0 {
|
||||
return zeroNumber
|
||||
}
|
||||
return Token{str: "u", num: uint64(n)}
|
||||
}
|
||||
|
||||
// Clone returns a copy of the token with a value that is not backed by the
|
||||
// [Decoder] buffer and therefore remains valid past subsequent [Decoder] calls.
|
||||
// It has no effect on tokens produced by constructor functions,
|
||||
// since those are already self-contained.
|
||||
func (t Token) Clone() Token {
|
||||
// TODO: Allow caller to avoid any allocations?
|
||||
if raw := t.raw; raw != nil {
|
||||
// Avoid copying globals.
|
||||
if t.raw.prevStart == 0 {
|
||||
switch t.raw {
|
||||
case Null.raw:
|
||||
return Null
|
||||
case False.raw:
|
||||
return False
|
||||
case True.raw:
|
||||
return True
|
||||
case BeginObject.raw:
|
||||
return BeginObject
|
||||
case EndObject.raw:
|
||||
return EndObject
|
||||
case BeginArray.raw:
|
||||
return BeginArray
|
||||
case EndArray.raw:
|
||||
return EndArray
|
||||
}
|
||||
}
|
||||
|
||||
// This only ever needs to clone strings and numbers.
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := bytes.Clone(raw.previousBuffer())
|
||||
return Token{raw: &decodeBuffer{buf: buf, prevStart: 0, prevEnd: len(buf)}}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Bool returns the value for a JSON boolean.
|
||||
// It panics if the token kind is not a JSON boolean.
|
||||
func (t Token) Bool() bool {
|
||||
switch t.raw {
|
||||
case True.raw:
|
||||
return true
|
||||
case False.raw:
|
||||
return false
|
||||
default:
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
}
|
||||
|
||||
// appendString appends a JSON string to dst and returns it.
|
||||
// It panics if t is not a JSON string.
|
||||
func (t Token) appendString(dst []byte, flags *jsonflags.Flags) ([]byte, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw string value.
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]) == '"' {
|
||||
if jsonwire.ConsumeSimpleString(buf) == len(buf) {
|
||||
return append(dst, buf...), nil
|
||||
}
|
||||
dst, _, err := jsonwire.ReformatString(dst, buf, flags)
|
||||
return dst, err
|
||||
}
|
||||
} else if len(t.str) != 0 && t.num == 0 {
|
||||
// Handle exact string value.
|
||||
return jsonwire.AppendQuote(dst, []byte(t.str), flags)
|
||||
}
|
||||
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
|
||||
// String returns the unescaped string value for a JSON string.
|
||||
// For other JSON kinds, this returns the raw JSON representation.
|
||||
func (t Token) String() string {
|
||||
// This is inlinable to take advantage of "function outlining".
|
||||
// This avoids an allocation for the string(b) conversion
|
||||
// if the caller does not use the string in an escaping manner.
|
||||
// See https://blog.filippo.io/efficient-go-apis-with-the-inliner/
|
||||
s, b := t.string()
|
||||
if len(b) > 0 {
|
||||
return string(b)
|
||||
}
|
||||
return s
|
||||
}
|
||||
func (t Token) string() (string, []byte) {
|
||||
if raw := t.raw; raw != nil {
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.previousBuffer()
|
||||
if buf[0] == '"' {
|
||||
// TODO: Preserve ValueFlags in Token?
|
||||
isVerbatim := jsonwire.ConsumeSimpleString(buf) == len(buf)
|
||||
return "", jsonwire.UnquoteMayCopy(buf, isVerbatim)
|
||||
}
|
||||
// Handle tokens that are not JSON strings for fmt.Stringer.
|
||||
return "", buf
|
||||
}
|
||||
if len(t.str) != 0 && t.num == 0 {
|
||||
return t.str, nil
|
||||
}
|
||||
// Handle tokens that are not JSON strings for fmt.Stringer.
|
||||
if t.num > 0 {
|
||||
switch t.str[0] {
|
||||
case 'F':
|
||||
return string(jsonwire.AppendFloat(nil, float64(math.Float32frombits(uint32(t.num))), 32)), nil
|
||||
case 'f':
|
||||
return string(jsonwire.AppendFloat(nil, float64(math.Float64frombits(uint64(t.num))), 64)), nil
|
||||
case 'i':
|
||||
return strconv.FormatInt(int64(t.num), 10), nil
|
||||
case 'u':
|
||||
return strconv.FormatUint(uint64(t.num), 10), nil
|
||||
}
|
||||
}
|
||||
return "<invalid jsontext.Token>", nil
|
||||
}
|
||||
|
||||
// appendNumber appends a JSON number to dst and returns it.
|
||||
// It panics if t is not a JSON number.
|
||||
func (t Token) appendNumber(dst []byte, flags *jsonflags.Flags) ([]byte, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw number value.
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
dst, _, err := jsonwire.ReformatNumber(dst, buf, flags)
|
||||
return dst, err
|
||||
}
|
||||
} else if t.num != 0 {
|
||||
// Handle exact number value.
|
||||
switch t.str[0] {
|
||||
case 'F':
|
||||
return jsonwire.AppendFloat(dst, float64(math.Float32frombits(uint32(t.num))), 32), nil
|
||||
case 'f':
|
||||
return jsonwire.AppendFloat(dst, float64(math.Float64frombits(uint64(t.num))), 64), nil
|
||||
case 'i':
|
||||
return strconv.AppendInt(dst, int64(t.num), 10), nil
|
||||
case 'u':
|
||||
return strconv.AppendUint(dst, uint64(t.num), 10), nil
|
||||
}
|
||||
}
|
||||
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
|
||||
// Float32 returns the floating-point value for a JSON number
|
||||
// parsed according to 32 bits of precision.
|
||||
//
|
||||
// If the JSON number is outside the representable range of a float32,
|
||||
// it returns +Inf or -Inf along with an error
|
||||
// that matches [strconv.ErrRange] according to [errors.Is].
|
||||
//
|
||||
// It returns a NaN, +Inf, or -Inf value for any JSON string
|
||||
// with the values "NaN", "Infinity", or "-Infinity".
|
||||
//
|
||||
// It panics if the token kind is not a JSON number
|
||||
// or a JSON string with the aforementioned values.
|
||||
//
|
||||
// Note that most JSON libraries and standards assume that JSON numbers
|
||||
// are 64-bit floating-point numbers.
|
||||
// This method should only be used if the caller knows
|
||||
// from other context that this token is a JSON number
|
||||
// formatted only to 32 bits of precision (such as being encoded
|
||||
// using the [Float32] constructor). For all other situations,
|
||||
// prefer using the [Token.Float] accessor instead.
|
||||
func (t Token) Float32() (float32, error) {
|
||||
f, err := t.float(32)
|
||||
return float32(f), err
|
||||
}
|
||||
|
||||
// Float returns the floating-point value for a JSON number
|
||||
// parsed according to 64 bits of precision.
|
||||
//
|
||||
// If the JSON number is outside the representable range of a float64,
|
||||
// it returns +Inf or -Inf along with an error
|
||||
// that matches [strconv.ErrRange] according to [errors.Is].
|
||||
//
|
||||
// It returns a NaN, +Inf, or -Inf value for any JSON string
|
||||
// with the values "NaN", "Infinity", or "-Infinity".
|
||||
//
|
||||
// It panics if the token kind is not a JSON number
|
||||
// or a JSON string with the aforementioned values.
|
||||
func (t Token) Float() (float64, error) {
|
||||
f, err := t.float(64)
|
||||
return float64(f), err
|
||||
}
|
||||
|
||||
func (t Token) float(bits int) (float64, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw JSON number value.
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
fv, err := strconv.ParseFloat(string(buf), bits)
|
||||
if err != nil {
|
||||
err = &numError{accessor: "Float", value: t.String(), err: errors.Unwrap(err)} // only ever ErrRange
|
||||
}
|
||||
return fv, err
|
||||
}
|
||||
} else if t.num != 0 {
|
||||
// Handle typed Go number value.
|
||||
switch t.str[0] {
|
||||
case 'F':
|
||||
return float64(math.Float32frombits(uint32(t.num))), nil
|
||||
case 'f':
|
||||
f64 := float64(math.Float64frombits(uint64(t.num)))
|
||||
if bits == 32 && !math.IsInf(f64, 0) && math.IsInf(float64(float32(f64)), 0) {
|
||||
return f64, &numError{accessor: "Float", value: t.String(), err: strconv.ErrRange}
|
||||
}
|
||||
return f64, nil
|
||||
case 'i':
|
||||
return float64(int64(t.num)), nil // NOTE: This may lead to loss of precision.
|
||||
case 'u':
|
||||
return float64(uint64(t.num)), nil // NOTE: This may lead to loss of precision.
|
||||
}
|
||||
}
|
||||
|
||||
// Handle string values with "NaN", "Infinity", or "-Infinity".
|
||||
if t.Kind() == '"' {
|
||||
switch t.String() {
|
||||
case "NaN":
|
||||
return math.NaN(), nil
|
||||
case "Infinity":
|
||||
return math.Inf(+1), nil
|
||||
case "-Infinity":
|
||||
return math.Inf(-1), nil
|
||||
}
|
||||
// TODO: Should this be an error instead of a panic?
|
||||
// We can safely switch from a panic to an error in the future.
|
||||
}
|
||||
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
|
||||
// Int returns the signed integer value for a JSON number.
|
||||
//
|
||||
// It reports an error that matches [strconv.ErrSyntax] according to [errors.Is]
|
||||
// if the JSON number does not match the restricted grammar of just a signed integer.
|
||||
// It reports an error that matches [strconv.ErrRange] according to [errors.Is]
|
||||
// if the JSON number is a signed integer, but outside the range of an int64.
|
||||
// Even if an error is reported, a reasonable value is still returned.
|
||||
// The fractional component of any number is ignored (truncation toward zero).
|
||||
// Any number beyond the representation of an int64 will be saturated
|
||||
// to the closest representable value.
|
||||
//
|
||||
// It panics if the token kind is not a JSON number.
|
||||
func (t Token) Int() (int64, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw JSON number value.
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.previousBuffer()
|
||||
if len(buf) > 0 && buf[0] == '-' {
|
||||
// Prospectively parse a negative integer.
|
||||
switch abs, ok := jsonwire.ParseUint(buf[len("-"):]); {
|
||||
case abs > -minInt64:
|
||||
return minInt64, &numError{accessor: "Int", value: t.String(), err: strconv.ErrRange}
|
||||
case ok:
|
||||
return -1 * int64(abs), nil
|
||||
}
|
||||
} else {
|
||||
// Prospectively parse a non-negative integer.
|
||||
switch abs, ok := jsonwire.ParseUint(buf); {
|
||||
case abs > +maxInt64:
|
||||
return maxInt64, &numError{accessor: "Int", value: t.String(), err: strconv.ErrRange}
|
||||
case ok:
|
||||
return +1 * int64(abs), nil
|
||||
}
|
||||
}
|
||||
// This is not a signed integer, which implies ErrSyntax.
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
f64, _ := strconv.ParseFloat(string(buf), 64)
|
||||
return f64toi64(f64), &numError{accessor: "Int", value: t.String(), err: strconv.ErrSyntax}
|
||||
}
|
||||
} else if t.num != 0 {
|
||||
// Handle typed Go number value.
|
||||
switch t.str[0] {
|
||||
case 'i':
|
||||
return int64(t.num), nil
|
||||
case 'u':
|
||||
if t.num > maxInt64 {
|
||||
return maxInt64, &numError{accessor: "Int", value: t.String(), err: strconv.ErrRange}
|
||||
}
|
||||
return int64(t.num), nil
|
||||
case 'f', 'F':
|
||||
f64 := float64(math.Float64frombits(uint64(t.num)))
|
||||
if t.str[0] == 'F' {
|
||||
f64 = float64(math.Float32frombits(uint32(t.num)))
|
||||
}
|
||||
switch i64 := f64toi64(f64); {
|
||||
case math.IsNaN(f64), math.Trunc(f64) != f64:
|
||||
return i64, &numError{accessor: "Int", value: t.String(), err: strconv.ErrSyntax}
|
||||
case (i64 == minInt64 && f64 < minInt64) || (i64 == maxInt64 && f64 > maxInt64):
|
||||
return i64, &numError{accessor: "Int", value: t.String(), err: strconv.ErrRange}
|
||||
default:
|
||||
return i64, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
|
||||
func f64toi64(f64 float64) int64 {
|
||||
switch {
|
||||
case math.IsNaN(f64):
|
||||
return 0
|
||||
case f64 >= maxInt64+1:
|
||||
return maxInt64
|
||||
case f64 < minInt64:
|
||||
return minInt64
|
||||
default:
|
||||
return int64(f64) // NOTE: This may lead to loss of precision.
|
||||
}
|
||||
}
|
||||
|
||||
// Uint returns the unsigned integer value for a JSON number.
|
||||
//
|
||||
// It reports an error that matches [strconv.ErrSyntax] if the JSON number
|
||||
// does not match the restricted grammar of just an unsigned integer.
|
||||
// It reports an error that matches [strconv.ErrRange] if the JSON number
|
||||
// is an unsigned integer, but outside the representable range of a uint64.
|
||||
// Even if an error is reported, a reasonable value is still returned.
|
||||
// The fractional component of any number is ignored (truncation toward zero).
|
||||
// Any number beyond the representation of a uint64 will be saturated
|
||||
// to the closest representable value.
|
||||
//
|
||||
// It panics if the token kind is not a JSON number.
|
||||
func (t Token) Uint() (uint64, error) {
|
||||
// NOTE: This accessor returns 0 for any negative JSON number,
|
||||
// which might be surprising, but is at least consistent with the behavior
|
||||
// of saturating out-of-bounds numbers to the closest representable number.
|
||||
// We report ErrSyntax instead of ErrRange since the grammar for
|
||||
// an unsigned integer does not permit a negative sign.
|
||||
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw JSON number value.
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.previousBuffer()
|
||||
// Prospectively parse an unsigned integer.
|
||||
switch abs, ok := jsonwire.ParseUint(buf); {
|
||||
case ok:
|
||||
return abs, nil
|
||||
case abs == maxUint64: // implies overflows
|
||||
return maxUint64, &numError{accessor: "Uint", value: t.String(), err: strconv.ErrRange}
|
||||
}
|
||||
// This is not an unsigned integer, which implies ErrSyntax.
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
f64, _ := strconv.ParseFloat(string(buf), 64)
|
||||
return f64tou64(f64), &numError{accessor: "Uint", value: t.String(), err: strconv.ErrSyntax}
|
||||
}
|
||||
} else if t.num != 0 {
|
||||
// Handle typed Go number value.
|
||||
switch t.str[0] {
|
||||
case 'u':
|
||||
return t.num, nil
|
||||
case 'i':
|
||||
if int64(t.num) < minUint64 {
|
||||
return minUint64, &numError{accessor: "Uint", value: t.String(), err: strconv.ErrSyntax}
|
||||
}
|
||||
return uint64(int64(t.num)), nil
|
||||
case 'f', 'F':
|
||||
f64 := float64(math.Float64frombits(uint64(t.num)))
|
||||
if t.str[0] == 'F' {
|
||||
f64 = float64(math.Float32frombits(uint32(t.num)))
|
||||
}
|
||||
switch u64 := f64tou64(f64); {
|
||||
case math.IsNaN(f64), math.Trunc(f64) != f64, math.Signbit(f64):
|
||||
return u64, &numError{accessor: "Uint", value: t.String(), err: strconv.ErrSyntax}
|
||||
case (u64 == minUint64 && f64 < minUint64) || (u64 == maxUint64 && f64 > maxUint64):
|
||||
return u64, &numError{accessor: "Uint", value: t.String(), err: strconv.ErrRange}
|
||||
default:
|
||||
return u64, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic("invalid JSON token kind: " + t.Kind().String())
|
||||
}
|
||||
|
||||
func f64tou64(f64 float64) uint64 {
|
||||
switch {
|
||||
case math.IsNaN(f64):
|
||||
return 0
|
||||
case f64 >= maxUint64+1:
|
||||
return maxUint64
|
||||
case f64 < minUint64:
|
||||
return minUint64
|
||||
default:
|
||||
return uint64(f64) // NOTE: This may lead to loss of precision.
|
||||
}
|
||||
}
|
||||
|
||||
// Kind returns the token kind.
|
||||
func (t Token) Kind() Kind {
|
||||
switch {
|
||||
case t.raw != nil:
|
||||
raw := t.raw
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
return Kind(t.raw.buf[raw.prevStart]).normalize()
|
||||
case t.num != 0:
|
||||
return '0'
|
||||
case len(t.str) != 0:
|
||||
return '"'
|
||||
default:
|
||||
return invalidKind
|
||||
}
|
||||
}
|
||||
|
||||
// A Kind represents the kind of a JSON token.
|
||||
//
|
||||
// Kind represents each possible JSON token kind with a single byte,
|
||||
// which is conveniently the first byte of that kind's grammar
|
||||
// with the restriction that numbers always be represented with '0'.
|
||||
type Kind byte
|
||||
|
||||
const (
|
||||
KindInvalid Kind = 0 // invalid kind
|
||||
KindNull Kind = 'n' // null
|
||||
KindFalse Kind = 'f' // false
|
||||
KindTrue Kind = 't' // true
|
||||
KindString Kind = '"' // string
|
||||
KindNumber Kind = '0' // number
|
||||
KindBeginObject Kind = '{' // begin object
|
||||
KindEndObject Kind = '}' // end object
|
||||
KindBeginArray Kind = '[' // begin array
|
||||
KindEndArray Kind = ']' // end array
|
||||
)
|
||||
|
||||
const invalidKind Kind = 0
|
||||
|
||||
// String prints the kind in a humanly readable fashion.
|
||||
func (k Kind) String() string {
|
||||
switch k {
|
||||
case 0:
|
||||
return "invalid"
|
||||
case 'n':
|
||||
return "null"
|
||||
case 'f':
|
||||
return "false"
|
||||
case 't':
|
||||
return "true"
|
||||
case '"':
|
||||
return "string"
|
||||
case '0':
|
||||
return "number"
|
||||
case '{':
|
||||
return "{"
|
||||
case '}':
|
||||
return "}"
|
||||
case '[':
|
||||
return "["
|
||||
case ']':
|
||||
return "]"
|
||||
default:
|
||||
return "<invalid jsontext.Kind: " + jsonwire.QuoteRune([]byte{byte(k)}) + ">"
|
||||
}
|
||||
}
|
||||
|
||||
var normKind = [256]Kind{
|
||||
'n': 'n',
|
||||
'f': 'f',
|
||||
't': 't',
|
||||
'"': '"',
|
||||
'{': '{',
|
||||
'}': '}',
|
||||
'[': '[',
|
||||
']': ']',
|
||||
'-': '0',
|
||||
'0': '0',
|
||||
'1': '0',
|
||||
'2': '0',
|
||||
'3': '0',
|
||||
'4': '0',
|
||||
'5': '0',
|
||||
'6': '0',
|
||||
'7': '0',
|
||||
'8': '0',
|
||||
'9': '0',
|
||||
}
|
||||
|
||||
// normalize coalesces all possible starting characters of a number as just '0',
|
||||
// and converts all invalid kinds to 0.
|
||||
func (k Kind) normalize() Kind {
|
||||
// A lookup table keeps the inlining cost as low as possible.
|
||||
return normKind[k]
|
||||
}
|
||||
414
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/value.go
generated
vendored
Normal file
414
tools/tsgo/vendor/github.com/go-json-experiment/json/jsontext/value.go
generated
vendored
Normal file
@@ -0,0 +1,414 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
// AppendFloat appends src to dst as a JSON number per RFC 8259, section 6.
|
||||
//
|
||||
// Except for -0, which is formatted as -0 instead of 0,
|
||||
// the output is identical to ECMA-262, 10th edition, section 7.1.12.1
|
||||
// and (for 64-bit precision) identical to RFC 8785, section 3.2.2.3.
|
||||
// The values NaN, +Inf, and -Inf will be represented as a JSON string
|
||||
// with the values "NaN", "Infinity", and "-Infinity".
|
||||
//
|
||||
// Note that most JSON libraries and standards assume that JSON numbers
|
||||
// are 64-bit floating-point numbers. As such, prefer using 64 bits
|
||||
// of precision unless the recipient can know from other context
|
||||
// that the encoded number uses 32 bits of precision.
|
||||
func AppendFloat(dst []byte, src float64, bits int) []byte {
|
||||
if bits != 32 && bits != 64 {
|
||||
panic("illegal AppendFloat bit size")
|
||||
}
|
||||
return jsonwire.AppendFloat(dst, src, bits)
|
||||
}
|
||||
|
||||
// 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[Bytes ~[]byte | ~string](dst []byte, src Bytes, opts ...Options) ([]byte, error) {
|
||||
e := getBufferedEncoder(opts...)
|
||||
defer putBufferedEncoder(e)
|
||||
e.s.Flags.Set(jsonflags.OmitTopLevelNewline | 1)
|
||||
if err := e.s.WriteValue(Value(src)); err != nil {
|
||||
return append(dst, src...), err
|
||||
}
|
||||
return append(dst, e.s.Buf...), nil
|
||||
}
|
||||
|
||||
// NOTE: Value is analogous to v1 json.RawMessage.
|
||||
|
||||
// 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!")
|
||||
// - a JSON number (e.g., 123.456)
|
||||
// - an entire JSON object (e.g., {"fizz":"buzz"} )
|
||||
// - an entire JSON array (e.g., [1,2,3] )
|
||||
//
|
||||
// Value can represent entire array or object values, while [Token] cannot.
|
||||
// Value may contain leading and/or trailing whitespace.
|
||||
type Value []byte
|
||||
|
||||
// Clone returns a copy of v.
|
||||
func (v Value) Clone() Value {
|
||||
return bytes.Clone(v)
|
||||
}
|
||||
|
||||
// String returns the string formatting of v.
|
||||
func (v Value) String() string {
|
||||
if v == nil {
|
||||
return "null"
|
||||
}
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// IsValid reports whether the raw JSON value is syntactically valid
|
||||
// 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).
|
||||
//
|
||||
// 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 of 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 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 an indented line according to the nesting.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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. To preserve the original representation
|
||||
// of JSON integers, additionally set [CanonicalizeRawInts] to false:
|
||||
//
|
||||
// v.Canonicalize(jsontext.CanonicalizeRawInts(false))
|
||||
func (v *Value) Canonicalize(opts ...Options) error {
|
||||
return v.format([]Options{
|
||||
CanonicalizeRawInts(true),
|
||||
CanonicalizeRawFloats(true),
|
||||
ReorderRawObjects(true),
|
||||
}, opts)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (v Value) MarshalJSON() ([]byte, error) {
|
||||
// NOTE: This matches the behavior of v1 json.RawMessage.MarshalJSON.
|
||||
if v == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets v as the JSON encoding of b.
|
||||
// It stores a copy of the provided raw JSON input without any validation.
|
||||
func (v *Value) UnmarshalJSON(b []byte) error {
|
||||
// NOTE: This matches the behavior of v1 json.RawMessage.UnmarshalJSON.
|
||||
if v == nil {
|
||||
return errors.New("jsontext.Value: UnmarshalJSON on nil pointer")
|
||||
}
|
||||
*v = append((*v)[:0], b...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Kind returns the starting token kind.
|
||||
// For a valid value, this will never include [KindEndObject] or [KindEndArray].
|
||||
func (v Value) Kind() Kind {
|
||||
if v := v[jsonwire.ConsumeWhitespace(v):]; len(v) > 0 {
|
||||
return Kind(v[0]).normalize()
|
||||
}
|
||||
return invalidKind
|
||||
}
|
||||
|
||||
const commaAndWhitespace = ", \n\r\t"
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
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 objectMemberPool = sync.Pool{New: func() any { return new([]objectMember) }}
|
||||
|
||||
func getObjectMembers() *[]objectMember {
|
||||
ns := objectMemberPool.Get().(*[]objectMember)
|
||||
*ns = (*ns)[:0]
|
||||
return ns
|
||||
}
|
||||
func putObjectMembers(ns *[]objectMember) {
|
||||
if cap(*ns) < 1<<10 {
|
||||
clear(*ns) // avoid pinning name and buffer
|
||||
objectMemberPool.Put(ns)
|
||||
}
|
||||
}
|
||||
|
||||
// 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).
|
||||
// - Initial call is provided a Decoder reading from the start of v.
|
||||
//
|
||||
// Post-conditions:
|
||||
// - Exactly one JSON value is read from the Decoder.
|
||||
// - All fully-parsed JSON objects are reordered by directly moving
|
||||
// the members in the value buffer.
|
||||
//
|
||||
// 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 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 := getObjectMembers()
|
||||
defer putObjectMembers(members)
|
||||
var prevMember objectMember
|
||||
isSorted := true
|
||||
|
||||
beforeBody := d.InputOffset() // offset after '{'
|
||||
for d.PeekKind() != '}' {
|
||||
beforeName := d.InputOffset()
|
||||
var flags jsonwire.ValueFlags
|
||||
name, _ := d.s.ReadValue(&flags)
|
||||
name = jsonwire.UnquoteMayCopy(name, flags.IsVerbatim())
|
||||
mustReorderObjectsFromDecoder(d, scratch)
|
||||
afterValue := d.InputOffset()
|
||||
|
||||
currMember := objectMember{name, d.s.buf[beforeName:afterValue]}
|
||||
if isSorted && len(*members) > 0 {
|
||||
isSorted = objectMember.Compare(prevMember, currMember) < 0
|
||||
}
|
||||
*members = append(*members, currMember)
|
||||
prevMember = currMember
|
||||
}
|
||||
afterBody := d.InputOffset() // offset before '}'
|
||||
d.ReadToken()
|
||||
|
||||
// Sort the members; return early if it's already sorted.
|
||||
if isSorted {
|
||||
return
|
||||
}
|
||||
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.
|
||||
// Avoid swapping in place since each member may be a different size
|
||||
// where moving a member over a smaller member may corrupt the data
|
||||
// for subsequent members before they have been moved.
|
||||
//
|
||||
// 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 {
|
||||
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) {
|
||||
panic("BUG: length invariant violated")
|
||||
}
|
||||
copy(d.s.buf[beforeBody:afterBody], sorted)
|
||||
|
||||
// Update scratch buffer to the largest amount ever used.
|
||||
if len(sorted) > len(*scratch) {
|
||||
*scratch = sorted
|
||||
}
|
||||
case '[':
|
||||
for d.PeekKind() != ']' {
|
||||
mustReorderObjectsFromDecoder(d, scratch)
|
||||
}
|
||||
d.ReadToken()
|
||||
default:
|
||||
if err != nil {
|
||||
panic("BUG: " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
60
tools/tsgo/vendor/github.com/go-json-experiment/json/migrate.sh
generated
vendored
Normal file
60
tools/tsgo/vendor/github.com/go-json-experiment/json/migrate.sh
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
GOROOT=${1:-../go}
|
||||
JSONROOT="."
|
||||
|
||||
cp $JSONROOT/alias_gen.go $JSONROOT/alias_gen.go.bak
|
||||
cp $JSONROOT/options_format_global.go $JSONROOT/options_format_global.go.bak
|
||||
cp $JSONROOT/options_format_test.go $JSONROOT/options_format_test.go.bak
|
||||
rm -r $JSONROOT/*.go $JSONROOT/internal $JSONROOT/jsontext $JSONROOT/v1
|
||||
mv $JSONROOT/alias_gen.go.bak $JSONROOT/alias_gen.go
|
||||
cp -r $GOROOT/src/encoding/json/v2/*.go $JSONROOT/
|
||||
cp -r $GOROOT/src/encoding/json/internal/ $JSONROOT/internal/
|
||||
cp -r $GOROOT/src/encoding/json/jsontext/ $JSONROOT/jsontext/
|
||||
mkdir $JSONROOT/v1
|
||||
for X in $GOROOT/src/encoding/json/v2_*.go; do
|
||||
cp $X $JSONROOT/v1/$(basename $X | sed "s/v2_//")
|
||||
done
|
||||
cd $JSONROOT
|
||||
for X in $(git ls-files --cached --others --exclude-standard | grep ".*[.]go$"); do
|
||||
if [ ! -e "$X" ]; then
|
||||
continue
|
||||
fi
|
||||
sed -i 's/go:build goexperiment.jsonv2 && !goexperiment.jsonformat$/go:build (!goexperiment.jsonv2 || !go1.25) \&\& !goexperiment.jsonformat/' $X
|
||||
sed -i 's/go:build goexperiment.jsonv2 && goexperiment.jsonformat$/go:build (!goexperiment.jsonv2 || !go1.25) \&\& goexperiment.jsonformat/' $X
|
||||
sed -i 's/go:build goexperiment.jsonv2$/go:build !goexperiment.jsonv2 || !go1.25/' $X
|
||||
sed -i 's|"encoding/json/v2"|"github.com/go-json-experiment/json"|' $X
|
||||
sed -i 's|"encoding/json/internal"|"github.com/go-json-experiment/json/internal"|' $X
|
||||
sed -i 's|"encoding/json/internal/jsonflags"|"github.com/go-json-experiment/json/internal/jsonflags"|' $X
|
||||
sed -i 's|"encoding/json/internal/jsonopts"|"github.com/go-json-experiment/json/internal/jsonopts"|' $X
|
||||
sed -i 's|"encoding/json/internal/jsontest"|"github.com/go-json-experiment/json/internal/jsontest"|' $X
|
||||
sed -i 's|"encoding/json/internal/jsonwire"|"github.com/go-json-experiment/json/internal/jsonwire"|' $X
|
||||
sed -i 's|"encoding/json/jsontext"|"github.com/go-json-experiment/json/jsontext"|' $X
|
||||
sed -i 's|"encoding/json"|"github.com/go-json-experiment/json/v1"|' $X
|
||||
sed -i 's|"internal/zstd"|"github.com/go-json-experiment/json/internal/zstd"|' $X
|
||||
goimports -w $X
|
||||
done
|
||||
sed -i 's/v2[.]struct/json.struct/' $JSONROOT/errors_test.go
|
||||
sed -i 's|jsonv1 "github.com/go-json-experiment/json/v1"|jsonv1 "encoding/json"|' $JSONROOT/bench_test.go
|
||||
sed -i '/testenv/d' $JSONROOT/jsontext/token_test.go
|
||||
|
||||
# Remove documentation that only makes sense within the stdlib.
|
||||
sed -i '/This package .* is experimental/,+4d' $JSONROOT/doc.go
|
||||
sed -i '/This package .* is experimental/,+4d' $JSONROOT/jsontext/doc.go
|
||||
|
||||
git checkout internal/zstd # we still need local copy of zstd for testing
|
||||
|
||||
go run alias_gen.go "encoding/json" $JSONROOT/v1
|
||||
go run alias_gen.go "encoding/json/v2" $JSONROOT
|
||||
go run alias_gen.go "encoding/json/jsontext" $JSONROOT/jsontext
|
||||
|
||||
mv $JSONROOT/options_format_global.go.bak $JSONROOT/options_format_global.go
|
||||
mv $JSONROOT/options_format_test.go.bak $JSONROOT/options_format_test.go
|
||||
cp $GOROOT/src/encoding/json/internal/jsonopts/options_format.go $JSONROOT/options_format.go
|
||||
sed -i '/go:build goexperiment.jsonv2/d' $JSONROOT/options_format.go
|
||||
sed -i 's/package jsonopts/package json/' $JSONROOT/options_format.go
|
||||
goimports -w $JSONROOT/options_format.go
|
||||
sed -i -E '/^func (Marshal|MarshalWrite|MarshalEncode|Unmarshal|UnmarshalRead|UnmarshalDecode)\(/a opts = mayAppendSupportFormatTag(opts)' $JSONROOT/alias.go
|
||||
sed -i -E '/^func (Marshal|MarshalWrite|MarshalEncode|Unmarshal|UnmarshalRead|UnmarshalDecode)\(/a opts = mayAppendSupportFormatTag(opts)' $JSONROOT/arshal.go
|
||||
goimports -w $JSONROOT/alias.go
|
||||
goimports -w $JSONROOT/arshal.go
|
||||
290
tools/tsgo/vendor/github.com/go-json-experiment/json/options.go
generated
vendored
Normal file
290
tools/tsgo/vendor/github.com/go-json-experiment/json/options.go
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-json-experiment/json/internal"
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
"github.com/go-json-experiment/json/internal/jsonopts"
|
||||
)
|
||||
|
||||
// Options configure [Marshal], [MarshalWrite], [MarshalEncode],
|
||||
// [Unmarshal], [UnmarshalRead], and [UnmarshalDecode] with specific features.
|
||||
// Each function takes in a variadic list of options, where properties
|
||||
// set in later options override the value of previously set properties.
|
||||
//
|
||||
// The Options type is identical to [encoding/json.Options] and
|
||||
// [encoding/json/jsontext.Options]. Options from the other packages can
|
||||
// be used interchangeably with functionality in this package.
|
||||
//
|
||||
// Options represent either a singular option or a set of options.
|
||||
// It can be functionally thought of as a Go map of option properties
|
||||
// (even though the underlying implementation avoids Go maps for performance).
|
||||
//
|
||||
// The constructors (e.g., [Deterministic]) return a singular option value:
|
||||
//
|
||||
// opt := Deterministic(true)
|
||||
//
|
||||
// which is analogous to creating a single entry map:
|
||||
//
|
||||
// opt := Options{"Deterministic": true}
|
||||
//
|
||||
// [JoinOptions] composes multiple options values together:
|
||||
//
|
||||
// out := JoinOptions(opts...)
|
||||
//
|
||||
// which is analogous to making a new map and copying the options over:
|
||||
//
|
||||
// out := make(Options)
|
||||
// for _, m := range opts {
|
||||
// for k, v := range m {
|
||||
// out[k] = v
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// [GetOption] looks up the value of an options parameter:
|
||||
//
|
||||
// v, ok := GetOption(opts, Deterministic)
|
||||
//
|
||||
// which is analogous to a Go map lookup:
|
||||
//
|
||||
// v, ok := Options["Deterministic"]
|
||||
//
|
||||
// There is a single Options type, which is used with both marshal and unmarshal.
|
||||
// Some options affect both operations, while others only affect one operation:
|
||||
//
|
||||
// - [StringifyNumbers] affects marshaling and unmarshaling
|
||||
// - [Deterministic] affects marshaling only
|
||||
// - [FormatNilSliceAsNull] affects marshaling only
|
||||
// - [FormatNilMapAsNull] affects marshaling only
|
||||
// - [OmitZeroStructFields] affects marshaling only
|
||||
// - [MatchCaseInsensitiveNames] affects marshaling and unmarshaling
|
||||
// - [RejectUnknownMembers] affects unmarshaling only
|
||||
// - [WithMarshalers] affects marshaling only
|
||||
// - [WithUnmarshalers] affects unmarshaling only
|
||||
//
|
||||
// Options that do not affect a particular operation are ignored.
|
||||
type Options = jsonopts.Options
|
||||
|
||||
// JoinOptions coalesces the provided list of options into a single Options.
|
||||
// Properties set in later options override the value of previously set properties.
|
||||
func JoinOptions(srcs ...Options) Options {
|
||||
var dst jsonopts.Struct
|
||||
dst.Join(srcs...)
|
||||
return &dst
|
||||
}
|
||||
|
||||
// GetOption returns the value stored in opts with the provided setter,
|
||||
// reporting whether the value is present.
|
||||
// If not present, the returned value is the zero value for type T.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// v, ok := json.GetOption(opts, json.Deterministic)
|
||||
//
|
||||
// Options are most commonly introspected to alter the JSON representation of
|
||||
// [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 the set of options in [encoding/json.DefaultOptionsV1]
|
||||
// all being set to false. All other options are not present.
|
||||
func DefaultOptionsV2() Options {
|
||||
return &jsonopts.DefaultOptionsV2
|
||||
}
|
||||
|
||||
// StringifyNumbers specifies that types that would normally be
|
||||
// encoded as a JSON number to instead be encoded as a JSON string
|
||||
// containing the equivalent JSON number value.
|
||||
// When unmarshaling, the value is parsed from a JSON string
|
||||
// containing the JSON number without any surrounding whitespace.
|
||||
//
|
||||
// When the `string` tag option is specified on a Go struct field,
|
||||
// this option is applied for the top-level JSON value for that field.
|
||||
// Unless StringifyNumbers was applied globally, the option does not
|
||||
// recursively apply to nested JSON numbers within a JSON object or array.
|
||||
// A Go type with custom marshal/unmarshal that represents a JSON number
|
||||
// should respect the StringifyNumbers option and if specified
|
||||
// serialize as a JSON number within a JSON string.
|
||||
//
|
||||
// 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.
|
||||
// This may cause decoders to lose precision for int64 and uint64 types.
|
||||
// Quoting JSON numbers as a JSON string preserves the exact precision.
|
||||
//
|
||||
// This affects either marshaling or unmarshaling.
|
||||
func StringifyNumbers(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.StringifyNumbers | 1
|
||||
} else {
|
||||
return jsonflags.StringifyNumbers | 0
|
||||
}
|
||||
}
|
||||
|
||||
// Deterministic specifies that the same input value will be serialized
|
||||
// as the exact same output bytes. Different processes of
|
||||
// the same program will serialize equal values to the same bytes,
|
||||
// but different versions of the same program are not guaranteed
|
||||
// to produce the exact same sequence of bytes.
|
||||
//
|
||||
// This only affects marshaling and is ignored when unmarshaling.
|
||||
func Deterministic(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.Deterministic | 1
|
||||
} else {
|
||||
return jsonflags.Deterministic | 0
|
||||
}
|
||||
}
|
||||
|
||||
// FormatNilSliceAsNull specifies that a nil Go slice should marshal as a
|
||||
// JSON null instead of the default representation as an empty JSON array
|
||||
// (or an empty JSON string in the case of ~[]byte).
|
||||
//
|
||||
// This only affects marshaling and is ignored when unmarshaling.
|
||||
func FormatNilSliceAsNull(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.FormatNilSliceAsNull | 1
|
||||
} else {
|
||||
return jsonflags.FormatNilSliceAsNull | 0
|
||||
}
|
||||
}
|
||||
|
||||
// FormatNilMapAsNull specifies that a nil Go map should marshal as a
|
||||
// JSON null instead of the default representation as an empty JSON object.
|
||||
//
|
||||
// This only affects marshaling and is ignored when unmarshaling.
|
||||
func FormatNilMapAsNull(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.FormatNilMapAsNull | 1
|
||||
} else {
|
||||
return jsonflags.FormatNilMapAsNull | 0
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// If a name matches multiple fields, it chooses the field with an exact
|
||||
// match of the name, otherwise it reports an error.
|
||||
// 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.
|
||||
//
|
||||
// This affects either marshaling or unmarshaling.
|
||||
//
|
||||
// By matching names in a case-insensitive manner, it also affects the detection
|
||||
// of duplicate names (assuming [jsontext.AllowDuplicateNames] is false) since
|
||||
// variations of the same name may match the same Go struct field.
|
||||
// For example, when unmarshaling, the names "foo" and "Foo" may both
|
||||
// match the same Go struct field and therefore be considered a duplicate name.
|
||||
// When marshaling, normally it is impossible for any two Go struct fields to
|
||||
// serialize in a way where they unmarshal into the same Go struct field
|
||||
// since they all have unique exact names.
|
||||
// However, with the use of an embedded fallback, it is possible for the
|
||||
// embedded fallback to contain a name that also matches the name for
|
||||
// a Go struct field, resulting in a duplicate name error.
|
||||
func MatchCaseInsensitiveNames(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.MatchCaseInsensitiveNames | 1
|
||||
} else {
|
||||
return jsonflags.MatchCaseInsensitiveNames | 0
|
||||
}
|
||||
}
|
||||
|
||||
// RejectUnknownMembers specifies that unknown members should be rejected
|
||||
// when unmarshaling a JSON object.
|
||||
//
|
||||
// This only affects unmarshaling and is ignored when marshaling.
|
||||
func RejectUnknownMembers(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.RejectUnknownMembers | 1
|
||||
} else {
|
||||
return jsonflags.RejectUnknownMembers | 0
|
||||
}
|
||||
}
|
||||
|
||||
// WithMarshalers specifies a list of type-specific marshalers to use,
|
||||
// which can be used to override the default marshal behavior for values
|
||||
// of particular types.
|
||||
//
|
||||
// This only affects marshaling and is ignored when unmarshaling.
|
||||
func WithMarshalers(v *Marshalers) Options {
|
||||
return (*marshalersOption)(v)
|
||||
}
|
||||
|
||||
// WithUnmarshalers specifies a list of type-specific unmarshalers to use,
|
||||
// which can be used to override the default unmarshal behavior for values
|
||||
// of particular types.
|
||||
//
|
||||
// This only affects unmarshaling and is ignored when marshaling.
|
||||
func WithUnmarshalers(v *Unmarshalers) Options {
|
||||
return (*unmarshalersOption)(v)
|
||||
}
|
||||
|
||||
// These option types are declared here instead of "jsonopts"
|
||||
// to avoid a dependency on "reflect" from "jsonopts".
|
||||
type (
|
||||
marshalersOption Marshalers
|
||||
unmarshalersOption Unmarshalers
|
||||
)
|
||||
|
||||
func (*marshalersOption) JSONOptions(internal.NotForPublicUse) {}
|
||||
func (*unmarshalersOption) JSONOptions(internal.NotForPublicUse) {}
|
||||
|
||||
// Inject support into "jsonopts" to handle these types.
|
||||
func init() {
|
||||
jsonopts.GetUnknownOption = func(src jsonopts.Struct, zero jsonopts.Options) (any, bool) {
|
||||
switch zero.(type) {
|
||||
case *marshalersOption:
|
||||
if !src.Flags.Has(jsonflags.Marshalers) {
|
||||
return (*Marshalers)(nil), false
|
||||
}
|
||||
return src.Marshalers.(*Marshalers), true
|
||||
case *unmarshalersOption:
|
||||
if !src.Flags.Has(jsonflags.Unmarshalers) {
|
||||
return (*Unmarshalers)(nil), false
|
||||
}
|
||||
return src.Unmarshalers.(*Unmarshalers), true
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown option %T", zero))
|
||||
}
|
||||
}
|
||||
jsonopts.JoinUnknownOption = func(dst jsonopts.Struct, src jsonopts.Options) jsonopts.Struct {
|
||||
switch src := src.(type) {
|
||||
case *marshalersOption:
|
||||
dst.Flags.Set(jsonflags.Marshalers | 1)
|
||||
dst.Marshalers = (*Marshalers)(src)
|
||||
case *unmarshalersOption:
|
||||
dst.Flags.Set(jsonflags.Unmarshalers | 1)
|
||||
dst.Unmarshalers = (*Unmarshalers)(src)
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown option %T", src))
|
||||
}
|
||||
return dst
|
||||
}
|
||||
}
|
||||
122
tools/tsgo/vendor/github.com/go-json-experiment/json/options_format.go
generated
vendored
Normal file
122
tools/tsgo/vendor/github.com/go-json-experiment/json/options_format.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2026 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package json
|
||||
|
||||
// NOTE: While [ExperimentalSupportFormatTag] is exported,
|
||||
// it is in an internal package and thus inaccessible for public use.
|
||||
//
|
||||
// The [github.com/go-json-experiment/json] module is kept in sync
|
||||
// with the Go standard standard library and will expose this option
|
||||
// in a way that public code can now directly reference.
|
||||
|
||||
type supportFormatTag struct {
|
||||
Options
|
||||
v bool
|
||||
}
|
||||
|
||||
var (
|
||||
enableFormatTag = supportFormatTag{v: true}
|
||||
disableFormatTag = supportFormatTag{v: false}
|
||||
)
|
||||
|
||||
// ExperimentalSupportFormatTag is a marker method that is specially
|
||||
// recognized by the "encoding/json/v2" runtime.
|
||||
func (o *supportFormatTag) ExperimentalSupportFormatTag() bool {
|
||||
return o.v
|
||||
}
|
||||
|
||||
// ExperimentalSupportFormatTag enables support for the `format` tag.
|
||||
//
|
||||
// WARNING: This is an experimental feature and will be removed in the future
|
||||
// as either a failed experiment or be formally included in "encoding/json/v2"
|
||||
// in some semantically similar form, in which case, users of this option
|
||||
// must migrate to the officially supported feature.
|
||||
//
|
||||
// The `format` tag was originally part of the "encoding/json/v2" experiment
|
||||
// but support for it was removed (see https://go.dev/issue/79071) for the
|
||||
// initial release of "encoding/json/v2" in light of the anticipation that
|
||||
// the Go language might support typed struct tags (https://go.dev/issue/74472).
|
||||
//
|
||||
// Typed struct tags are a more expressive and type-safe way to express format
|
||||
// attributes than the bespoke `format` tag option that implements a miniature
|
||||
// domain-specific language (DSL) within the "json" package itself.
|
||||
//
|
||||
// While "encoding/json/v2" was in the experimental phase,
|
||||
// some users were already depending on the `format` tag option.
|
||||
// This experimental option exists to provide a temporary workaround
|
||||
// until the (hopeful) inclusion of typed struct tags and support for
|
||||
// formatting directives in "encoding/json/v2" using that language mechanism.
|
||||
//
|
||||
// This option enables support for the `format` tag option, which specifies
|
||||
// a format flag used to specialize the formatting of the field value.
|
||||
// The option is a key-value pair specified as "format:value" where
|
||||
// the value must be either a literal consisting of letters and numbers
|
||||
// (e.g., `format:RFC3339`) or a single-quoted string literal
|
||||
// (e.g., `format:'2006-01-02'`). The interpretation of the format flag
|
||||
// is determined by the struct field type.
|
||||
//
|
||||
// Go types with alternative representations are as follows:
|
||||
//
|
||||
// - A Go []byte or [N]byte is usually represented as a JSON string
|
||||
// containing the binary value encoded using RFC 4648.
|
||||
// If the format is "base64" or unspecified, then this uses RFC 4648, section 4.
|
||||
// If the format is "base64url", then this uses RFC 4648, section 5.
|
||||
// If the format is "base32", then this uses RFC 4648, section 6.
|
||||
// If the format is "base32hex", then this uses RFC 4648, section 7.
|
||||
// If the format is "base16" or "hex", then this uses RFC 4648, section 8.
|
||||
// If the format is "array", then the bytes value is represented as a JSON array
|
||||
// where each element recursively uses the JSON representation of each byte.
|
||||
//
|
||||
// - A Go float is usually represented as a JSON number.
|
||||
// If the format is "nonfinite", then NaN, +Inf, and -Inf are represented as
|
||||
// the JSON strings "NaN", "Infinity", and "-Infinity", respectively.
|
||||
// Without the use of this format, such string values result in a [SemanticError].
|
||||
//
|
||||
// - A nil Go map is usually encoded using an empty JSON object.
|
||||
// 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.
|
||||
//
|
||||
// - A nil Go slice is usually encoded using an empty JSON array.
|
||||
// If the format is "emitnull", then a nil slice is encoded as a JSON null.
|
||||
// If the format is "emitempty", then a nil slice is encoded as an empty JSON array,
|
||||
// regardless of whether [FormatNilSliceAsNull] is specified.
|
||||
//
|
||||
// - A Go pointer usually uses the JSON representation of the underlying value.
|
||||
// The format is forwarded to the marshaling and unmarshaling of the underlying type.
|
||||
//
|
||||
// - A Go [time.Time] is usually represented as a JSON string containing
|
||||
// the timestamp formatted in RFC 3339 with nanosecond precision.
|
||||
// If the format matches one of the format constants declared
|
||||
// in the time package (e.g., RFC1123), then that format is used.
|
||||
// If the format is "unix", "unixmilli", "unixmicro", or "unixnano",
|
||||
// then the timestamp is represented as a possibly fractional JSON number
|
||||
// of the number of seconds (or milliseconds, microseconds, or nanoseconds)
|
||||
// since the Unix epoch, which is January 1st, 1970 at 00:00:00 UTC.
|
||||
// To avoid a fractional component when encoding,
|
||||
// round the timestamp to the relevant unit.
|
||||
// Otherwise if non-empty, the format is used as-is and
|
||||
// encoded using [time.Time.Format] and
|
||||
// decoded using [time.Time.Parse].
|
||||
//
|
||||
// - A Go [time.Duration] usually has no default representation.
|
||||
// If the format is "sec", "milli", "micro", or "nano",
|
||||
// then the duration is represented as a possibly fractional JSON number
|
||||
// of the number of seconds (or milliseconds, microseconds, or nanoseconds).
|
||||
// To avoid a fractional component when encoding,
|
||||
// round the duration to the relevant unit.
|
||||
// If the format is "units", it is represented as a JSON string
|
||||
// encoded using [time.Duration.String] and decoded using [time.ParseDuration]
|
||||
// (e.g., "1h30m" for 1 hour 30 minutes).
|
||||
// If the format is "iso8601", it is represented as a JSON string using the
|
||||
// ISO 8601 standard for durations (e.g., "PT1H30M" for 1 hour 30 minutes)
|
||||
// using only accurate units of hours, minutes, and seconds.
|
||||
func ExperimentalSupportFormatTag(v bool) Options {
|
||||
if v {
|
||||
return &enableFormatTag
|
||||
} else {
|
||||
return &disableFormatTag
|
||||
}
|
||||
}
|
||||
30
tools/tsgo/vendor/github.com/go-json-experiment/json/options_format_global.go
generated
vendored
Normal file
30
tools/tsgo/vendor/github.com/go-json-experiment/json/options_format_global.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2026 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package json
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
var globalEnableFormatTag atomic.Bool
|
||||
|
||||
// ExperimentalGlobalSupportFormatTag globally enables
|
||||
// [ExperimentalSupportFormatTag] for all calls to
|
||||
// [Marshal], [MarshalWrite], [MarshalEncode],
|
||||
// [Unmarshal], [UnmarshalRead], or [UnmarshalDecode].
|
||||
//
|
||||
// WARNING: This is an experimental feature and will be removed in the future
|
||||
// as either a failed experiment or be formally included in "github.com/go-json-experiment/json"
|
||||
// in some semantically similar form, in which case, users of this option
|
||||
// must migrate to the officially supported feature.
|
||||
func ExperimentalGlobalSupportFormatTag(v bool) {
|
||||
globalEnableFormatTag.Store(v)
|
||||
}
|
||||
|
||||
func mayAppendSupportFormatTag(opts []Options) []Options {
|
||||
if globalEnableFormatTag.Load() {
|
||||
var optsArr [4]Options
|
||||
opts = append(append(optsArr[:0], opts...), &enableFormatTag)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
Reference in New Issue
Block a user