| // 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" |
| "syscall/zx" |
| ) |
| |
| const ( |
| allocPresent uint64 = math.MaxUint64 |
| noAlloc = 0 |
| ) |
| |
| const ( |
| handlePresent uint32 = math.MaxUint32 |
| noHandle = 0 |
| ) |
| |
| var ( |
| // TODO(mknyszek): Add support here for process, thread, job, resource, |
| // interrupt, eventpair, fifo, guest, and time once these are actually |
| // supported in the Go runtime. |
| handleType reflect.Type = reflect.TypeOf(zx.Handle(0)) |
| channelType = reflect.TypeOf(zx.Channel(0)) |
| logType = reflect.TypeOf(zx.Log(0)) |
| portType = reflect.TypeOf(zx.Port(0)) |
| vmoType = reflect.TypeOf(zx.VMO(0)) |
| eventType = reflect.TypeOf(zx.Event(0)) |
| socketType = reflect.TypeOf(zx.Socket(0)) |
| vmarType = reflect.TypeOf(zx.VMAR(0)) |
| interfaceRequestType = reflect.TypeOf(InterfaceRequest{}) |
| proxyType = reflect.TypeOf(Proxy{}) |
| ) |
| |
| type structKind int |
| |
| const ( |
| _ structKind = iota |
| aStruct |
| aUnion |
| aTable |
| ) |
| |
| // whichStructKind returns the kind of FIDL type this golang type represents. |
| func whichStructKind(t reflect.Type) structKind { |
| // This is a safe way to check if it's a union type because the generated |
| // code inserts a "dummy" field (of type struct{}) at the beginning as a |
| // marker that the struct should be treated as a FIDL union. Because all FIDL |
| // fields are exported, there's no potential for name collision either, and a |
| // struct accidentally being treated as a union. |
| if t.Kind() == reflect.Struct && t.NumField() > 1 && t.Field(0).Tag.Get("fidl") == "tag" { |
| return aUnion |
| } |
| // We place a marker "t,..." on tables in the first field of the struct. |
| if t.Kind() == reflect.Struct && t.NumField() > 1 && strings.HasPrefix(t.Field(0).Tag.Get("fidl2"), "t") { |
| return aTable |
| } |
| return aStruct |
| } |
| |
| // isHandleType returns true if the reflected type is a Fuchsia handle type. |
| func isHandleType(t reflect.Type) bool { |
| switch t { |
| case handleType: |
| fallthrough |
| case channelType: |
| fallthrough |
| case logType: |
| fallthrough |
| case portType: |
| fallthrough |
| case vmoType: |
| fallthrough |
| case eventType: |
| fallthrough |
| case socketType: |
| fallthrough |
| case vmarType: |
| return true |
| } |
| return false |
| } |
| |
| // isInterfaceType returns true if the reflected type is a FIDL interface type. |
| func isInterfaceType(t reflect.Type) bool { |
| // FIDL interfaces are represented as aliases over Proxy. |
| return t.ConvertibleTo(proxyType) |
| } |
| |
| // isInterfaceRequestType returns true if the reflected type is a FIDL interface |
| // request type. |
| func isInterfaceRequestType(t reflect.Type) bool { |
| // FIDL interfaces are represented as aliases over InterfaceRequest. |
| return t.ConvertibleTo(interfaceRequestType) |
| } |
| |
| // getSize returns the size of the type. Occasionally this requires the value, |
| // particularly for a struct. |
| func getSize(t reflect.Type, v reflect.Value) (int, error) { |
| switch t.Kind() { |
| case reflect.Array: |
| if v.Len() == 0 { |
| return 0, nil |
| } |
| s, err := getSize(t.Elem(), v.Index(0)) |
| if err != nil { |
| return 0, err |
| } |
| return v.Len() * s, nil |
| case reflect.Bool, reflect.Int8, reflect.Uint8: |
| return 1, nil |
| case reflect.Int16, reflect.Uint16: |
| return 2, nil |
| case reflect.Int32, reflect.Uint32, reflect.Float32: |
| return 4, nil |
| case reflect.Int64, reflect.Uint64, reflect.Float64: |
| return 8, nil |
| case reflect.Ptr: |
| i := t.Elem() |
| switch i.Kind() { |
| case reflect.Slice: |
| return 16, nil |
| case reflect.String: |
| return 16, nil |
| case reflect.Struct: |
| // Handles structs, unions, and tables. |
| return 8, nil |
| } |
| return 0, newValueError(ErrInvalidPointerType, t.Name()) |
| case reflect.String: |
| return 16, nil |
| case reflect.Slice: |
| return 16, nil |
| case reflect.Struct: |
| // Handles structs, unions, and tables. |
| return getPayloadSize(t, v) |
| } |
| return 0, newValueError(ErrInvalidInlineType, t.Name()) |
| } |
| |
| func structAsPayload(t reflect.Type, v reflect.Value) (Payload, error) { |
| // Get the size and alignment for the struct. |
| // |
| // Note that Addr can fail if the originally derived value is not "addressable", |
| // meaning the root ValueOf() call was on a struct value, not a pointer. However, |
| // we guarantee the struct is addressable by forcing a Payload to be passed in |
| // (a struct value will never cast as an interface). |
| // |
| // We avoid using Implements(), MethodByName(), and Call() here because they're |
| // very slow. |
| payload, ok := v.Addr().Interface().(Payload) |
| if !ok { |
| return nil, ErrStructIsNotPayload |
| } |
| return payload, nil |
| } |
| |
| func getPayloadSize(t reflect.Type, v reflect.Value) (int, error) { |
| p, err := structAsPayload(t, v) |
| if err != nil { |
| return 0, err |
| } |
| return p.InlineSize(), nil |
| } |
| |
| func getPayloadAlignment(t reflect.Type, v reflect.Value) (int, error) { |
| p, err := structAsPayload(t, v) |
| if err != nil { |
| return 0, err |
| } |
| return p.InlineAlignment(), nil |
| } |
| |
| // fieldData contains metadata for a single struct field for use during encoding and |
| // decoding. It is derived from a struct field tag, and generally contains facilities |
| // for managing state in a recursive context (since even the type in a single struct field |
| // may be recursively defined). |
| type nestedTypeData struct { |
| // maxElems represents the maximum number of elements allowed for a |
| // container type. "nil" means there is no maximum. It is represented |
| // as a slice because FIDL container types may be nested arbitrarily |
| // deeply. The lower the index, the more deeply nested the type is. |
| maxElems []*int |
| |
| // nullable reflects whether the innermost type for a struct field is nullable. |
| // This is only used for types where nullability is non-obvious, which is only |
| // handles for now. |
| nullable bool |
| |
| // ordinal captures the table ordinal if applicable |
| ordinal int |
| } |
| |
| // Unnest attempts to unnest the nestedTypeData one level. If it succeeds, it returns the type |
| // data for that nesting level, and modifies the data structure for the next nested container. |
| func (n *nestedTypeData) Unnest() *int { |
| if len(n.maxElems) == 0 { |
| return nil |
| } |
| v := n.maxElems[len(n.maxElems)-1] |
| n.maxElems = n.maxElems[:len(n.maxElems)-1] |
| return v |
| } |
| |
| // FromTag derives metadata from data serialized into a golang struct field |
| // tag. |
| func (n *nestedTypeData) FromTag(tag reflect.StructTag, isTable bool) error { |
| raw, ok := tag.Lookup("fidl") |
| if !ok { |
| return nil |
| } |
| split := strings.Split(raw, ",") |
| if split[0] == "*" { |
| n.nullable = true |
| split = split[1:] |
| } |
| var maxElems []*int |
| for _, e := range split { |
| if e == "" { |
| maxElems = append(maxElems, nil) |
| continue |
| } |
| i, err := strconv.ParseInt(e, 0, 64) |
| if err != nil { |
| return err |
| } |
| val := int(i) |
| maxElems = append(maxElems, &val) |
| } |
| if isTable { |
| lenMaxElems := len(maxElems) |
| n.ordinal = *maxElems[lenMaxElems-1] |
| maxElems = maxElems[:lenMaxElems-1] |
| } |
| n.maxElems = maxElems |
| return nil |
| } |
| |
| // align increases size such that size is aligned to bytes, and returns the new size. |
| // |
| // bytes must be a power of 2. |
| func align(size, bytes int) int { |
| offset := size & (bytes - 1) |
| // If we're not currently aligned to |bytes| bytes, add padding. |
| if offset != 0 { |
| size += (bytes - offset) |
| } |
| return size |
| } |
| |
| // encoder represents the encoding context that is necessary to maintain across |
| // recursive calls within the same FIDL object. |
| type encoder struct { |
| // head is the index into buffer at which new data will be written to for the current |
| // object. It must be updated before writing to a new out-of-line object, and then |
| // fixed when that object is finished. |
| head int |
| |
| // buffer represents the output buffer that the encoder writes into. |
| buffer []byte |
| |
| // handles are the handles discovered when traversing the FIDL data |
| // structure. They are referenced from within the serialized data |
| // structure in buffer. |
| handles []zx.Handle |
| } |
| |
| func (e *encoder) newObject(size int) int { |
| size = align(size, 8) |
| start := len(e.buffer) |
| e.buffer = append(e.buffer, make([]byte, size)...) |
| return start |
| } |
| |
| // writeInt writes an integer of byte-width size to the buffer. |
| // |
| // Before writing, it pads the buffer such that the integer is aligned to |
| // its own byte-width. |
| // |
| // size must be a power of 2 <= 8. |
| func (e *encoder) writeInt(val int64, size int) { |
| e.writeUint(uint64(val), size) |
| } |
| |
| // writeUint writes an unsigned integer of byte-width size to the buffer. |
| // |
| // Before writing, it pads the buffer such that the integer is aligned to |
| // its own byte-width. |
| // |
| // size must be a power of 2 <= 8. |
| func (e *encoder) writeUint(val uint64, size int) { |
| e.head = align(e.head, size) |
| for i := e.head; i < e.head+size; i++ { |
| e.buffer[i] = byte(val & 0xFF) |
| val >>= 8 |
| } |
| e.head += size |
| } |
| |
| // marshalStructOrUnionInline first aligns head to the struct's or union's alignment |
| // factor, and then writes its field(s) inline. |
| // |
| // Expects the Type t and Value v to refer to a golang struct value, not a pointer. |
| func (e *encoder) marshalStructOrUnionInline(t reflect.Type, v reflect.Value) error { |
| a, err := getPayloadAlignment(t, v) |
| if err != nil { |
| return err |
| } |
| e.head = align(e.head, a) |
| switch whichStructKind(t) { |
| case aUnion: |
| err = e.marshalUnion(t, v, a) |
| case aStruct: |
| err = e.marshalStructFields(t, v) |
| case aTable: |
| err = e.marshalTable(t, v) |
| } |
| if err != nil { |
| return err |
| } |
| e.head = align(e.head, a) |
| return nil |
| } |
| |
| // marshalStructOrUnionPointer marshals a nullable struct's or union's reference, and then |
| // marshals the value itself out-of-line. |
| // |
| // Expects the Type t and Value v to refer to a pointer to a golang struct. |
| func (e *encoder) marshalStructOrUnionPointer(t reflect.Type, v reflect.Value) error { |
| if v.IsNil() { |
| e.writeUint(noAlloc, 8) |
| return nil |
| } |
| e.writeUint(allocPresent, 8) |
| et := t.Elem() |
| ev := v.Elem() |
| // Encode the value out-of-line. |
| payload, err := structAsPayload(et, ev) |
| if err != nil { |
| return err |
| } |
| oldHead := e.head |
| e.head = e.newObject(align(payload.InlineSize(), 8)) |
| switch whichStructKind(et) { |
| case aUnion: |
| err = e.marshalUnion(et, ev, payload.InlineAlignment()) |
| case aStruct: |
| err = e.marshalStructFields(et, ev) |
| case aTable: |
| err = e.marshalTable(et, ev) |
| } |
| if err != nil { |
| return err |
| } |
| e.head = oldHead |
| return nil |
| } |
| |
| // marshalStructFields marshals the fields of a struct inline without any alignment. |
| // |
| // Expects the Type t and Value v to refer to a struct value, not a pointer. |
| // |
| // It marshals only exported struct fields. |
| func (e *encoder) marshalStructFields(t reflect.Type, v reflect.Value) error { |
| for i := 0; i < t.NumField(); i++ { |
| f := t.Field(i) |
| // If it's an unexported field, ignore it. |
| if f.PkgPath != "" { |
| continue |
| } |
| var n nestedTypeData |
| if err := n.FromTag(f.Tag, false); err != nil { |
| return err |
| } |
| if err := e.marshal(f.Type, v.Field(i), n); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| // marshalUnion marshals a FIDL union represented as a golang struct inline, |
| // without any external alignment. |
| // |
| // Expects the Type t and Value v to refer to a golang struct value, not a |
| // pointer. The alignment field is used to align the union's field. |
| func (e *encoder) marshalUnion(t reflect.Type, v reflect.Value, alignment int) error { |
| fieldIndex := int(v.Field(0).Uint()) // Union tag values start at 1. |
| if fieldIndex < 1 || fieldIndex >= t.NumField() { |
| return newValueError(ErrInvalidUnionTag, fieldIndex) |
| } |
| |
| kind := uint64(fieldIndex - 1) // Wire format tags start at 0. |
| // Save the head for proper padding. |
| head := e.head |
| e.writeUint(kind, 4) |
| |
| f := t.Field(fieldIndex) |
| var n nestedTypeData |
| if err := n.FromTag(f.Tag, false); err != nil { |
| return err |
| } |
| // Re-align to the union's alignment before writing its field. |
| e.head = align(e.head, alignment) |
| if err := e.marshal(f.Type, v.Field(fieldIndex), n); err != nil { |
| return err |
| } |
| s, err := getPayloadSize(t, v) |
| if err != nil { |
| return err |
| } |
| e.head = head + s |
| return nil |
| } |
| |
| func (e *encoder) marshalTable(t reflect.Type, v reflect.Value) error { |
| m, err := createMarshaler(t) |
| if err != nil { |
| return err |
| } |
| return m.marshal(v, e) |
| } |
| |
| // marshalArray marshals a FIDL array inline. |
| func (e *encoder) marshalArray(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| elemType := t.Elem() |
| for i := 0; i < t.Len(); i++ { |
| if err := e.marshal(elemType, v.Index(i), n); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| // marshalInline marshals a number of different supported FIDL types inline. |
| func (e *encoder) marshalInline(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| switch t.Kind() { |
| case reflect.Array: |
| return e.marshalArray(t, v, n) |
| case reflect.Bool: |
| i := uint64(0) |
| if v.Bool() { |
| i = 1 |
| } |
| e.writeUint(i, 1) |
| case reflect.Int8: |
| e.writeInt(v.Int(), 1) |
| case reflect.Int16: |
| e.writeInt(v.Int(), 2) |
| case reflect.Int32: |
| e.writeInt(v.Int(), 4) |
| case reflect.Int64: |
| e.writeInt(v.Int(), 8) |
| case reflect.Uint8: |
| e.writeUint(v.Uint(), 1) |
| case reflect.Uint16: |
| e.writeUint(v.Uint(), 2) |
| case reflect.Uint32: |
| e.writeUint(v.Uint(), 4) |
| case reflect.Uint64: |
| e.writeUint(v.Uint(), 8) |
| case reflect.Float32: |
| e.writeUint(uint64(math.Float32bits(float32(v.Float()))), 4) |
| case reflect.Float64: |
| e.writeUint(math.Float64bits(v.Float()), 8) |
| case reflect.Struct: |
| return e.marshalStructOrUnionInline(t, v) |
| default: |
| return newValueError(ErrInvalidInlineType, t.Name()) |
| } |
| return nil |
| } |
| |
| // marshalVector writes the vector metadata inline and the elements of the vector in a new |
| // out-of-line object. Expects the value to be a regular string value, i.e. of type string. |
| func (e *encoder) marshalVector(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| if !v.IsValid() { |
| e.writeUint(0, 8) |
| e.writeUint(noAlloc, 8) |
| return nil |
| } |
| max := n.Unnest() |
| if max != nil && v.Len() > *max { |
| return newExpectError(ErrVectorTooLong, *max, v.Len()) |
| } |
| e.writeUint(uint64(v.Len()), 8) |
| e.writeUint(allocPresent, 8) |
| // Don't bother creating the out-of-line struct if its length is 0. |
| if v.Len() == 0 { |
| return nil |
| } |
| elemType := t.Elem() |
| elemSize, err := getSize(elemType, v.Index(0)) |
| if err != nil { |
| return err |
| } |
| // Encode in the out-of-line object. |
| oldHead := e.head |
| e.head = e.newObject(v.Len() * elemSize) |
| |
| if elemType.Kind() == reflect.Uint8 { |
| // optimize the fdio case of a vector of bytes |
| copy(e.buffer[e.head:], v.Bytes()) |
| } else { |
| for i := 0; i < v.Len(); i++ { |
| if err := e.marshal(elemType, v.Index(i), n); err != nil { |
| return err |
| } |
| } |
| } |
| e.head = oldHead |
| return nil |
| } |
| |
| // marshalString writes the string metadata inline and the bytes of the string in a new |
| // out-of-line object. Expects the value to be a regular string value, i.e. of type string. |
| func (e *encoder) marshalString(v reflect.Value, n nestedTypeData) error { |
| if !v.IsValid() { |
| e.writeUint(0, 8) |
| e.writeUint(noAlloc, 8) |
| return nil |
| } |
| s := v.String() |
| max := n.Unnest() |
| if max != nil && len(s) > *max { |
| return newExpectError(ErrStringTooLong, *max, len(s)) |
| } |
| e.writeUint(uint64(len(s)), 8) |
| e.writeUint(allocPresent, 8) |
| |
| // Create a new out-of-line object and write bytes of the string. |
| head := e.newObject(len(s)) |
| for i := 0; i < len(s); i++ { |
| e.buffer[head+i] = s[i] |
| } |
| return nil |
| } |
| |
| // marshalHandle marshals a Fuchsia handle type, and ensures that the handle is |
| // valid if it is not nullable. |
| func (e *encoder) marshalHandle(v reflect.Value, n nestedTypeData) 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 !n.nullable { |
| return ErrUnexpectedNullHandle |
| } |
| e.writeUint(uint64(noHandle), 4) |
| return nil |
| } |
| e.handles = append(e.handles, raw) |
| e.writeUint(uint64(handlePresent), 4) |
| return nil |
| } |
| |
| // marshalPointer marshals nullable FIDL types that are represented by golang pointer |
| // indirections. The input type and value should be the dereference of the golang pointers, |
| // that is, if we're marshalling *string, we should get string. |
| func (e *encoder) marshalPointer(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| switch t.Elem().Kind() { |
| case reflect.Slice: |
| return e.marshalVector(t.Elem(), v.Elem(), n) |
| case reflect.String: |
| return e.marshalString(v.Elem(), n) |
| case reflect.Struct: |
| return e.marshalStructOrUnionPointer(t, v) |
| } |
| return newValueError(ErrInvalidPointerType, t.Name()) |
| } |
| |
| // marshal is the central recursive function core to marshalling, and |
| // traverses the tree-like structure of the input type t. v represents |
| // the value associated with the type t. |
| func (e *encoder) marshal(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| switch t.Kind() { |
| case reflect.Ptr: |
| return e.marshalPointer(t, v, n) |
| case reflect.Slice: |
| return e.marshalVector(t, v, n) |
| case reflect.String: |
| return e.marshalString(v, n) |
| } |
| if isHandleType(t) { |
| return e.marshalHandle(v, n) |
| } |
| if isInterfaceType(t) || isInterfaceRequestType(t) { |
| // 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. |
| return e.marshalHandle(v.Field(0), n) |
| } |
| return e.marshalInline(t, v, n) |
| } |
| |
| // MarshalHeader encodes the FIDL message header into the beginning of data. |
| func MarshalHeader(header *MessageHeader, data []byte) { |
| // Clear the buffer so we can append to it. |
| e := encoder{buffer: data[:0]} |
| e.head = e.newObject(MessageHeaderSize) |
| e.writeUint(uint64(header.Txid), 4) |
| e.writeUint(uint64(header.Reserved), 4) |
| e.writeUint(uint64(header.Flags), 4) |
| e.writeUint(uint64(header.Ordinal), 4) |
| } |
| |
| // Marshal the FIDL payload in s into data and handles. |
| // |
| // s must be a pointer to a struct, since the primary object in a FIDL message |
| // is always a struct. |
| // |
| // Marshal traverses the value s recursively, following nested type values via |
| // reflection in order to encode the FIDL struct. |
| func Marshal(s Payload, data []byte, handles []zx.Handle) (int, int, error) { |
| // First, let's make sure we have the right type in s. |
| t := reflect.TypeOf(s) |
| if t.Kind() != reflect.Ptr { |
| return 0, 0, errors.New("expected a pointer") |
| } |
| t = t.Elem() |
| if t.Kind() != reflect.Struct { |
| return 0, 0, errors.New("primary object must be a struct") |
| } |
| |
| // 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. |
| v := reflect.ValueOf(s).Elem() |
| e := encoder{buffer: data[:0], handles: handles[:0]} |
| e.head = e.newObject(s.InlineSize()) |
| if err := e.marshalStructFields(t, v); err != nil { |
| return 0, 0, err |
| } |
| return len(e.buffer), len(e.handles), nil |
| } |
| |
| // decoder represents the decoding context that is necessary to maintain |
| // across recursive calls within the same FIDL object. |
| type decoder struct { |
| // head is the index into buffer at which new data will be read from for the current |
| // object. It must be updated before reading from a new out-of-line object, and then |
| // fixed when that object is finished. |
| head int |
| |
| // nextObject is the byte index of the next out-of-line object in buffer. |
| nextObject int |
| |
| // buffer represents the buffer we're decoding from. |
| buffer []byte |
| |
| // handles represents the input untyped handled we're decoding. |
| handles []zx.Handle |
| } |
| |
| // readInt reads a signed integer value of byte-width size from the buffer. |
| // |
| // Before it reads, however, it moves the head forward so as to be naturally |
| // aligned with the byte-width of the integer it is reading. |
| // |
| // size must be a power of 2 <= 8. |
| // TODO(pascallouis): remove when switched over to new marshaling. |
| func (d *decoder) readInt(size int) int64 { |
| return int64(d.readUint(size)) |
| } |
| |
| // readUint reads an unsigned integer value of byte-width size from the buffer. |
| // |
| // Before it reads, however, it moves the head forward so as to be naturally |
| // aligned with the byte-width of the integer it is reading. |
| // |
| // size must be a power of 2 <= 8. |
| // TODO(pascallouis): remove when switched over to new marshaling. |
| func (d *decoder) readUint(size int) uint64 { |
| val, err := d.safeReadUint(size) |
| if err != nil { |
| panic(err) |
| } |
| return val |
| } |
| |
| // safeReadUint reads an unsigned integer value of byte-width size from the |
| // buffer, and protects against buffer overflows. |
| // |
| // Before a read, the head is moved forward so as to be naturally aligned with |
| // the byte-width of the integer it is reading. |
| // |
| // Since this is a low-level read, directly off a byte buffer, the only exposed |
| // shape is `uint64` with the expectation that callers will do the appropriate |
| // conversation. |
| // |
| // size must be a power of 2 <= 8. |
| func (d *decoder) safeReadUint(size int) (uint64, error) { |
| d.head = align(d.head, size) |
| if len(d.buffer) < d.head+size { |
| return 0, ErrPayloadTooSmall |
| } |
| var val uint64 |
| for i := d.head + size - 1; i >= d.head; i-- { |
| val <<= 8 |
| val |= uint64(d.buffer[i]) |
| } |
| d.head += size |
| return val, nil |
| } |
| |
| // unmarshalStructOrUnionInline unmarshals a struct or union inline based on Type t |
| // into Value v, first aligning head to the alignment of the value. |
| // |
| // Expects the Type t and Value v to refer to a golang struct value, not a pointer. |
| func (d *decoder) unmarshalStructOrUnionInline(t reflect.Type, v reflect.Value) error { |
| a, err := getPayloadAlignment(t, v) |
| if err != nil { |
| return err |
| } |
| d.head = align(d.head, a) |
| switch whichStructKind(t) { |
| case aUnion: |
| err = d.unmarshalUnion(t, v, a) |
| case aStruct: |
| err = d.unmarshalStructFields(t, v) |
| case aTable: |
| err = d.unmarshalTable(t, v) |
| } |
| if err != nil { |
| return err |
| } |
| d.head = align(d.head, a) |
| return nil |
| } |
| |
| // unmarshalStructOrUnionPointer unmarshals a pointer to a golang struct (i.e. a |
| // nullable FIDL struct or union) into the Value v. |
| // |
| // Expects the Type t and Value v to refer to a pointer to a golang struct. |
| func (d *decoder) unmarshalStructOrUnionPointer(t reflect.Type, v reflect.Value) error { |
| if d.readUint(8) == noAlloc { |
| v.Set(reflect.Zero(t)) |
| return nil |
| } |
| // Create the new struct. |
| v.Set(reflect.New(t.Elem())) |
| et := t.Elem() |
| ev := v.Elem() |
| |
| // Set up the out-of-line space and the head. |
| oldHead := d.head |
| d.head = d.nextObject |
| payload, err := structAsPayload(et, ev) |
| if err != nil { |
| return err |
| } |
| d.nextObject += align(payload.InlineSize(), 8) |
| |
| // Unmarshal the value itself out-of-line. |
| switch whichStructKind(et) { |
| case aUnion: |
| err = d.unmarshalUnion(et, ev, payload.InlineAlignment()) |
| case aStruct: |
| err = d.unmarshalStructFields(et, ev) |
| case aTable: |
| err = d.unmarshalTable(et, ev) |
| } |
| if err != nil { |
| return err |
| } |
| |
| // Fix up head to the old head if it's a nullable struct. |
| d.head = oldHead |
| return nil |
| } |
| |
| // unmarshalStructFields unmarshals the exported fields of the struct at d.head into |
| // the Value v. |
| // |
| // Expects the Type t and Value v to refer to a struct value, not a pointer. |
| func (d *decoder) unmarshalStructFields(t reflect.Type, v reflect.Value) error { |
| for i := 0; i < t.NumField(); i++ { |
| f := t.Field(i) |
| // If it's an unexported field, ignore it. |
| if f.PkgPath != "" { |
| continue |
| } |
| var n nestedTypeData |
| if err := n.FromTag(f.Tag, false); err != nil { |
| return err |
| } |
| if err := d.unmarshal(f.Type, v.Field(i), n); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| // unmarshalUnion unmarshals a FIDL union at d.head into the Value v (a golang |
| // struct) without any external alignment. |
| // |
| // Expects the Type t and Value v to refer to a golang struct value, not a pointer. |
| // The alignment field is used to align to the union's field before reading. |
| func (d *decoder) unmarshalUnion(t reflect.Type, v reflect.Value, alignment int) error { |
| // Save the head for proper padding. |
| head := d.head |
| kind := d.readUint(4) |
| |
| // Index into the fields of the struct, adding 1 for the tag. |
| fieldIndex := int(kind) + 1 |
| if fieldIndex >= t.NumField() { |
| return ErrInvalidUnionTag |
| } |
| v.Field(0).SetUint(kind + 1) |
| |
| f := t.Field(fieldIndex) |
| var n nestedTypeData |
| if err := n.FromTag(f.Tag, false); err != nil { |
| return err |
| } |
| d.head = align(d.head, alignment) |
| if err := d.unmarshal(f.Type, v.Field(fieldIndex), n); err != nil { |
| return err |
| } |
| s, err := getPayloadSize(t, v) |
| if err != nil { |
| return err |
| } |
| d.head = head + s |
| return nil |
| } |
| |
| func (d *decoder) unmarshalTable(t reflect.Type, v reflect.Value) error { |
| m, err := createMarshaler(t) |
| if err != nil { |
| return err |
| } |
| return m.unmarshal(d, v) |
| } |
| |
| // unmarshalArray unmarshals an array inline based on Type t into Value v, taking into account |
| // nestedTypeData n, since an array is a container type. |
| func (d *decoder) unmarshalArray(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| elemType := t.Elem() |
| for i := 0; i < t.Len(); i++ { |
| if err := d.unmarshal(elemType, v.Index(i), n); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| // unmarshalInline unmarshals a variety of types inline, or delegates to their types' specific |
| // unmarshalling functions. |
| func (d *decoder) unmarshalInline(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| switch t.Kind() { |
| case reflect.Array: |
| return d.unmarshalArray(t, v, n) |
| case reflect.Bool: |
| i := d.readUint(1) |
| switch i { |
| case 0: |
| v.SetBool(false) |
| case 1: |
| v.SetBool(true) |
| default: |
| return newValueError(ErrInvalidBoolValue, i) |
| } |
| case reflect.Int8: |
| v.SetInt(d.readInt(1)) |
| case reflect.Int16: |
| v.SetInt(d.readInt(2)) |
| case reflect.Int32: |
| v.SetInt(d.readInt(4)) |
| case reflect.Int64: |
| v.SetInt(d.readInt(8)) |
| case reflect.Uint8: |
| v.SetUint(d.readUint(1)) |
| case reflect.Uint16: |
| v.SetUint(d.readUint(2)) |
| case reflect.Uint32: |
| v.SetUint(d.readUint(4)) |
| case reflect.Uint64: |
| v.SetUint(d.readUint(8)) |
| case reflect.Float32: |
| v.SetFloat(float64(math.Float32frombits(uint32(d.readUint(4))))) |
| case reflect.Float64: |
| v.SetFloat(math.Float64frombits(d.readUint(8))) |
| case reflect.Struct: |
| return d.unmarshalStructOrUnionInline(t, v) |
| default: |
| return newValueError(ErrInvalidInlineType, t.Name()) |
| } |
| return nil |
| } |
| |
| // unmarshalVector unmarshals an out-of-line FIDL vector into a golang slice which is placed |
| // into v. nestedTypeData is also necessary because a vector can have a maximum size). The |
| // expected types and values are either pointers, i.e. *[]int8 or an "inline" vector value |
| // i.e. []int8. |
| func (d *decoder) unmarshalVector(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| size := int64(d.readUint(8)) |
| if size < 0 { |
| return newExpectError(ErrVectorTooLong, math.MaxInt64, size) |
| } |
| if ptr := d.readUint(8); ptr == noAlloc { |
| if t.Kind() != reflect.Ptr { |
| return newValueError(ErrUnexpectedNullRef, "vector") |
| } |
| v.Set(reflect.Zero(t)) |
| return nil |
| } |
| max := n.Unnest() |
| if max != nil && int(size) > *max { |
| return newExpectError(ErrVectorTooLong, *max, size) |
| } |
| |
| // Create the slice with reflection. |
| sliceType := t |
| elemType := t.Elem() |
| if t.Kind() == reflect.Ptr { |
| sliceType = elemType |
| elemType = sliceType.Elem() |
| } |
| s := reflect.MakeSlice(sliceType, int(size), int(size)) |
| |
| // Unmarshal the out-of-line structure. |
| oldHead := d.head |
| d.head = d.nextObject |
| // TODO(mknyszek): Get rid of this extra reflect.New somehow. |
| elemSize, err := getSize(elemType, reflect.New(elemType).Elem()) |
| if err != nil { |
| return err |
| } |
| d.nextObject += align(int(size)*elemSize, 8) |
| if elemType.Kind() == reflect.Uint8 { |
| copy(s.Bytes(), d.buffer[d.head:]) |
| } else { |
| for i := 0; i < int(size); i++ { |
| if err := d.unmarshal(elemType, s.Index(i), n); err != nil { |
| return err |
| } |
| } |
| } |
| d.head = oldHead |
| if t.Kind() == reflect.Ptr { |
| v.Set(reflect.New(t.Elem())) |
| v.Elem().Set(s) |
| } else { |
| v.Set(s) |
| } |
| return nil |
| } |
| |
| // unmarshalString unmarshals an out-of-line FIDL string into a golang string which is placed |
| // into v. nestedTypeData is also necessary because it can have a maximum size). The expected |
| // types and values are either pointers, i.e. *string or an "inline" string value. i.e. string. |
| // This method uses whether or not the type is a golang pointer type to determine if it is |
| // nullable. |
| func (d *decoder) unmarshalString(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| size := int64(d.readUint(8)) |
| if size < 0 { |
| return newExpectError(ErrStringTooLong, math.MaxInt64, size) |
| } |
| if ptr := d.readUint(8); ptr == noAlloc { |
| if t.Kind() != reflect.Ptr { |
| return newValueError(ErrUnexpectedNullRef, "string") |
| } |
| v.Set(reflect.Zero(t)) |
| return nil |
| } |
| max := n.Unnest() |
| if max != nil && int(size) > *max { |
| return newExpectError(ErrStringTooLong, *max, size) |
| } |
| s := string(d.buffer[d.nextObject : d.nextObject+int(size)]) |
| if t.Kind() == reflect.Ptr { |
| v.Set(reflect.New(t.Elem())) |
| v.Elem().Set(reflect.ValueOf(s)) |
| } else { |
| v.Set(reflect.ValueOf(s)) |
| } |
| d.nextObject += align(int(size), 8) |
| return nil |
| } |
| |
| // unmarshalHandle unmarshals a handle into a value, validating that it is valid if the handle is |
| // not nullable. |
| func (d *decoder) unmarshalHandle(v reflect.Value, n nestedTypeData) error { |
| h := d.readUint(4) |
| switch h { |
| case uint64(noHandle): |
| if !n.nullable { |
| return ErrUnexpectedNullHandle |
| } |
| v.SetUint(uint64(zx.HandleInvalid)) |
| case uint64(handlePresent): |
| if len(d.handles) == 0 { |
| return ErrNotEnoughHandles |
| } |
| v.SetUint(uint64(d.handles[0])) |
| d.handles = d.handles[1:] |
| default: |
| return newValueError(ErrBadHandleEncoding, h) |
| } |
| return nil |
| } |
| |
| // unmarshalPointer unmarshals nullable FIDL types that are represented by golang pointer |
| // indirections. The expected types and values t and v are pointers, i.e. they start with *. |
| func (d *decoder) unmarshalPointer(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| switch t.Elem().Kind() { |
| case reflect.Slice: |
| return d.unmarshalVector(t, v, n) |
| case reflect.String: |
| return d.unmarshalString(t, v, n) |
| case reflect.Struct: |
| return d.unmarshalStructOrUnionPointer(t, v) |
| } |
| return newValueError(ErrInvalidPointerType, t.Name()) |
| } |
| |
| // unmarshal is the central recursive function core to unmarshalling, and |
| // traverses the tree-like structure of the input type t. v represents |
| // the value associated with the type t. |
| func (d *decoder) unmarshal(t reflect.Type, v reflect.Value, n nestedTypeData) error { |
| switch t.Kind() { |
| case reflect.Ptr: |
| return d.unmarshalPointer(t, v, n) |
| case reflect.Slice: |
| return d.unmarshalVector(t, v, n) |
| case reflect.String: |
| return d.unmarshalString(t, v, n) |
| } |
| if isHandleType(t) { |
| return d.unmarshalHandle(v, n) |
| } |
| if isInterfaceType(t) || isInterfaceRequestType(t) { |
| // 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. |
| return d.unmarshalHandle(v.Field(0), n) |
| } |
| return d.unmarshalInline(t, v, n) |
| } |
| |
| // UnmarshalHeader parses a FIDL header in the data into m. |
| func UnmarshalHeader(data []byte, m *MessageHeader) error { |
| if len(data) < 16 { |
| return ErrMessageTooSmall |
| } |
| d := decoder{buffer: data} |
| m.Txid = uint32(d.readUint(4)) |
| m.Reserved = uint32(d.readUint(4)) |
| m.Flags = uint32(d.readUint(4)) |
| m.Ordinal = uint32(d.readUint(4)) |
| return nil |
| } |
| |
| // Unmarshal parses the encoded FIDL payload in data and handles, storing the |
| // decoded payload in s. |
| // |
| // The value pointed to by s must be a pointer to a golang struct which represents |
| // the decoded primary object of a FIDL message. The data decode process is guided |
| // by the structure of the struct pointed to by s. |
| // |
| // TODO(mknyszek): More rigorously validate the input. |
| func Unmarshal(data []byte, handles []zx.Handle, s Payload) error { |
| // First, let's make sure we have the right type in s. |
| t := reflect.TypeOf(s) |
| if t.Kind() != reflect.Ptr { |
| return errors.New("expected a pointer") |
| } |
| t = t.Elem() |
| if t.Kind() != reflect.Struct { |
| return errors.New("primary object must be a struct") |
| } |
| |
| // Get the payload's value and unmarshal it. |
| nextObject := align(s.InlineSize(), 8) |
| d := decoder{ |
| buffer: data, |
| handles: handles, |
| nextObject: nextObject, |
| } |
| return d.unmarshalStructFields(t, reflect.ValueOf(s).Elem()) |
| } |