github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/internal/decoder/slice.go (about) 1 package decoder 2 3 import ( 4 "reflect" 5 "sync" 6 "unsafe" 7 8 "github.com/trim21/go-phpserialize/internal/errors" 9 "github.com/trim21/go-phpserialize/internal/runtime" 10 ) 11 12 var ( 13 sliceType = runtime.Type2RType( 14 reflect.TypeOf((*sliceHeader)(nil)).Elem(), 15 ) 16 nilSlice = unsafe.Pointer(&sliceHeader{}) 17 ) 18 19 type sliceDecoder struct { 20 elemType *runtime.Type 21 isElemPointerType bool 22 valueDecoder Decoder 23 size uintptr 24 arrayPool sync.Pool 25 structName string 26 fieldName string 27 } 28 29 // If use reflect.SliceHeader, data type is uintptr. 30 // In this case, Go compiler cannot trace reference created by newArray(). 31 // So, define using unsafe.Pointer as data type 32 type sliceHeader struct { 33 data unsafe.Pointer 34 len int 35 cap int 36 } 37 38 const ( 39 defaultSliceCapacity = 2 40 ) 41 42 func newSliceDecoder(dec Decoder, elemType *runtime.Type, size uintptr, structName, fieldName string) *sliceDecoder { 43 return &sliceDecoder{ 44 valueDecoder: dec, 45 elemType: elemType, 46 isElemPointerType: elemType.Kind() == reflect.Ptr || elemType.Kind() == reflect.Map, 47 size: size, 48 arrayPool: sync.Pool{ 49 New: func() any { 50 return &sliceHeader{ 51 data: newArray(elemType, defaultSliceCapacity), 52 len: 0, 53 cap: defaultSliceCapacity, 54 } 55 }, 56 }, 57 structName: structName, 58 fieldName: fieldName, 59 } 60 } 61 62 func (d *sliceDecoder) releaseSlice(p *sliceHeader) { 63 d.arrayPool.Put(p) 64 } 65 66 //go:linkname copySlice reflect.typedslicecopy 67 func copySlice(elemType *runtime.Type, dst, src sliceHeader) int 68 69 //go:linkname newArray reflect.unsafe_NewArray 70 func newArray(*runtime.Type, int) unsafe.Pointer 71 72 //go:linkname typedmemmove reflect.typedmemmove 73 func typedmemmove(t *runtime.Type, dst, src unsafe.Pointer) 74 75 func (d *sliceDecoder) errNumber(offset int64) *errors.UnmarshalTypeError { 76 return &errors.UnmarshalTypeError{ 77 Value: "number", 78 Type: reflect.SliceOf(runtime.RType2Type(d.elemType)), 79 Struct: d.structName, 80 Field: d.fieldName, 81 Offset: offset, 82 } 83 } 84 85 func (d *sliceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 86 buf := ctx.Buf 87 depth++ 88 if depth > maxDecodeNestingDepth { 89 return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) 90 } 91 92 switch buf[cursor] { 93 case 'N': 94 if err := validateNull(buf, cursor); err != nil { 95 return 0, err 96 } 97 cursor += 2 98 typedmemmove(sliceType, p, nilSlice) 99 return cursor, nil 100 case 'a': 101 cursor++ 102 if buf[cursor] != ':' { 103 return cursor, errors.ErrExpected("':' before array length", cursor) 104 } 105 106 cursor++ 107 if buf[cursor] == '0' { 108 err := validateEmptyArray(buf, cursor) 109 if err != nil { 110 return cursor, err 111 } 112 113 dst := (*sliceHeader)(p) 114 if dst.data == nil { 115 dst.data = newArray(d.elemType, 0) 116 } else { 117 dst.len = 0 118 } 119 120 return cursor + 4, nil 121 } 122 123 arrLen, end, err := readLengthInt(buf, cursor-1) 124 if err != nil { 125 return cursor, err 126 } 127 cursor = end 128 129 if buf[cursor] != '{' { 130 return cursor, errors.ErrInvalidBeginningOfArray(buf[cursor], cursor) 131 } 132 cursor++ 133 134 // pre-alloc 135 slice := &sliceHeader{ 136 data: newArray(d.elemType, arrLen), 137 len: 0, 138 cap: arrLen, 139 } 140 srcLen := slice.len 141 capacity := slice.cap 142 data := slice.data 143 144 idx := 0 145 for { 146 currentIndex, end, err := readInt(buf, cursor) 147 if err != nil { 148 return 0, err 149 } 150 151 idx = currentIndex 152 cursor = end 153 154 if capacity <= idx { 155 src := sliceHeader{data: data, len: idx, cap: capacity} 156 capacity *= 2 157 data = newArray(d.elemType, capacity) 158 dst := sliceHeader{data: data, len: idx, cap: capacity} 159 copySlice(d.elemType, dst, src) 160 } 161 ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size) 162 // if srcLen is greater than idx, keep the original reference 163 if srcLen <= idx { 164 if d.isElemPointerType { 165 **(**unsafe.Pointer)(unsafe.Pointer(&ep)) = nil // initialize elem pointer 166 } else { 167 // assign new element to the slice 168 typedmemmove(d.elemType, ep, unsafe_New(d.elemType)) 169 } 170 } 171 172 c, err := d.valueDecoder.Decode(ctx, cursor, depth, ep) 173 if err != nil { 174 return 0, err 175 } 176 177 cursor = c 178 if buf[cursor] == '}' { 179 slice.cap = capacity 180 slice.len = idx + 1 181 slice.data = data 182 dst := (*sliceHeader)(p) 183 dst.len = idx + 1 184 if dst.len > dst.cap { 185 dst.data = newArray(d.elemType, dst.len) 186 dst.cap = dst.len 187 } 188 copySlice(d.elemType, *dst, *slice) 189 d.releaseSlice(slice) 190 cursor++ 191 return cursor, nil 192 } 193 } 194 case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 195 return 0, d.errNumber(cursor) 196 default: 197 return 0, errors.ErrUnexpectedEnd("slice", cursor) 198 } 199 }