aboutsummaryrefslogtreecommitdiff
path: root/src/encoding/json/v2_stream.go
AgeCommit message (Collapse)Author
2026-02-02encoding/json: use pooled encoder in Encoder.EncodeJoe Tsai
Due to the lack of MarshalWrite in the v1 API, it is unfortunately common to see: json.NewEncoder(out).Encode(in) where a single-use Encoder is constructed and thrown away. This performed acceptably in v1 since every call to Encode used a globally pooled encoder resource. Prior to this change, the v1-in-v2 implementation relied on a bytes.Buffer cached only for the lifetime of the Encoder object itself. Thus, a single-use Encoder does not benefit. Modify the wrapper implementation to use the internal pooled encoder from v2 and use the intermediate buffer to write directly to the output io.Writer. We assume that the user-provided io.Writer never leaks the buffer, but this assumption was already held in the v1 implementation. We are not increasing the surface area of data corruption risk. Performance of v1 to v1-in-v2 (before the pool fix): name old time/op new time/op delta NewEncoderEncode-32 30.2ms ± 4% 28.3ms ± 9% -6.19% (p=0.002 n=9+10) name old alloc/op new alloc/op delta NewEncoderEncode-32 7.64MB ± 0% 28.37MB ± 0% +271.23% (p=0.000 n=10+10) name old allocs/op new allocs/op delta NewEncoderEncode-32 200k ± 0% 100k ± 0% -49.99% (p=0.000 n=9+10) Interestingly, v1-in-2 is slightly faster, but the amount of allocated memory is massive. Performance of v1 to v1-in-v2 (after the pool fix): name old time/op new time/op delta NewEncoderEncode-32 30.2ms ± 4% 24.0ms ± 7% -20.36% (p=0.000 n=9+10) name old alloc/op new alloc/op delta NewEncoderEncode-32 7.64MB ± 0% 4.09MB ± 3% -46.52% (p=0.000 n=10+10) name old allocs/op new allocs/op delta NewEncoderEncode-32 200k ± 0% 100k ± 0% -50.00% (p=0.000 n=9+9) Now, the v1-in-v2 implementation is better than v1 on all metrics. Fixes #75026 Change-Id: I50c975b1d5b8da806e46bc627966b0a39c1817eb Reviewed-on: https://go-review.googlesource.com/c/go/+/740660 Reviewed-by: Michael Pratt <mpratt@google.com> Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-12-11encoding/json: report true from v2 Decoder.More when an error is pendingDamien Neil
Historically, Decoder.More reports true when the next read will return an error. Adjust the v2 Decoder to follow this behavior. Fixes #76467 Change-Id: I03bfa391e4e89ada8ca869db43c1d0bb63cc0413 Reviewed-on: https://go-review.googlesource.com/c/go/+/728300 Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Joseph Tsai <joetsai@digital-static.net> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-10-01encoding/json: fix Decoder.InputOffset regression in goexperiment.jsonv2Joe Tsai
The Decoder.InputOffset method was always ambiguous about the exact offset returned since anything between the end of the previous token to the start of the next token could be a valid result. Empirically, it seems that the behavior was to report the end of the previous token unless Decoder.More is called, in which case it reports the start of the next token. This is an odd semantic since a relatively side-effect free method like More is not quite so side-effect free. However, our goal is to preserve historical v1 semantic when possible regardless of whether it made sense. Note that jsontext.Decoder.InputOffset consistently always reports the end of the previous token. Users can explicitly choose the exact position they want by inspecting the UnreadBuffer. Fixes #75468 Change-Id: I1e946e83c9d29dfc09f2913ff8d6b2b80632f292 Reviewed-on: https://go-review.googlesource.com/c/go/+/703856 Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Junyang Shao <shaojunyang@google.com>
2025-07-25encoding/json: fix truncated Token error regression in goexperiment.jsonv2Joe Tsai
The jsontext.Decoder.ReadToken method reports a non-EOF error, if the token stream is truncated and does not form a valid JSON value. In contrast, the v1 json.Decoder.Token method would report EOF so long as the input was a prefix of some valid JSON value. Modify json.Decoder.Token to preserve historical behavior. This only modifies code that is compiled in under goexperiment.jsonv2. Updates #69449 Fixes #74750 Change-Id: Ifd281c46f118f0e748076013fefc7659f77c56ed Reviewed-on: https://go-review.googlesource.com/c/go/+/689516 Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Joseph Tsai <joetsai@digital-static.net> Reviewed-by: Michael Knyszek <mknyszek@google.com>
2025-07-11encoding/json/v2: report wrapped io.ErrUnexpectedEOFJoe Tsai
In the event that the input is just JSON whitespace, the underlying jsontext.Decoder treats this as an empty stream and reports io.EOF. The logic in unmarshalFull simply casted io.EOF as io.ErrUnexpectedEOF, which is inconsistent with how all other io.ErrUnexpectedEOF are reported, which are wrapped within a jsontext.SyntacticError. Do the same thing for consistency. We add a v1 test (without goexperiment.jsonv2) to verify that the behavior is identical to how v1 has always behaved. We add a v1in2 test (with goexperiment.jsonv2) to verify that the v1in2 behavior correctly replicates historical v1 behavior. We also fix a faulty check in v1 Decoder.Decode, where it tried to detect errUnexpectedEnd and return an unwrapped io.ErrUnexpectedEOF error. This is the exact semantic that v1 has always done in streaming Decoder.Decode (but not non-streaming Unmarshal). There is a prior bug reported in #25956 about this inconsistency, but we aim to preserve historical v1 behavior to reduce the probability of churn when v1 is re-implemented in terms of v2. Fixes #74548 Change-Id: Ibca52c3699ff3c09141e081c85f853781a86ec8e Reviewed-on: https://go-review.googlesource.com/c/go/+/687115 Auto-Submit: Joseph Tsai <joetsai@digital-static.net> Reviewed-by: Carlos Amedee <carlos@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Damien Neil <dneil@google.com>
2025-04-18encoding/json: add json/v2 with GOEXPERIMENT=jsonv2 guardDamien Neil
This imports the proposed new v2 JSON API implemented in github.com/go-json-experiment/json as of commit d3c622f1b874954c355e60c8e6b6baa5f60d2fed. When GOEXPERIMENT=jsonv2 is set, the encoding/json/v2 and encoding/jsontext packages are visible, the encoding/json package is implemented in terms of encoding/json/v2, and the encoding/json package include various additional APIs. (See #71497 for details.) When GOEXPERIMENT=jsonv2 is not set, the new API is not present and the encoding/json package is unchanged. The experimental API is not bound by the Go compatibility promise and is expected to evolve as updates are made to the json/v2 proposal. The contents of encoding/json/internal/jsontest/testdata are compressed with zstd v1.5.7 with the -19 option. Fixes #71845 For #71497 Change-Id: Ib8c94e5f0586b6aaa22833190b41cf6ef59f4f01 Reviewed-on: https://go-review.googlesource.com/c/go/+/665796 Auto-Submit: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com> Reviewed-by: Joseph Tsai <joetsai@digital-static.net> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>