blob: 650243d3419193e78289f32e65b8d114b304128c [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.
// +build fuchsia
package fidl
import (
"errors"
"math"
"reflect"
"strconv"
"strings"
"sync"
"syscall/zx"
"unicode/utf8"
)
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 reflect.Value, out *encoder) error {
m.once.Do(m.init)
return m.delegate.marshal(ctx, v, out)
}
func (m *lazyMarshaler) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
m.once.Do(m.init)
return m.delegate.unmarshal(ctx, in, v)
}
// Marshal marshals (or encodes) a message into the data and handles slices.
func Marshal(message Message, data []byte, handles []zx.Handle) (int, int, error) {
return MarshalWithContext(newCtx(), message, data, handles)
}
// Marshal marshals (or encodes) a message into the data and handles slices.
func MarshalWithContext(ctx MarshalerContext, message Message, data []byte, handles []zx.Handle) (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 = reflect.ValueOf(message).Elem()
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], handles: handles[:0]}
out.head = out.newObject(m.getMarshalSize(ctx))
if err := m.marshal(ctx, v, out); err != nil {
return 0, 0, err
}
return len(out.buffer), len(out.handles), nil
}
// Unmarshal unmarshals (or decodes) into message using the data and handles
// slices.
func Unmarshal(data []byte, handles []zx.Handle, message Message) (int, int, error) {
return UnmarshalWithContext(newCtx(), data, handles, message)
}
func UnmarshalWithContext(ctx MarshalerContext, data []byte, handles []zx.Handle, 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 = reflect.ValueOf(message).Elem()
m = message.Marshaler()
)
// Get the payload's value and unmarshal it.
nextObject := align(m.getUnmarshalSize(ctx), 8)
in := &decoder{
buffer: data,
handles: handles,
nextObject: nextObject,
}
if err := m.unmarshal(ctx, in, v); err != nil {
return 0, 0, err
}
return in.nextObject, len(handles) - len(in.handles), nil
}
const tagKey = "fidl"
const tagSizeV1 = "fidl_size_v1"
const tagAlignmentV1 = "fidl_alignment_v1"
const tagOffsetV1 = "fidl_offset_v1"
type tagKind int
const (
_ = iota
structTag tagKind = iota
unionTag
xunionTag
strictXunionTag
tableTag
boundsTag
)
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 {
if len(typ.Name()) == 0 {
return typ.Kind().String()
} else {
return typ.Name() + " (" + typ.Kind().String() + ")"
}
}
func createMarshaler(typ reflect.Type) (Marshaler, error) {
// field 0 holds the tag
tagField := typ.Field(0)
marshalerKind, marshalerBounds := readTag(tagField)
var (
kind = marshalerKind
sizeOld = marshalerBounds[0]
alignmentOld = marshalerBounds[1]
fields []mField
ordinals []int
offsetsOld []int
offsetsV1 []int
)
// field 1 and up are the actual data fields
// - structs, unions, and xunions have fields one after the other;
// - tables have a field, followed by a bool presence indicator, etc.
for index := 1; index < typ.NumField(); index++ {
field := typ.Field(index)
_, fieldBounds := readTag(field)
switch kind {
case structTag:
offsetOld, actualBounds := fieldBounds.pop()
offsetsOld = append(offsetsOld, offsetOld)
offsetV1, err := readOffsetV1(field)
if err != nil {
return nil, err
}
offsetsV1 = append(offsetsV1, offsetV1)
fieldBounds = actualBounds
case unionTag, xunionTag, strictXunionTag, tableTag:
ordinal, actualBounds := fieldBounds.pop()
ordinals = append(ordinals, ordinal)
fieldBounds = actualBounds
default:
}
fieldMarshaler, err := createMarshalerForField(field.Type, fieldBounds)
if err != nil {
return nil, err
}
fields = append(fields, mField{fieldMarshaler, index})
if kind == tableTag {
if typ.Field(index+1).Type.Kind() != reflect.Bool {
return nil, errors.New("incorrect presence field on " + nicefmt(typ))
}
index++ // i.e. skip presence field.
}
}
sizeV1, err := readIntTag(tagField, tagSizeV1)
if err != nil {
return nil, errors.New("error creating marshaller for " + typ.String() + ": " + err.Error())
}
alignmentV1, 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 []mFieldWithOffset
for i := 0; i < len(fields); i++ {
structFields = append(structFields, mFieldWithOffset{
mField: fields[i],
offsetOld: offsetsOld[i],
offsetV1: offsetsV1[i],
})
}
return mStruct{
fields: structFields,
sizeOld: sizeOld,
alignmentOld: alignmentOld,
sizeV1: sizeV1,
alignmentV1: alignmentV1,
}, nil
case unionTag:
m := mUnion{
fields: fields,
ordinals: ordinals,
sizeOld: sizeOld,
alignmentOld: alignmentOld,
sizeV1: sizeV1,
alignmentV1: alignmentV1,
}
m.mXUnion = mUnionToMXUnion(m)
return m, nil
case xunionTag, strictXunionTag:
strictness := strictness(kind == strictXunionTag)
ordinalToFields := make(map[int]mField)
for i := 0; i < len(fields); i++ {
ordinalToFields[ordinals[i]] = fields[i]
}
return mXUnion{
fields: ordinalToFields,
sizeOld: sizeOld,
alignmentOld: alignmentOld,
sizeV1: sizeV1,
alignmentV1: alignmentV1,
strictness: strictness,
}, nil
case tableTag:
return mTable{
fields: fields,
sizeOld: sizeOld,
alignmentOld: alignmentOld,
sizeV1: sizeV1,
alignmentV1: alignmentV1,
ordinals: ordinals,
}, nil
default:
return nil, errors.New("missing kind marker on " + nicefmt(typ))
}
}
func mUnionToMXUnion(m mUnion) mXUnion {
ordinalToFields := make(map[int]mField)
for i := 0; i < len(m.fields); i++ {
ordinalToFields[m.ordinals[i]] = m.fields[i]
}
return mXUnion{
fields: ordinalToFields,
unionToXunion: true,
sizeV1: m.sizeV1,
alignmentV1: m.alignmentV1,
strictness: true,
}
}
func readIntTag(field reflect.StructField, tagKey string) (int, error) {
content, ok := field.Tag.Lookup(tagKey)
if !ok {
return 0, errors.New("tag " + tagKey + " not found")
}
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
}
// readTag reads a fidl tag which can be one of
//
// s,size,alignment -- marking a struct, with its size, and alignment
// u,size,alignment -- marking a union, with its size, and alignment
// x,size,alignment -- marking a flexible xunion, with its size, and alignment
// x!,size,alignment -- marking a strict xunion, with its size, and alignment
// t,size,alignment -- marking a table, with its size, and alignment
// num1, num2, ... -- recording bounds of the types present on the field
// ! num1, num2, ... -- strict, recording bounds of the types present on the field
//
// When handles or interfaces are present, the bounds indicate nullability,
// with 0 indicating false (not nullable), and any other value true (nullable).
func readTag(field reflect.StructField) (tagKind, bounds) {
content, ok := field.Tag.Lookup(tagKey)
if !ok {
return boundsTag, nil
}
elems := strings.Split(content, ",")
switch elems[0] {
case "s":
return structTag, toBounds(elems[1:])
case "u":
return unionTag, toBounds(elems[1:])
case "x":
return xunionTag, toBounds(elems[1:])
case "x!":
return strictXunionTag, toBounds(elems[1:])
case "t":
return tableTag, toBounds(elems[1:])
default:
return boundsTag, toBounds(elems)
}
}
func readOffsetV1(field reflect.StructField) (int, error) {
val, ok := field.Tag.Lookup(tagOffsetV1)
if !ok {
return 0, errors.New(tagOffsetV1 + " not found on field " + field.Name)
}
offset, err := strconv.ParseInt(val, 0, 64)
if err != nil {
return 0, err
}
return int(offset), nil
}
func toBounds(elems []string) bounds {
var nums []int
for _, elem := range elems {
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 createMarshalerForField(typ reflect.Type, bounds bounds) (Marshaler, error) {
if isHandleType(typ) {
// TODO(pascallouis): Check that handles' subtype conform?
nullable, _ := bounds.pop()
return mHandle(nullable != 0), nil
}
if isInterfaceType(typ) || isInterfaceRequestType(typ) {
nullable, _ := bounds.pop()
return mInterface(nullable != 0), nil
}
switch typ.Kind() {
case reflect.Bool:
return mBool{}, nil
case reflect.Int8:
return mInt(1), nil
case reflect.Int16:
return mInt(2), nil
case reflect.Int32:
return mInt(4), nil
case reflect.Int64:
return mInt(8), nil
case reflect.Uint8:
return mUint(1), nil
case reflect.Uint16:
return mUint(2), nil
case reflect.Uint32:
return mUint(4), nil
case reflect.Uint64:
return mUint(8), 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)
if err != nil {
return nil, err
}
return mArray{
Marshaler: elemMarshaler,
size: typ.Len(),
}, nil
case reflect.Slice:
maxSize, remainder := bounds.pop()
elemTyp := typ.Elem()
elemMarshaler, err := createMarshalerForField(elemTyp, remainder)
if err != nil {
return nil, err
}
return mVector{
Marshaler: elemMarshaler,
maxSize: maxSize,
sliceTyp: reflect.SliceOf(elemTyp),
isVectorUint8: elemTyp.Kind() == reflect.Uint8,
}, nil
case reflect.Struct:
return createMarshaler(typ)
case reflect.Ptr:
return createOptMarshalerForField(typ.Elem(), bounds)
default:
return nil, errors.New("unable to create field marshaler for " + nicefmt(typ))
}
}
func createOptMarshalerForField(typ reflect.Type, bounds bounds) (Marshaler, error) {
m, err := createMarshalerForField(typ, bounds)
if err != nil {
return nil, err
}
switch m := m.(type) {
case mString:
return mOptString(m), nil
case mVector:
return mOptVector(m), nil
case mEmptyStruct:
return mPointer{
Marshaler: m,
elemTyp: typ,
}, nil
case mStruct:
return mPointer{
Marshaler: m,
elemTyp: typ,
}, nil
case mUnion:
return mOptUnion{
mPointer: mPointer{
Marshaler: m,
elemTyp: typ,
},
mOptXUnion: mOptXUnion{
mXUnion: m.mXUnion,
typ: 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 MarshalerContext struct {
DecodeUnionsFromXUnionBytes bool
EncodeUnionsAsXUnionBytes bool
}
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 reflect.Value, out *encoder) error
unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error
}
// Assert various encoders implement the Marshaler interface.
var _ = []Marshaler{
mStruct{},
mEmptyStruct{},
mUnion{},
mXUnion{},
mOptXUnion{},
mTable{},
mPointer{},
mOptUnion{},
mArray{},
mVector{},
mOptVector{},
mBool{},
mInt(0),
mUint(0),
mFloat32{},
mFloat64{},
mString(0),
mOptString(0),
}
type mField struct {
Marshaler
index int
}
type mFieldWithOffset struct {
mField
offsetOld int
offsetV1 int
}
func (m mFieldWithOffset) getMarshalOffset(ctx MarshalerContext) int {
if ctx.EncodeUnionsAsXUnionBytes {
return m.offsetV1
}
return m.offsetOld
}
func (m mFieldWithOffset) getUnmarshalOffset(ctx MarshalerContext) int {
if ctx.DecodeUnionsFromXUnionBytes {
return m.offsetV1
}
return m.offsetOld
}
type mStruct struct {
fields []mFieldWithOffset
sizeOld, alignmentOld int
sizeV1, alignmentV1 int
}
func (m mStruct) getMarshalSize(ctx MarshalerContext) int {
if ctx.EncodeUnionsAsXUnionBytes {
return m.sizeV1
}
return m.sizeOld
}
func (m mStruct) getUnmarshalSize(ctx MarshalerContext) int {
if ctx.DecodeUnionsFromXUnionBytes {
return m.sizeV1
}
return m.sizeOld
}
func (m mStruct) getMarshalAlignment(ctx MarshalerContext) int {
if ctx.EncodeUnionsAsXUnionBytes {
return m.alignmentV1
}
return m.alignmentOld
}
func (m mStruct) getUnmarshalAlignment(ctx MarshalerContext) int {
if ctx.DecodeUnionsFromXUnionBytes {
return m.alignmentV1
}
return m.alignmentOld
}
func (m mStruct) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
structStart := align(out.head, m.getMarshalAlignment(ctx))
for _, field := range m.fields {
out.head = structStart + field.getMarshalOffset(ctx)
if err := field.Marshaler.marshal(ctx, v.Field(field.index), out); err != nil {
return err
}
}
out.head = align(out.head, m.getMarshalAlignment(ctx))
return nil
}
func (m mStruct) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
structStart := align(in.head, m.getUnmarshalAlignment(ctx))
for _, field := range m.fields {
in.head = structStart + field.getUnmarshalOffset(ctx)
if err := field.Marshaler.unmarshal(ctx, in, v.Field(field.index)); err != nil {
return err
}
}
in.head = align(in.head, m.getUnmarshalAlignment(ctx))
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 reflect.Value, out *encoder) error {
out.writeUint(0, 1)
return nil
}
func (_ mEmptyStruct) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
zero, err := in.readUint(1)
if err != nil {
return err
} else if zero != 0 {
return newValueError(ErrInvalidEmptyStruct, zero)
}
return nil
}
type mUnion struct {
fields []mField
ordinals []int
sizeOld, alignmentOld int
sizeV1, alignmentV1 int
mXUnion
}
func (m mUnion) getMarshalSize(ctx MarshalerContext) int {
if ctx.EncodeUnionsAsXUnionBytes {
return m.sizeV1
}
return m.sizeOld
}
func (m mUnion) getUnmarshalSize(ctx MarshalerContext) int {
if ctx.DecodeUnionsFromXUnionBytes {
return m.sizeV1
}
return m.sizeOld
}
func (m mUnion) getMarshalAlignment(ctx MarshalerContext) int {
if ctx.EncodeUnionsAsXUnionBytes {
return m.alignmentV1
}
return m.alignmentOld
}
func (m mUnion) getUnmarshalAlignment(ctx MarshalerContext) int {
if ctx.DecodeUnionsFromXUnionBytes {
return m.alignmentV1
}
return m.alignmentOld
}
func (m mUnion) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
if ctx.EncodeUnionsAsXUnionBytes {
return m.mXUnion.marshal(ctx, v, out)
}
// Kind.
kind := int(v.Field(0).Uint()) - 1
if kind < 0 || len(m.fields) <= kind {
return newValueError(ErrInvalidUnionTag, kind)
}
// Save the head for proper padding.
head := out.head
out.writeUint(uint64(kind), 4)
// Re-align to the union's alignment before writing its field.
out.head = align(out.head, m.getMarshalAlignment(ctx))
// Marshal field.
if err := m.fields[kind].Marshaler.marshal(ctx, v.Field(kind+1), out); err != nil {
return err
}
// Re-position head.
out.head = head + m.getMarshalSize(ctx)
return nil
}
func (m mUnion) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
if ctx.DecodeUnionsFromXUnionBytes {
return m.mXUnion.unmarshal(ctx, in, v)
}
// Save the head for proper padding.
head := in.head
// Kind.
kind, err := in.readUint(4)
if err != nil {
return err
}
if uint64(len(m.fields)) <= kind {
return newValueError(ErrInvalidUnionTag, kind)
}
v.Field(0).SetUint(kind + 1)
// Re-align to the union's alignment before writing its field.
in.head = align(in.head, m.getUnmarshalAlignment(ctx))
// Unmarshal field.
ikind := int(kind)
if err := m.fields[ikind].Marshaler.unmarshal(ctx, in, v.Field(ikind+1)); err != nil {
return err
}
// Re-position head.
in.head = head + m.getUnmarshalSize(ctx)
return nil
}
type mXUnion struct {
fields map[int]mField
unionToXunion bool
sizeOld, alignmentOld int
sizeV1, alignmentV1 int
strictness
}
func (m mXUnion) getMarshalSize(ctx MarshalerContext) int {
if ctx.EncodeUnionsAsXUnionBytes {
return m.sizeV1
}
return m.sizeOld
}
func (m mXUnion) getUnmarshalSize(ctx MarshalerContext) int {
if ctx.DecodeUnionsFromXUnionBytes {
return m.sizeV1
}
return m.sizeOld
}
func (m mXUnion) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
// Ordinal.
ordinal := int(v.Field(0).Uint())
field, ok := m.fields[ordinal]
if !ok {
return newValueError(ErrInvalidXUnionTag, ordinal)
}
out.writeUint(uint64(ordinal), 8)
// Field.
if err := marshalEnvelopePresent(ctx, field, v.Field(field.index), out); err != nil {
return err
}
return nil
}
func (m mXUnion) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
return m.unmarshalWithOptSpecified(ctx, in, v, nil)
}
func (m mXUnion) unmarshalWithOptSpecified(ctx MarshalerContext, in *decoder, v reflect.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, err := in.readUint(4)
if err != nil {
return err
}
padding, err := in.readUint(4)
if err != nil {
return err
}
if padding != 0 {
return newValueError(ErrNonZeroPadding, padding)
}
// ordinal=0 indicates that there MUST be no envelope.
if ordinal == 0 && !optAllowed {
return newValueError(ErrInvalidXUnionTag, ordinal)
} else if ordinal == 0 && optAllowed {
h, err := unmarshalEnvelopeHeader(in)
if err != nil {
return err
}
if h.isPresent() {
return newValueError(ErrInvalidXUnionTag, ordinal)
}
return nil
}
// If we reach here, ordinal != 0.
field, ok := m.fields[int(ordinal)]
if !ok {
if m.strictness == isStrict {
return newValueError(ErrInvalidXUnionTag, ordinal)
}
v.Field(0).SetUint(ordinal)
unknownData, err := unmarshalEnvelopeUnknown(in)
if err != nil {
return err
}
unknownDataField := v.FieldByName("I_unknownData")
// * FieldByName() returns a zero Value, which is _non-settable_ & non-addressable, if the
// field isn't found.
// * So, we use .CanSet() below on the returned field to check whether the field exists.
// * This enables this code to properly work with FIDL-generated Go bindings that hasn't
// been updated with the UnknownValue field yet.
// * This enables this code to land as a soft transition
// <https://fuchsia.googlesource.com/fuchsia/+/master/docs/development/workflows/multilayer_changes.md#hard-and-soft-transitions>.
if unknownDataField.CanSet() {
unknownDataField.SetBytes(unknownData)
}
return nil
}
if optAllowed {
v.Set(reflect.New(typ))
v = v.Elem()
}
ordinalToSet := ordinal
if m.unionToXunion {
ordinalToSet = uint64(field.index)
}
v.Field(0).SetUint(ordinalToSet)
var mode unmarshalEnvelopeMode
if optAllowed {
mode = knownMayBeAbsent
} else {
mode = knownMustBePresent
}
isPresent, err := unmarshalEnvelope(ctx, field.Marshaler, in, v.Field(field.index), mode)
if err != nil {
return err
}
if !isPresent {
v.Set(reflect.Zero(reflect.PtrTo(typ)))
}
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 reflect.Value, out *encoder) error {
if v.IsNil() {
out.writeUint(0, 8) // ordinal + padding
marshalEnvelopeAbsent(out)
return nil
} else {
return m.mXUnion.marshal(ctx, v.Elem(), out)
}
}
func (m mOptXUnion) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
return m.unmarshalWithOptSpecified(ctx, in, v, m.typ)
}
type mTable struct {
fields []mField
ordinals []int
sizeOld, alignmentOld int
sizeV1, alignmentV1 int
}
func (m mTable) getMarshalSize(ctx MarshalerContext) int {
if ctx.EncodeUnionsAsXUnionBytes {
return m.sizeV1
}
return m.sizeOld
}
func (m mTable) getUnmarshalSize(ctx MarshalerContext) int {
if ctx.DecodeUnionsFromXUnionBytes {
return m.sizeV1
}
return m.sizeOld
}
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 reflect.Value, out *encoder) error {
numHandles := len(out.handles)
numBytes := len(out.buffer)
head := out.head
out.head = out.newObject(m.getMarshalSize(ctx))
if err := m.marshal(ctx, v, out); err != nil {
return err
}
numHandles = len(out.handles) - numHandles
numBytes = len(out.buffer) - numBytes
out.head = head
out.writeUint(uint64(numBytes), 4)
out.writeUint(uint64(numHandles), 4)
out.writeUint(allocPresent, 8)
return nil
}
func marshalEnvelopeAbsent(out *encoder) {
out.writeUint(0, 8) // both numBytes, and numHandles
out.writeUint(noAlloc, 8)
}
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) (envelopeHeader, error) {
var h envelopeHeader
byteCount, err := in.readUint(4)
if err != nil {
return h, err
}
h.byteCount = uint32(byteCount)
handleCount, err := in.readUint(4)
if err != nil {
return h, err
}
h.handleCount = uint32(handleCount)
h.presence, err = in.readUint(8)
if err != nil {
return h, err
}
switch h.presence {
case allocPresent, noAlloc:
break
default:
return h, newValueError(ErrBadRefEncoding, h)
}
if end := in.nextObject + int(h.byteCount); end < 0 || end > len(in.buffer) {
return h, newValueError(ErrEnvelopeTooLong, h)
}
if h.handleCount > uint32(len(in.handles)) {
return h, newValueError(ErrTooManyHandles, h)
}
return h, nil
}
func unmarshalEnvelopeUnknown(in *decoder) ([]byte, error) {
header, err := unmarshalEnvelopeHeader(in)
if err != nil {
return nil, err
}
for i := uint32(0); i < header.handleCount; i++ {
in.handles[0].Close() // best effort
in.handles = in.handles[1:]
}
// Don't need to check for overflow when calculating the end of the envelope, since
// unmarshalEnvelopeHeader() does that.
start := in.nextObject
end := in.nextObject + int(header.byteCount)
unknownData := in.buffer[start:end]
in.nextObject = end
return unknownData, nil
}
func unmarshalEnvelopeContent(ctx MarshalerContext, header envelopeHeader, m Marshaler, in *decoder, v reflect.Value, mode unmarshalEnvelopeMode) (bool, error) {
savedHead := in.head
in.head = in.nextObject
in.nextObject += align(m.getUnmarshalSize(ctx), 8)
if err := m.unmarshal(ctx, in, v); err != nil {
return false, err
}
in.head = savedHead
return true, nil
}
func unmarshalEnvelope(ctx MarshalerContext, m Marshaler, in *decoder, v reflect.Value, mode unmarshalEnvelopeMode) (bool, error) {
header, err := unmarshalEnvelopeHeader(in)
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 reflect.Value, out *encoder) error {
// Determining max ordinal.
var (
maxOrdinal int
numKnown = len(m.ordinals)
fieldPresent = make([]bool, numKnown)
)
for index := 0; index < numKnown; index++ {
presenceIndex := index*2 + 2
fieldPresent[index] = v.Field(presenceIndex).Bool()
if fieldPresent[index] {
if fieldOrdinal := m.ordinals[index]; maxOrdinal < fieldOrdinal {
maxOrdinal = fieldOrdinal
}
}
}
// Vector of envelopes header.
out.writeUint(uint64(maxOrdinal), 8)
out.writeUint(allocPresent, 8)
// Early exit on empty table.
if maxOrdinal == 0 {
return nil
}
// Encode in the out-of-line object.
oldHead := out.head
out.head = out.newObject(maxOrdinal * envelopeSize)
// Envelopes.
var (
ordinal = 1
index, fieldIndex, presenceIndex = 0, 1, 2
)
for ordinal <= maxOrdinal {
fieldKnown := index < numKnown && ordinal == m.ordinals[index]
if fieldKnown && fieldPresent[index] {
if err := marshalEnvelopePresent(ctx, m.fields[index], v.Field(fieldIndex), out); err != nil {
return err
}
} else {
marshalEnvelopeAbsent(out)
}
ordinal++
if fieldKnown {
index++
fieldIndex += 2 // i.e. skip presenece field
presenceIndex += 2 // i.e. skip field
}
}
// Re-position head.
out.head = oldHead
return nil
}
func (m mTable) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
mou, err := in.readUint(8)
if err != nil {
return err
}
// 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 := int(mou)
allocPtr, err := in.readUint(8)
if err != nil {
return err
}
switch 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 = 1
index, fieldIndex, presenceIndex = 0, 1, 2
)
tableBodyHead := in.nextObject
in.nextObject += maxOrdinal * envelopeSize
oldHead := in.head
in.head = tableBodyHead
for ordinal <= maxOrdinal {
fieldKnown := index < numKnown && ordinal == m.ordinals[index]
if fieldKnown {
if isPresent, err := unmarshalEnvelope(ctx, m.fields[index], in, v.Field(fieldIndex), knownMayBeAbsent); err != nil {
return err
} else if isPresent {
v.Field(presenceIndex).SetBool(true)
}
} else {
if _, err := unmarshalEnvelopeUnknown(in); err != nil {
return err
}
}
ordinal++
if fieldKnown {
index++
fieldIndex += 2 // i.e skip presence field
presenceIndex += 2 // i.e skip field
}
}
in.head = oldHead
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 reflect.Value, out *encoder) error {
// Nil?
if v.IsNil() {
out.writeUint(noAlloc, 8)
return nil
}
// Write out allocation marker.
out.writeUint(allocPresent, 8)
// Set up the out-of-line space and the head.
oldHead := out.head
out.head = out.newObject(align(m.Marshaler.getMarshalSize(ctx), 8))
// Marshal field.
if err := m.Marshaler.marshal(ctx, v.Elem(), out); err != nil {
return err
}
// Re-position head.
out.head = oldHead
return nil
}
func (m mPointer) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
// Nil?
ptr, err := in.readUint(8)
if err != nil {
return err
}
switch ptr {
case noAlloc:
v.Set(reflect.Zero(v.Type()))
return nil
case allocPresent:
// good
default:
return newValueError(ErrBadRefEncoding, v)
}
// Create the new struct.
v.Set(reflect.New(m.elemTyp))
// Set up the out-of-line space and the head.
oldHead := in.head
in.head = in.nextObject
in.nextObject += align(m.Marshaler.getUnmarshalSize(ctx), 8)
// Unmarshal field.
if err := m.Marshaler.unmarshal(ctx, in, v.Elem()); err != nil {
return err
}
// Re-position head.
in.head = oldHead
return nil
}
type mOptUnion struct {
mPointer
mOptXUnion
}
func (m mOptUnion) getMarshalSize(ctx MarshalerContext) int {
if ctx.EncodeUnionsAsXUnionBytes {
return 24
}
return 8
}
func (m mOptUnion) getUnmarshalSize(ctx MarshalerContext) int {
if ctx.DecodeUnionsFromXUnionBytes {
return 24
}
return 8
}
func (m mOptUnion) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
if ctx.EncodeUnionsAsXUnionBytes {
return m.mOptXUnion.marshal(ctx, v, out)
} else {
return m.mPointer.marshal(ctx, v, out)
}
}
func (m mOptUnion) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
if ctx.DecodeUnionsFromXUnionBytes {
return m.mOptXUnion.unmarshal(ctx, in, v)
} else {
return m.mPointer.unmarshal(ctx, in, v)
}
}
type mArray struct {
Marshaler
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 reflect.Value, out *encoder) error {
for i, len := 0, v.Len(); i < len; i++ {
if err := m.Marshaler.marshal(ctx, v.Index(i), out); err != nil {
return err
}
}
return nil
}
func (m mArray) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
for i, len := 0, v.Len(); i < len; i++ {
if err := m.Marshaler.unmarshal(ctx, in, v.Index(i)); err != nil {
return err
}
}
return nil
}
type mVector struct {
Marshaler
maxSize int
sliceTyp reflect.Type
isVectorUint8 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 reflect.Value, out *encoder) error {
// Bounds check.
vLen := v.Len()
if m.maxSize < vLen {
return newExpectError(ErrVectorTooLong, m.maxSize, vLen)
}
// Vector header.
out.writeUint(uint64(vLen), 8)
out.writeUint(allocPresent, 8)
// Early exit if the vector is empty.
if vLen == 0 {
return nil
}
// Encode in the out-of-line object.
oldHead := out.head
out.head = out.newObject(vLen * m.Marshaler.getMarshalSize(ctx))
// Marshal elements.
if m.isVectorUint8 {
copy(out.buffer[out.head:], v.Bytes())
} else {
for i := 0; i < vLen; i++ {
if err := m.Marshaler.marshal(ctx, v.Index(i), out); err != nil {
return err
}
}
}
// Re-position head.
out.head = oldHead
return nil
}
func (m mVector) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
size, err := in.readUint(8)
if err != nil {
return err
}
ptr, err := in.readUint(8)
if err != nil {
return err
}
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 reflect.Value, size int) error {
if size < 0 || m.maxSize < size {
return newExpectError(ErrVectorTooLong, m.maxSize, size)
}
// Unmarshal in the out-of-line object.
oldHead := in.head
in.head = in.nextObject
elemSize := m.Marshaler.getUnmarshalSize(ctx)
in.nextObject += align(size*elemSize, 8)
// Unmarshal elements.
if m.isVectorUint8 {
data := make([]uint8, size, size)
copy(data, in.buffer[in.head:])
v.Set(reflect.ValueOf(data))
} else {
v.Set(reflect.MakeSlice(m.sliceTyp, size, size))
for i := 0; i < size; i++ {
if err := m.Marshaler.unmarshal(ctx, in, v.Index(i)); err != nil {
return err
}
}
}
// Re-position head.
in.head = oldHead
return nil
}
type mOptVector mVector
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 reflect.Value, out *encoder) error {
if v.IsNil() {
out.writeUint(0, 8)
out.writeUint(noAlloc, 8)
return nil
}
return mVector(m).marshal(ctx, v.Elem(), out)
}
func (m mOptVector) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
size, err := in.readUint(8)
if err != nil {
return err
}
ptr, err := in.readUint(8)
if err != nil {
return err
}
switch ptr {
case noAlloc:
v.Set(reflect.Zero(reflect.PtrTo(m.sliceTyp)))
return nil
case allocPresent:
v.Set(reflect.New(m.sliceTyp))
return mVector(m).unmarshalWithUncheckedSize(ctx, in, v.Elem(), 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 reflect.Value, out *encoder) error {
if v.Bool() {
out.writeUint(1, 1)
} else {
out.writeUint(0, 1)
}
return nil
}
func (m mBool) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
b, err := in.readUint(1)
if err != nil {
return err
}
switch b {
case 0, 1:
v.SetBool(b == 1)
return nil
default:
return newValueError(ErrInvalidBoolValue, b)
}
}
// int is size (1 for int8, 2 for int16, etc.)
type mInt int
func (m mInt) getMarshalSize(ctx MarshalerContext) int {
return int(m)
}
func (m mInt) getUnmarshalSize(ctx MarshalerContext) int {
return int(m)
}
func (m mInt) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
size := int(m)
out.writeUint(uint64(v.Int()), size)
return nil
}
func (m mInt) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
size := int(m)
val, err := in.readUint(size)
if err != nil {
return err
}
v.SetInt(int64(val))
return nil
}
// uint is size (1 for uint8, 2 f or uint16, etc.)
type mUint int
func (m mUint) getMarshalSize(ctx MarshalerContext) int {
return int(m)
}
func (m mUint) getUnmarshalSize(ctx MarshalerContext) int {
return int(m)
}
func (m mUint) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
size := int(m)
out.writeUint(v.Uint(), size)
return nil
}
func (m mUint) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
size := int(m)
val, err := in.readUint(size)
if err != nil {
return err
}
v.SetUint(val)
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 reflect.Value, out *encoder) error {
out.writeUint(uint64(math.Float32bits(float32(v.Float()))), 4)
return nil
}
func (m mFloat32) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
val, err := in.readUint(4)
if err != nil {
return err
}
v.SetFloat(float64(math.Float32frombits(uint32(val))))
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 reflect.Value, out *encoder) error {
out.writeUint(math.Float64bits(v.Float()), 8)
return nil
}
func (m mFloat64) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
val, err := in.readUint(8)
if err != nil {
return err
}
v.SetFloat(math.Float64frombits(val))
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 reflect.Value, out *encoder) error {
var (
maxSize = int(m)
s = v.String()
length = len(s)
)
if maxSize < length {
return newExpectError(ErrStringTooLong, maxSize, length)
}
// length, allocPresent
out.writeUint(uint64(length), 8)
out.writeUint(allocPresent, 8)
// 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, v reflect.Value) error {
size, err := in.readUint(8)
if err != nil {
return err
}
ptr, err := in.readUint(8)
if err != nil {
return err
}
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 reflect.Value, size int) error {
if maxSize := int(m); size < 0 || maxSize < size {
return newExpectError(ErrStringTooLong, maxSize, size)
}
start, end := in.nextObject, in.nextObject+size
if len(in.buffer) < end {
return newValueError(ErrMessageTooSmall, v)
}
s := string(in.buffer[start:end])
if !utf8.ValidString(s) {
return newValueError(ErrStringNotUTF8, v)
}
v.SetString(s)
in.nextObject += align(size, 8)
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 reflect.Value, out *encoder) error {
if v.IsNil() {
out.writeUint(0, 8)
out.writeUint(noAlloc, 8)
return nil
}
return mString(m).marshal(ctx, v.Elem(), out)
}
var (
nilString = reflect.Zero(reflect.PtrTo(reflect.TypeOf("")))
typString = reflect.TypeOf("")
)
func (m mOptString) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
size, err := in.readUint(8)
if err != nil {
return err
}
ptr, err := in.readUint(8)
if err != nil {
return err
}
switch ptr {
case noAlloc:
v.Set(nilString)
return nil
case allocPresent:
v.Set(reflect.New(typString))
return mString(m).unmarshalWithUncheckedSize(in, v.Elem(), int(size))
default:
return newValueError(ErrBadRefEncoding, v)
}
}
type mHandle bool
func (m mHandle) getMarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mHandle) getUnmarshalSize(ctx MarshalerContext) int {
return 4
}
func (m mHandle) isOpt() bool {
return bool(m)
}
func (m mHandle) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) 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.Uint())
if raw == zx.HandleInvalid {
if !m.isOpt() {
return ErrUnexpectedNullHandle
}
out.writeUint(uint64(noHandle), 4)
return nil
}
out.handles = append(out.handles, raw)
out.writeUint(uint64(handlePresent), 4)
return nil
}
func (m mHandle) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
h, err := in.readUint(4)
if err != nil {
return err
}
switch uint32(h) {
case noHandle:
if !m.isOpt() {
return ErrUnexpectedNullHandle
}
v.SetUint(uint64(zx.HandleInvalid))
return nil
case handlePresent:
if len(in.handles) == 0 {
return ErrNotEnoughHandles
}
v.SetUint(uint64(in.handles[0]))
in.handles = in.handles[1:]
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 reflect.Value, out *encoder) error {
return mHandle(m).marshal(ctx, v.Field(0), out)
}
func (m mInterface) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
return mHandle(m).unmarshal(ctx, in, v.Field(0))
}