Add DecodeFieldPriorityFirstWin option

This commit is contained in:
Masaaki Goshima 2021-06-05 13:00:26 +09:00
parent b074c98070
commit cbda08a525
2 changed files with 88 additions and 14 deletions

View File

@ -375,6 +375,34 @@ func Benchmark_Decode_LargeStruct_Unmarshal_GoJsonNoEscape(b *testing.B) {
} }
} }
func Benchmark_Decode_LargeStruct_Unmarshal_GoJsonFirstWinMode(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
result := LargePayload{}
if err := gojson.UnmarshalWithOption(
LargeFixture,
&result,
gojson.DecodeFieldPriorityFirstWin(),
); err != nil {
b.Fatal(err)
}
}
}
func Benchmark_Decode_LargeStruct_Unmarshal_GoJsonNoEscapeFirstWinMode(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
result := LargePayload{}
if err := gojson.UnmarshalNoEscape(
LargeFixture,
&result,
gojson.DecodeFieldPriorityFirstWin(),
); err != nil {
b.Fatal(err)
}
}
}
func Benchmark_Decode_LargeStruct_Stream_EncodingJson(b *testing.B) { func Benchmark_Decode_LargeStruct_Stream_EncodingJson(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
reader := bytes.NewReader(LargeFixture) reader := bytes.NewReader(LargeFixture)

View File

@ -17,22 +17,24 @@ type structFieldSet struct {
dec Decoder dec Decoder
offset uintptr offset uintptr
isTaggedKey bool isTaggedKey bool
fieldIdx int
key string key string
keyLen int64 keyLen int64
err error err error
} }
type structDecoder struct { type structDecoder struct {
fieldMap map[string]*structFieldSet fieldMap map[string]*structFieldSet
stringDecoder *stringDecoder fieldUniqueNameNum int
structName string stringDecoder *stringDecoder
fieldName string structName string
isTriedOptimize bool fieldName string
keyBitmapUint8 [][256]uint8 isTriedOptimize bool
keyBitmapUint16 [][256]uint16 keyBitmapUint8 [][256]uint8
sortedFieldSets []*structFieldSet keyBitmapUint16 [][256]uint16
keyDecoder func(*structDecoder, []byte, int64) (int64, *structFieldSet, error) sortedFieldSets []*structFieldSet
keyStreamDecoder func(*structDecoder, *Stream) (*structFieldSet, string, error) keyDecoder func(*structDecoder, []byte, int64) (int64, *structFieldSet, error)
keyStreamDecoder func(*structDecoder, *Stream) (*structFieldSet, string, error)
} }
var ( var (
@ -66,6 +68,21 @@ const (
) )
func (d *structDecoder) tryOptimize() { func (d *structDecoder) tryOptimize() {
fieldUniqueNameMap := map[string]int{}
fieldIdx := -1
for k, v := range d.fieldMap {
lower := strings.ToLower(k)
idx, exists := fieldUniqueNameMap[lower]
if exists {
v.fieldIdx = idx
} else {
fieldIdx++
v.fieldIdx = fieldIdx
}
fieldUniqueNameMap[lower] = fieldIdx
}
d.fieldUniqueNameNum = len(fieldUniqueNameMap)
if d.isTriedOptimize { if d.isTriedOptimize {
return return
} }
@ -706,6 +723,14 @@ func (d *structDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsaf
cursor++ cursor++
return cursor, nil return cursor, nil
} }
var (
seenFields map[int]struct{}
seenFieldNum int
)
firstWin := ctx.Option.FirstWin
if firstWin {
seenFields = make(map[int]struct{}, d.fieldUniqueNameNum)
}
for { for {
c, field, err := d.keyDecoder(d, buf, cursor) c, field, err := d.keyDecoder(d, buf, cursor)
if err != nil { if err != nil {
@ -723,11 +748,32 @@ func (d *structDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsaf
if field.err != nil { if field.err != nil {
return 0, field.err return 0, field.err
} }
c, err := field.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+field.offset)) if firstWin {
if err != nil { if _, exists := seenFields[field.fieldIdx]; exists {
return 0, err c, err := skipValue(buf, cursor, depth)
if err != nil {
return 0, err
}
cursor = c
} else {
c, err := field.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+field.offset))
if err != nil {
return 0, err
}
cursor = c
seenFieldNum++
if d.fieldUniqueNameNum <= seenFieldNum {
return skipObject(buf, cursor, depth)
}
seenFields[field.fieldIdx] = struct{}{}
}
} else {
c, err := field.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+field.offset))
if err != nil {
return 0, err
}
cursor = c
} }
cursor = c
} else { } else {
c, err := skipValue(buf, cursor, depth) c, err := skipValue(buf, cursor, depth)
if err != nil { if err != nil {