Repository navigation
Decoder: decode the values held by interface values by their type, as encoding/json does - #663
Merged
Merged
Conversation
…for its type
The decoder of an interface value found, for every value it decoded, how the
value it holds is decoded: into what a pointer points to, as a new value, or
by an unmarshaler, by reflect and by type assertions. That depends on the type
of the value only: it is found once for a type now, and kept by the decoder
by the type, or by the itab for an interface type with methods, with the
decoder of the pointer. The benchmarks decode into values of interface{}
which hold pointers, and into values of an interface type with methods which
implement json.Unmarshaler.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…/json does
A value of an interface type with methods was decoded by the unmarshaler it
implements, whatever it was, and was an error otherwise. encoding/json
decodes it as it decodes a value of interface{}: a pointer which it holds is
decoded into what it points to, as a value of the pointer type, whose
unmarshalers are used, and null sets the interface value to nil. Any other
value, which is not replaced as the one of interface{} is, is a type error:
before Go 1.27, the value is kept; with Go 1.27, it is set to nil and the
error is reported at the start of the value. The type error of a pointer
which implements encoding.TextUnmarshaler reports the interface type before
Go 1.27, as encoding/json does.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The copy of the bytes given to UnmarshalJSON was made by append, which goes through growslice and rounds the capacity up: a slice of the exact length, filled by copy, costs less. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #663 +/- ##
==========================================
+ Coverage 86.47% 86.78% +0.31%
==========================================
Files 73 73
Lines 11509 11485 -24
==========================================
+ Hits 9952 9967 +15
+ Misses 1557 1518 -39 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The decoder of an interface value decided, for every value it decoded, how the value held by the interface value is decoded: by
reflect.TypeOf,KindandElemforinterface{}, and byreflect.ValueOf,NumMethod,Interfaceand up to three type assertions for an interface type with methods. That depends on the type of the held value only.runtime.TypeCache, by the type of the held value forinterface{}and by the itab for an interface type with methods ( one itab per held type for the interface type of the decoder ), with the decoder of the pointer type, which was looked up byDecoderOfat every value.interface{}: a pointer is decoded into what it points to as a value of the pointer type, with the unmarshalers of the pointer type, and null sets the interface value to nil. Any other value is a type error, which keeps the held value before Go 1.27; with Go 1.27, it is set to nil and the error is at the start of the value. A pointer which implementsencoding.TextUnmarshalerreports the interface type before Go 1.27.UnmarshalJSONare copied into a slice of their length. The copy was made byappend, which goes throughgrowslice( 26% of the benchmark of the unmarshalers ).Benchmarks
Two benchmarks decode into values set before the decoding: eight values of
interface{}holding pointers to structs (InterfacePointer), and eight values of an interface type with methods holding pointers which implementjson.Unmarshaler(InterfaceUnmarshaler).amd64 CI, three machines ( EPYC 7763, Xeon 8370C, EPYC 9V74 ), master and the head each with two layouts ( a function added, to see the effect of the layout alone ):
The benchmark gates of this pull request: decode means +0.00%, -0.10%, -0.62%; SmallStruct +0.16%.
Tests
TestDecodeTypeErrorsOfInterfaceValuescompares with encoding/json the errors ( value, type, offset, struct, field, message ) and the decoded values of values of an interface type with methods and ofinterface{}holding nil, pointers implementingjson.Unmarshalerorencoding.TextUnmarshaler, a pointer to a struct, a struct, and a nil pointer, for objects, null, strings, arrays, numbers and bools.TestUnmarshalIntoInterfaceValuesOfTypesdecodes values of types which alternate in one field. All the tests pass with Go 1.21 to 1.27, and with Go 1.27 andGOEXPERIMENT=nojsonv2.🤖 Generated with Claude Code