github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/internal/encoder/slice.go (about)

     1  package encoder
     2  
     3  import (
     4  	"reflect"
     5  	"unsafe"
     6  
     7  	"github.com/trim21/go-phpserialize/internal/runtime"
     8  )
     9  
    10  const lenOffset = unsafe.Offsetof(reflect.SliceHeader{}.Len)
    11  
    12  func compileSlice(rt *runtime.Type, seen seenMap) (encoder, error) {
    13  	offset := rt.Elem().Size()
    14  	var enc encoder
    15  	var err error
    16  
    17  	if rt.Elem().Kind() == reflect.Map {
    18  		enc, err = compile(runtime.PtrTo(rt.Elem()), seen)
    19  		if err != nil {
    20  			return nil, err
    21  		}
    22  	} else {
    23  		enc, err = compile(rt.Elem(), seen)
    24  		if err != nil {
    25  			return nil, err
    26  		}
    27  	}
    28  	return func(ctx *Ctx, b []byte, p uintptr) ([]byte, error) {
    29  		if p == 0 {
    30  			return appendNull(b), nil
    31  		}
    32  		dataPtr := **(**uintptr)(unsafe.Pointer(&p))
    33  		// no data ptr, nil slice
    34  		if dataPtr == 0 {
    35  			return appendNull(b), nil
    36  		}
    37  
    38  		length := *(*int)(unsafe.Add(ptrToUnsafePtr(p), lenOffset))
    39  
    40  		b = appendArrayBegin(b, int64(length))
    41  		var err error // create a new error value, so shadow compiler's error
    42  		for i := 0; i < length; i++ {
    43  			b = appendIntBytes(b, int64(i))
    44  			b, err = enc(ctx, b, dataPtr+offset*uintptr(i))
    45  			if err != nil {
    46  				return b, err
    47  			}
    48  		}
    49  		return append(b, '}'), nil
    50  	}, nil
    51  }