summaryrefslogtreecommitdiff
path: root/vendor/github.com/goccy/go-json/internal/decoder/struct.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/goccy/go-json/internal/decoder/struct.go')
-rw-r--r--vendor/github.com/goccy/go-json/internal/decoder/struct.go819
1 files changed, 819 insertions, 0 deletions
diff --git a/vendor/github.com/goccy/go-json/internal/decoder/struct.go b/vendor/github.com/goccy/go-json/internal/decoder/struct.go
new file mode 100644
index 000000000..2c6468045
--- /dev/null
+++ b/vendor/github.com/goccy/go-json/internal/decoder/struct.go
@@ -0,0 +1,819 @@
+package decoder
+
+import (
+ "fmt"
+ "math"
+ "math/bits"
+ "sort"
+ "strings"
+ "unicode"
+ "unicode/utf16"
+ "unsafe"
+
+ "github.com/goccy/go-json/internal/errors"
+)
+
+type structFieldSet struct {
+ dec Decoder
+ offset uintptr
+ isTaggedKey bool
+ fieldIdx int
+ key string
+ keyLen int64
+ err error
+}
+
+type structDecoder struct {
+ fieldMap map[string]*structFieldSet
+ fieldUniqueNameNum int
+ stringDecoder *stringDecoder
+ structName string
+ fieldName string
+ isTriedOptimize bool
+ keyBitmapUint8 [][256]uint8
+ keyBitmapUint16 [][256]uint16
+ sortedFieldSets []*structFieldSet
+ keyDecoder func(*structDecoder, []byte, int64) (int64, *structFieldSet, error)
+ keyStreamDecoder func(*structDecoder, *Stream) (*structFieldSet, string, error)
+}
+
+var (
+ largeToSmallTable [256]byte
+)
+
+func init() {
+ for i := 0; i < 256; i++ {
+ c := i
+ if 'A' <= c && c <= 'Z' {
+ c += 'a' - 'A'
+ }
+ largeToSmallTable[i] = byte(c)
+ }
+}
+
+func newStructDecoder(structName, fieldName string, fieldMap map[string]*structFieldSet) *structDecoder {
+ return &structDecoder{
+ fieldMap: fieldMap,
+ stringDecoder: newStringDecoder(structName, fieldName),
+ structName: structName,
+ fieldName: fieldName,
+ keyDecoder: decodeKey,
+ keyStreamDecoder: decodeKeyStream,
+ }
+}
+
+const (
+ allowOptimizeMaxKeyLen = 64
+ allowOptimizeMaxFieldLen = 16
+)
+
+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 {
+ return
+ }
+ fieldMap := map[string]*structFieldSet{}
+ conflicted := map[string]struct{}{}
+ for k, v := range d.fieldMap {
+ key := strings.ToLower(k)
+ if key != k {
+ // already exists same key (e.g. Hello and HELLO has same lower case key
+ if _, exists := conflicted[key]; exists {
+ d.isTriedOptimize = true
+ return
+ }
+ conflicted[key] = struct{}{}
+ }
+ if field, exists := fieldMap[key]; exists {
+ if field != v {
+ d.isTriedOptimize = true
+ return
+ }
+ }
+ fieldMap[key] = v
+ }
+
+ if len(fieldMap) > allowOptimizeMaxFieldLen {
+ d.isTriedOptimize = true
+ return
+ }
+
+ var maxKeyLen int
+ sortedKeys := []string{}
+ for key := range fieldMap {
+ keyLen := len(key)
+ if keyLen > allowOptimizeMaxKeyLen {
+ d.isTriedOptimize = true
+ return
+ }
+ if maxKeyLen < keyLen {
+ maxKeyLen = keyLen
+ }
+ sortedKeys = append(sortedKeys, key)
+ }
+ sort.Strings(sortedKeys)
+
+ // By allocating one extra capacity than `maxKeyLen`,
+ // it is possible to avoid the process of comparing the index of the key with the length of the bitmap each time.
+ bitmapLen := maxKeyLen + 1
+ if len(sortedKeys) <= 8 {
+ keyBitmap := make([][256]uint8, bitmapLen)
+ for i, key := range sortedKeys {
+ for j := 0; j < len(key); j++ {
+ c := key[j]
+ keyBitmap[j][c] |= (1 << uint(i))
+ }
+ d.sortedFieldSets = append(d.sortedFieldSets, fieldMap[key])
+ }
+ d.keyBitmapUint8 = keyBitmap
+ d.keyDecoder = decodeKeyByBitmapUint8
+ d.keyStreamDecoder = decodeKeyByBitmapUint8Stream
+ } else {
+ keyBitmap := make([][256]uint16, bitmapLen)
+ for i, key := range sortedKeys {
+ for j := 0; j < len(key); j++ {
+ c := key[j]
+ keyBitmap[j][c] |= (1 << uint(i))
+ }
+ d.sortedFieldSets = append(d.sortedFieldSets, fieldMap[key])
+ }
+ d.keyBitmapUint16 = keyBitmap
+ d.keyDecoder = decodeKeyByBitmapUint16
+ d.keyStreamDecoder = decodeKeyByBitmapUint16Stream
+ }
+}
+
+// decode from '\uXXXX'
+func decodeKeyCharByUnicodeRune(buf []byte, cursor int64) ([]byte, int64) {
+ const defaultOffset = 4
+ const surrogateOffset = 6
+
+ r := unicodeToRune(buf[cursor : cursor+defaultOffset])
+ if utf16.IsSurrogate(r) {
+ cursor += defaultOffset
+ if cursor+surrogateOffset >= int64(len(buf)) || buf[cursor] != '\\' || buf[cursor+1] != 'u' {
+ return []byte(string(unicode.ReplacementChar)), cursor + defaultOffset - 1
+ }
+ cursor += 2
+ r2 := unicodeToRune(buf[cursor : cursor+defaultOffset])
+ if r := utf16.DecodeRune(r, r2); r != unicode.ReplacementChar {
+ return []byte(string(r)), cursor + defaultOffset - 1
+ }
+ }
+ return []byte(string(r)), cursor + defaultOffset - 1
+}
+
+func decodeKeyCharByEscapedChar(buf []byte, cursor int64) ([]byte, int64) {
+ c := buf[cursor]
+ cursor++
+ switch c {
+ case '"':
+ return []byte{'"'}, cursor
+ case '\\':
+ return []byte{'\\'}, cursor
+ case '/':
+ return []byte{'/'}, cursor
+ case 'b':
+ return []byte{'\b'}, cursor
+ case 'f':
+ return []byte{'\f'}, cursor
+ case 'n':
+ return []byte{'\n'}, cursor
+ case 'r':
+ return []byte{'\r'}, cursor
+ case 't':
+ return []byte{'\t'}, cursor
+ case 'u':
+ return decodeKeyCharByUnicodeRune(buf, cursor)
+ }
+ return nil, cursor
+}
+
+func decodeKeyByBitmapUint8(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
+ var (
+ curBit uint8 = math.MaxUint8
+ )
+ b := (*sliceHeader)(unsafe.Pointer(&buf)).data
+ for {
+ switch char(b, cursor) {
+ case ' ', '\n', '\t', '\r':
+ cursor++
+ case '"':
+ cursor++
+ c := char(b, cursor)
+ switch c {
+ case '"':
+ cursor++
+ return cursor, nil, nil
+ case nul:
+ return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
+ }
+ keyIdx := 0
+ bitmap := d.keyBitmapUint8
+ start := cursor
+ for {
+ c := char(b, cursor)
+ switch c {
+ case '"':
+ fieldSetIndex := bits.TrailingZeros8(curBit)
+ field := d.sortedFieldSets[fieldSetIndex]
+ keyLen := cursor - start
+ cursor++
+ if keyLen < field.keyLen {
+ // early match
+ return cursor, nil, nil
+ }
+ return cursor, field, nil
+ case nul:
+ return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
+ case '\\':
+ cursor++
+ chars, nextCursor := decodeKeyCharByEscapedChar(buf, cursor)
+ for _, c := range chars {
+ curBit &= bitmap[keyIdx][largeToSmallTable[c]]
+ if curBit == 0 {
+ return decodeKeyNotFound(b, cursor)
+ }
+ keyIdx++
+ }
+ cursor = nextCursor
+ default:
+ curBit &= bitmap[keyIdx][largeToSmallTable[c]]
+ if curBit == 0 {
+ return decodeKeyNotFound(b, cursor)
+ }
+ keyIdx++
+ }
+ cursor++
+ }
+ default:
+ return cursor, nil, errors.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
+ }
+ }
+}
+
+func decodeKeyByBitmapUint16(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
+ var (
+ curBit uint16 = math.MaxUint16
+ )
+ b := (*sliceHeader)(unsafe.Pointer(&buf)).data
+ for {
+ switch char(b, cursor) {
+ case ' ', '\n', '\t', '\r':
+ cursor++
+ case '"':
+ cursor++
+ c := char(b, cursor)
+ switch c {
+ case '"':
+ cursor++
+ return cursor, nil, nil
+ case nul:
+ return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
+ }
+ keyIdx := 0
+ bitmap := d.keyBitmapUint16
+ start := cursor
+ for {
+ c := char(b, cursor)
+ switch c {
+ case '"':
+ fieldSetIndex := bits.TrailingZeros16(curBit)
+ field := d.sortedFieldSets[fieldSetIndex]
+ keyLen := cursor - start
+ cursor++
+ if keyLen < field.keyLen {
+ // early match
+ return cursor, nil, nil
+ }
+ return cursor, field, nil
+ case nul:
+ return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
+ case '\\':
+ cursor++
+ chars, nextCursor := decodeKeyCharByEscapedChar(buf, cursor)
+ for _, c := range chars {
+ curBit &= bitmap[keyIdx][largeToSmallTable[c]]
+ if curBit == 0 {
+ return decodeKeyNotFound(b, cursor)
+ }
+ keyIdx++
+ }
+ cursor = nextCursor
+ default:
+ curBit &= bitmap[keyIdx][largeToSmallTable[c]]
+ if curBit == 0 {
+ return decodeKeyNotFound(b, cursor)
+ }
+ keyIdx++
+ }
+ cursor++
+ }
+ default:
+ return cursor, nil, errors.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
+ }
+ }
+}
+
+func decodeKeyNotFound(b unsafe.Pointer, cursor int64) (int64, *structFieldSet, error) {
+ for {
+ cursor++
+ switch char(b, cursor) {
+ case '"':
+ cursor++
+ return cursor, nil, nil
+ case '\\':
+ cursor++
+ if char(b, cursor) == nul {
+ return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
+ }
+ case nul:
+ return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
+ }
+ }
+}
+
+func decodeKey(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
+ key, c, err := d.stringDecoder.decodeByte(buf, cursor)
+ if err != nil {
+ return 0, nil, err
+ }
+ cursor = c
+ k := *(*string)(unsafe.Pointer(&key))
+ field, exists := d.fieldMap[k]
+ if !exists {
+ return cursor, nil, nil
+ }
+ return cursor, field, nil
+}
+
+func decodeKeyByBitmapUint8Stream(d *structDecoder, s *Stream) (*structFieldSet, string, error) {
+ var (
+ curBit uint8 = math.MaxUint8
+ )
+ _, cursor, p := s.stat()
+ for {
+ switch char(p, cursor) {
+ case ' ', '\n', '\t', '\r':
+ cursor++
+ case nul:
+ s.cursor = cursor
+ if s.read() {
+ _, cursor, p = s.stat()
+ continue
+ }
+ return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
+ case '"':
+ cursor++
+ FIRST_CHAR:
+ start := cursor
+ switch char(p, cursor) {
+ case '"':
+ cursor++
+ s.cursor = cursor
+ return nil, "", nil
+ case nul:
+ s.cursor = cursor
+ if s.read() {
+ _, cursor, p = s.stat()
+ goto FIRST_CHAR
+ }
+ return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
+ }
+ keyIdx := 0
+ bitmap := d.keyBitmapUint8
+ for {
+ c := char(p, cursor)
+ switch c {
+ case '"':
+ fieldSetIndex := bits.TrailingZeros8(curBit)
+ field := d.sortedFieldSets[fieldSetIndex]
+ keyLen := cursor - start
+ cursor++
+ s.cursor = cursor
+ if keyLen < field.keyLen {
+ // early match
+ return nil, field.key, nil
+ }
+ return field, field.key, nil
+ case nul:
+ s.cursor = cursor
+ if s.read() {
+ _, cursor, p = s.stat()
+ continue
+ }
+ return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
+ case '\\':
+ s.cursor = cursor + 1 // skip '\' char
+ chars, err := decodeKeyCharByEscapeCharStream(s)
+ if err != nil {
+ return nil, "", err
+ }
+ cursor = s.cursor
+ for _, c := range chars {
+ curBit &= bitmap[keyIdx][largeToSmallTable[c]]
+ if curBit == 0 {
+ s.cursor = cursor
+ return decodeKeyNotFoundStream(s, start)
+ }
+ keyIdx++
+ }
+ default:
+ curBit &= bitmap[keyIdx][largeToSmallTable[c]]
+ if curBit == 0 {
+ s.cursor = cursor
+ return decodeKeyNotFoundStream(s, start)
+ }
+ keyIdx++
+ }
+ cursor++
+ }
+ default:
+ return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
+ }
+ }
+}
+
+func decodeKeyByBitmapUint16Stream(d *structDecoder, s *Stream) (*structFieldSet, string, error) {
+ var (
+ curBit uint16 = math.MaxUint16
+ )
+ _, cursor, p := s.stat()
+ for {
+ switch char(p, cursor) {
+ case ' ', '\n', '\t', '\r':
+ cursor++
+ case nul:
+ s.cursor = cursor
+ if s.read() {
+ _, cursor, p = s.stat()
+ continue
+ }
+ return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
+ case '"':
+ cursor++
+ FIRST_CHAR:
+ start := cursor
+ switch char(p, cursor) {
+ case '"':
+ cursor++
+ s.cursor = cursor
+ return nil, "", nil
+ case nul:
+ s.cursor = cursor
+ if s.read() {
+ _, cursor, p = s.stat()
+ goto FIRST_CHAR
+ }
+ return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
+ }
+ keyIdx := 0
+ bitmap := d.keyBitmapUint16
+ for {
+ c := char(p, cursor)
+ switch c {
+ case '"':
+ fieldSetIndex := bits.TrailingZeros16(curBit)
+ field := d.sortedFieldSets[fieldSetIndex]
+ keyLen := cursor - start
+ cursor++
+ s.cursor = cursor
+ if keyLen < field.keyLen {
+ // early match
+ return nil, field.key, nil
+ }
+ return field, field.key, nil
+ case nul:
+ s.cursor = cursor
+ if s.read() {
+ _, cursor, p = s.stat()
+ continue
+ }
+ return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
+ case '\\':
+ s.cursor = cursor + 1 // skip '\' char
+ chars, err := decodeKeyCharByEscapeCharStream(s)
+ if err != nil {
+ return nil, "", err
+ }
+ cursor = s.cursor
+ for _, c := range chars {
+ curBit &= bitmap[keyIdx][largeToSmallTable[c]]
+ if curBit == 0 {
+ s.cursor = cursor
+ return decodeKeyNotFoundStream(s, start)
+ }
+ keyIdx++
+ }
+ default:
+ curBit &= bitmap[keyIdx][largeToSmallTable[c]]
+ if curBit == 0 {
+ s.cursor = cursor
+ return decodeKeyNotFoundStream(s, start)
+ }
+ keyIdx++
+ }
+ cursor++
+ }
+ default:
+ return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
+ }
+ }
+}
+
+// decode from '\uXXXX'
+func decodeKeyCharByUnicodeRuneStream(s *Stream) ([]byte, error) {
+ const defaultOffset = 4
+ const surrogateOffset = 6
+
+ if s.cursor+defaultOffset >= s.length {
+ if !s.read() {
+ return nil, errors.ErrInvalidCharacter(s.char(), "escaped unicode char", s.totalOffset())
+ }
+ }
+
+ r := unicodeToRune(s.buf[s.cursor : s.cursor+defaultOffset])
+ if utf16.IsSurrogate(r) {
+ s.cursor += defaultOffset
+ if s.cursor+surrogateOffset >= s.length {
+ s.read()
+ }
+ if s.cursor+surrogateOffset >= s.length || s.buf[s.cursor] != '\\' || s.buf[s.cursor+1] != 'u' {
+ s.cursor += defaultOffset - 1
+ return []byte(string(unicode.ReplacementChar)), nil
+ }
+ r2 := unicodeToRune(s.buf[s.cursor+defaultOffset+2 : s.cursor+surrogateOffset])
+ if r := utf16.DecodeRune(r, r2); r != unicode.ReplacementChar {
+ s.cursor += defaultOffset - 1
+ return []byte(string(r)), nil
+ }
+ }
+ s.cursor += defaultOffset - 1
+ return []byte(string(r)), nil
+}
+
+func decodeKeyCharByEscapeCharStream(s *Stream) ([]byte, error) {
+ c := s.buf[s.cursor]
+ s.cursor++
+RETRY:
+ switch c {
+ case '"':
+ return []byte{'"'}, nil
+ case '\\':
+ return []byte{'\\'}, nil
+ case '/':
+ return []byte{'/'}, nil
+ case 'b':
+ return []byte{'\b'}, nil
+ case 'f':
+ return []byte{'\f'}, nil
+ case 'n':
+ return []byte{'\n'}, nil
+ case 'r':
+ return []byte{'\r'}, nil
+ case 't':
+ return []byte{'\t'}, nil
+ case 'u':
+ return decodeKeyCharByUnicodeRuneStream(s)
+ case nul:
+ if !s.read() {
+ return nil, errors.ErrInvalidCharacter(s.char(), "escaped char", s.totalOffset())
+ }
+ goto RETRY
+ default:
+ return nil, errors.ErrUnexpectedEndOfJSON("struct field", s.totalOffset())
+ }
+}
+
+func decodeKeyNotFoundStream(s *Stream, start int64) (*structFieldSet, string, error) {
+ buf, cursor, p := s.stat()
+ for {
+ cursor++
+ switch char(p, cursor) {
+ case '"':
+ b := buf[start:cursor]
+ key := *(*string)(unsafe.Pointer(&b))
+ cursor++
+ s.cursor = cursor
+ return nil, key, nil
+ case '\\':
+ cursor++
+ if char(p, cursor) == nul {
+ s.cursor = cursor
+ if !s.read() {
+ return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
+ }
+ buf, cursor, p = s.statForRetry()
+ }
+ case nul:
+ s.cursor = cursor
+ if !s.read() {
+ return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
+ }
+ buf, cursor, p = s.statForRetry()
+ }
+ }
+}
+
+func decodeKeyStream(d *structDecoder, s *Stream) (*structFieldSet, string, error) {
+ key, err := d.stringDecoder.decodeStreamByte(s)
+ if err != nil {
+ return nil, "", err
+ }
+ k := *(*string)(unsafe.Pointer(&key))
+ return d.fieldMap[k], k, nil
+}
+
+func (d *structDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
+ depth++
+ if depth > maxDecodeNestingDepth {
+ return errors.ErrExceededMaxDepth(s.char(), s.cursor)
+ }
+
+ c := s.skipWhiteSpace()
+ switch c {
+ case 'n':
+ if err := nullBytes(s); err != nil {
+ return err
+ }
+ return nil
+ default:
+ if s.char() != '{' {
+ return errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset())
+ }
+ }
+ s.cursor++
+ if s.skipWhiteSpace() == '}' {
+ s.cursor++
+ return nil
+ }
+ var (
+ seenFields map[int]struct{}
+ seenFieldNum int
+ )
+ firstWin := (s.Option.Flags & FirstWinOption) != 0
+ if firstWin {
+ seenFields = make(map[int]struct{}, d.fieldUniqueNameNum)
+ }
+ for {
+ s.reset()
+ field, key, err := d.keyStreamDecoder(d, s)
+ if err != nil {
+ return err
+ }
+ if s.skipWhiteSpace() != ':' {
+ return errors.ErrExpected("colon after object key", s.totalOffset())
+ }
+ s.cursor++
+ if field != nil {
+ if field.err != nil {
+ return field.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)
+ } else {
+ if err := s.skipValue(depth); err != nil {
+ return err
+ }
+ }
+ c := s.skipWhiteSpace()
+ if c == '}' {
+ s.cursor++
+ return nil
+ }
+ if c != ',' {
+ return errors.ErrExpected("comma after object element", s.totalOffset())
+ }
+ s.cursor++
+ }
+}
+
+func (d *structDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
+ buf := ctx.Buf
+ depth++
+ if depth > maxDecodeNestingDepth {
+ return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
+ }
+ buflen := int64(len(buf))
+ cursor = skipWhiteSpace(buf, cursor)
+ b := (*sliceHeader)(unsafe.Pointer(&buf)).data
+ switch char(b, cursor) {
+ case 'n':
+ if err := validateNull(buf, cursor); err != nil {
+ return 0, err
+ }
+ cursor += 4
+ return cursor, nil
+ case '{':
+ default:
+ return 0, errors.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
+ }
+ cursor++
+ cursor = skipWhiteSpace(buf, cursor)
+ if buf[cursor] == '}' {
+ cursor++
+ return cursor, nil
+ }
+ var (
+ seenFields map[int]struct{}
+ seenFieldNum int
+ )
+ firstWin := (ctx.Option.Flags & FirstWinOption) != 0
+ if firstWin {
+ seenFields = make(map[int]struct{}, d.fieldUniqueNameNum)
+ }
+ for {
+ c, field, err := d.keyDecoder(d, buf, cursor)
+ if err != nil {
+ return 0, err
+ }
+ cursor = skipWhiteSpace(buf, c)
+ if char(b, cursor) != ':' {
+ return 0, errors.ErrExpected("colon after object key", cursor)
+ }
+ cursor++
+ if cursor >= buflen {
+ return 0, errors.ErrExpected("object value after colon", cursor)
+ }
+ if field != nil {
+ if field.err != nil {
+ return 0, field.err
+ }
+ if firstWin {
+ if _, exists := seenFields[field.fieldIdx]; exists {
+ 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
+ }
+ } else {
+ c, err := skipValue(buf, cursor, depth)
+ if err != nil {
+ return 0, err
+ }
+ cursor = c
+ }
+ cursor = skipWhiteSpace(buf, cursor)
+ if char(b, cursor) == '}' {
+ cursor++
+ return cursor, nil
+ }
+ if char(b, cursor) != ',' {
+ return 0, errors.ErrExpected("comma after object element", cursor)
+ }
+ cursor++
+ }
+}