| // 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)) |
| } |