Highlights
- Compatible with encoding/json of the Go version in use: values are decoded and encoded, and errors are reported (their types, messages and offsets), as
encoding/jsondoes, including Go 1.27, whoseencoding/jsonis built onencoding/json/v2. Many reported compatibility issues are fixed. - Much faster: with the same behavior as
encoding/json( a live heap of 64 MB, geometric mean over the payloads of the report ), go-json decodes about 4.2–4.4x and encodes about 2.7–2.8x as fast asencoding/json, on amd64 and arm64 ( the results of this release are at https://goccy.github.io/go-json/ ). - A benchmark report of the JSON libraries of Go, measured on GitHub Actions for every change and published at https://goccy.github.io/go-json/: the libraries are compared by category of behavior ( the behavior of
encoding/json, ofencoding/json/v2, without HTML escaping, key sorting and string copying, and every library at its fastest ), which every run checks, and the result files are attested with GitHub Artifact Attestations. - No more
go:linkname: go-json no longer links to the internals of the runtime or ofreflect, so it builds with the restrictions of newer Go versions ongo:linkname.
New APIs
MarshalOf[T]: encodes a value of typeTwithout copying it to the heap ( #632 ).UnmarshalOf[T]: decodes into a*Twhich may stay on the stack of the caller, without an allocation for the argument ( #658 ).OptimizeFieldOrder(): lets the encoder order the fields of a struct as it encodes them fastest, when the order of the keys doesn't matter ( #646 ).DecodeNoCopyString(): lets the decoded strings refer to the input instead of copying them ( #658 ).
Changes of behavior
These follow encoding/json, and may change what a program which relied on the previous behavior gets:
- The values of the keys which match no field are validated: invalid JSON anywhere in the input is a syntax error, as with
encoding/json( #664 ). - Syntax errors and type errors are reported as
encoding/jsonof the Go version reports them: their types, messages and offsets ( #661, #664 ). - A decoded string is a copy of its bytes, and doesn't keep the input alive; short strings share buffers of up to 16 KB ( #658 ). Use
DecodeNoCopyString()to refer to the input instead. - The elements of an array are decoded into the existing elements of a slice, including the ones after its length within its capacity, as
encoding/jsondoes ( #666 ). MarshalNoEscapeandUnmarshalNoEscapeare deprecated: useMarshalandUnmarshalOf( #631, #658 ).DecodeFieldPriorityFirstWin()no longer allocates for each object, and is as fast as the default decode or faster ( #668 ).
What's Changed
Decoder
- Decoder: decode a stream by the byte slice decoders, compare the decoder with sonic by @goccy in #656
- Decoder: allocate and assign through reflect instead of go:linkname by @goccy in #657
- Decoder: UnmarshalOf, copies of the decoded strings, recent decoders of a context by @goccy in #658
- Decoder and encoder: struct keys by a table, numbers in one pass, strings by AVX2, benchmarks of real APIs by @goccy in #659
- Decoder: allocate the value of a pointer by new of its kind by @goccy in #660
- Decoder: report the type errors as encoding/json of the Go version does by @goccy in #661
- Decoder: decode a value of interface{} without reflect by @goccy in #662
- Decoder: decode the values held by interface values by their type, as encoding/json does by @goccy in #663
- Decoder: fix the reported decoder issues, as encoding/json of the Go version decodes by @goccy in #664
- Decoder: narrow the gaps to sonic on escaped strings, and compare with sonic as a real program runs by @goccy in #665
- Decoder: narrow the gaps to sonic at its fastest: slices, maps, long escaped strings and interface values by @goccy in #666
- Decoder: remember the decoded fields of DecodeFieldPriorityFirstWin in a bit set by @goccy in #668
- Decoder: narrow the gap to sonic on interface{} values and string fields by @goccy in #670
Encoder
- Remove go:linkname to reflect.(*rtype) methods by @goccy in #624
- Remove runtime.Type and go:linkname to reflect.ifaceIndir by @goccy in #625
- Always give the address of the value to the encoder VM by @goccy in #626
- Fix the encoding of recursive structs and omitempty with a pointer receiver marshaler by @goccy in #628
- Fix the indent of deep recursive structs, marshalers of empty values and embedded structs by @goccy in #630
- Carry the values of the encoder VM as unsafe.Pointer and remove KeepRefs by @goccy in #631
- Add MarshalOf, which encodes a value without copying it to the heap by @goccy in #632
- Speed up the encoder: a hash table for the cache of a type, a lighter VM and a faster AppendString by @goccy in #634
- Encode a scalar held by an interface value without a frame by @goccy in #635
- Scan a string for a byte to escape by SIMD by @goccy in #637
- Call the method of a marshaler directly by its code by @goccy in #638
- Copy the output of a marshaler as it is if it is compact and valid by @goccy in #639
- Encoder: inline the helpers of the VM, and shorter paths for strings and maps by @goccy in #640
- Write the key of a map of strings by the opcode of the map entry by @goccy in #641
- Encoder: encode fields of the same kind in a row without a dispatch between them by @goccy in #643
- Encode a list of values of a recursive type in one frame by @goccy in #645
- Add the option to let the encoder order the fields of a struct by @goccy in #646
- Read the entries of a map without a linkname of the runtime by @goccy in #647
- Encoder: compare the keys of a map by their first bytes as a number first by @goccy in #649
- Encoder: cheaper frames of interface values by @goccy in #650
- Encoder: write the scalar entries of an unordered map of interface values as the map is read by @goccy in #651
- Write null for an interface value which holds nothing without a call by @goccy in #652
- Encoder: encode a field of interface{} by an opcode of its own by @goccy in #653
- Test the cases of the reported issues of the encoder by @goccy in #655
Other fixes
- Fix encode/decoder compile data race by @TelpeNight in #567
Benchmarks, documents and CI
- Fix CI failures on master caused by outdated Go versions by @goccy in #594
- Add bench-check task to fail CI on performance degradation by @goccy in #623
- Judge the benchmarks by the mean and by the fastest results by @goccy in #629
- Benchmarks: add the benchmarks of sonic's encoder, with go-json beside it by @goccy in #648
- Modernize the code: any, unsafe.Add, slices.Contains, min, max, bytes.Cut by @goccy in #654
- Publish a benchmark report of the JSON libraries by category of behavior, describe the decoder as it is now in the README, and run no CI for documents alone by @goccy in #667
- Benchmarks: color the bars of the summary by library by @goccy in #669
New Contributors
- @TelpeNight made their first contribution in #567
Full Changelog: https://github.com/goccy/go-json/compare/v0.10.6...v0.11.0