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

     1  package phpserialize
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"unsafe"
     7  
     8  	"github.com/trim21/go-phpserialize/internal/decoder"
     9  	"github.com/trim21/go-phpserialize/internal/errors"
    10  	"github.com/trim21/go-phpserialize/internal/runtime"
    11  )
    12  
    13  type emptyInterface struct {
    14  	typ *runtime.Type
    15  	ptr unsafe.Pointer
    16  }
    17  
    18  func unmarshal(data []byte, v any) error {
    19  	header := (*emptyInterface)(unsafe.Pointer(&v))
    20  
    21  	if err := validateType(header.typ, uintptr(header.ptr)); err != nil {
    22  		return err
    23  	}
    24  
    25  	src := make([]byte, len(data)) // append nul byte to the end
    26  	copy(src, data)
    27  
    28  	dec, err := decoder.CompileToGetDecoder(header.typ)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	ctx := decoder.TakeRuntimeContext()
    33  	ctx.Buf = src
    34  	cursor, err := dec.Decode(ctx, 0, 0, header.ptr)
    35  	if err != nil {
    36  		decoder.ReleaseRuntimeContext(ctx)
    37  		return err
    38  	}
    39  	decoder.ReleaseRuntimeContext(ctx)
    40  	return validateEndBuf(src, cursor)
    41  }
    42  
    43  func validateEndBuf(src []byte, cursor int64) error {
    44  	if int64(len(src)) == cursor {
    45  		return nil
    46  	}
    47  
    48  	return errors.ErrSyntax(
    49  		fmt.Sprintf("invalid character '%c' after top-level value", src[cursor]),
    50  		cursor+1,
    51  	)
    52  }
    53  
    54  func validateType(typ *runtime.Type, p uintptr) error {
    55  	if typ == nil || typ.Kind() != reflect.Ptr || p == 0 {
    56  		return &errors.InvalidUnmarshalError{Type: runtime.RType2Type(typ)}
    57  	}
    58  	return nil
    59  }