Skip to content

v0.11.0

Choose a tag to compare

@goccy goccy released this 27 Sep 08:26
Immutable release. Only release title and notes can be modified.
331b5ae

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/json does, including Go 1.27, whose encoding/json is built on encoding/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 as encoding/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, of encoding/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 of reflect, so it builds with the restrictions of newer Go versions on go:linkname.

New APIs

  • MarshalOf[T]: encodes a value of type T without copying it to the heap ( #632 ).
  • UnmarshalOf[T]: decodes into a *T which 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/json of 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/json does ( #666 ).
  • MarshalNoEscape and UnmarshalNoEscape are deprecated: use Marshal and UnmarshalOf ( #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

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

Full Changelog: v0.10.6...v0.11.0