| // 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" |
| "unsafe" |
| ) |
| |
| type strictness bool |
| |
| const ( |
| isFlexible strictness = false |
| isStrict strictness = true |
| ) |
| |
| // MustCreateMarshaler is like CreateMarshaler but panics if the sample struct |
| // cannot be used to create a marshaler. It simplifies safe initialization of |
| // global variables holding marshalers. |
| func MustCreateMarshaler(sample interface{}) Marshaler { |
| m, err := CreateMarshaler(sample) |
| if err != nil { |
| panic(err) |
| } |
| return m |
| } |
| |
| // CreateMarshaler creates a marshaler from a sample struct. |
| func CreateMarshaler(sample interface{}) (Marshaler, error) { |
| typ := reflect.TypeOf(sample) |
| if typ.Kind() == reflect.Ptr { |
| typ = typ.Elem() |
| } |
| if typ.Kind() == reflect.Struct { |
| return createMarshaler(typ) |
| } |
| return nil, errors.New("unable to create marshaler for " + nicefmt(typ)) |
| } |
| |
| // CreateMarshaler creates a lazy marshaler from a sample struct. This lazy |
| // marshaler initializes its actual delegate marshaler on first use, rather |
| // than on creation. As a result, there is no validation on creation, and |
| // instead the lazy marshaler will panic on first use if a marshaler |
| // cannot be created of the sample provided. |
| func CreateLazyMarshaler(sample interface{}) Marshaler { |
| return &lazyMarshaler{ |
| sample: sample, |
| } |
| } |
| |
| type lazyMarshaler struct { |
| once sync.Once |
| sample interface{} |
| delegate Marshaler |
| } |
| |
| // Assert that lazyMarshaler implements the Marshaler interface. |
| var _ Marshaler = &lazyMarshaler{} |
| |
| func (m *lazyMarshaler) init() { |
| m.delegate = MustCreateMarshaler(m.sample) |
| } |
| |
| func (m *lazyMarshaler) getMarshalSize(ctx MarshalerContext) int { |
| m.once.Do(m.init) |
| return m.delegate.getMarshalSize(ctx) |
| } |
| |
| func (m *lazyMarshaler) getUnmarshalSize(ctx MarshalerContext) int { |
| m.once.Do(m.init) |
| return m.delegate.getUnmarshalSize(ctx) |
| } |
| |
| func (m *lazyMarshaler) marshal(ctx MarshalerContext, v 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, handleDispositions []zx.HandleDisposition) (int, int, error) { |
| return MarshalWithContext(newCtx(), message, data, handleDispositions) |
| } |
| |
| // Marshal marshals (or encodes) a message into the data and handles slices. |
| func MarshalWithContext(ctx MarshalerContext, message Message, data []byte, handleDispositions []zx.HandleDisposition) (int, int, error) { |
| // By construction, we know that message is a pointer to a struct since |
| // we only generate pointer receiver methods for top-level messages. |
| // Should one implement the interface differently, and call into this |
| // code, it would fail in an obsure way withing relfection. Just don't do |
| // that. |
| var ( |
| v = 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], handleDispositions: handleDispositions[: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.handleDispositions), nil |
| } |
| |
| // Unmarshal unmarshals (or decodes) into message using the data and handles |
| // slices. |
| func Unmarshal(data []byte, handleInfos []zx.HandleInfo, message Message) (int, int, error) { |
| return UnmarshalWithContext2(newCtx(), data, handleInfos, message) |
| } |
| |
| // UnmarshalWithContext2 behaves identically to UnmarshalWithContext but takes a HandleInfo. |
| func UnmarshalWithContext2(ctx MarshalerContext, data []byte, handleInfos []zx.HandleInfo, message Message) (int, int, error) { |
| // By construction, we know that message is a pointer to a struct since |
| // we only generate pointer receiver methods for top-level messages. |
| // Should one implement the interface differently, and call into this |
| // code, it would fail in an obsure way withing relfection. Just don't do |
| // that. |
| var ( |
| v = 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, |
| handleInfos: handleInfos, |
| nextObject: nextObject, |
| } |
| if err := m.unmarshal(ctx, in, v); err != nil { |
| return 0, 0, err |
| } |
| return in.nextObject, len(handleInfos) - len(in.handleInfos), nil |
| } |
| |
| func UnmarshalWithContext(ctx MarshalerContext, data []byte, handles []zx.Handle, message Message) (int, int, error) { |
| resphi := messageHandleInfosPool.Get().([]zx.HandleInfo) |
| defer messageHandleInfosPool.Put(resphi) |
| |
| for i, handle := range handles { |
| resphi[i] = zx.HandleInfo{ |
| Handle: handle, |
| Type: zx.ObjectTypeNone, |
| Rights: zx.RightSameRights, |
| } |
| } |
| |
| return UnmarshalWithContext2(ctx, data, resphi[:len(handles)], message) |
| } |
| |
| const tagSizeV1 = "fidl_size_v1" |
| const tagAlignmentV1 = "fidl_alignment_v1" |
| const tagOffsetV1 = "fidl_offset_v1" |
| const tagHandleRights = "fidl_handle_rights" |
| const tagHandleSubtype = "fidl_handle_subtype" |
| const tagOrdinal = "fidl_ordinal" |
| const tagBounds = "fidl_bounds" |
| const tagMarshalerKind = "fidl" |
| |
| // These rights come from FTP-028 |
| // (all requests and interfaces have the same handle rights) |
| const ProtocolRights = zx.RightTransfer | zx.RightWait | zx.RightInspect | zx.RightWrite | zx.RightRead | zx.RightSignal | zx.RightSignalPeer |
| |
| type tagKind int |
| |
| const ( |
| _ = iota |
| structTag tagKind = iota |
| xunionTag |
| strictXunionTag |
| tableTag |
| ) |
| |
| type bounds []int |
| |
| func (b bounds) pop() (int, []int) { |
| if len(b) == 0 { |
| return math.MaxInt32, nil |
| } |
| return b[0], b[1:] |
| } |
| |
| func nicefmt(typ reflect.Type) string { |
| if len(typ.Name()) == 0 { |
| return typ.Kind().String() |
| } else { |
| return typ.Name() + " (" + typ.Kind().String() + ")" |
| } |
| } |
| |
| // Extracts a list of fields for struct, union, table types that can be used |
| // for iterating. |
| // This skips the inital tag field. |
| func dataFields(typ reflect.Type) []reflect.StructField { |
| if typ.Kind() != reflect.Struct { |
| panic("expected struct") |
| } |
| if name := typ.Field(0).Name; name != "_" && !strings.HasPrefix(name, "I_") { |
| panic("expected first field to be a metadata field") |
| } |
| fields := make([]reflect.StructField, typ.NumField()-1) |
| for i := 1; i < typ.NumField(); i++ { |
| fields[i-1] = typ.Field(i) |
| } |
| return fields |
| } |
| |
| // Returns true if the go type has the same layout as the FIDL wire format. |
| func matchesWireFormatLayout(marshaler Marshaler, typ reflect.Type) bool { |
| switch marshaler := marshaler.(type) { |
| case mBool: |
| // Need to validate 0, 1. |
| return false |
| case mUint: |
| return true |
| case mInt: |
| return true |
| case mFloat32: |
| // Need to validate. |
| return false |
| case mFloat64: |
| // Need to validate. |
| return false |
| case mArray: |
| return matchesWireFormatLayout(marshaler.Marshaler, typ.Elem()) |
| case mStructUnsafeCopy: |
| return true |
| case mStruct: |
| // Note: In some cases, go structs may be different size than wire format |
| // structs but otherwise identical. It may be possible to change this |
| // logic to allow a partial copy in the future. |
| if marshaler.size != int(typ.Size()) { |
| return false |
| } |
| if marshaler.alignment != typ.Align() { |
| return false |
| } |
| for i, rField := range dataFields(typ) { |
| field := marshaler.fields[i] |
| if field.offset != int(rField.Offset) { |
| return false |
| } |
| if !matchesWireFormatLayout(field.Marshaler, rField.Type) { |
| return false |
| } |
| } |
| return true |
| case mEmptyStruct: |
| // Note: empty struct is 0 or 1 bytes at different times in go. |
| return false |
| case mHandle: |
| // Note: In the future, we might instead consider treating handles |
| // like uint32 and decoding in a subsequent step. |
| return false |
| case mXUnion, mOptXUnion, mTable, mVector, mString, mPointer: |
| return false |
| default: |
| panic("unhandledType " + reflect.TypeOf(marshaler).Name()) |
| } |
| } |
| |
| func createMarshaler(typ reflect.Type) (Marshaler, error) { |
| // field 0 holds the tag |
| tagField := typ.Field(0) |
| marshalerKind, err := readKindTag(tagField) |
| if err != nil { |
| return nil, err |
| } |
| |
| var ( |
| kind = marshalerKind |
| fields []mField |
| ordinals []uint64 |
| offsets []int |
| ) |
| |
| // - structs, unions, and xunions have fields one after the other; |
| // - tables have a field, followed by a bool presence indicator, etc. |
| for index, field := range dataFields(typ) { |
| if kind == tableTag && index%2 == 1 { |
| // Presence field |
| if field.Type.Kind() != reflect.Bool { |
| return nil, errors.New("incorrect presence field on " + nicefmt(typ)) |
| } |
| continue |
| } |
| fieldBounds := readBoundsTag(field) |
| handleRights, handleSubtype, err := readHandleRightsAndSubtype(field) |
| if err != nil { |
| return nil, err |
| } |
| switch kind { |
| case structTag: |
| offset, err := readIntTag(field, tagOffsetV1) |
| if err != nil { |
| return nil, err |
| } |
| offsets = append(offsets, offset) |
| case xunionTag, strictXunionTag, tableTag: |
| ordinal := readOrdinalTag(field) |
| ordinals = append(ordinals, uint64(ordinal)) |
| default: |
| } |
| fieldMarshaler, err := createMarshalerForField(field.Type, fieldBounds, handleRights, handleSubtype) |
| if err != nil { |
| return nil, err |
| } |
| fields = append(fields, mField{fieldMarshaler, field.Index[0]}) |
| } |
| |
| size, err := readIntTag(tagField, tagSizeV1) |
| if err != nil { |
| return nil, errors.New("error creating marshaller for " + typ.String() + ": " + err.Error()) |
| } |
| alignment, err := readIntTag(tagField, tagAlignmentV1) |
| if err != nil { |
| return nil, errors.New("error creating marshaller for " + typ.String() + ": " + err.Error()) |
| } |
| |
| switch kind { |
| case structTag: |
| if len(fields) == 0 { |
| return mEmptyStruct{}, nil |
| } |
| |
| var structFields []mFieldWithOffset |
| for i := 0; i < len(fields); i++ { |
| structFields = append(structFields, mFieldWithOffset{ |
| mField: fields[i], |
| offset: offsets[i], |
| }) |
| } |
| s := mStruct{ |
| fields: structFields, |
| size: size, |
| alignment: alignment, |
| } |
| if matchesWireFormatLayout(s, typ) { |
| return mStructUnsafeCopy{ |
| fields: structFields, |
| size: size, |
| alignment: alignment, |
| }, nil |
| } |
| return s, nil |
| case xunionTag, strictXunionTag: |
| strictness := strictness(kind == strictXunionTag) |
| |
| ordinalToFields := make(map[uint64]mField) |
| for i := 0; i < len(fields); i++ { |
| ordinalToFields[ordinals[i]] = fields[i] |
| } |
| return mXUnion{ |
| fields: ordinalToFields, |
| ordinals: ordinals, |
| size: size, |
| alignment: alignment, |
| strictness: strictness, |
| }, nil |
| case tableTag: |
| return mTable{ |
| fields: fields, |
| size: size, |
| alignment: alignment, |
| ordinals: ordinals, |
| }, nil |
| default: |
| return nil, errors.New("unknown kind tag on " + nicefmt(typ)) |
| } |
| } |
| |
| func readKindTag(field reflect.StructField) (tagKind, error) { |
| content, ok := field.Tag.Lookup(tagMarshalerKind) |
| if !ok { |
| return 0, errors.New(tagMarshalerKind + " not found on field " + field.Name) |
| } |
| switch content { |
| case "s": |
| return structTag, nil |
| case "x": |
| return xunionTag, nil |
| case "x!": |
| return strictXunionTag, nil |
| case "t": |
| return tableTag, nil |
| default: |
| return 0, errors.New("unknown kind tag: " + content) |
| } |
| } |
| |
| func readHandleRightsAndSubtype(field reflect.StructField) (zx.Rights, zx.ObjectType, error) { |
| if !containsHandleType(field.Type) { |
| // Skip non-handle field types and don't return an error. |
| return zx.RightSameRights, zx.ObjectTypeNone, nil |
| } |
| |
| // Read handle rights |
| val, ok := field.Tag.Lookup(tagHandleRights) |
| if !ok { |
| return zx.RightSameRights, zx.ObjectTypeNone, ErrUnspecifiedHandleRights |
| } |
| rights, err := strconv.ParseInt(val, 0, 64) |
| if err != nil { |
| return zx.RightSameRights, zx.ObjectTypeNone, err |
| } |
| convertedRights := zx.Rights(rights) |
| |
| // Read handle subtype |
| val, ok = field.Tag.Lookup(tagHandleSubtype) |
| if !ok { |
| return zx.RightSameRights, zx.ObjectTypeNone, ErrUnspecifiedHandleType |
| } |
| subtype, err := strconv.ParseInt(val, 0, 64) |
| if err != nil { |
| return zx.RightSameRights, zx.ObjectTypeNone, err |
| } |
| convertedSubtype := zx.ObjectType(subtype) |
| |
| return convertedRights, convertedSubtype, nil |
| } |
| |
| func containsHandleType(typ reflect.Type) bool { |
| // Protocols and requests are technically handle types but their rights / |
| // subtypes are handled elsewhere. |
| if typ.ConvertibleTo(proxyType) || typ.ConvertibleTo(interfaceRequestType) { |
| return false |
| } |
| if isHandleType(typ) { |
| return true |
| } |
| if typ.Kind() == reflect.Slice || typ.Kind() == reflect.Ptr { |
| return containsHandleType(typ.Elem()) |
| } |
| return false |
| } |
| |
| func readOrdinalTag(field reflect.StructField) int { |
| ordinal, err := readIntTag(field, tagOrdinal) |
| if err != nil { |
| return math.MaxInt32 |
| } |
| return ordinal |
| } |
| |
| func readBoundsTag(field reflect.StructField) bounds { |
| content, ok := field.Tag.Lookup(tagBounds) |
| if !ok { |
| return nil |
| } |
| |
| var nums []int |
| for _, elem := range strings.Split(content, ",") { |
| var ( |
| num = math.MaxInt32 |
| err error |
| ) |
| if len(elem) != 0 { |
| num, err = strconv.Atoi(elem) |
| if err != nil { |
| panic(elem + ": " + err.Error()) |
| } |
| } |
| nums = append(nums, num) |
| } |
| return bounds(nums) |
| } |
| |
| func readIntTag(field reflect.StructField, tagKey string) (int, error) { |
| content, ok := field.Tag.Lookup(tagKey) |
| if !ok { |
| return 0, errors.New(tagKey + " not found on field " + field.Name) |
| } |
| res, err := strconv.ParseInt(content, 0, 64) |
| if err != nil { |
| return 0, errors.New("error parsing int body from tag " + tagKey + " " + err.Error()) |
| } |
| return int(res), nil |
| } |
| |
| func readHandleRightsTag(field reflect.StructField) (zx.Rights, error) { |
| val, ok := field.Tag.Lookup(tagHandleRights) |
| if !ok { |
| return zx.RightSameRights, nil |
| } |
| rights, err := strconv.ParseInt(val, 0, 64) |
| if err != nil { |
| return zx.RightSameRights, err |
| } |
| convertedRights := zx.Rights(rights) |
| return convertedRights, nil |
| } |
| |
| func createMarshalerForField(typ reflect.Type, bounds bounds, handleRights zx.Rights, handleSubtype zx.ObjectType) (Marshaler, error) { |
| if isHandleType(typ) { |
| nullable, _ := bounds.pop() |
| return mHandle{nullable: nullable != 0, rights: handleRights, subtype: handleSubtype}, nil |
| } |
| |
| if typ.ConvertibleTo(proxyType) || typ.ConvertibleTo(interfaceRequestType) { |
| nullable, _ := bounds.pop() |
| return mInterface{ |
| nullable: nullable != 0, |
| rights: ProtocolRights, |
| subtype: zx.ObjectTypeChannel}, nil |
| } |
| |
| 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, handleRights, handleSubtype) |
| 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, handleRights, handleSubtype) |
| if err != nil { |
| return nil, err |
| } |
| return mVector{ |
| Marshaler: elemMarshaler, |
| maxSize: maxSize, |
| sliceTyp: reflect.SliceOf(elemTyp), |
| // The fast path only supports `[]uint8`. A slice of aliased types must be handled via the slow path. |
| // We therefore prefer a monomorphic check. |
| isVectorUint8: elemTyp == reflect.TypeOf(uint8(0)), |
| }, nil |
| case reflect.Struct: |
| return createMarshaler(typ) |
| case reflect.Ptr: |
| return createOptMarshalerForField(typ.Elem(), bounds, handleRights, handleSubtype) |
| default: |
| return nil, errors.New("unable to create field marshaler for " + nicefmt(typ)) |
| } |
| } |
| |
| func createOptMarshalerForField(typ reflect.Type, bounds bounds, handleRights zx.Rights, handleSubtype zx.ObjectType) (Marshaler, error) { |
| m, err := createMarshalerForField(typ, bounds, handleRights, handleSubtype) |
| if err != nil { |
| return nil, err |
| } |
| switch m := m.(type) { |
| case mString: |
| return mOptString(m), nil |
| case mVector: |
| return mOptVector(m), nil |
| case mEmptyStruct: |
| return mPointer{ |
| Marshaler: m, |
| elemTyp: typ, |
| }, nil |
| case mStruct, mStructUnsafeCopy: |
| return mPointer{ |
| Marshaler: m, |
| elemTyp: typ, |
| }, nil |
| case mXUnion: |
| return mOptXUnion{ |
| mXUnion: m, |
| typ: typ, |
| }, nil |
| default: |
| return nil, errors.New("unable to create optional field marshaler for " + nicefmt(typ)) |
| } |
| } |
| |
| // Message is implemented by any value that represents a FIDL message. |
| type Message interface { |
| Marshaler() Marshaler |
| } |
| |
| type Marshaler interface { |
| // Marshal and unmarshal sizes can be different because they can be context dependent. |
| // e.g. it is possible to write a new format but still read the old format |
| getMarshalSize(ctx MarshalerContext) int |
| getUnmarshalSize(ctx MarshalerContext) int |
| marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error |
| unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error |
| } |
| |
| // Assert various encoders implement the Marshaler interface. |
| var _ = []Marshaler{ |
| mStruct{}, |
| mStructUnsafeCopy{}, |
| mEmptyStruct{}, |
| mXUnion{}, |
| mOptXUnion{}, |
| mTable{}, |
| mPointer{}, |
| mArray{}, |
| mVector{}, |
| mOptVector{}, |
| mBool{}, |
| mInt(0), |
| mUint(0), |
| mFloat32{}, |
| mFloat64{}, |
| mString(0), |
| mOptString(0), |
| } |
| |
| type mField struct { |
| Marshaler |
| index int |
| } |
| |
| type mFieldWithOffset struct { |
| mField |
| offset int |
| } |
| |
| func (m mFieldWithOffset) getMarshalOffset(ctx MarshalerContext) int { |
| return m.offset |
| } |
| |
| func (m mFieldWithOffset) getUnmarshalOffset(ctx MarshalerContext) int { |
| return m.offset |
| } |
| |
| type mStruct struct { |
| fields []mFieldWithOffset |
| size, alignment int |
| } |
| |
| func (m mStruct) getMarshalSize(ctx MarshalerContext) int { |
| return m.size |
| } |
| |
| func (m mStruct) getUnmarshalSize(ctx MarshalerContext) int { |
| return m.size |
| } |
| |
| func (m mStruct) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error { |
| structStart := align(out.head, m.alignment) |
| 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.alignment) |
| return nil |
| } |
| |
| func (m mStruct) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error { |
| structStart := align(in.head, m.alignment) |
| 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.alignment) |
| return nil |
| } |
| |
| type mStructUnsafeCopy struct { |
| fields []mFieldWithOffset |
| size, alignment int |
| } |
| |
| func (m mStructUnsafeCopy) getMarshalSize(ctx MarshalerContext) int { |
| return m.size |
| } |
| |
| func (m mStructUnsafeCopy) getUnmarshalSize(ctx MarshalerContext) int { |
| return m.size |
| } |
| |
| func (m mStructUnsafeCopy) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error { |
| // Directly copy the object's memory to the buffer. |
| out.head = align(out.head, m.alignment) |
| sh := reflect.SliceHeader{ |
| Data: uintptr(unsafe.Pointer(v.UnsafeAddr())), |
| Len: m.size, |
| Cap: m.size, |
| } |
| s := *(*[]uint8)(unsafe.Pointer(&sh)) |
| copy(out.buffer[out.head:], s) |
| out.head += m.size |
| return nil |
| } |
| |
| func (m mStructUnsafeCopy) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error { |
| // Directly copy from the buffer to the object's memory. |
| in.head = align(in.head, m.alignment) |
| if len(in.buffer) < in.head+m.size { |
| return ErrPayloadTooSmall |
| } |
| sh := reflect.SliceHeader{ |
| Data: uintptr(unsafe.Pointer(v.UnsafeAddr())), |
| Len: m.size, |
| Cap: m.size, |
| } |
| s := *(*[]uint8)(unsafe.Pointer(&sh)) |
| copy(s, in.buffer[in.head:]) |
| in.head += m.size |
| 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 mXUnion struct { |
| fields map[uint64]mField |
| ordinals []uint64 |
| size, alignment int |
| strictness |
| } |
| |
| func (m mXUnion) getMarshalSize(ctx MarshalerContext) int { |
| return m.size |
| } |
| |
| func (m mXUnion) getUnmarshalSize(ctx MarshalerContext) int { |
| return m.size |
| } |
| |
| func (m mXUnion) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error { |
| var ordinal uint64 |
| ordinal = v.Field(0).Uint() |
| field, ok := m.fields[ordinal] |
| if !ok { |
| return newValueError(ErrInvalidXUnionTag, ordinal) |
| } |
| out.writeUint(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(8) |
| if err != nil { |
| return err |
| } |
| |
| // 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[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() |
| } |
| |
| ordinalOrFieldIndex := ordinal |
| v.Field(0).SetUint(ordinalOrFieldIndex) |
| |
| 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 []uint64 |
| size, alignment int |
| } |
| |
| func (m mTable) getMarshalSize(ctx MarshalerContext) int { |
| return m.size |
| } |
| |
| func (m mTable) getUnmarshalSize(ctx MarshalerContext) int { |
| return m.size |
| } |
| |
| const envelopeSize = 16 |
| |
| type envelopeHeader struct { |
| byteCount uint32 |
| handleCount uint32 |
| presence uint64 |
| } |
| |
| func (h envelopeHeader) isPresent() bool { |
| return h.presence == allocPresent |
| } |
| |
| func marshalEnvelopePresent(ctx MarshalerContext, m Marshaler, v reflect.Value, out *encoder) error { |
| numHandleDispositions := len(out.handleDispositions) |
| 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 |
| } |
| numHandleDispositions = len(out.handleDispositions) - numHandleDispositions |
| numBytes = len(out.buffer) - numBytes |
| out.head = head |
| out.writeUint(uint64(numBytes), 4) |
| out.writeUint(uint64(numHandleDispositions), 4) |
| out.writeUint(allocPresent, 8) |
| return nil |
| } |
| |
| func marshalEnvelopeAbsent(out *encoder) { |
| out.writeUint(0, 8) // both numBytes, and numHandleDispositions |
| 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.handleInfos)) { |
| 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.handleInfos[0].Handle.Close() // best effort |
| in.handleInfos = in.handleInfos[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 uint64 |
| 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(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(int(maxOrdinal) * envelopeSize) |
| |
| // Envelopes. |
| var ( |
| ordinal uint64 = 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 := 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 uint64 = 1 |
| index, fieldIndex, presenceIndex = 0, 1, 2 |
| ) |
| tableBodyHead := in.nextObject |
| in.nextObject += int(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 { |
| return 24 |
| } |
| |
| func (m mOptUnion) getUnmarshalSize(ctx MarshalerContext) int { |
| return 24 |
| } |
| |
| func (m mOptUnion) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error { |
| return m.mOptXUnion.marshal(ctx, v, out) |
| } |
| |
| func (m mOptUnion) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error { |
| return m.mOptXUnion.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) |
| } |
| |
| if !utf8.ValidString(s) { |
| return newValueError(ErrStringNotUTF8, v) |
| } |
| |
| // 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 struct { |
| nullable bool |
| rights zx.Rights |
| subtype zx.ObjectType |
| } |
| |
| func (m mHandle) getMarshalSize(ctx MarshalerContext) int { |
| return 4 |
| } |
| func (m mHandle) getUnmarshalSize(ctx MarshalerContext) int { |
| return 4 |
| } |
| |
| func (m mHandle) isOpt() bool { |
| return m.nullable |
| } |
| |
| func (m mHandle) marshal(ctx MarshalerContext, v 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.handleDispositions = append(out.handleDispositions, zx.HandleDisposition{ |
| Operation: zx.HandleOpMove, |
| Handle: raw, |
| Type: m.subtype, |
| Rights: m.rights, |
| Result: zx.ErrOk, |
| }) |
| out.writeUint(uint64(handlePresent), 4) |
| return nil |
| } |
| |
| func (m mHandle) requiredRightsArePresent(actual zx.Rights) bool { |
| if m.rights == zx.RightSameRights { |
| return true |
| } |
| return actual.SupersetOf(m.rights) |
| } |
| |
| func (m mHandle) filterOutUnspecifiedRights(actual zx.Rights) zx.Rights { |
| if m.rights == zx.RightSameRights { |
| return actual |
| } |
| return actual & m.rights |
| } |
| |
| func (m mHandle) unmarshal(ctx MarshalerContext, in *decoder, 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.handleInfos) == 0 { |
| return ErrNotEnoughHandles |
| } |
| handleInfo := in.handleInfos[0] |
| in.handleInfos = in.handleInfos[1:] |
| |
| if !m.requiredRightsArePresent(handleInfo.Rights) { |
| return newExpectError(ErrMissingRequiredHandleRights, m.rights, handleInfo.Rights) |
| } |
| |
| handle := handleInfo.Handle |
| reducedRights := m.filterOutUnspecifiedRights(handleInfo.Rights) |
| if handleInfo.Rights != reducedRights { |
| handle, err = handle.Replace(reducedRights) |
| if err != nil { |
| return newValueError(ErrUnableToReduceHandleRights, handle) |
| } |
| } |
| |
| if m.subtype != zx.ObjectTypeNone && m.subtype != handleInfo.Type { |
| return newExpectError(ErrIncorrectHandleType, m.subtype, handleInfo.Type) |
| } |
| |
| v.SetUint(uint64(handle)) |
| return nil |
| default: |
| return newValueError(ErrBadHandleEncoding, h) |
| } |
| } |
| |
| // An interface is represented by a Proxy, whose first field is |
| // a zx.Channel, and we can just marshal that. Same goes for an |
| // interface request, which is just an InterfaceRequest whose |
| // first field is a zx.Channel. |
| type mInterface mHandle |
| |
| func (m mInterface) getMarshalSize(ctx MarshalerContext) int { |
| return mHandle(m).getMarshalSize(ctx) |
| } |
| func (m mInterface) getUnmarshalSize(ctx MarshalerContext) int { |
| return mHandle(m).getUnmarshalSize(ctx) |
| } |
| |
| func (m mInterface) marshal(ctx MarshalerContext, v 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)) |
| } |