github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/internal/decoder/map.go (about) 1 package decoder 2 3 import ( 4 "reflect" 5 "unsafe" 6 7 "github.com/trim21/go-phpserialize/internal/errors" 8 "github.com/trim21/go-phpserialize/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/goccy/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 func (d *mapDecoder) mapassign(t *runtime.Type, m, k, v unsafe.Pointer) { 49 if d.canUseAssignFaststrType { 50 mapV := runtime.MapAssignFastStr(t, m, *(*string)(k)) 51 typedmemmove(d.valueType, mapV, v) 52 } else { 53 runtime.MapAssign(t, m, k, v) 54 } 55 } 56 57 func (d *mapDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 58 buf := ctx.Buf 59 depth++ 60 if depth > maxDecodeNestingDepth { 61 return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) 62 } 63 64 buflen := int64(len(buf)) 65 if buflen < 2 { 66 return 0, errors.ErrExpected("{} for map", cursor) 67 } 68 switch buf[cursor] { 69 case 'N': 70 if err := validateNull(buf, cursor); err != nil { 71 return 0, err 72 } 73 cursor += 2 74 **(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil 75 return cursor, nil 76 case 'O': 77 // O:8:"stdClass":1:{s:1:"a";s:1:"q";} 78 end, err := skipClassName(buf, cursor) 79 if err != nil { 80 return cursor, err 81 } 82 cursor = end 83 fallthrough 84 case 'a': 85 // array case 86 cursor++ 87 default: 88 return 0, errors.ErrUnexpectedStart("map", buf, cursor) 89 } 90 91 l, end, err := readLength(buf, cursor) 92 if err != nil { 93 return 0, err 94 } 95 96 cursor = end 97 if buf[cursor] != '{' { 98 return 0, errors.ErrExpected("{ character for map value", cursor) 99 } 100 101 mapValue := *(*unsafe.Pointer)(p) 102 if mapValue == nil { 103 mapValue = runtime.MakeMap(d.mapType, int(l)) 104 } 105 106 cursor++ 107 if buf[cursor] == '}' { 108 **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue 109 cursor++ 110 return cursor, nil 111 } 112 113 for { 114 k := unsafe_New(d.keyType) 115 keyCursor, err := d.keyDecoder.Decode(ctx, cursor, depth, k) 116 if err != nil { 117 return 0, err 118 } 119 cursor = keyCursor 120 v := unsafe_New(d.valueType) 121 valueCursor, err := d.valueDecoder.Decode(ctx, cursor, depth, v) 122 if err != nil { 123 return 0, err 124 } 125 126 d.mapassign(d.mapType, mapValue, k, v) 127 cursor = valueCursor 128 if buf[cursor] == '}' { 129 **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue 130 cursor++ 131 return cursor, nil 132 } 133 } 134 }