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  }