github.com/night-codes/go-json@v0.9.15/internal/decoder/map.go (about) 1 package decoder 2 3 import ( 4 "reflect" 5 "unsafe" 6 7 "github.com/night-codes/go-json/internal/errors" 8 "github.com/night-codes/go-json/internal/runtime" 9 ) 10 11 type mapDecoder struct { 12 mapType *runtime.Type 13 keyType *runtime.Type 14 valueType *runtime.Type 15 canUseAssignFaststrType bool 16 keyDecoder Decoder 17 valueDecoder Decoder 18 structName string 19 fieldName string 20 } 21 22 func newMapDecoder(mapType *runtime.Type, keyType *runtime.Type, keyDec Decoder, valueType *runtime.Type, valueDec Decoder, structName, fieldName string) *mapDecoder { 23 return &mapDecoder{ 24 mapType: mapType, 25 keyDecoder: keyDec, 26 keyType: keyType, 27 canUseAssignFaststrType: canUseAssignFaststrType(keyType, valueType), 28 valueType: valueType, 29 valueDecoder: valueDec, 30 structName: structName, 31 fieldName: fieldName, 32 } 33 } 34 35 const ( 36 mapMaxElemSize = 128 37 ) 38 39 // See detail: https://github.com/night-codes/go-json/pull/283 40 func canUseAssignFaststrType(key *runtime.Type, value *runtime.Type) bool { 41 indirectElem := value.Size() > mapMaxElemSize 42 if indirectElem { 43 return false 44 } 45 return key.Kind() == reflect.String 46 } 47 48 //go:linkname makemap reflect.makemap 49 func makemap(*runtime.Type, int) unsafe.Pointer 50 51 //nolint:golint 52 //go:linkname mapassign_faststr runtime.mapassign_faststr 53 //go:noescape 54 func mapassign_faststr(t *runtime.Type, m unsafe.Pointer, s string) unsafe.Pointer 55 56 //go:linkname mapassign reflect.mapassign 57 //go:noescape 58 func mapassign(t *runtime.Type, m unsafe.Pointer, k, v unsafe.Pointer) 59 60 func (d *mapDecoder) mapassign(t *runtime.Type, m, k, v unsafe.Pointer) { 61 if d.canUseAssignFaststrType { 62 mapV := mapassign_faststr(t, m, *(*string)(k)) 63 typedmemmove(d.valueType, mapV, v) 64 } else { 65 mapassign(t, m, k, v) 66 } 67 } 68 69 func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 70 depth++ 71 if depth > maxDecodeNestingDepth { 72 return errors.ErrExceededMaxDepth(s.char(), s.cursor) 73 } 74 75 switch s.skipWhiteSpace() { 76 case 'n': 77 if err := nullBytes(s); err != nil { 78 return err 79 } 80 **(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil 81 return nil 82 case '{': 83 default: 84 return errors.ErrExpected("{ character for map value", s.totalOffset()) 85 } 86 mapValue := *(*unsafe.Pointer)(p) 87 if mapValue == nil { 88 mapValue = makemap(d.mapType, 0) 89 } 90 s.cursor++ 91 if s.equalChar('}') { 92 *(*unsafe.Pointer)(p) = mapValue 93 s.cursor++ 94 return nil 95 } 96 for { 97 k := unsafe_New(d.keyType) 98 if err := d.keyDecoder.DecodeStream(s, depth, k); err != nil { 99 return err 100 } 101 s.skipWhiteSpace() 102 if !s.equalChar(':') { 103 return errors.ErrExpected("colon after object key", s.totalOffset()) 104 } 105 s.cursor++ 106 v := unsafe_New(d.valueType) 107 if err := d.valueDecoder.DecodeStream(s, depth, v); err != nil { 108 return err 109 } 110 d.mapassign(d.mapType, mapValue, k, v) 111 s.skipWhiteSpace() 112 if s.equalChar('}') { 113 **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue 114 s.cursor++ 115 return nil 116 } 117 if !s.equalChar(',') { 118 return errors.ErrExpected("comma after object value", s.totalOffset()) 119 } 120 s.cursor++ 121 } 122 } 123 124 func (d *mapDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 125 buf := ctx.Buf 126 depth++ 127 if depth > maxDecodeNestingDepth { 128 return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) 129 } 130 131 cursor = skipWhiteSpace(buf, cursor) 132 buflen := int64(len(buf)) 133 if buflen < 2 { 134 return 0, errors.ErrExpected("{} for map", cursor) 135 } 136 switch buf[cursor] { 137 case 'n': 138 if err := validateNull(buf, cursor); err != nil { 139 return 0, err 140 } 141 cursor += 4 142 **(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil 143 return cursor, nil 144 case '{': 145 default: 146 return 0, errors.ErrExpected("{ character for map value", cursor) 147 } 148 cursor++ 149 cursor = skipWhiteSpace(buf, cursor) 150 mapValue := *(*unsafe.Pointer)(p) 151 if mapValue == nil { 152 mapValue = makemap(d.mapType, 0) 153 } 154 if buf[cursor] == '}' { 155 **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue 156 cursor++ 157 return cursor, nil 158 } 159 for { 160 k := unsafe_New(d.keyType) 161 keyCursor, err := d.keyDecoder.Decode(ctx, cursor, depth, k) 162 if err != nil { 163 return 0, err 164 } 165 cursor = skipWhiteSpace(buf, keyCursor) 166 if buf[cursor] != ':' { 167 return 0, errors.ErrExpected("colon after object key", cursor) 168 } 169 cursor++ 170 v := unsafe_New(d.valueType) 171 valueCursor, err := d.valueDecoder.Decode(ctx, cursor, depth, v) 172 if err != nil { 173 return 0, err 174 } 175 d.mapassign(d.mapType, mapValue, k, v) 176 cursor = skipWhiteSpace(buf, valueCursor) 177 if buf[cursor] == '}' { 178 **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue 179 cursor++ 180 return cursor, nil 181 } 182 if buf[cursor] != ',' { 183 return 0, errors.ErrExpected("comma after object value", cursor) 184 } 185 cursor++ 186 } 187 }