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 }