123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- package decoder
- import (
- "reflect"
- "unsafe"
- "github.com/goccy/go-json/internal/errors"
- "github.com/goccy/go-json/internal/runtime"
- )
- type mapDecoder struct {
- mapType *runtime.Type
- keyType *runtime.Type
- valueType *runtime.Type
- canUseAssignFaststrType bool
- keyDecoder Decoder
- valueDecoder Decoder
- structName string
- fieldName string
- }
- func newMapDecoder(mapType *runtime.Type, keyType *runtime.Type, keyDec Decoder, valueType *runtime.Type, valueDec Decoder, structName, fieldName string) *mapDecoder {
- return &mapDecoder{
- mapType: mapType,
- keyDecoder: keyDec,
- keyType: keyType,
- canUseAssignFaststrType: canUseAssignFaststrType(keyType, valueType),
- valueType: valueType,
- valueDecoder: valueDec,
- structName: structName,
- fieldName: fieldName,
- }
- }
- const (
- mapMaxElemSize = 128
- )
- // See detail: https://github.com/goccy/go-json/pull/283
- func canUseAssignFaststrType(key *runtime.Type, value *runtime.Type) bool {
- indirectElem := value.Size() > mapMaxElemSize
- if indirectElem {
- return false
- }
- return key.Kind() == reflect.String
- }
- //go:linkname makemap reflect.makemap
- func makemap(*runtime.Type, int) unsafe.Pointer
- //nolint:golint
- //go:linkname mapassign_faststr runtime.mapassign_faststr
- //go:noescape
- func mapassign_faststr(t *runtime.Type, m unsafe.Pointer, s string) unsafe.Pointer
- //go:linkname mapassign reflect.mapassign
- //go:noescape
- func mapassign(t *runtime.Type, m unsafe.Pointer, k, v unsafe.Pointer)
- func (d *mapDecoder) mapassign(t *runtime.Type, m, k, v unsafe.Pointer) {
- if d.canUseAssignFaststrType {
- mapV := mapassign_faststr(t, m, *(*string)(k))
- typedmemmove(d.valueType, mapV, v)
- } else {
- mapassign(t, m, k, v)
- }
- }
- func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
- depth++
- if depth > maxDecodeNestingDepth {
- return errors.ErrExceededMaxDepth(s.char(), s.cursor)
- }
- switch s.skipWhiteSpace() {
- case 'n':
- if err := nullBytes(s); err != nil {
- return err
- }
- **(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil
- return nil
- case '{':
- default:
- return errors.ErrExpected("{ character for map value", s.totalOffset())
- }
- mapValue := *(*unsafe.Pointer)(p)
- if mapValue == nil {
- mapValue = makemap(d.mapType, 0)
- }
- s.cursor++
- if s.skipWhiteSpace() == '}' {
- *(*unsafe.Pointer)(p) = mapValue
- s.cursor++
- return nil
- }
- for {
- k := unsafe_New(d.keyType)
- if err := d.keyDecoder.DecodeStream(s, depth, k); err != nil {
- return err
- }
- s.skipWhiteSpace()
- if !s.equalChar(':') {
- return errors.ErrExpected("colon after object key", s.totalOffset())
- }
- s.cursor++
- v := unsafe_New(d.valueType)
- if err := d.valueDecoder.DecodeStream(s, depth, v); err != nil {
- return err
- }
- d.mapassign(d.mapType, mapValue, k, v)
- s.skipWhiteSpace()
- if s.equalChar('}') {
- **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue
- s.cursor++
- return nil
- }
- if !s.equalChar(',') {
- return errors.ErrExpected("comma after object value", s.totalOffset())
- }
- s.cursor++
- }
- }
- func (d *mapDecoder) 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)
- }
- cursor = skipWhiteSpace(buf, cursor)
- buflen := int64(len(buf))
- if buflen < 2 {
- return 0, errors.ErrExpected("{} for map", cursor)
- }
- switch buf[cursor] {
- case 'n':
- if err := validateNull(buf, cursor); err != nil {
- return 0, err
- }
- cursor += 4
- **(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil
- return cursor, nil
- case '{':
- default:
- return 0, errors.ErrExpected("{ character for map value", cursor)
- }
- cursor++
- cursor = skipWhiteSpace(buf, cursor)
- mapValue := *(*unsafe.Pointer)(p)
- if mapValue == nil {
- mapValue = makemap(d.mapType, 0)
- }
- if buf[cursor] == '}' {
- **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue
- cursor++
- return cursor, nil
- }
- for {
- k := unsafe_New(d.keyType)
- keyCursor, err := d.keyDecoder.Decode(ctx, cursor, depth, k)
- if err != nil {
- return 0, err
- }
- cursor = skipWhiteSpace(buf, keyCursor)
- if buf[cursor] != ':' {
- return 0, errors.ErrExpected("colon after object key", cursor)
- }
- cursor++
- v := unsafe_New(d.valueType)
- valueCursor, err := d.valueDecoder.Decode(ctx, cursor, depth, v)
- if err != nil {
- return 0, err
- }
- d.mapassign(d.mapType, mapValue, k, v)
- cursor = skipWhiteSpace(buf, valueCursor)
- if buf[cursor] == '}' {
- **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue
- cursor++
- return cursor, nil
- }
- if buf[cursor] != ',' {
- return 0, errors.ErrExpected("comma after object value", cursor)
- }
- cursor++
- }
- }
- func (d *mapDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
- buf := ctx.Buf
- depth++
- if depth > maxDecodeNestingDepth {
- return nil, 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
- }
- cursor = skipWhiteSpace(buf, cursor)
- buflen := int64(len(buf))
- if buflen < 2 {
- return nil, 0, errors.ErrExpected("{} for map", cursor)
- }
- switch buf[cursor] {
- case 'n':
- if err := validateNull(buf, cursor); err != nil {
- return nil, 0, err
- }
- cursor += 4
- return [][]byte{nullbytes}, cursor, nil
- case '{':
- default:
- return nil, 0, errors.ErrExpected("{ character for map value", cursor)
- }
- cursor++
- cursor = skipWhiteSpace(buf, cursor)
- if buf[cursor] == '}' {
- cursor++
- return nil, cursor, nil
- }
- keyDecoder, ok := d.keyDecoder.(*stringDecoder)
- if !ok {
- return nil, 0, &errors.UnmarshalTypeError{
- Value: "string",
- Type: reflect.TypeOf(""),
- Offset: cursor,
- Struct: d.structName,
- Field: d.fieldName,
- }
- }
- ret := [][]byte{}
- for {
- key, keyCursor, err := keyDecoder.decodeByte(buf, cursor)
- if err != nil {
- return nil, 0, err
- }
- cursor = skipWhiteSpace(buf, keyCursor)
- if buf[cursor] != ':' {
- return nil, 0, errors.ErrExpected("colon after object key", cursor)
- }
- cursor++
- child, found, err := ctx.Option.Path.Field(string(key))
- if err != nil {
- return nil, 0, err
- }
- if found {
- if child != nil {
- oldPath := ctx.Option.Path.node
- ctx.Option.Path.node = child
- paths, c, err := d.valueDecoder.DecodePath(ctx, cursor, depth)
- if err != nil {
- return nil, 0, err
- }
- ctx.Option.Path.node = oldPath
- ret = append(ret, paths...)
- cursor = c
- } else {
- start := cursor
- end, err := skipValue(buf, cursor, depth)
- if err != nil {
- return nil, 0, err
- }
- ret = append(ret, buf[start:end])
- cursor = end
- }
- } else {
- c, err := skipValue(buf, cursor, depth)
- if err != nil {
- return nil, 0, err
- }
- cursor = c
- }
- cursor = skipWhiteSpace(buf, cursor)
- if buf[cursor] == '}' {
- cursor++
- return ret, cursor, nil
- }
- if buf[cursor] != ',' {
- return nil, 0, errors.ErrExpected("comma after object value", cursor)
- }
- cursor++
- }
- }
|