github.com/philpearl/plenc@v0.0.15/plenccodec/wrapper.go (about) 1 package plenccodec 2 3 import ( 4 "fmt" 5 "unsafe" 6 7 "github.com/philpearl/plenc/plenccore" 8 ) 9 10 // PointerWrapper wraps a codec so it can be used for a pointer to the type 11 type PointerWrapper struct { 12 Underlying Codec 13 } 14 15 func (p PointerWrapper) Omit(ptr unsafe.Pointer) bool { 16 t := *(*unsafe.Pointer)(ptr) 17 return t == nil 18 } 19 20 func (p PointerWrapper) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) { 21 t := (*unsafe.Pointer)(ptr) 22 if *t == nil { 23 *t = p.Underlying.New() 24 } 25 26 return p.Underlying.Read(data, *t, wt) 27 } 28 29 func (p PointerWrapper) New() unsafe.Pointer { 30 v := p.Underlying.New() 31 return unsafe.Pointer(&v) 32 } 33 34 func (p PointerWrapper) WireType() plenccore.WireType { 35 return p.Underlying.WireType() 36 } 37 38 func (p PointerWrapper) Descriptor() Descriptor { 39 d := p.Underlying.Descriptor() 40 d.ExplicitPresence = true 41 return d 42 } 43 44 func (p PointerWrapper) Size(ptr unsafe.Pointer, tag []byte) int { 45 // This should never be called if Omit returns true 46 t := *(*unsafe.Pointer)(ptr) 47 if t == nil { 48 return 0 49 } 50 return p.Underlying.Size(t, tag) 51 } 52 53 func (p PointerWrapper) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte { 54 // This should never be called if Omit returns true 55 t := *(*unsafe.Pointer)(ptr) 56 if t == nil { 57 return data 58 } 59 return p.Underlying.Append(data, t, tag) 60 } 61 62 type BaseSliceWrapper struct { 63 Underlying Codec 64 EltSize uintptr 65 EltType unsafe.Pointer 66 } 67 68 func (c BaseSliceWrapper) Omit(ptr unsafe.Pointer) bool { 69 h := *(*sliceHeader)(ptr) 70 return h.Len == 0 71 } 72 73 func (c BaseSliceWrapper) New() unsafe.Pointer { 74 return unsafe.Pointer(&sliceHeader{}) 75 } 76 77 func (c BaseSliceWrapper) WireType() plenccore.WireType { 78 return plenccore.WTLength 79 } 80 81 func (c BaseSliceWrapper) Descriptor() Descriptor { 82 return Descriptor{ 83 Type: FieldTypeSlice, 84 Elements: []Descriptor{ 85 c.Underlying.Descriptor(), 86 }, 87 } 88 } 89 90 // WTLengthSliceWrapper is a codec for a slice of a type that's encoded using 91 // the WTLength wire type. It uses the WTSlice wire type for the slice itself. 92 type WTLengthSliceWrapper struct { 93 BaseSliceWrapper 94 } 95 96 func (c WTLengthSliceWrapper) size(ptr unsafe.Pointer) int { 97 h := *(*sliceHeader)(ptr) 98 size := plenccore.SizeVarUint(uint64(h.Len)) 99 for i := 0; i < h.Len; i++ { 100 s := c.Underlying.Size(unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), nil) 101 size += s + plenccore.SizeVarUint(uint64(s)) 102 } 103 return size 104 } 105 106 // append encodes the slice, and appends the encoded version to data 107 func (c WTLengthSliceWrapper) append(data []byte, ptr unsafe.Pointer) []byte { 108 h := *(*sliceHeader)(ptr) 109 110 // Append the count of items in the slice 111 data = plenccore.AppendVarUint(data, uint64(h.Len)) 112 // Append each of the items. They're all prefixed by their length 113 for i := 0; i < h.Len; i++ { 114 ptr := unsafe.Pointer(uintptr(h.Data) + uintptr(i)*c.EltSize) 115 data = plenccore.AppendVarUint(data, uint64(c.Underlying.Size(ptr, nil))) 116 data = c.Underlying.Append(data, ptr, nil) 117 } 118 return data 119 } 120 121 // Read decodes a slice. It assumes the WTLength tag has already been decoded 122 // and that the data slice is the corect size for the slice 123 func (c WTLengthSliceWrapper) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) { 124 if wt == plenccore.WTLength { 125 return c.readAsWTLength(data, ptr) 126 } 127 128 // First we read the number of items in the slice 129 count, n := plenccore.ReadVarUint(data) 130 if n < 0 { 131 return 0, fmt.Errorf("corrupt data looking for WTSlice count") 132 } 133 134 // Now make sure we have enough capacity in the slice 135 h := (*sliceHeader)(ptr) 136 if h.Cap < int(count) { 137 // Ensure the GC knows the type of this slice. 138 h.Data = unsafe_NewArray(c.EltType, int(count)) 139 h.Cap = int(count) 140 } else { 141 // We're going to re-use the backing array. It's going to be surprising 142 // if we don't start from zeros so we zero everything. 143 for i := 0; i < int(count); i++ { 144 // We'll only write to fields if the data is present, so start by zeroing 145 // the target 146 ptr := unsafe.Add(h.Data, i*int(c.EltSize)) 147 typedmemclr(unpackEFace(c.EltType).data, ptr) 148 } 149 } 150 h.Len = int(count) 151 152 offset := n 153 for i := 0; i < h.Len; i++ { 154 s, n := plenccore.ReadVarUint(data[offset:]) 155 if n <= 0 { 156 return 0, fmt.Errorf("invalid varint for slice entry %d", i) 157 } 158 offset += n 159 160 ptr := unsafe.Add(h.Data, i*int(c.EltSize)) 161 n, err := c.Underlying.Read(data[offset:offset+int(s)], ptr, plenccore.WTLength) 162 if err != nil { 163 return 0, err 164 } 165 offset += n 166 } 167 168 return offset, nil 169 } 170 171 // readAsWTLength is here for protobuf compatibility. protobuf writes certain 172 // array types by simply repeating the encoding for an individual field. So here 173 // we just read one underlying value and append it to the slice 174 func (c WTLengthSliceWrapper) readAsWTLength(data []byte, ptr unsafe.Pointer) (n int, err error) { 175 h := (*sliceHeader)(ptr) 176 if h.Cap == h.Len { 177 // Need to make room 178 cap := h.Cap * 2 179 if cap == 0 { 180 cap = 8 181 } 182 nh := sliceHeader{ 183 Data: unsafe_NewArray(c.EltType, int(cap)), 184 Len: h.Len, 185 Cap: cap, 186 } 187 if h.Len != 0 { 188 // copy over the old data 189 typedslicecopy(c.EltType, nh, *h) 190 } 191 nh.Len = h.Len 192 nh.Cap = cap 193 194 *h = nh 195 } 196 197 dptr := unsafe.Add(h.Data, h.Len*int(c.EltSize)) 198 typedmemclr(unpackEFace(c.EltType).data, dptr) 199 n, err = c.Underlying.Read(data, dptr, plenccore.WTLength) 200 if err != nil { 201 return 0, err 202 } 203 h.Len++ 204 return n, nil 205 } 206 207 func (c WTLengthSliceWrapper) WireType() plenccore.WireType { 208 return plenccore.WTSlice 209 } 210 211 func (c WTLengthSliceWrapper) Size(ptr unsafe.Pointer, tag []byte) int { 212 return c.size(ptr) + len(tag) 213 } 214 215 func (c WTLengthSliceWrapper) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte { 216 data = append(data, tag...) 217 return c.append(data, ptr) 218 } 219 220 // WTFixedSliceWrapper is a codec for a type that's encoded as a fixed 32 or 64 221 // byte value (i.e. float32 or float64) 222 type WTFixedSliceWrapper struct { 223 BaseSliceWrapper 224 } 225 226 // append encodes the slice without the tag 227 func (c WTFixedSliceWrapper) append(data []byte, ptr unsafe.Pointer) []byte { 228 h := *(*sliceHeader)(ptr) 229 for i := 0; i < h.Len; i++ { 230 data = c.Underlying.Append(data, unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), nil) 231 } 232 return data 233 } 234 235 // Read decodes a slice. It assumes the WTLength tag has already been decoded 236 // and that the data slice is the corect size for the slice 237 func (c WTFixedSliceWrapper) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) { 238 count := len(data) / c.Underlying.Size(nil, nil) 239 240 // Now make sure we have enough data in the slice 241 h := (*sliceHeader)(ptr) 242 if h.Cap < count { 243 // Ensure the GC knows the type of this slice. 244 h.Data = unsafe_NewArray(c.EltType, int(count)) 245 h.Cap = int(count) 246 } 247 h.Len = count 248 249 var offset int 250 for i := 0; i < h.Len; i++ { 251 n, err := c.Underlying.Read(data[offset:], unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), c.Underlying.WireType()) 252 if err != nil { 253 return 0, err 254 } 255 offset += n 256 } 257 258 return offset, nil 259 } 260 261 func (p WTFixedSliceWrapper) Size(ptr unsafe.Pointer, tag []byte) int { 262 h := *(*sliceHeader)(ptr) 263 l := p.Underlying.Size(nil, nil) * h.Len 264 if len(tag) > 0 { 265 l += len(tag) + plenccore.SizeVarUint(uint64(l)) 266 } 267 return l 268 } 269 270 func (p WTFixedSliceWrapper) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte { 271 if len(tag) > 0 { 272 data = append(data, tag...) 273 data = plenccore.AppendVarUint(data, uint64(p.Size(ptr, nil))) 274 } 275 return p.append(data, ptr) 276 } 277 278 // WTVarIntSliceWrapper is a codec for a type encoded using the WTVarInt wire 279 // type. 280 type WTVarIntSliceWrapper struct { 281 BaseSliceWrapper 282 } 283 284 func (c WTVarIntSliceWrapper) size(ptr unsafe.Pointer) int { 285 h := *(*sliceHeader)(ptr) 286 size := 0 287 for i := 0; i < h.Len; i++ { 288 size += c.Underlying.Size(unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), nil) 289 } 290 return size 291 } 292 293 // append encodes the slice without the tag 294 func (c WTVarIntSliceWrapper) append(data []byte, ptr unsafe.Pointer) []byte { 295 h := *(*sliceHeader)(ptr) 296 for i := 0; i < h.Len; i++ { 297 data = c.Underlying.Append(data, unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), nil) 298 } 299 return data 300 } 301 302 // Read decodes a slice. It assumes the WTLength tag has already been decoded 303 // and that the data slice is the correct size for the slice 304 func (c WTVarIntSliceWrapper) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) { 305 // We step forward through out data to count how many things are in the slice 306 var offset, count int 307 for offset < len(data) { 308 _, n := plenccore.ReadVarUint(data[offset:]) 309 if n < 0 { 310 return 0, fmt.Errorf("corrupt data") 311 } 312 offset += n 313 count++ 314 } 315 316 // Now make sure we have enough data in the slice 317 h := (*sliceHeader)(ptr) 318 if h.Cap < count { 319 // Ensure the GC knows the type of this slice. 320 h.Data = unsafe_NewArray(c.EltType, int(count)) 321 h.Cap = int(count) 322 } 323 h.Len = count 324 325 offset = 0 326 for i := 0; i < h.Len; i++ { 327 n, err := c.Underlying.Read(data[offset:], unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), plenccore.WTVarInt) 328 if err != nil { 329 return 0, err 330 } 331 offset += n 332 } 333 334 return offset, nil 335 } 336 337 func (p WTVarIntSliceWrapper) Size(ptr unsafe.Pointer, tag []byte) int { 338 l := p.size(ptr) 339 if len(tag) > 0 { 340 l += len(tag) + plenccore.SizeVarUint(uint64(l)) 341 } 342 return l 343 } 344 345 func (p WTVarIntSliceWrapper) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte { 346 if len(tag) > 0 { 347 data = append(data, tag...) 348 data = plenccore.AppendVarUint(data, uint64(p.size(ptr))) 349 } 350 return p.append(data, ptr) 351 } 352 353 // ProtoSliceWrapper is for encoding slices of WTLength encoded objects how 354 // protobuf does it. When writing the elements of the slice are simply repeated. 355 // When reading each element is treated separately and appended to the slice. 356 // 357 // Note this does not work outside of a struct as we don't add tags and 358 // therefore the length of each element is not known 359 type ProtoSliceWrapper struct { 360 BaseSliceWrapper 361 } 362 363 // Size calculates the amount of data needed for this slice, including 364 // repeated tags and lengths 365 func (c ProtoSliceWrapper) Size(ptr unsafe.Pointer, tag []byte) int { 366 h := *(*sliceHeader)(ptr) 367 var l int 368 for i := 0; i < h.Len; i++ { 369 l += c.Underlying.Size(unsafe.Add(h.Data, uintptr(i)*c.EltSize), tag) 370 } 371 return l 372 } 373 374 // Append appends the data for this slice, including repeated tags and 375 // lengths for each element 376 func (c ProtoSliceWrapper) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte { 377 h := *(*sliceHeader)(ptr) 378 for i := 0; i < h.Len; i++ { 379 data = c.Underlying.Append(data, unsafe.Pointer(uintptr(h.Data)+uintptr(i)*c.EltSize), tag) 380 } 381 return data 382 } 383 384 func (c ProtoSliceWrapper) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) { 385 h := (*sliceHeader)(ptr) 386 if h.Cap == h.Len { 387 // Need to make room 388 cap := h.Cap * 2 389 if cap == 0 { 390 cap = 8 391 } 392 nh := sliceHeader{ 393 Data: unsafe_NewArray(c.EltType, int(cap)), 394 Len: h.Len, 395 Cap: cap, 396 } 397 if h.Len != 0 { 398 // copy over the old data 399 typedslicecopy(c.EltType, nh, *h) 400 } 401 nh.Len = h.Len 402 nh.Cap = cap 403 404 *h = nh 405 } 406 407 dptr := unsafe.Add(h.Data, h.Len*int(c.EltSize)) 408 typedmemclr(unpackEFace(c.EltType).data, dptr) 409 n, err = c.Underlying.Read(data, dptr, plenccore.WTLength) 410 if err != nil { 411 return 0, err 412 } 413 h.Len++ 414 return n, nil 415 }