github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/internal/encoder/slice_reflect.go (about) 1 package encoder 2 3 import ( 4 "reflect" 5 "unsafe" 6 7 "github.com/trim21/go-phpserialize/internal/runtime" 8 ) 9 10 func unpackIface(p uintptr) uintptr { 11 return uintptr((**(**emptyInterface)(unsafe.Pointer(&p))).ptr) 12 } 13 14 func reflectSlice(ctx *Ctx, b []byte, rv reflect.Value, p uintptr) ([]byte, error) { 15 rt := rv.Type() 16 17 // not slice of interface, fast path 18 if rt.Elem().Kind() != reflect.Interface { 19 return reflectConcreteSlice(ctx, b, runtime.Type2RType(rt), p) 20 } 21 22 shPtr := unpackIface(p) 23 // no data ptr, nil slice 24 // even empty slice has a non-zero data ptr 25 if shPtr == 0 { 26 return appendNull(b), nil 27 } 28 29 el := runtime.Type2RType(rt.Elem()) 30 31 encoder, err := compileInterface(el) 32 if err != nil { 33 return nil, err 34 } 35 36 sh := **(**runtime.SliceHeader)(unsafe.Pointer(&shPtr)) 37 offset := rt.Elem().Size() 38 39 dataPtr := uintptr(sh.Data) 40 41 b = appendArrayBegin(b, int64(sh.Len)) 42 43 for i := 0; i < sh.Len; i++ { 44 b = appendIntBytes(b, int64(i)) 45 b, err = encoder(ctx, b, dataPtr+uintptr(i)*offset) 46 if err != nil { 47 return b, err 48 } 49 } 50 51 return append(b, '}'), nil 52 } 53 54 func reflectConcreteSlice(ctx *Ctx, b []byte, rt *runtime.Type, p uintptr) ([]byte, error) { 55 enc, err := compileWithCache(rt) 56 if err != nil { 57 return nil, err 58 } 59 60 p = unpackIface(p) 61 62 return enc(ctx, b, p) 63 }