blob: 9ecf9115f419e43f3fa31e2d5d8b3fcbf138fd19 [file] [log] [blame]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fidl
import (
"errors"
"math"
"reflect"
"strconv"
"strings"
"sync"
"syscall/zx"
"syscall/zx/fidl/internal/unsafevalue"
"unicode/utf8"
"unsafe"
)
type strictness bool
const (
isFlexible strictness = false
isStrict strictness = true
)
// MustCreateMarshaler is like CreateMarshaler but panics if the sample struct
// cannot be used to create a marshaler. It simplifies safe initialization of
// global variables holding marshalers.
func MustCreateMarshaler(sample interface{}) Marshaler {
m, err := CreateMarshaler(sample)
if err != nil {
panic(err)
}
return m
}
// CreateMarshaler creates a marshaler from a sample struct.
func CreateMarshaler(sample interface{}) (Marshaler, error) {
typ := reflect.TypeOf(sample)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() == reflect.Struct {
return createMarshaler(typ)
}
return nil, errors.New("unable to create marshaler for " + nicefmt(typ))
}
// CreateMarshaler creates a lazy marshaler from a sample struct. This lazy
// marshaler initializes its actual delegate marshaler on first use, rather
// than on creation. As a result, there is no validation on creation, and
// instead the lazy marshaler will panic on first use if a marshaler
// cannot be created of the sample provided.
func CreateLazyMarshaler(sample interface{}) Marshaler {
return &lazyMarshaler{
sample: sample,
}
}
type lazyMarshaler struct {
once sync.Once
sample interface{}
delegate Marshaler
}
// Assert that lazyMarshaler implements the Marshaler interface.
var _ Marshaler = &lazyMarshaler{}
func (m *lazyMarshaler) init() {
m.delegate = MustCreateMarshaler(m.sample)
}
func (m *lazyMarshaler) getMarshalSize(ctx MarshalerContext) int {
m.once.Do(m.init)
return m.delegate.getMarshalSize(ctx)
}
func (m *lazyMarshaler) getUnmarshalSize(ctx MarshalerContext) int {
m.once.Do(m.init)
return m.delegate.getUnmarshalSize(ctx)
}
func (m *lazyMarshaler) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
m.once.Do(m.init)
return m.delegate.marshal(ctx, v, out, offset)
}
func (m *lazyMarshaler) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
m.once.Do(m.init)
return m.delegate.unmarshal(ctx, in, offset, v)
}
// Marshal marshals (or encodes) a message into the data and handles slices.
func Marshal(message Message, data []byte, handleDispositions []zx.HandleDisposition) (int, int, error) {
return MarshalWithContext(newCtx(), message, data, handleDispositions)
}
// Marshal marshals (or encodes) a message into the data and handles slices.
func MarshalWithContext(ctx MarshalerContext, message Message, data []byte, handleDispositions []zx.HandleDisposition) (int, int, error) {
// By construction, we know that message is a pointer to a struct since
// we only generate pointer receiver methods for top-level messages.
// Should one implement the interface differently, and call into this
// code, it would fail in an obsure way withing relfection. Just don't do
// that.
var (
v = unsafevalue.ValueOf(message)
m = message.Marshaler()
)
// Now, let's get the value of s, marshal the header into a starting
// buffer, and then marshal the rest of the payload in s.
out := &encoder{buffer: data[:0], handleDispositions: handleDispositions[:0]}
offset := out.newObject(m.getMarshalSize(ctx))
if err := m.marshal(ctx, v, out, offset); err != nil {
return 0, 0, err
}
return len(out.buffer), len(out.handleDispositions), nil
}
// Unmarshal unmarshals (or decodes) into message using the data and handles
// slices.
func Unmarshal(data []byte, handleInfos []zx.HandleInfo, message Message) (int, int, error) {
return UnmarshalWithContext2(newCtx(), data, handleInfos, message)
}
// UnmarshalWithContext2 behaves identically to UnmarshalWithContext but takes a HandleInfo.
func UnmarshalWithContext2(ctx MarshalerContext, data []byte, handleInfos []zx.HandleInfo, message Message) (int, int, error) {
// By construction, we know that message is a pointer to a struct since
// we only generate pointer receiver methods for top-level messages.
// Should one implement the interface differently, and call into this
// code, it would fail in an obsure way withing relfection. Just don't do
// that.
var (
v = unsafevalue.ValueOf(message)
m = message.Marshaler()
)
// Get the payload's value and unmarshal it.
in := &decoder{
buffer: data,
handleInfos: handleInfos,
}
offset, err := in.newObject(m.getUnmarshalSize(ctx))
if err != nil {
return 0, 0, err
}
if err := m.unmarshal(ctx, in, offset, v); err != nil {
return 0, 0, err
}
return in.nextObject, len(handleInfos) - len(in.handleInfos), nil
}
func UnmarshalWithContext(ctx MarshalerContext, data []byte, handles []zx.Handle, message Message) (int, int, error) {
resphi := messageHandleInfosPool.Get().([]zx.HandleInfo)
defer messageHandleInfosPool.Put(resphi)
handleInfos := resphi[:len(handles)]
for i, handle := range handles {
handleInfos[i] = zx.HandleInfo{
Handle: handle,
Type: zx.ObjectTypeNone,
Rights: zx.RightSameRights,
}
}
return UnmarshalWithContext2(ctx, data, handleInfos, message)
}
const tagSizeV1 = "fidl_size_v1"
const tagAlignmentV1 = "fidl_alignment_v1"
const tagOffsetV1 = "fidl_offset_v1"
const tagHandleRights = "fidl_handle_rights"
const tagHandleSubtype = "fidl_handle_subtype"
const tagOrdinal = "fidl_ordinal"
const tagBounds = "fidl_bounds"
const tagMarshalerKind = "fidl"
// These rights come from FTP-028
// (all requests and interfaces have the same handle rights)
const ProtocolRights = zx.RightTransfer | zx.RightWait | zx.RightInspect | zx.RightWrite | zx.RightRead | zx.RightSignal | zx.RightSignalPeer
type tagKind int
const (
_ = iota
structTag tagKind = iota
xunionTag
strictXunionTag
tableTag
)
type bounds []int
func (b bounds) pop() (int, []int) {
if len(b) == 0 {
return math.MaxInt32, nil
}
return b[0], b[1:]
}
func nicefmt(typ reflect.Type) string {
typKindName := typ.Kind().String()
if typName := typ.Name(); len(typName) != 0 {
return typName + " (" + typKindName + ")"
}
return typKindName
}
// Extracts a list of fields for struct, union, table types that can be used
// for iterating.
// This skips the initial tag field.
func dataFields(typ reflect.Type) []reflect.StructField {
if typ.Kind() != reflect.Struct {
panic("expected struct")
}
if name := typ.Field(0).Name; name != "_" && !strings.HasPrefix(name, "I_") {
panic("expected first field to be a metadata field")
}
fields := make([]reflect.StructField, typ.NumField()-1)
for i := range fields {
fields[i] = typ.Field(i + 1)
}
return fields
}
// Identifies the fields in a union that store unknown data, and that therefore
// do not need to have a field marshaler created
func isUnknownDataField(fieldName string) bool {
return fieldName == "I_unknownData" || fieldName == "I_unknownHandles"
}
// Returns true if the go type has the same layout as the FIDL wire format.
func matchesWireFormatLayout(marshaler Marshaler, typ reflect.Type) bool {
switch marshaler := marshaler.(type) {
case mBool:
// Need to validate 0, 1.
return false
case mInt8, mInt16, mInt32, mInt64, mUint8, mUint16, mUint32, mUint64:
return true
case mEnumOfInt8, mEnumOfInt16, mEnumOfInt32, mEnumOfInt64:
// Need to validate.
return false
case mEnumOfUint8, mEnumOfUint16, mEnumOfUint32, mEnumOfUint64:
// Need to validate.
return false
case mBitsOfUint8, mBitsOfUint16, mBitsOfUint32, mBitsOfUint64:
// Need to validate.
return false
case mFloat32:
// Need to validate.
return false
case mFloat64:
// Need to validate.
return false
case mArray:
return matchesWireFormatLayout(marshaler.Marshaler, typ.Elem())
case mStructUnsafeCopy:
return true
case mStruct:
// Note: In some cases, go structs may be different size than wire format
// structs but otherwise identical. It may be possible to change this
// logic to allow a partial copy in the future.
if marshaler.size != int(typ.Size()) {
return false
}
if marshaler.alignment != typ.Align() {
return false
}
for i, rField := range dataFields(typ) {
field := marshaler.fields[i]
if field.offset != rField.Offset {
return false
}
if !matchesWireFormatLayout(field.Marshaler, rField.Type) {
return false
}
}
return true
case mEmptyStruct:
// Note: empty struct is 0 or 1 bytes at different times in go.
return false
case mHandle, mInterface:
// Note: In the future, we might instead consider treating handles
// like uint32 and decoding in a subsequent step.
return false
case mXUnion, mOptXUnion, mTable, mVector, mOptVector, mString, mOptString, mPointer:
return false
default:
panic("unhandledType " + reflect.TypeOf(marshaler).Name() + " for " + typ.String())
}
}
func createMarshaler(typ reflect.Type) (Marshaler, error) {
// field 0 holds the tag
tagField := typ.Field(0)
marshalerKind, err := readKindTag(tagField)
if err != nil {
return nil, err
}
var (
kind = marshalerKind
fields []mField
ordinals []uint64
wireOffsets []int
presenceOffsets []uintptr
)
// - structs, unions, and xunions have fields one after the other;
// - tables have a field, followed by a bool presence indicator, etc.
for index, field := range dataFields(typ) {
if (kind == xunionTag || kind == tableTag) && isUnknownDataField(field.Name) {
continue
}
if kind == tableTag && index%2 == 0 {
// Presence field
if field.Type.Kind() != reflect.Bool {
return nil, errors.New("incorrect presence field on " + nicefmt(typ))
}
presenceOffsets = append(presenceOffsets, field.Offset)
continue
}
fieldBounds := readBoundsTag(field)
handleRights, handleSubtype, err := readHandleRightsAndSubtype(field)
if err != nil {
return nil, err
}
switch kind {
case structTag:
offset, err := readIntTag(field, tagOffsetV1)
if err != nil {
return nil, err
}
wireOffsets = append(wireOffsets, offset)
case xunionTag, strictXunionTag, tableTag:
ordinal := readOrdinalTag(field)
ordinals = append(ordinals, uint64(ordinal))
default:
}
fieldMarshaler, err := createMarshalerForField(field.Type, fieldBounds, handleRights, handleSubtype)
if err != nil {
return nil, err
}
fields = append(fields, mField{fieldMarshaler, field.Index[0], field.Offset})
}
size, err := readIntTag(tagField, tagSizeV1)
if err != nil {
return nil, errors.New("error creating marshaller for " + typ.String() + ": " + err.Error())
}
alignment, err := readIntTag(tagField, tagAlignmentV1)
if err != nil {
return nil, errors.New("error creating marshaller for " + typ.String() + ": " + err.Error())
}
switch kind {
case structTag:
if len(fields) == 0 {
return mEmptyStruct{}, nil
}
var structFields []mFieldWithWireOffset
for i := 0; i < len(fields); i++ {
structFields = append(structFields, mFieldWithWireOffset{
mField: fields[i],
wireOffset: wireOffsets[i],
})
}
s := mStruct{
fields: structFields,
size: size,
alignment: alignment,
}
if matchesWireFormatLayout(s, typ) {
return mStructUnsafeCopy{
size: size,
}, nil
}
return s, nil
case xunionTag, strictXunionTag:
strictness := strictness(kind == strictXunionTag)
ordinalToFields := make(map[uint64]mField)
for i := 0; i < len(fields); i++ {
ordinalToFields[ordinals[i]] = fields[i]
}
return mXUnion{
typ: typ,
fields: ordinalToFields,
ordinals: ordinals,
size: size,
alignment: alignment,
strictness: strictness,
}, nil
case tableTag:
return mTable{
fields: fields,
presenceOffsets: presenceOffsets,
size: size,
alignment: alignment,
ordinals: ordinals,
}, nil
default:
return nil, errors.New("unknown kind tag on " + nicefmt(typ))
}
}
func readKindTag(field reflect.StructField) (tagKind, error) {
content, ok := field.Tag.Lookup(tagMarshalerKind)
if !ok {
return 0, errors.New(tagMarshalerKind + " not found on field " + field.Name)
}
switch content {
case "s":
return structTag, nil
case "x":
return xunionTag, nil
case "x!":
return strictXunionTag, nil
case "t":
return tableTag, nil
default:
return 0, errors.New("unknown kind tag: " + content)
}
}
func readHandleRightsAndSubtype(field reflect.StructField) (zx.Rights, zx.ObjectType, error) {
if !containsHandleType(field.Type) {
// Skip non-handle field types and don't return an error.
return zx.RightSameRights, zx.ObjectTypeNone, nil
}
// Read handle rights
val, ok := field.Tag.Lookup(tagHandleRights)
if !ok {
return zx.RightSameRights, zx.ObjectTypeNone, ErrUnspecifiedHandleRights
}
rights, err := strconv.ParseInt(val, 0, 64)
if err != nil {
return zx.RightSameRights, zx.ObjectTypeNone, err
}
convertedRights := zx.Rights(rights)
// Read handle subtype
val, ok = field.Tag.Lookup(tagHandleSubtype)
if !ok {
return zx.RightSameRights, zx.ObjectTypeNone, ErrUnspecifiedHandleType
}
subtype, err := strconv.ParseInt(val, 0, 64)
if err != nil {
return zx.RightSameRights, zx.ObjectTypeNone, err
}
convertedSubtype := zx.ObjectType(subtype)
return convertedRights, convertedSubtype, nil
}
func containsHandleType(typ reflect.Type) bool {
// Protocols and requests are technically handle types but their rights /
// subtypes are handled elsewhere.
if typ.ConvertibleTo(proxyType) || typ.ConvertibleTo(interfaceRequestType) {
return false
}
if isHandleType(typ) {
return true
}
if typ.Kind() == reflect.Slice || typ.Kind() == reflect.Ptr {
return containsHandleType(typ.Elem())
}
return false
}
func readOrdinalTag(field reflect.StructField) int {
ordinal, err := readIntTag(field, tagOrdinal)
if err != nil {
return math.MaxInt32
}
return ordinal
}
func readBoundsTag(field reflect.StructField) bounds {
content, ok := field.Tag.Lookup(tagBounds)
if !ok {
return nil
}
var nums []int
for _, elem := range strings.Split(content, ",") {
var (
num = math.MaxInt32
err error
)
if len(elem) != 0 {
num, err = strconv.Atoi(elem)
if err != nil {
panic(elem + ": " + err.Error())
}
}
nums = append(nums, num)
}
return bounds(nums)
}
func readIntTag(field reflect.StructField, tagKey string) (int, error) {
content, ok := field.Tag.Lookup(tagKey)
if !ok {
return 0, errors.New(tagKey + " not found on field " + field.Name)
}
res, err := strconv.ParseInt(content, 0, 64)
if err != nil {
return 0, errors.New("error parsing int body from tag " + tagKey + " " + err.Error())
}
return int(res), nil
}
func readHandleRightsTag(field reflect.StructField) (zx.Rights, error) {
val, ok := field.Tag.Lookup(tagHandleRights)
if !ok {
return zx.RightSameRights, nil
}
rights, err := strconv.ParseInt(val, 0, 64)
if err != nil {
return zx.RightSameRights, err
}
convertedRights := zx.Rights(rights)
return convertedRights, nil
}
func toInt8Map(values reflect.Value) map[int8]struct{} {
m := make(map[int8]struct{})
for i := 0; i < values.Len(); i++ {
value := values.Index(i)
m[int8(value.Int())] = struct{}{}
}
return m
}
func toInt16Map(values reflect.Value) map[int16]struct{} {
m := make(map[int16]struct{})
for i := 0; i < values.Len(); i++ {
value := values.Index(i)
m[int16(value.Int())] = struct{}{}
}
return m
}
func toInt32Map(values reflect.Value) map[int32]struct{} {
m := make(map[int32]struct{})
for i := 0; i < values.Len(); i++ {
value := values.Index(i)
m[int32(value.Int())] = struct{}{}
}
return m
}
func toInt64Map(values reflect.Value) map[int64]struct{} {
m := make(map[int64]struct{})
for i := 0; i < values.Len(); i++ {
value := values.Index(i)
m[int64(value.Int())] = struct{}{}
}
return m
}
func toUint8Map(values reflect.Value) map[uint8]struct{} {
m := make(map[uint8]struct{})
for i := 0; i < values.Len(); i++ {
value := values.Index(i)
m[uint8(value.Uint())] = struct{}{}
}
return m
}
func toUint16Map(values reflect.Value) map[uint16]struct{} {
m := make(map[uint16]struct{})
for i := 0; i < values.Len(); i++ {
value := values.Index(i)
m[uint16(value.Uint())] = struct{}{}
}
return m
}
func toUint32Map(values reflect.Value) map[uint32]struct{} {
m := make(map[uint32]struct{})
for i := 0; i < values.Len(); i++ {
value := values.Index(i)
m[uint32(value.Uint())] = struct{}{}
}
return m
}
func toUint64Map(values reflect.Value) map[uint64]struct{} {
m := make(map[uint64]struct{})
for i := 0; i < values.Len(); i++ {
value := values.Index(i)
m[uint64(value.Uint())] = struct{}{}
}
return m
}
func createMarshalerForField(typ reflect.Type, bounds bounds, handleRights zx.Rights, handleSubtype zx.ObjectType) (Marshaler, error) {
if isHandleType(typ) {
nullable, _ := bounds.pop()
return mHandle{nullable: nullable != 0, rights: handleRights, subtype: handleSubtype}, nil
}
if typ.ConvertibleTo(proxyType) || typ.ConvertibleTo(interfaceRequestType) {
nullable, _ := bounds.pop()
return mInterface{
nullable: nullable != 0,
rights: ProtocolRights,
subtype: zx.ObjectTypeChannel}, nil
}
if m, ok := typ.MethodByName("I_EnumValues"); ok {
values := m.Func.Call([]reflect.Value{reflect.Zero(typ)})[0]
var isStrict bool
if m, ok := typ.MethodByName("I_EnumIsStrict"); ok {
isStrict = m.Func.Call([]reflect.Value{reflect.Zero(typ)})[0].Interface().(bool)
} else {
return nil, errors.New("unable to create field marshaler for enum of " + nicefmt(typ) + ", missing I_EnumIsStrict method")
}
switch typ.Kind() {
case reflect.Int8:
return mEnumOfInt8{
isStrict: isStrict,
values: toInt8Map(values),
}, nil
case reflect.Int16:
return mEnumOfInt16{
isStrict: isStrict,
values: toInt16Map(values),
}, nil
case reflect.Int32:
return mEnumOfInt32{
isStrict: isStrict,
values: toInt32Map(values),
}, nil
case reflect.Int64:
return mEnumOfInt64{
isStrict: isStrict,
values: toInt64Map(values),
}, nil
case reflect.Uint8:
return mEnumOfUint8{
isStrict: isStrict,
values: toUint8Map(values),
}, nil
case reflect.Uint16:
return mEnumOfUint16{
isStrict: isStrict,
values: toUint16Map(values),
}, nil
case reflect.Uint32:
return mEnumOfUint32{
isStrict: isStrict,
values: toUint32Map(values),
}, nil
case reflect.Uint64:
return mEnumOfUint64{
isStrict: isStrict,
values: toUint64Map(values),
}, nil
default:
return nil, errors.New("unable to create field marshaler for enum of " + nicefmt(typ))
}
}
if m, ok := typ.MethodByName("I_BitsMask"); ok {
mask := m.Func.Call([]reflect.Value{reflect.Zero(typ)})[0]
var isStrict bool
if m, ok := typ.MethodByName("I_BitsIsStrict"); ok {
isStrict = m.Func.Call([]reflect.Value{reflect.Zero(typ)})[0].Interface().(bool)
} else {
return nil, errors.New("unable to create field marshaler for bits of " + nicefmt(typ) + ", missing I_BitsIsStrict method")
}
switch typ.Kind() {
case reflect.Uint8:
return mBitsOfUint8{
isStrict: isStrict,
mask: uint8(mask.Uint()),
}, nil
case reflect.Uint16:
return mBitsOfUint16{
isStrict: isStrict,
mask: uint16(mask.Uint()),
}, nil
case reflect.Uint32:
return mBitsOfUint32{
isStrict: isStrict,
mask: uint32(mask.Uint()),
}, nil
case reflect.Uint64:
return mBitsOfUint64{
isStrict: isStrict,
mask: uint64(mask.Uint()),
}, nil
default:
return nil, errors.New("unable to create field marshaler for bits of " + nicefmt(typ))
}
}
switch typ.Kind() {
case reflect.Bool:
return mBool{}, nil
case reflect.Int8:
return mInt8{}, nil
case reflect.Int16:
return mInt16{}, nil
case reflect.Int32:
return mInt32{}, nil
case reflect.Int64:
return mInt64{}, nil
case reflect.Uint8:
return mUint8{}, nil
case reflect.Uint16:
return mUint16{}, nil
case reflect.Uint32:
return mUint32{}, nil
case reflect.Uint64:
return mUint64{}, nil
case reflect.Float32:
return mFloat32{}, nil
case reflect.Float64:
return mFloat64{}, nil
case reflect.String:
maxSize, _ := bounds.pop()
return mString(maxSize), nil
case reflect.Array:
elemMarshaler, err := createMarshalerForField(typ.Elem(), bounds, handleRights, handleSubtype)
if err != nil {
return nil, err
}
return mArray{
Marshaler: elemMarshaler,
size: typ.Len(),
rtElemSize: typ.Elem().Size(),
}, nil
case reflect.Slice:
maxSize, remainder := bounds.pop()
elemTyp := typ.Elem()
elemMarshaler, err := createMarshalerForField(elemTyp, remainder, handleRights, handleSubtype)
if err != nil {
return nil, err
}
return mVector{
Marshaler: elemMarshaler,
maxSize: maxSize,
elemTyp: elemTyp,
useUnsafeCopy: matchesWireFormatLayout(elemMarshaler, elemTyp),
}, nil
case reflect.Struct:
return createMarshaler(typ)
case reflect.Ptr:
return createOptMarshalerForField(typ.Elem(), bounds, handleRights, handleSubtype)
default:
return nil, errors.New("unable to create field marshaler for " + nicefmt(typ))
}
}
func createOptMarshalerForField(typ reflect.Type, bounds bounds, handleRights zx.Rights, handleSubtype zx.ObjectType) (Marshaler, error) {
m, err := createMarshalerForField(typ, bounds, handleRights, handleSubtype)
if err != nil {
return nil, err
}
switch m := m.(type) {
case mString:
return mOptString(m), nil
case mVector:
return mOptVector{
mVector: m,
sliceTyp: typ,
}, nil
case mEmptyStruct:
return mPointer{
Marshaler: m,
elemTyp: typ,
}, nil
case mStruct, mStructUnsafeCopy:
return mPointer{
Marshaler: m,
elemTyp: typ,
}, nil
case mXUnion:
return mOptXUnion{
mXUnion: m,
typ: typ,
}, nil
default:
return nil, errors.New("unable to create optional field marshaler for " + nicefmt(typ))
}
}
// Message is implemented by any value that represents a FIDL message.
type Message interface {
Marshaler() Marshaler
}
type Marshaler interface {
// Marshal and unmarshal sizes can be different because they can be context dependent.
// e.g. it is possible to write a new format but still read the old format
getMarshalSize(ctx MarshalerContext) int
getUnmarshalSize(ctx MarshalerContext) int
marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error
unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error
}
// Assert various encoders implement the Marshaler interface.
var _ = []Marshaler{
mStruct{},
mStructUnsafeCopy{},
mEmptyStruct{},
mXUnion{},
mOptXUnion{},
mTable{},
mPointer{},
mArray{},
mVector{},
mOptVector{},
mBool{},
mInt8{},
mInt16{},
mInt32{},
mInt64{},
mUint8{},
mUint16{},
mUint32{},
mUint64{},
mEnumOfInt8{},
mEnumOfInt16{},
mEnumOfInt32{},
mEnumOfInt64{},
mEnumOfUint8{},
mEnumOfUint16{},
mEnumOfUint32{},
mEnumOfUint64{},
mBitsOfUint8{},
mBitsOfUint16{},
mBitsOfUint32{},
mBitsOfUint64{},
mFloat32{},
mFloat64{},
mString(0),
mOptString(0),
}
type mField struct {
Marshaler
index int
offset uintptr
}
type mFieldWithWireOffset struct {
mField
wireOffset int
}
type mStruct struct {
fields []mFieldWithWireOffset
size, alignment int
}
func (m mStruct) getMarshalSize(ctx MarshalerContext) int {
return m.size
}
func (m mStruct) getUnmarshalSize(ctx MarshalerContext) int {
return m.size
}
func (m mStruct) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
for _, field := range m.fields {
fieldOffset := offset + field.wireOffset
if err := field.Marshaler.marshal(ctx, v.StructFieldOffset(field.offset), out, fieldOffset); err != nil {
return err
}
}
return nil
}
func (m mStruct) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
for _, field := range m.fields {
fieldOffset := offset + field.wireOffset
if err := field.Marshaler.unmarshal(ctx, in, fieldOffset, v.StructFieldOffset(field.offset)); err != nil {
return err
}
}
return nil
}
type mStructUnsafeCopy struct {
size int
}
func (m mStructUnsafeCopy) getMarshalSize(ctx MarshalerContext) int {
return m.size
}
func (m mStructUnsafeCopy) getUnmarshalSize(ctx MarshalerContext) int {
return m.size
}
func (m mStructUnsafeCopy) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
// Directly copy the object's memory to the buffer.
sh := reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(v.UnsafeAddr())),
Len: m.size,
Cap: m.size,
}
s := *(*[]uint8)(unsafe.Pointer(&sh))
copy(out.buffer[offset:], s)
return nil
}
func (m mStructUnsafeCopy) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
// Directly copy from the buffer to the object's memory.
if len(in.buffer) < offset+m.size {
return ErrPayloadTooSmall
}
sh := reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(v.UnsafeAddr())),
Len: m.size,
Cap: m.size,
}
s := *(*[]uint8)(unsafe.Pointer(&sh))
copy(s, in.buffer[offset:])
return nil
}
type mEmptyStruct struct{}
func (_ mEmptyStruct) getMarshalSize(ctx MarshalerContext) int {
return 1
}
func (_ mEmptyStruct) getUnmarshalSize(ctx MarshalerContext) int {
return 1
}
func (_ mEmptyStruct) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint8(offset, 0)
return nil
}
func (_ mEmptyStruct) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
if structByte := in.readUint8(offset); structByte != 0 {
return newValueError(ErrInvalidEmptyStruct, structByte)
}
return nil
}
type mXUnion struct {
typ reflect.Type
fields map[uint64]mField
ordinals []uint64
size, alignment int
strictness
}
func (m mXUnion) getMarshalSize(ctx MarshalerContext) int {
return m.size
}
func (m mXUnion) getUnmarshalSize(ctx MarshalerContext) int {
return m.size
}
func (m mXUnion) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
var ordinal uint64
ordinal = v.StructFieldOffset(0).Uint64()
field, ok := m.fields[ordinal]
out.writeUint64(offset, ordinal)
if !ok {
if m.strictness == isStrict {
return newValueError(ErrInvalidXUnionTag, ordinal)
}
bytesFld, bytesOk := m.typ.FieldByName("I_unknownData")
handlesFld, handlesOk := m.typ.FieldByName("I_unknownHandles")
if bytesOk && handlesOk {
marshalEnvelopeUnknown(
out,
offset+8,
v.StructFieldOffset(bytesFld.Offset).Bytes(),
v.StructFieldOffset(handlesFld.Offset).HandleInfos())
}
return nil
}
// Field.
if err := marshalEnvelopePresent(ctx, field, v.StructFieldOffset(field.offset), out, offset+8); err != nil {
return err
}
return nil
}
func (m mXUnion) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
return m.unmarshalWithOptSpecified(ctx, in, offset, v, nil)
}
func (m mXUnion) unmarshalWithOptSpecified(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value, typ reflect.Type) error {
// Can this envelope be optional? If it can, the type must be provided
// in order to reflectively create a pointer container.
optAllowed := typ != nil
ordinal := in.readUint64(offset)
// ordinal=0 indicates that there MUST be no envelope.
if ordinal == 0 {
if !optAllowed {
return newValueError(ErrInvalidXUnionTag, ordinal)
}
h, err := unmarshalEnvelopeHeader(in, offset+8)
if err != nil {
return err
}
if h.isPresent() {
return newValueError(ErrInvalidXUnionTag, ordinal)
}
return nil
}
// If we reach here, ordinal != 0.
field, ok := m.fields[ordinal]
if !ok {
if m.strictness == isStrict {
// Best effort.
_, unknownHandles, err := unmarshalEnvelopeUnknown(in, offset+8)
if err == nil {
for i := range unknownHandles {
_ = handleClose(&unknownHandles[i].Handle)
}
}
return newValueError(ErrInvalidXUnionTag, ordinal)
}
v.StructFieldOffset(0).SetUint64(ordinal)
unknownData, unknownHandles, err := unmarshalEnvelopeUnknown(in, offset+8)
if err != nil {
return err
}
fld, ok := m.typ.FieldByName("I_unknownData")
if ok {
v.StructFieldOffset(fld.Offset).SetBytes(unknownData)
}
fld, ok = m.typ.FieldByName("I_unknownHandles")
if ok {
v.StructFieldOffset(fld.Offset).SetHandleInfos(unknownHandles)
}
return nil
}
if optAllowed {
v.PointerSetNew(m.typ)
v = v.PointerElem()
}
ordinalOrFieldIndex := ordinal
v.StructFieldOffset(0).SetUint64(ordinalOrFieldIndex)
var mode unmarshalEnvelopeMode
if optAllowed {
mode = knownMayBeAbsent
} else {
mode = knownMustBePresent
}
isPresent, err := unmarshalEnvelope(ctx, field.Marshaler, in, offset+8, v.StructFieldOffset(field.offset), mode)
if err != nil {
return err
}
if !isPresent {
v.PointerSetNil()
}
return nil
}
type mOptXUnion struct {
mXUnion
typ reflect.Type
}
func (m mOptXUnion) getMarshalSize(ctx MarshalerContext) int {
return m.mXUnion.getMarshalSize(ctx)
}
func (m mOptXUnion) getUnmarshalSize(ctx MarshalerContext) int {
return m.mXUnion.getUnmarshalSize(ctx)
}
func (m mOptXUnion) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if v.PointerIsNil() {
out.writeUint64(offset, 0) // ordinal + padding
marshalEnvelopeAbsent(out, offset+8)
return nil
} else {
return m.mXUnion.marshal(ctx, v.PointerElem(), out, offset)
}
}
func (m mOptXUnion) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
return m.unmarshalWithOptSpecified(ctx, in, offset, v, m.typ)
}
// This assumes that I_unknownData in tables is always the first non-zero
// sized member.
const unknownTableDataOffset = 0
type mTable struct {
typ reflect.Type
fields []mField
presenceOffsets []uintptr
ordinals []uint64
size, alignment int
}
func (m mTable) getMarshalSize(ctx MarshalerContext) int {
return m.size
}
func (m mTable) getUnmarshalSize(ctx MarshalerContext) int {
return m.size
}
const envelopeSize = 16
type envelopeHeader struct {
byteCount uint32
handleCount uint32
presence uint64
}
func (h envelopeHeader) isPresent() bool {
return h.presence == allocPresent
}
func marshalEnvelopePresent(ctx MarshalerContext, m Marshaler, v unsafevalue.Value, out *encoder, offset int) error {
numHandleDispositions := len(out.handleDispositions)
numBytes := len(out.buffer)
outOfLineOffset := out.newObject(m.getMarshalSize(ctx))
if err := m.marshal(ctx, v, out, outOfLineOffset); err != nil {
return err
}
numHandleDispositions = len(out.handleDispositions) - numHandleDispositions
numBytes = len(out.buffer) - numBytes
out.writeUint32(offset, uint32(numBytes))
out.writeUint32(offset+4, uint32(numHandleDispositions))
out.writeUint64(offset+8, allocPresent)
return nil
}
func marshalEnvelopeAbsent(out *encoder, offset int) {
out.writeUint64(offset, 0) // both numBytes, and numHandleDispositions
out.writeUint64(offset+8, noAlloc)
}
func marshalEnvelopeUnknown(out *encoder, offset int, unknownData []byte, unknownHandles []zx.HandleInfo) {
outOfLineOffset := out.newObject(len(unknownData))
copy(out.buffer[outOfLineOffset:], unknownData)
for _, info := range unknownHandles {
out.handleDispositions = append(out.handleDispositions, zx.HandleDisposition{
Operation: zx.HandleOpMove,
Handle: info.Handle,
Type: info.Type,
Rights: info.Rights,
Result: zx.ErrOk,
})
}
out.writeUint32(offset, uint32(len(unknownData)))
out.writeUint32(offset+4, uint32(len(unknownHandles)))
out.writeUint64(offset+8, allocPresent)
}
type unmarshalEnvelopeMode int
const (
_ unmarshalEnvelopeMode = iota
// knownMayBeAbsent indicates that the content of the envelope is known,
// and that it may be absent, i.e. encountering an empty envelope is
// expected
knownMayBeAbsent
// knownMustBePresent indicates that the content of the envelope is known,
// and that it must be present, i.e. encountering an empty envelope
// should be considered a failure
knownMustBePresent
)
func unmarshalEnvelopeHeader(in *decoder, offset int) (envelopeHeader, error) {
h := envelopeHeader{
byteCount: in.readUint32(offset),
handleCount: in.readUint32(offset + 4),
presence: in.readUint64(offset + 8),
}
switch h.presence {
case allocPresent, noAlloc:
default:
return h, newValueError(ErrBadRefEncoding, h)
}
if h.handleCount > uint32(len(in.handleInfos)) {
return h, newValueError(ErrTooManyHandles, h)
}
return h, nil
}
func unmarshalEnvelopeUnknown(in *decoder, offset int) ([]byte, []zx.HandleInfo, error) {
header, err := unmarshalEnvelopeHeader(in, offset)
if err != nil {
return nil, nil, err
}
var unknownHandles []zx.HandleInfo
if header.handleCount != 0 {
// Slice the end off first; in Go, slicing the head is a more conservative
// operation than slicing the tail because the tail is allowed to reach
// into capacity, but the head may only use length. Slicing the head first
// allows the compiler to elide bounds checks on all the subsequent code.
//
// Even slicing from zero incurs a bounds check, hence the above check.
usedHandles := in.handleInfos[header.handleCount:]
// The compiler's bounds check elimination is not smart enough to avoid
// bounds checks in the loop body without this local variable.
unknownHandles = in.handleInfos[:header.handleCount]
in.handleInfos = usedHandles
}
start, err := in.newObject(int(header.byteCount))
if err != nil {
return nil, nil, err
}
unknownData := in.buffer[start:][:header.byteCount]
return unknownData, unknownHandles, nil
}
func unmarshalEnvelopeContent(ctx MarshalerContext, header envelopeHeader, m Marshaler, in *decoder, v unsafevalue.Value, mode unmarshalEnvelopeMode) (bool, error) {
outOfLineOffset, err := in.newObject(m.getUnmarshalSize(ctx))
if err != nil {
return false, err
}
if err := m.unmarshal(ctx, in, outOfLineOffset, v); err != nil {
return false, err
}
return true, nil
}
func unmarshalEnvelope(ctx MarshalerContext, m Marshaler, in *decoder, offset int, v unsafevalue.Value, mode unmarshalEnvelopeMode) (bool, error) {
header, err := unmarshalEnvelopeHeader(in, offset)
if err != nil {
return false, err
}
if !header.isPresent() {
if mode == knownMustBePresent {
return false, newValueError(ErrUnexpectedNullRef, v)
}
if header.byteCount != 0 {
return false, newValueError(ErrUnexpectedNumBytes, header.byteCount)
}
if header.handleCount != 0 {
return false, newValueError(ErrUnexpectedNumHandles, header.handleCount)
}
return false, nil
}
return unmarshalEnvelopeContent(ctx, header, m, in, v, mode)
}
func (m mTable) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
var (
maxOrdinal uint64
numKnown = len(m.ordinals)
fieldPresent = make([]bool, numKnown)
unknownData map[uint64]UnknownData
)
rawUnknownData := v.StructFieldOffset(unknownTableDataOffset).Interface()
if rawUnknownData != nil {
var ok bool
unknownData, ok = rawUnknownData.(map[uint64]UnknownData)
if !ok {
return errorf("internal error (unexpected I_unknownData type {})", rawUnknownData)
}
// Determining max ordinal from unknown ordinals.
for unknownOrdinal := range unknownData {
if maxOrdinal < unknownOrdinal {
maxOrdinal = unknownOrdinal
}
}
}
// Determining max ordinal from known ordinals.
for index := 0; index < numKnown; index++ {
fieldPresent[index] = v.StructFieldOffset(m.presenceOffsets[index]).Bool()
if fieldPresent[index] {
if fieldOrdinal := m.ordinals[index]; maxOrdinal < fieldOrdinal {
maxOrdinal = fieldOrdinal
}
}
}
// Vector of envelopes header.
out.writeUint64(offset, maxOrdinal)
out.writeUint64(offset+8, allocPresent)
// Early exit on empty table.
if maxOrdinal == 0 {
return nil
}
// Encode in the out-of-line object.
outOfLineOffset := out.newObject(int(maxOrdinal) * envelopeSize)
// Envelopes.
var (
ordinal uint64 = 1
index = 0
)
envelopeOffset := outOfLineOffset
for ordinal <= maxOrdinal {
fieldKnown := index < numKnown && ordinal == m.ordinals[index]
if fieldKnown && fieldPresent[index] {
if fieldPresent[index] {
if err := marshalEnvelopePresent(ctx, m.fields[index], v.StructFieldOffset(m.fields[index].offset), out, envelopeOffset); err != nil {
return err
}
} else {
// This else clause is redundant with the else clause in the top level if statement but
// saves a map lookup in the common case (all fields are known)
marshalEnvelopeAbsent(out, envelopeOffset)
}
} else if unknownField, ok := unknownData[ordinal]; ok {
marshalEnvelopeUnknown(
out,
envelopeOffset,
unknownField.Bytes,
unknownField.Handles,
)
} else {
marshalEnvelopeAbsent(out, envelopeOffset)
}
ordinal++
envelopeOffset += 16
if fieldKnown {
index++
}
}
return nil
}
func (m mTable) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
mou := in.readUint64(offset)
// uints/ints are only guaranteed to be 32 bit longs.
// we use maxOrdinal as an int, so we must make sure that it fits.
if mou > uint64(^uint(0)) {
return newValueError(ErrUnexpectedOrdinal, v)
}
maxOrdinal := mou
switch allocPtr := in.readUint64(offset + 8); allocPtr {
case allocPresent:
// good
case noAlloc:
return newValueError(ErrUnexpectedNullRef, v)
default:
return newValueError(ErrBadRefEncoding, v)
}
// Early exit on empty table.
if maxOrdinal == 0 {
return nil
}
// Envelopes.
var (
numKnown = len(m.ordinals)
ordinal uint64 = 1
index = 0
unknownData map[uint64]UnknownData
)
outOfLineOffset, err := in.newObject(int(maxOrdinal) * envelopeSize)
if err != nil {
return err
}
envelopeOffset := outOfLineOffset
for ordinal <= maxOrdinal {
fieldKnown := index < numKnown && ordinal == m.ordinals[index]
if fieldKnown {
if isPresent, err := unmarshalEnvelope(ctx, m.fields[index], in, envelopeOffset, v.StructFieldOffset(m.fields[index].offset), knownMayBeAbsent); err != nil {
return err
} else if isPresent {
v.StructFieldOffset(m.presenceOffsets[index]).SetBool(true)
}
} else {
bytes, handles, err := unmarshalEnvelopeUnknown(in, envelopeOffset)
if err != nil {
return err
}
if len(bytes) > 0 || len(handles) > 0 {
if unknownData == nil {
unknownData = make(map[uint64]UnknownData)
v.StructFieldOffset(unknownTableDataOffset).SetInterface(unknownData)
}
unknownData[ordinal] = UnknownData{
Bytes: bytes,
Handles: handles,
}
}
}
ordinal++
envelopeOffset += 16
if fieldKnown {
index++
}
}
return nil
}
type mPointer struct {
Marshaler
elemTyp reflect.Type
}
func (m mPointer) getMarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mPointer) getUnmarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mPointer) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
// Nil?
if v.PointerIsNil() {
out.writeUint64(offset, noAlloc)
return nil
}
// Write out allocation marker.
out.writeUint64(offset, allocPresent)
// Set up the out-of-line space.
outOfLineOffset := out.newObject(align(m.Marshaler.getMarshalSize(ctx), 8))
// Marshal field.
if err := m.Marshaler.marshal(ctx, v.PointerElem(), out, outOfLineOffset); err != nil {
return err
}
return nil
}
func (m mPointer) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
// Nil?
switch ptr := in.readUint64(offset); ptr {
case noAlloc:
v.PointerSetNil()
return nil
case allocPresent:
// good
default:
return newValueError(ErrBadRefEncoding, v)
}
// Create the new struct.
v.PointerSetNew(m.elemTyp)
// Set up the out-of-line space and the head.
outOfLineOffset, err := in.newObject(m.Marshaler.getUnmarshalSize(ctx))
if err != nil {
return err
}
// Unmarshal field.
if err := m.Marshaler.unmarshal(ctx, in, outOfLineOffset, v.PointerElem()); err != nil {
return err
}
return nil
}
type mOptUnion struct {
mPointer
mOptXUnion
}
func (m mOptUnion) getMarshalSize(ctx MarshalerContext) int {
return 24
}
func (m mOptUnion) getUnmarshalSize(ctx MarshalerContext) int {
return 24
}
func (m mOptUnion) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
return m.mOptXUnion.marshal(ctx, v, out, offset)
}
func (m mOptUnion) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
return m.mOptXUnion.unmarshal(ctx, in, offset, v)
}
type mArray struct {
Marshaler
rtElemSize uintptr
size int
}
func (m mArray) getMarshalSize(ctx MarshalerContext) int {
return m.size * m.Marshaler.getMarshalSize(ctx)
}
func (m mArray) getUnmarshalSize(ctx MarshalerContext) int {
return m.size * m.Marshaler.getUnmarshalSize(ctx)
}
func (m mArray) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
elemSize := m.Marshaler.getMarshalSize(ctx)
for i, len := 0, m.size; i < len; i++ {
if err := m.Marshaler.marshal(ctx, v.ArrayIndex(m.rtElemSize, i), out, offset+i*elemSize); err != nil {
return err
}
}
return nil
}
func (m mArray) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
elemSize := m.Marshaler.getUnmarshalSize(ctx)
for i, len := 0, m.size; i < len; i++ {
if err := m.Marshaler.unmarshal(ctx, in, offset+i*elemSize, v.ArrayIndex(m.rtElemSize, i)); err != nil {
return err
}
}
return nil
}
type mVector struct {
Marshaler
maxSize int
elemTyp reflect.Type
useUnsafeCopy bool
}
func (m mVector) getMarshalSize(ctx MarshalerContext) int {
return 16
}
func (m mVector) getUnmarshalSize(ctx MarshalerContext) int {
return 16
}
func (m mVector) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
// Bounds check.
vLen := v.SliceLen()
if m.maxSize < vLen {
return newExpectError(ErrVectorTooLong, m.maxSize, vLen)
}
// Vector header.
out.writeUint64(offset, uint64(vLen))
out.writeUint64(offset+8, allocPresent)
// Early exit if the vector is empty.
if vLen == 0 {
return nil
}
// Encode in the out-of-line object.
outOfLineOffset := out.newObject(vLen * m.Marshaler.getMarshalSize(ctx))
// Marshal elements.
wireElemSize := m.Marshaler.getMarshalSize(ctx)
if m.useUnsafeCopy {
var bytes []byte
sh := (*reflect.SliceHeader)(unsafe.Pointer(&bytes))
sh.Data = (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr())).Data
sh.Len = vLen * wireElemSize
sh.Cap = vLen * wireElemSize
copy(out.buffer[outOfLineOffset:], bytes)
} else {
elemTypSize := m.elemTyp.Size()
for i := 0; i < vLen; i++ {
if err := m.Marshaler.marshal(ctx, v.SliceIndex(elemTypSize, i), out, outOfLineOffset+i*wireElemSize); err != nil {
return err
}
}
}
return nil
}
func (m mVector) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
size := in.readUint64(offset)
ptr := in.readUint64(offset + 8)
switch ptr {
case noAlloc:
return newValueError(ErrUnexpectedNullRef, v)
case allocPresent:
return m.unmarshalWithUncheckedSize(ctx, in, v, int(size))
default:
return newValueError(ErrBadRefEncoding, v)
}
}
func (m mVector) unmarshalWithUncheckedSize(ctx MarshalerContext, in *decoder, v unsafevalue.Value, size int) error {
if size < 0 || m.maxSize < size {
return newExpectError(ErrVectorTooLong, m.maxSize, size)
}
// Unmarshal in the out-of-line object.
elemSize := m.Marshaler.getUnmarshalSize(ctx)
outOfLineOffset, err := in.newObject(size * elemSize)
if err != nil {
return err
}
// Unmarshal elements.
if m.useUnsafeCopy {
// Copy the data as bytes, then construct a slice header with the appropriate size
// for the slice type (if it is not bytes).
s := make([]byte, size*m.Marshaler.getUnmarshalSize(ctx))
copy(s, in.buffer[outOfLineOffset:])
sh := (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr()))
sh.Data = (*reflect.SliceHeader)(unsafe.Pointer(&s)).Data
sh.Len = size
sh.Cap = size
} else {
elemTypSize := m.elemTyp.Size()
v.SliceSetMakeSlice(m.elemTyp, size, size)
for i := 0; i < size; i++ {
if err := m.Marshaler.unmarshal(ctx, in, outOfLineOffset+i*elemSize, v.SliceIndex(elemTypSize, i)); err != nil {
return err
}
}
}
return nil
}
type mOptVector struct {
mVector
sliceTyp reflect.Type
}
func (m mOptVector) getMarshalSize(ctx MarshalerContext) int {
return 16
}
func (m mOptVector) getUnmarshalSize(ctx MarshalerContext) int {
return 16
}
func (m mOptVector) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if v.PointerIsNil() {
out.writeUint64(offset, 0)
out.writeUint64(offset+8, noAlloc)
return nil
}
return m.mVector.marshal(ctx, v.PointerElem(), out, offset)
}
func (m mOptVector) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
size := in.readUint64(offset)
ptr := in.readUint64(offset + 8)
switch ptr {
case noAlloc:
v.PointerSetNil()
return nil
case allocPresent:
v.PointerSetNew(m.sliceTyp)
return m.mVector.unmarshalWithUncheckedSize(ctx, in, v.PointerElem(), int(size))
default:
return newValueError(ErrBadRefEncoding, v)
}
}
type mBool struct{}
func (m mBool) getMarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mBool) getUnmarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mBool) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if v.Bool() {
out.writeUint8(offset, 1)
} else {
out.writeUint8(offset, 0)
}
return nil
}
func (m mBool) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
b := in.readUint8(offset)
switch b {
case 0, 1:
v.SetBool(b == 1)
return nil
default:
return newValueError(ErrInvalidBoolValue, b)
}
}
type mInt8 struct{}
func (m mInt8) getMarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mInt8) getUnmarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mInt8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint8(offset, uint8(v.Int8()))
return nil
}
func (m mInt8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
v.SetInt8(int8(in.readUint8(offset)))
return nil
}
type mInt16 struct{}
func (m mInt16) getMarshalSize(ctx MarshalerContext) int {
return 2
}
func (m mInt16) getUnmarshalSize(ctx MarshalerContext) int {
return 2
}
func (m mInt16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint16(offset, uint16(v.Int16()))
return nil
}
func (m mInt16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
v.SetInt16(int16(in.readUint16(offset)))
return nil
}
type mInt32 struct{}
func (m mInt32) getMarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mInt32) getUnmarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mInt32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint32(offset, uint32(v.Int32()))
return nil
}
func (m mInt32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
v.SetInt32(int32(in.readUint32(offset)))
return nil
}
type mInt64 struct{}
func (m mInt64) getMarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mInt64) getUnmarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mInt64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint64(offset, uint64(v.Int64()))
return nil
}
func (m mInt64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
v.SetInt64(int64(in.readUint64(offset)))
return nil
}
type mUint8 struct{}
func (m mUint8) getMarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mUint8) getUnmarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mUint8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint8(offset, v.Uint8())
return nil
}
func (m mUint8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
v.SetUint8(in.readUint8(offset))
return nil
}
type mUint16 struct{}
func (m mUint16) getMarshalSize(ctx MarshalerContext) int {
return 2
}
func (m mUint16) getUnmarshalSize(ctx MarshalerContext) int {
return 2
}
func (m mUint16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint16(offset, v.Uint16())
return nil
}
func (m mUint16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
v.SetUint16(in.readUint16(offset))
return nil
}
type mUint32 struct{}
func (m mUint32) getMarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mUint32) getUnmarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mUint32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint32(offset, v.Uint32())
return nil
}
func (m mUint32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
v.SetUint32(in.readUint32(offset))
return nil
}
type mUint64 struct{}
func (m mUint64) getMarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mUint64) getUnmarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mUint64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint64(offset, v.Uint64())
return nil
}
func (m mUint64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
v.SetUint64(in.readUint64(offset))
return nil
}
type mEnumOfInt8 struct {
isStrict bool
values map[int8]struct{}
}
func (m mEnumOfInt8) getMarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mEnumOfInt8) getUnmarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mEnumOfInt8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if _, ok := m.values[v.Int8()]; !ok {
return newValueError(ErrInvalidEnumValue, v.Int8())
}
}
out.writeUint8(offset, uint8(v.Int8()))
return nil
}
func (m mEnumOfInt8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := int8(in.readUint8(offset))
if m.isStrict {
if _, ok := m.values[i]; !ok {
return newValueError(ErrInvalidEnumValue, i)
}
}
v.SetInt8(i)
return nil
}
type mEnumOfInt16 struct {
isStrict bool
values map[int16]struct{}
}
func (m mEnumOfInt16) getMarshalSize(ctx MarshalerContext) int {
return 2
}
func (m mEnumOfInt16) getUnmarshalSize(ctx MarshalerContext) int {
return 2
}
func (m mEnumOfInt16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if _, ok := m.values[v.Int16()]; !ok {
return newValueError(ErrInvalidEnumValue, v.Int16())
}
}
out.writeUint16(offset, uint16(v.Int16()))
return nil
}
func (m mEnumOfInt16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := int16(in.readUint16(offset))
if m.isStrict {
if _, ok := m.values[i]; !ok {
return newValueError(ErrInvalidEnumValue, i)
}
}
v.SetInt16(i)
return nil
}
type mEnumOfInt32 struct {
isStrict bool
values map[int32]struct{}
}
func (m mEnumOfInt32) getMarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mEnumOfInt32) getUnmarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mEnumOfInt32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if _, ok := m.values[v.Int32()]; !ok {
return newValueError(ErrInvalidEnumValue, v.Int32())
}
}
out.writeUint32(offset, uint32(v.Int32()))
return nil
}
func (m mEnumOfInt32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := int32(in.readUint32(offset))
if m.isStrict {
if _, ok := m.values[i]; !ok {
return newValueError(ErrInvalidEnumValue, i)
}
}
v.SetInt32(i)
return nil
}
type mEnumOfInt64 struct {
isStrict bool
values map[int64]struct{}
}
func (m mEnumOfInt64) getMarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mEnumOfInt64) getUnmarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mEnumOfInt64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if _, ok := m.values[v.Int64()]; !ok {
return newValueError(ErrInvalidEnumValue, v.Int64())
}
}
out.writeUint64(offset, uint64(v.Int64()))
return nil
}
func (m mEnumOfInt64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := int64(in.readUint64(offset))
if m.isStrict {
if _, ok := m.values[i]; !ok {
return newValueError(ErrInvalidEnumValue, i)
}
}
v.SetInt64(i)
return nil
}
type mEnumOfUint8 struct {
isStrict bool
values map[uint8]struct{}
}
func (m mEnumOfUint8) getMarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mEnumOfUint8) getUnmarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mEnumOfUint8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if _, ok := m.values[v.Uint8()]; !ok {
return newValueError(ErrInvalidEnumValue, v.Uint8())
}
}
out.writeUint8(offset, v.Uint8())
return nil
}
func (m mEnumOfUint8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := in.readUint8(offset)
if m.isStrict {
if _, ok := m.values[i]; !ok {
return newValueError(ErrInvalidEnumValue, i)
}
}
v.SetUint8(i)
return nil
}
type mEnumOfUint16 struct {
isStrict bool
values map[uint16]struct{}
}
func (m mEnumOfUint16) getMarshalSize(ctx MarshalerContext) int {
return 2
}
func (m mEnumOfUint16) getUnmarshalSize(ctx MarshalerContext) int {
return 2
}
func (m mEnumOfUint16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if _, ok := m.values[v.Uint16()]; !ok {
return newValueError(ErrInvalidEnumValue, v.Uint16())
}
}
out.writeUint16(offset, v.Uint16())
return nil
}
func (m mEnumOfUint16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := in.readUint16(offset)
if m.isStrict {
if _, ok := m.values[i]; !ok {
return newValueError(ErrInvalidEnumValue, i)
}
}
v.SetUint16(i)
return nil
}
type mEnumOfUint32 struct {
isStrict bool
values map[uint32]struct{}
}
func (m mEnumOfUint32) getMarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mEnumOfUint32) getUnmarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mEnumOfUint32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if _, ok := m.values[v.Uint32()]; !ok {
return newValueError(ErrInvalidEnumValue, v.Uint32())
}
}
out.writeUint32(offset, v.Uint32())
return nil
}
func (m mEnumOfUint32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := in.readUint32(offset)
if m.isStrict {
if _, ok := m.values[i]; !ok {
return newValueError(ErrInvalidEnumValue, i)
}
}
v.SetUint32(i)
return nil
}
type mEnumOfUint64 struct {
isStrict bool
values map[uint64]struct{}
}
func (m mEnumOfUint64) getMarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mEnumOfUint64) getUnmarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mEnumOfUint64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if _, ok := m.values[v.Uint64()]; !ok {
return newValueError(ErrInvalidEnumValue, v.Uint64())
}
}
out.writeUint64(offset, v.Uint64())
return nil
}
func (m mEnumOfUint64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := in.readUint64(offset)
if m.isStrict {
if _, ok := m.values[i]; !ok {
return newValueError(ErrInvalidEnumValue, i)
}
}
v.SetUint64(i)
return nil
}
type mBitsOfUint8 struct {
isStrict bool
mask uint8
}
func (m mBitsOfUint8) getMarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mBitsOfUint8) getUnmarshalSize(ctx MarshalerContext) int {
return 1
}
func (m mBitsOfUint8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if (v.Uint8() & m.mask) != v.Uint8() {
return newValueError(ErrInvalidBitsValue, v.Uint8())
}
}
out.writeUint8(offset, v.Uint8())
return nil
}
func (m mBitsOfUint8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := in.readUint8(offset)
if m.isStrict {
if (i & m.mask) != i {
return newValueError(ErrInvalidBitsValue, i)
}
}
v.SetUint8(i)
return nil
}
type mBitsOfUint16 struct {
isStrict bool
mask uint16
}
func (m mBitsOfUint16) getMarshalSize(ctx MarshalerContext) int {
return 2
}
func (m mBitsOfUint16) getUnmarshalSize(ctx MarshalerContext) int {
return 2
}
func (m mBitsOfUint16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if (v.Uint16() & m.mask) != v.Uint16() {
return newValueError(ErrInvalidBitsValue, v.Uint16())
}
}
out.writeUint16(offset, v.Uint16())
return nil
}
func (m mBitsOfUint16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := in.readUint16(offset)
if m.isStrict {
if (i & m.mask) != i {
return newValueError(ErrInvalidBitsValue, i)
}
}
v.SetUint16(i)
return nil
}
type mBitsOfUint32 struct {
isStrict bool
mask uint32
}
func (m mBitsOfUint32) getMarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mBitsOfUint32) getUnmarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mBitsOfUint32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if (v.Uint32() & m.mask) != v.Uint32() {
return newValueError(ErrInvalidBitsValue, v.Uint32())
}
}
out.writeUint32(offset, v.Uint32())
return nil
}
func (m mBitsOfUint32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := in.readUint32(offset)
if m.isStrict {
if (i & m.mask) != i {
return newValueError(ErrInvalidBitsValue, i)
}
}
v.SetUint32(i)
return nil
}
type mBitsOfUint64 struct {
isStrict bool
mask uint64
}
func (m mBitsOfUint64) getMarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mBitsOfUint64) getUnmarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mBitsOfUint64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if m.isStrict {
if (v.Uint64() & m.mask) != v.Uint64() {
return newValueError(ErrInvalidBitsValue, v.Uint64())
}
}
out.writeUint64(offset, v.Uint64())
return nil
}
func (m mBitsOfUint64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
i := in.readUint64(offset)
if m.isStrict {
if (i & m.mask) != i {
return newValueError(ErrInvalidBitsValue, i)
}
}
v.SetUint64(i)
return nil
}
type mFloat32 struct{}
func (m mFloat32) getMarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mFloat32) getUnmarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mFloat32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint32(offset, math.Float32bits(v.Float32()))
return nil
}
func (m mFloat32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
v.SetFloat32(math.Float32frombits(in.readUint32(offset)))
return nil
}
type mFloat64 struct{}
func (m mFloat64) getMarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mFloat64) getUnmarshalSize(ctx MarshalerContext) int {
return 8
}
func (m mFloat64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
out.writeUint64(offset, math.Float64bits(v.Float64()))
return nil
}
func (m mFloat64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
v.SetFloat64(math.Float64frombits(in.readUint64(offset)))
return nil
}
type mString int
func (m mString) getMarshalSize(ctx MarshalerContext) int {
return 16
}
func (m mString) getUnmarshalSize(ctx MarshalerContext) int {
return 16
}
func (m mString) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
var (
maxSize = int(m)
s = v.String()
length = len(s)
)
if maxSize < length {
return newExpectError(ErrStringTooLong, maxSize, length)
}
if !utf8.ValidString(s) {
return newValueError(ErrStringNotUTF8, v)
}
// length, allocPresent
out.writeUint64(offset, uint64(length))
out.writeUint64(offset+8, allocPresent)
// Create a new out-of-line object and write bytes of the string.
head := out.newObject(length)
copy(out.buffer[head:], s)
return nil
}
func (m mString) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
size := in.readUint64(offset)
ptr := in.readUint64(offset + 8)
switch ptr {
case noAlloc:
return newValueError(ErrUnexpectedNullRef, "string")
case allocPresent:
return m.unmarshalWithUncheckedSize(in, v, int(size))
default:
return newValueError(ErrBadRefEncoding, "string")
}
}
func (m mString) unmarshalWithUncheckedSize(in *decoder, v unsafevalue.Value, size int) error {
if maxSize := int(m); size < 0 || maxSize < size {
return newExpectError(ErrStringTooLong, maxSize, size)
}
start, err := in.newObject(size)
if err != nil {
return err
}
s := string(in.buffer[start:][:size])
if !utf8.ValidString(s) {
return newValueError(ErrStringNotUTF8, v)
}
v.SetString(s)
return nil
}
type mOptString uint64
func (m mOptString) getMarshalSize(ctx MarshalerContext) int {
return 16
}
func (m mOptString) getUnmarshalSize(ctx MarshalerContext) int {
return 16
}
func (m mOptString) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
if v.PointerIsNil() {
out.writeUint64(offset, 0)
out.writeUint64(offset+8, noAlloc)
return nil
}
return mString(m).marshal(ctx, v.PointerElem(), out, offset)
}
var (
typString = reflect.TypeOf("")
)
func (m mOptString) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
size := in.readUint64(offset)
ptr := in.readUint64(offset + 8)
switch ptr {
case noAlloc:
v.PointerSetNil()
return nil
case allocPresent:
v.PointerSetNew(typString)
return mString(m).unmarshalWithUncheckedSize(in, v.PointerElem(), int(size))
default:
return newValueError(ErrBadRefEncoding, v)
}
}
type mHandle struct {
nullable bool
rights zx.Rights
subtype zx.ObjectType
}
func (m mHandle) getMarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mHandle) getUnmarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mHandle) isOpt() bool {
return m.nullable
}
func (m mHandle) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
// The underlying type of all the handles is a uint32, so we're
// safe calling Uint(). This will panic if that is no longer true.
raw := zx.Handle(v.Uint32())
if raw == zx.HandleInvalid {
if !m.isOpt() {
return ErrUnexpectedNullHandle
}
out.writeUint32(offset, noHandle)
return nil
}
out.handleDispositions = append(out.handleDispositions, zx.HandleDisposition{
Operation: zx.HandleOpMove,
Handle: raw,
Type: m.subtype,
Rights: m.rights,
Result: zx.ErrOk,
})
out.writeUint32(offset, handlePresent)
return nil
}
func (m mHandle) requiredRightsArePresent(actual zx.Rights) bool {
if m.rights == zx.RightSameRights {
return true
}
return actual.SupersetOf(m.rights)
}
func (m mHandle) filterOutUnspecifiedRights(actual zx.Rights) zx.Rights {
if m.rights == zx.RightSameRights {
return actual
}
return actual & m.rights
}
func (m mHandle) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
h := in.readUint32(offset)
switch uint32(h) {
case noHandle:
if !m.isOpt() {
return ErrUnexpectedNullHandle
}
v.SetUint32(uint32(zx.HandleInvalid))
return nil
case handlePresent:
if len(in.handleInfos) == 0 {
return ErrNotEnoughHandles
}
handleInfo := in.handleInfos[0]
in.handleInfos = in.handleInfos[1:]
if !m.requiredRightsArePresent(handleInfo.Rights) {
return newExpectError(ErrMissingRequiredHandleRights, m.rights, handleInfo.Rights)
}
handle := handleInfo.Handle
reducedRights := m.filterOutUnspecifiedRights(handleInfo.Rights)
if handleInfo.Rights != reducedRights {
handle, err := handleReplace(handle, reducedRights)
if err != nil {
return newValueError(ErrUnableToReduceHandleRights, handle)
}
}
if m.subtype != zx.ObjectTypeNone && m.subtype != handleInfo.Type {
return newExpectError(ErrIncorrectHandleType, m.subtype, handleInfo.Type)
}
v.SetUint32(uint32(handle))
return nil
default:
return newValueError(ErrBadHandleEncoding, h)
}
}
// An interface is represented by a Proxy, whose first field is
// a zx.Channel, and we can just marshal that. Same goes for an
// interface request, which is just an InterfaceRequest whose
// first field is a zx.Channel.
type mInterface mHandle
func (m mInterface) getMarshalSize(ctx MarshalerContext) int {
return mHandle(m).getMarshalSize(ctx)
}
func (m mInterface) getUnmarshalSize(ctx MarshalerContext) int {
return mHandle(m).getUnmarshalSize(ctx)
}
func (m mInterface) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
return mHandle(m).marshal(ctx, v.StructFieldOffset(0), out, offset)
}
func (m mInterface) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
return mHandle(m).unmarshal(ctx, in, offset, v.StructFieldOffset(0))
}