blob: e22a560c2624eaeab78e9d9a610faaecbf83313f [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"
)
// 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 {
sync.Mutex
sample interface{}
delegate Marshaler
}
// Assert that lazyMarshaler implements the Marshaler interface.
var _ Marshaler = &lazyMarshaler{}
func (m *lazyMarshaler) init() {
m.Lock()
defer m.Unlock()
// Someone could have raced us between a call to init() and
// locking, avoid erroring out by referencing a nil sample.
if m.sample == nil {
return
}
m.delegate = MustCreateMarshaler(m.sample)
m.sample = nil
}
func (m *lazyMarshaler) getSize() int {
if m.delegate == nil {
m.init()
}
return m.delegate.getSize()
}
func (m *lazyMarshaler) marshal(v reflect.Value, out *encoder) error {
if m.delegate == nil {
m.init()
}
return m.delegate.marshal(v, out)
}
func (m *lazyMarshaler) unmarshal(in *decoder, v reflect.Value) error {
if m.delegate == nil {
m.init()
}
return m.delegate.unmarshal(in, v)
}
// MarshalNew marshals (or encodes) a message into the data and handles slices.
func MarshalNew(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.getSize())
if err := m.marshal(v, out); err != nil {
return 0, 0, err
}
return len(out.buffer), len(out.handles), nil
}
// UnmarshalNew unmarshals (or decodes) into message using the data and handles
// slices.
func UnmarshalNew(data []byte, handles []zx.Handle, message Message) 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.getSize(), 8)
in := &decoder{
buffer: data,
handles: handles,
nextObject: nextObject,
}
return m.unmarshal(in, v)
}
const tagKey = "fidl2"
type tagKind int
const (
_ = iota
structTag tagKind = iota
unionTag
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
marshalerKind, marshalerBounds := readTag(typ.Field(0))
var (
kind = marshalerKind
size = marshalerBounds[0]
alignment = marshalerBounds[1]
fields []mField
ordinals []int
)
// field 1 and up are the actual data fields
// - structs and unions 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)
if kind == tableTag {
ordinal, actualBounds := fieldBounds.pop()
ordinals = append(ordinals, ordinal)
fieldBounds = actualBounds
}
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.
}
}
switch kind {
case structTag:
return mStruct{
fields: fields,
size: size,
alignment: alignment,
}, nil
case unionTag:
return mUnion{
fields: fields,
size: size,
alignment: alignment,
}, nil
case tableTag:
return mTable{
mStruct: mStruct{
fields: fields,
size: size,
alignment: alignment,
},
ordinals: ordinals,
}, nil
default:
return nil, errors.New("missing kind marker on " + nicefmt(typ))
}
}
// readTag reads a fidl tag which can be one of
//
// s,size,alignement -- marking a struct, with its size, and alignment
// u,size,alignement -- marking a union, with its size, and alignment
// t,size,alignement -- marking a table, with its size, and alignment
// num1, num2, ... -- 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 "t":
return tableTag, toBounds(elems[1:])
default:
return boundsTag, toBounds(elems)
}
}
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 mStruct:
return mOptStructUnionTable{
Marshaler: m,
elemTyp: typ,
}, nil
case mUnion:
return mOptStructUnionTable{
Marshaler: m,
elemTyp: typ,
}, nil
case mTable:
return mOptStructUnionTable{
Marshaler: m,
elemTyp: typ,
}, nil
default:
return nil, errors.New("unable to create optional field marshaler for " + nicefmt(typ))
}
}
type Message interface {
Payload // TODO(pascallouis): remove
Marshaler() Marshaler
}
type Marshaler interface {
getSize() int
marshal(v reflect.Value, out *encoder) error
unmarshal(in *decoder, v reflect.Value) error
}
// Assert various encoders implement the Marshaler interface.
var _ = []Marshaler{
mStruct{},
mUnion{},
mTable{},
mOptStructUnionTable{},
mArray{},
mVector{},
mBool{},
mInt(0),
mUint(0),
mFloat32{},
mFloat64{},
mString(0),
mOptString(0),
}
type mField struct {
Marshaler
index int
}
type mStruct struct {
fields []mField
size, alignment int
}
func (m mStruct) getSize() int {
return m.size
}
func (m mStruct) marshal(v reflect.Value, out *encoder) error {
for _, field := range m.fields {
if err := field.Marshaler.marshal(v.Field(field.index), out); err != nil {
return err
}
}
return nil
}
func (m mStruct) unmarshal(in *decoder, v reflect.Value) error {
for _, field := range m.fields {
if err := field.Marshaler.unmarshal(in, v.Field(field.index)); err != nil {
return err
}
}
return nil
}
type mUnion struct {
fields []mField
size, alignment int
}
func (m mUnion) getSize() int {
return m.size
}
func (m mUnion) marshal(v reflect.Value, out *encoder) error {
// 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.alignment)
// Marshal field.
if err := m.fields[kind].Marshaler.marshal(v.Field(kind+1), out); err != nil {
return err
}
// Re-position head.
out.head = head + m.size
return nil
}
func (m mUnion) unmarshal(in *decoder, v reflect.Value) error {
// Save the head for proper padding.
head := in.head
// Kind.
kind, err := in.safeReadUint(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 alignement before writing its field.
in.head = align(in.head, m.alignment)
// Unmarshal field.
ikind := int(kind)
if err := m.fields[ikind].Marshaler.unmarshal(in, v.Field(ikind+1)); err != nil {
return err
}
// Re-position head.
in.head = head + m.size
return nil
}
type mTable struct {
mStruct
ordinals []int
}
func (m mTable) marshal(v reflect.Value, out *encoder) error {
// Vector of envelopes header.
numKnown := len(m.ordinals)
maxOrdinal := m.ordinals[numKnown-1]
out.writeUint(uint64(maxOrdinal), 8)
out.writeUint(allocPresent, 8)
// Sizing.
numPresent := 0
for i := 0; i < len(m.fields); i++ {
if v.Field(2 * (i + 1)).Bool() {
numPresent++
}
}
numBytesPerElement := m.fields[0].Marshaler.getSize()
// Encode in the out-of-line object.
oldHead := out.head
out.head = out.newObject(maxOrdinal*16 + numPresent*numBytesPerElement)
// Envelopes.
var (
ordinal = 1
index, fieldIndex, presenceIndex = 0, 1, 2
nextFieldHead = out.head + maxOrdinal*16
)
for ordinal <= maxOrdinal {
fieldKnown := index < numKnown && ordinal == m.ordinals[index]
fieldPresent := fieldKnown && v.Field(presenceIndex).Bool()
if fieldPresent {
numHandles := len(out.handles)
savedHead := out.head
out.head = nextFieldHead
if err := m.fields[index].marshal(v.Field(fieldIndex), out); err != nil {
return err
}
numHandles = len(out.handles) - numHandles
out.head = savedHead
nextFieldHead += numBytesPerElement
out.writeUint(uint64(numBytesPerElement), 4)
out.writeUint(uint64(numHandles), 4)
out.writeUint(allocPresent, 8)
} else {
// Write both num bytes and num handles at once.
out.writeUint(0, 8)
out.writeUint(noAlloc, 8)
}
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(in *decoder, v reflect.Value) error {
maxOrdinal := int(in.readUint(8))
allocPtr := in.readUint(8)
switch allocPtr {
case allocPresent:
// good
case noAlloc:
return newValueError(ErrUnexpectedNullRef, v)
default:
return newValueError(ErrBadRefEncoding, v)
}
// Envelopes.
var (
numKnown = len(m.ordinals)
ordinal = 1
index, fieldIndex, presenceIndex = 0, 1, 2
nextFieldHead = in.head + maxOrdinal*16
)
for ordinal <= maxOrdinal {
fieldKnown := index < numKnown && ordinal == m.ordinals[index]
numBytes := int(in.readUint(4))
numHandles := int(in.readUint(4))
fieldPresent := in.readUint(8)
switch fieldPresent {
case allocPresent:
if fieldKnown {
savedHead := in.head
in.head = nextFieldHead
if err := m.fields[index].unmarshal(in, v.Field(fieldIndex)); err != nil {
return err
}
in.head = savedHead
v.Field(presenceIndex).SetBool(true)
} else {
for i := 0; i < numHandles; i++ {
in.handles[0].Close() // best effort
in.handles = in.handles[1:]
}
}
nextFieldHead += numBytes
case noAlloc:
// TODO(FIDL-237): We should check that numBytes and numHandles
// are 0, and reject messages where this is not the case. This
// requires all other bindings from properly memseting to 0
// all bytes of the buffer.
default:
return newValueError(ErrBadRefEncoding, v)
}
ordinal++
if fieldKnown {
index++
fieldIndex += 2 // i.e skip presence field
presenceIndex += 2 // i.e skip field
}
}
return nil
}
type mOptStructUnionTable struct {
Marshaler
elemTyp reflect.Type
}
func (m mOptStructUnionTable) getSize() int {
return 8
}
func (m mOptStructUnionTable) marshal(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.getSize(), 8))
// Marshal field.
if err := m.Marshaler.marshal(v.Elem(), out); err != nil {
return err
}
// Re-position head.
out.head = oldHead
return nil
}
func (m mOptStructUnionTable) unmarshal(in *decoder, v reflect.Value) error {
// Nil?
ptr, err := in.safeReadUint(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.getSize(), 8)
// Unmarshal field.
if err := m.Marshaler.unmarshal(in, v.Elem()); err != nil {
return err
}
// Re-position head.
in.head = oldHead
return nil
}
type mArray struct {
Marshaler
size int
}
func (m mArray) getSize() int {
return m.size * m.Marshaler.getSize()
}
func (m mArray) marshal(v reflect.Value, out *encoder) error {
for i, len := 0, v.Len(); i < len; i++ {
if err := m.Marshaler.marshal(v.Index(i), out); err != nil {
return err
}
}
return nil
}
func (m mArray) unmarshal(in *decoder, v reflect.Value) error {
for i, len := 0, v.Len(); i < len; i++ {
if err := m.Marshaler.unmarshal(in, v.Index(i)); err != nil {
return err
}
}
return nil
}
type mVector struct {
Marshaler
maxSize int
sliceTyp reflect.Type
isVectorUint8 bool
}
func (m mVector) getSize() int {
return 16
}
func (m mVector) marshal(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.getSize())
// Marshal elements.
if m.isVectorUint8 {
copy(out.buffer[out.head:], v.Bytes())
} else {
for i := 0; i < vLen; i++ {
if err := m.Marshaler.marshal(v.Index(i), out); err != nil {
return err
}
}
}
// Re-position head.
out.head = oldHead
return nil
}
func (m mVector) unmarshal(in *decoder, v reflect.Value) error {
size, err := in.safeReadUint(8)
if err != nil {
return err
}
ptr, err := in.safeReadUint(8)
if err != nil {
return err
}
switch ptr {
case noAlloc:
return newValueError(ErrUnexpectedNullRef, v)
case allocPresent:
return m.unmarshalWithUncheckedSize(in, v, int(size))
default:
return newValueError(ErrBadRefEncoding, v)
}
}
func (m mVector) unmarshalWithUncheckedSize(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.getSize()
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(in, v.Index(i)); err != nil {
return err
}
}
}
// Re-position head.
in.head = oldHead
return nil
}
type mOptVector mVector
func (m mOptVector) getSize() int {
return 16
}
func (m mOptVector) marshal(v reflect.Value, out *encoder) error {
if v.IsNil() {
out.writeUint(0, 8)
out.writeUint(noAlloc, 8)
return nil
}
return mVector(m).marshal(v.Elem(), out)
}
func (m mOptVector) unmarshal(in *decoder, v reflect.Value) error {
size, err := in.safeReadUint(8)
if err != nil {
return err
}
ptr, err := in.safeReadUint(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(in, v.Elem(), int(size))
default:
return newValueError(ErrBadRefEncoding, v)
}
}
type mBool struct{}
func (m mBool) getSize() int {
return 1
}
func (m mBool) marshal(v reflect.Value, out *encoder) error {
if v.Bool() {
out.writeUint(1, 1)
} else {
out.writeUint(0, 1)
}
return nil
}
func (m mBool) unmarshal(in *decoder, v reflect.Value) error {
b, err := in.safeReadUint(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) getSize() int {
return int(m)
}
func (m mInt) marshal(v reflect.Value, out *encoder) error {
size := int(m)
out.writeInt(v.Int(), size)
return nil
}
func (m mInt) unmarshal(in *decoder, v reflect.Value) error {
size := int(m)
val, err := in.safeReadUint(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) getSize() int {
return int(m)
}
func (m mUint) marshal(v reflect.Value, out *encoder) error {
size := int(m)
out.writeUint(v.Uint(), size)
return nil
}
func (m mUint) unmarshal(in *decoder, v reflect.Value) error {
size := int(m)
val, err := in.safeReadUint(size)
if err != nil {
return err
}
v.SetUint(val)
return nil
}
type mFloat32 struct{}
func (m mFloat32) getSize() int {
return 4
}
func (m mFloat32) marshal(v reflect.Value, out *encoder) error {
out.writeUint(uint64(math.Float32bits(float32(v.Float()))), 4)
return nil
}
func (m mFloat32) unmarshal(in *decoder, v reflect.Value) error {
val, err := in.safeReadUint(4)
if err != nil {
return err
}
v.SetFloat(float64(math.Float32frombits(uint32(val))))
return nil
}
type mFloat64 struct{}
func (m mFloat64) getSize() int {
return 8
}
func (m mFloat64) marshal(v reflect.Value, out *encoder) error {
out.writeUint(math.Float64bits(v.Float()), 8)
return nil
}
func (m mFloat64) unmarshal(in *decoder, v reflect.Value) error {
val, err := in.safeReadUint(8)
if err != nil {
return err
}
v.SetFloat(math.Float64frombits(val))
return nil
}
type mString int
func (m mString) getSize() int {
return 16
}
func (m mString) marshal(v reflect.Value, out *encoder) error {
// v must be addressable string
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(in *decoder, v reflect.Value) error {
size, err := in.safeReadUint(8)
if err != nil {
return err
}
ptr, err := in.safeReadUint(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)
}
s := string(in.buffer[in.nextObject : in.nextObject+size])
v.SetString(s)
in.nextObject += align(size, 8)
return nil
}
type mOptString uint64
func (m mOptString) getSize() int {
return 16
}
func (m mOptString) marshal(v reflect.Value, out *encoder) error {
// v must be Ptr, to string
if v.IsNil() {
out.writeUint(0, 8)
out.writeUint(noAlloc, 8)
return nil
}
return mString(m).marshal(v.Elem(), out)
}
var (
nilString = reflect.Zero(reflect.PtrTo(reflect.TypeOf("")))
typString = reflect.TypeOf("")
)
func (m mOptString) unmarshal(in *decoder, v reflect.Value) error {
size, err := in.safeReadUint(8)
if err != nil {
return err
}
ptr, err := in.safeReadUint(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) getSize() int {
return 4
}
func (m mHandle) isOpt() bool {
return bool(m)
}
func (m mHandle) marshal(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(in *decoder, v reflect.Value) error {
h, err := in.safeReadUint(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) getSize() int {
return mHandle(m).getSize()
}
func (m mInterface) marshal(v reflect.Value, out *encoder) error {
return mHandle(m).marshal(v.Field(0), out)
}
func (m mInterface) unmarshal(in *decoder, v reflect.Value) error {
return mHandle(m).unmarshal(in, v.Field(0))
}