forked from mirror/go-json
Add DecodeFieldPriorityFirstWin option
This commit is contained in:
parent
b074c98070
commit
cbda08a525
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue