Enable FirstWin option for stream decoder

This commit is contained in:
Masaaki Goshima 2021-06-06 11:24:56 +09:00
parent 34b7053412
commit 5c39787fbd
4 changed files with 51 additions and 2 deletions

View File

@ -462,3 +462,18 @@ func Benchmark_Decode_LargeStruct_Stream_GoJson(b *testing.B) {
}
}
}
func Benchmark_Decode_LargeStruct_Stream_GoJsonFirstWinMode(b *testing.B) {
b.ReportAllocs()
reader := bytes.NewReader(LargeFixture)
for i := 0; i < b.N; i++ {
result := LargePayload{}
reader.Reset(LargeFixture)
if err := gojson.NewDecoder(reader).DecodeWithOption(
&result,
gojson.DecodeFieldPriorityFirstWin(),
); err != nil {
b.Fatal(err)
}
}
}

View File

@ -134,6 +134,10 @@ func (d *Decoder) Buffered() io.Reader {
// See the documentation for Unmarshal for details about
// the conversion of JSON into a Go value.
func (d *Decoder) Decode(v interface{}) error {
return d.DecodeWithOption(v)
}
func (d *Decoder) DecodeWithOption(v interface{}, optFuncs ...DecodeOptionFunc) error {
header := (*emptyInterface)(unsafe.Pointer(&v))
typ := header.typ
ptr := uintptr(header.ptr)
@ -153,6 +157,9 @@ func (d *Decoder) Decode(v interface{}) error {
return err
}
s := d.s
for _, optFunc := range optFuncs {
optFunc(s.Option)
}
if err := dec.DecodeStream(s, 0, header.ptr); err != nil {
return err
}

View File

@ -25,6 +25,7 @@ type Stream struct {
allRead bool
UseNumber bool
DisallowUnknownFields bool
Option *Option
}
func NewStream(r io.Reader) *Stream {
@ -32,6 +33,7 @@ func NewStream(r io.Reader) *Stream {
r: r,
bufSize: initBufSize,
buf: make([]byte, initBufSize),
Option: &Option{},
}
}

View File

@ -661,6 +661,14 @@ func (d *structDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) e
s.cursor++
return nil
}
var (
seenFields map[int]struct{}
seenFieldNum int
)
firstWin := (s.Option.Flag & FirstWinOption) != 0
if firstWin {
seenFields = make(map[int]struct{}, d.fieldUniqueNameNum)
}
for {
s.reset()
field, key, err := d.keyStreamDecoder(d, s)
@ -675,8 +683,25 @@ func (d *structDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) e
if field.err != nil {
return field.err
}
if err := field.dec.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+field.offset)); err != nil {
return err
if firstWin {
if _, exists := seenFields[field.fieldIdx]; exists {
if err := s.skipValue(depth); err != nil {
return err
}
} else {
if err := field.dec.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+field.offset)); err != nil {
return err
}
seenFieldNum++
if d.fieldUniqueNameNum <= seenFieldNum {
return s.skipObject(depth)
}
seenFields[field.fieldIdx] = struct{}{}
}
} else {
if err := field.dec.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+field.offset)); err != nil {
return err
}
}
} else if s.DisallowUnknownFields {
return fmt.Errorf("json: unknown field %q", key)