[fidl][go] Switch to new encoding/decoding
Change-Id: Id5638e9c893d541b4fd39a77c77ba5fd446a2e3b
diff --git a/src/syscall/zx/fidl/bindings.go b/src/syscall/zx/fidl/bindings.go
index b330cea..4e9e56b 100644
--- a/src/syscall/zx/fidl/bindings.go
+++ b/src/syscall/zx/fidl/bindings.go
@@ -147,10 +147,10 @@
msg := respb[:nb]
handles := resph[:nh]
var header MessageHeader
- if err := UnmarshalHeader(msg, &header); err != nil {
+ if _, _, err := UnmarshalNew(msg, nil, &header); err != nil {
return false, err
}
- p, err := b.Stub.Dispatch(header.Ordinal, msg[MessageHeaderSize:], handles)
+ p, err := b.Stub.DispatchNew(header.Ordinal, msg[MessageHeaderSize:], handles)
switch {
case err == ErrUnknownOrdinal:
// If we couldn't determine the ordinal, we still own the
@@ -166,7 +166,7 @@
if p == nil {
return false, nil
}
- cnb, cnh, err := MarshalMessage(&header, p, respb, resph)
+ cnb, cnh, err := marshalHeaderThenMessage(&header, p, respb, resph)
if err != nil {
return false, err
}
diff --git a/src/syscall/zx/fidl/deletemepostmigration.go b/src/syscall/zx/fidl/deletemepostmigration.go
new file mode 100644
index 0000000..5aa4788
--- /dev/null
+++ b/src/syscall/zx/fidl/deletemepostmigration.go
@@ -0,0 +1,67 @@
+// Copyright 2019 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 "syscall/zx"
+
+// TODO(pascallouis): Delete all of this post migration.
+
+const MessageHeaderSize int = 16
+
+type Payload interface{}
+
+func MarshalHeader(header *MessageHeader, data []byte) {
+ _, _, err := MarshalNew(header, data, nil)
+ if err != nil {
+ panic(err)
+ }
+}
+
+func UnmarshalHeader(data []byte, header *MessageHeader) error {
+ _, _, err := UnmarshalNew(data, nil, header)
+ return err
+}
+
+func MarshalMessage(header *MessageHeader, s Payload, data []byte, handles []zx.Handle) (int, int, error) {
+ return marshalHeaderThenMessage(header, toMessage(s), data, handles)
+}
+
+func UnmarshalMessage(data []byte, handles []zx.Handle, header *MessageHeader, s Payload) error {
+ return unmarshalHeaderThenMessage(data, handles, header, toMessage(s))
+}
+
+func (p *ChannelProxy) Send(ordinal uint32, req Payload) error {
+ return p.SendNew(ordinal, toMessage(req))
+}
+
+func (p *ChannelProxy) Recv(ordinal uint32, resp Payload, gen_ordinal ...uint32) error {
+ return p.RecvNew(ordinal, toMessage(resp), gen_ordinal...)
+}
+
+func (p *ChannelProxy) Call(ordinal uint32, req Payload, resp Payload) error {
+ return p.CallNew(ordinal, toMessage(req), resp.(Message))
+}
+
+func (p *SocketControlProxy) Send(ordinal uint32, req Payload) error {
+ return p.SendNew(ordinal, toMessage(req))
+}
+
+func (p *SocketControlProxy) Recv(ordinal uint32, resp Payload, gen_ordinal ...uint32) error {
+ return p.RecvNew(ordinal, toMessage(resp), gen_ordinal...)
+}
+
+func (p *SocketControlProxy) Call(ordinal uint32, req Payload, resp Payload) error {
+ return p.CallNew(ordinal, toMessage(req), toMessage(resp))
+}
+
+func toMessage(s Payload) Message {
+ var msg Message
+ if s != nil {
+ msg = s.(Message)
+ }
+ return msg
+}
diff --git a/src/syscall/zx/fidl/encoding.go b/src/syscall/zx/fidl/encoding.go
index e2c6394..7410470 100644
--- a/src/syscall/zx/fidl/encoding.go
+++ b/src/syscall/zx/fidl/encoding.go
@@ -7,11 +7,8 @@
package fidl
import (
- "errors"
"math"
"reflect"
- "strconv"
- "strings"
"syscall/zx"
)
@@ -41,33 +38,6 @@
proxyType = reflect.TypeOf(ChannelProxy{})
)
-type structKind int
-
-const (
- _ structKind = iota
- aStruct
- aUnion
- aXUnion
- aTable
-)
-
-// whichStructKind returns the kind of FIDL type this golang type represents.
-func whichStructKind(t reflect.Type) structKind {
- if t.Kind() != reflect.Struct {
- panic("can only determine struct kind")
- }
- if t.NumField() > 1 {
- if t.Field(0).Tag.Get("fidl") == "tag" {
- return aUnion
- } else if strings.HasPrefix(t.Field(0).Tag.Get("fidl2"), "t") {
- return aTable
- } else if strings.HasPrefix(t.Field(0).Tag.Get("fidl2"), "x") {
- return aXUnion
- }
- }
- return aStruct
-}
-
// isHandleType returns true if the reflected type is a Fuchsia handle type.
func isHandleType(t reflect.Type) bool {
switch t {
@@ -104,158 +74,6 @@
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
-
- // tagKind: fidl2 tag
- tagKind
-
- // bounds: fidl2 bounds
- bounds
-}
-
-// 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(field reflect.StructField, isTable bool) error {
- tagKind, bounds := readTag(field)
- n.tagKind = tagKind
- n.bounds = bounds
-
- raw, ok := field.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.
@@ -292,16 +110,6 @@
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
@@ -317,373 +125,6 @@
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 aXUnion:
- fallthrough
- case aTable:
- err = e.marshalWithMarshaler(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)
- }
- 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 {
- // TODO(FIDL-410): Currently, we generate a request or response struct with
- // size 0 for methods and events with no arguments or returns. This escapes
- // this case. We shouldn't generate those structs, and instead assemble
- // transactional messages differently.
- if v.Addr().Interface().(Payload).InlineSize() == 0 {
- return nil
- }
-
- var exportedNumFields int
-
- for i := 0; i < t.NumField(); i++ {
- f := t.Field(i)
- // If it's an unexported field, ignore it.
- if f.PkgPath != "" {
- continue
- }
-
- exportedNumFields++
-
- var n nestedTypeData
- if err := n.FromTag(f, false); err != nil {
- return err
- }
- if err := e.marshal(f.Type, v.Field(i), n); err != nil {
- return err
- }
- }
-
- if exportedNumFields == 0 {
- // Encountered an empty struct, which is all-zero and has a size of 1.
- e.writeUint(0, 1)
- }
-
- 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, 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) marshalWithMarshaler(t reflect.Type, v reflect.Value) error {
- m, err := createMarshaler(t)
- if err != nil {
- return err
- }
- return m.marshal(v, e)
-}
-
-func (e *encoder) marshalWithOptMarshaler(t reflect.Type, v reflect.Value, n nestedTypeData) error {
- m, err := createOptMarshalerForField(t.Elem(), n.bounds)
- 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:
- if whichStructKind(t.Elem()) == aXUnion {
- return e.marshalWithOptMarshaler(t, v, n)
- }
- 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 {
@@ -702,18 +143,6 @@
handles []zx.Handle
}
-// readInt reads an integer value of byte-width size from the
-// buffer, and protects against reading past the end of the buffer.
-//
-// Before a read, the head is moved 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.
-func (d *decoder) readInt(size int) (int64, error) {
- i, err := d.readUint(size)
- return int64(i), err
-}
-
// readUint reads an unsigned integer value of byte-width size from the
// buffer, and protects against reading past the end of the buffer.
//
@@ -738,453 +167,3 @@
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 aXUnion:
- fallthrough
- case aTable:
- err = d.unmarshalWithMarshaler(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 i, err := d.readUint(8); err != nil {
- return err
- } else if i == 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)
- }
- 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 {
- // TODO(FIDL-410): Currently, we generate a request or response struct with
- // size 0 for methods and events with no arguments or returns. This escapes
- // this case. We shouldn't generate those structs, and instead assemble
- // transactional messages differently.
- if v.Addr().Interface().(Payload).InlineSize() == 0 {
- return nil
- }
-
- var exportedNumFields int
-
- for i := 0; i < t.NumField(); i++ {
- f := t.Field(i)
-
- // If it's an unexported field, ignore it.
- if f.PkgPath != "" {
- continue
- }
-
- exportedNumFields++
-
- var n nestedTypeData
- if err := n.FromTag(f, false); err != nil {
- return err
- }
-
- if err := d.unmarshal(f.Type, v.Field(i), n); err != nil {
- return err
- }
- }
-
- if exportedNumFields == 0 {
- // Encountered an empty struct, which should be all-zero and has a size of
- // 1.
- expectedZero, err := d.readUint(1)
- if err != nil {
- return err
- } else if expectedZero != 0 {
- return newValueError(ErrInvalidEmptyStruct, expectedZero)
- }
- }
-
- 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, err := d.readUint(4)
- if err != nil {
- return err
- }
-
- // 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, 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) unmarshalWithMarshaler(t reflect.Type, v reflect.Value) error {
- m, err := createMarshaler(t)
- if err != nil {
- return err
- }
- return m.unmarshal(d, v)
-}
-
-func (d *decoder) unmarshalWithOptMarshaler(t reflect.Type, v reflect.Value, n nestedTypeData) error {
- m, err := createOptMarshalerForField(t.Elem(), n.bounds)
- 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 {
- kind := t.Kind()
- if kind == reflect.Array {
- return d.unmarshalArray(t, v, n)
- }
- if kind == reflect.Struct {
- return d.unmarshalStructOrUnionInline(t, v)
- }
-
- val, err := d.readUint(int(t.Size()))
- if err != nil {
- return err
- }
-
- switch kind {
- case reflect.Bool:
- switch val {
- case 0:
- v.SetBool(false)
- case 1:
- v.SetBool(true)
- default:
- return newValueError(ErrInvalidBoolValue, val)
- }
- case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- v.SetInt(int64(val))
- case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- v.SetUint(val)
- case reflect.Float32:
- v.SetFloat(float64(math.Float32frombits(uint32(val))))
- case reflect.Float64:
- v.SetFloat(math.Float64frombits(val))
- 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, err := d.readInt(8)
- if err != nil {
- return err
- } else if size < 0 {
- return newExpectError(ErrVectorTooLong, math.MaxInt64, size)
- }
- if ptr, err := d.readUint(8); err != nil {
- return err
- } else if 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, err := d.readInt(8)
- if err != nil {
- return err
- } else if size < 0 {
- return newExpectError(ErrVectorTooLong, math.MaxInt64, size)
- }
-
- if ptr, err := d.readUint(8); err != nil {
- return err
- } else if 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, err := d.readUint(4)
- if err != nil {
- return err
- }
- 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:
- if whichStructKind(t.Elem()) == aXUnion {
- return d.unmarshalWithOptMarshaler(t, v, n)
- }
- 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}
- txid, err := d.readUint(4)
- if err != nil {
- return err
- }
- reserved, err := d.readUint(4)
- if err != nil {
- return err
- }
- flags, err := d.readUint(4)
- if err != nil {
- return err
- }
- ordinal, err := d.readUint(4)
- if err != nil {
- return err
- }
-
- m.Txid = uint32(txid)
- m.Reserved = uint32(reserved)
- m.Flags = uint32(flags)
- m.Ordinal = uint32(ordinal)
- 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())
-}
diff --git a/src/syscall/zx/fidl/encoding_new.go b/src/syscall/zx/fidl/encoding_new.go
index 2233235..c49f270 100644
--- a/src/syscall/zx/fidl/encoding_new.go
+++ b/src/syscall/zx/fidl/encoding_new.go
@@ -94,7 +94,12 @@
return m.delegate.unmarshal(in, v)
}
-// MarshalNew marshals (or encodes) a message into the data and handles slices.
+// Marshal marshals (or encodes) a message into the data and handles slices.
+func Marshal(s Payload, data []byte, handles []zx.Handle) (int, int, error) {
+ return MarshalNew(s.(Message), data, handles)
+}
+
+// TODO(pascallouis): promote to being The Marshal.
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.
@@ -116,9 +121,15 @@
return len(out.buffer), len(out.handles), nil
}
-// UnmarshalNew unmarshals (or decodes) into message using the data and handles
+// Unmarshal unmarshals (or decodes) into message using the data and handles
// slices.
-func UnmarshalNew(data []byte, handles []zx.Handle, message Message) error {
+func Unmarshal(data []byte, handles []zx.Handle, s Payload) error {
+ _, _, err := UnmarshalNew(data, handles, s.(Message))
+ return err
+}
+
+// TODO(pascallouis): promote to being The Unmarshal.
+func UnmarshalNew(data []byte, handles []zx.Handle, message Message) (int, int, error) {
// By construction, we know that message is a pointer to a struct since
// we only generate pointer receiver methods for top-level messages.
// Should one implement the interface differently, and call into this
@@ -136,7 +147,10 @@
handles: handles,
nextObject: nextObject,
}
- return m.unmarshal(in, v)
+ if err := m.unmarshal(in, v); err != nil {
+ return 0, 0, err
+ }
+ return len(in.buffer), len(handles) - len(in.handles), nil
}
const tagKey = "fidl2"
@@ -1147,7 +1161,7 @@
func (m mInt) marshal(v reflect.Value, out *encoder) error {
size := int(m)
- out.writeInt(v.Int(), size)
+ out.writeUint(uint64(v.Int()), size)
return nil
}
diff --git a/src/syscall/zx/fidl/fidl_test/encoding_new_test.go b/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
index 561de06..49e5034 100644
--- a/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
+++ b/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
@@ -19,30 +19,6 @@
. "syscall/zx/fidl/bindingstest"
)
-type marshalFunc struct {
- name string
- fn func(Message, []byte, []zx.Handle) (int, int, error)
-}
-
-var marshalFuncs = []marshalFunc{
- {"Marshal", func(input Message, respb []byte, resph []zx.Handle) (int, int, error) {
- return Marshal(input, respb, resph)
- }},
- {"MarshalNew", MarshalNew},
-}
-
-type unmarshalFunc struct {
- name string
- fn func([]byte, []zx.Handle, Message) error
-}
-
-var unmarshalFuncs = []unmarshalFunc{
- {"Unmarshal", func(respb []byte, resph []zx.Handle, output Message) error {
- return Unmarshal(respb, resph, output)
- }},
- {"UnmarshalNew", UnmarshalNew},
-}
-
type example struct {
name string
input Message
@@ -306,12 +282,8 @@
}
}
-type checker struct {
- marshalFunc
- unmarshalFunc
-}
-
-func (c checker) check(t *testing.T, input Message, expectSize int) {
+// TODO(FIDL-508): Convert all these tests to be conformance tests, using GIDL.
+func check(t *testing.T, input Message, expectSize int) {
t.Helper()
defer func() {
if r := recover(); r != nil {
@@ -324,7 +296,7 @@
}()
var respb [zx.ChannelMaxMessageBytes]byte
var resph [zx.ChannelMaxMessageHandles]zx.Handle
- nb, nh, err := c.marshalFunc.fn(input, respb[:], resph[:])
+ nb, nh, err := MarshalNew(input, respb[:], resph[:])
if err != nil {
t.Fatalf("marshal: failed: %s", err)
}
@@ -332,29 +304,31 @@
t.Fatalf("marshal: expected size %d but got %d: %v", expectSize, nb, respb[:nb])
}
output := makeDefault(input)
- if err := c.unmarshalFunc.fn(respb[:nb], resph[:nh], output); err != nil {
+ nbActual, nhActual, err := UnmarshalNew(respb[:nb], resph[:nh], output)
+ if err != nil {
t.Fatalf("unmarshal: failed: %s", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("unmarshal: expected: %v, got: %v", input, output)
}
+ if nb != nbActual {
+ t.Fatalf("unmarshal: num bytes, expected: %d, got: %d", nb, nbActual)
+ }
+ if nh != nhActual {
+ t.Fatalf("unmarshal: num handles, expected: %d, got: %d", nh, nhActual)
+ }
}
func TestCorrectness(t *testing.T) {
- for _, m := range marshalFuncs {
- for _, u := range unmarshalFuncs {
- c := checker{m, u}
- for _, ex := range general() {
- t.Run(fmt.Sprintf("%s-%s-%s", m.name, u.name, ex.name), func(t *testing.T) {
- c.check(t, ex.input, ex.expectSize)
- })
- }
- for _, ex := range fuchsia() {
- t.Run(fmt.Sprintf("%s-%s-%s", m.name, u.name, ex.name), func(t *testing.T) {
- c.check(t, ex.input, ex.expectSize)
- })
- }
- }
+ for _, ex := range general() {
+ t.Run(ex.name, func(t *testing.T) {
+ check(t, ex.input, ex.expectSize)
+ })
+ }
+ for _, ex := range fuchsia() {
+ t.Run(ex.name, func(t *testing.T) {
+ check(t, ex.input, ex.expectSize)
+ })
}
}
@@ -376,46 +350,49 @@
}
func (ex successCase) check(t *testing.T) {
- for _, mFn := range marshalFuncs {
- for _, uFn := range unmarshalFuncs {
- t.Run(ex.name+"_"+mFn.name+"_"+uFn.name, func(t *testing.T) {
- defer func() {
- if r := recover(); r != nil {
- // When running tests on device, this bubbles up the error
- // on the console launching the tests, rather than having
- // to look at the device's kernel logs.
- t.Fatalf("panic: %s", r)
- panic(r)
- }
- }()
- var respb [zx.ChannelMaxMessageBytes]byte
- var resph [zx.ChannelMaxMessageHandles]zx.Handle
- nb, nh, err := mFn.fn(ex.input, respb[:], resph[:])
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(ex.bytes, respb[:nb]) {
- t.Fatalf("expected %x, got %x", ex.bytes, respb[:nb])
- }
- if len(ex.handles) == 0 {
- if nh != 0 {
- t.Fatalf("no handles expected, got %d", nh)
- }
- } else {
- if !reflect.DeepEqual(ex.handles, resph[:nh]) {
- t.Fatalf("expected %v, got %v", ex.handles, resph[:nh])
- }
- }
- output := makeDefault(ex.input)
- if err := uFn.fn(respb[:nb], resph[:nh], output); err != nil {
- t.Fatalf("unmarshal: failed: %s", err)
- }
- if !reflect.DeepEqual(ex.input, output) {
- t.Fatalf("unmarshal: expected: %v, got: %v", ex.input, output)
- }
- })
+ t.Run(ex.name, func(t *testing.T) {
+ defer func() {
+ // When running tests on device, this bubbles up the error on the
+ // console launching the tests, rather than having to look at the
+ // device's kernel logs.
+ if r := recover(); r != nil {
+ t.Fatalf("panic: %s", r)
+ panic(r)
+ }
+ }()
+ var respb [zx.ChannelMaxMessageBytes]byte
+ var resph [zx.ChannelMaxMessageHandles]zx.Handle
+ nb, nh, err := MarshalNew(ex.input, respb[:], resph[:])
+ if err != nil {
+ t.Fatal(err)
}
- }
+ if !bytes.Equal(ex.bytes, respb[:nb]) {
+ t.Fatalf("expected %x, got %x", ex.bytes, respb[:nb])
+ }
+ if len(ex.handles) == 0 {
+ if nh != 0 {
+ t.Fatalf("no handles expected, got %d", nh)
+ }
+ } else {
+ if !reflect.DeepEqual(ex.handles, resph[:nh]) {
+ t.Fatalf("expected %v, got %v", ex.handles, resph[:nh])
+ }
+ }
+ output := makeDefault(ex.input)
+ nbActual, nhActual, err := UnmarshalNew(respb[:nb], resph[:nh], output)
+ if err != nil {
+ t.Fatalf("unmarshal: failed: %s", err)
+ }
+ if !reflect.DeepEqual(ex.input, output) {
+ t.Fatalf("unmarshal: expected: %v, got: %v", ex.input, output)
+ }
+ if nb != nbActual {
+ t.Fatalf("unmarshal: num bytes, expected: %d, got: %d", nb, nbActual)
+ }
+ if nh != nhActual {
+ t.Fatalf("unmarshal: num handles, expected: %d, got: %d", nh, nhActual)
+ }
+ })
}
type errorCaseUnmarshal struct {
@@ -894,12 +871,12 @@
&TestNewerSimpleTable{Table: nst},
}
for _, expected := range cases {
- for _, u := range unmarshalFuncs {
- output := makeDefault(expected)
- u.fn(simpleTableWithXY, nil, output)
- if !reflect.DeepEqual(expected, output) {
- t.Fatalf("unmarshal: expected: %v, got: %v", expected, output)
- }
+ output := makeDefault(expected)
+ if _, _, err := UnmarshalNew(simpleTableWithXY, nil, output); err != nil {
+ t.Fatalf("unmarshal: failed: %s", err)
+ }
+ if !reflect.DeepEqual(expected, output) {
+ t.Fatalf("unmarshal: expected: %v, got: %v", expected, output)
}
}
}
@@ -916,12 +893,13 @@
&TestNewerSimpleTable{Table: nst},
}
for _, expected := range cases {
- for _, u := range unmarshalFuncs {
- output := makeDefault(expected)
- u.fn(simpleTableWithY, nil, output)
- if !reflect.DeepEqual(expected, output) {
- t.Fatalf("unmarshal: expected: %v, got: %v", expected, output)
- }
+ output := makeDefault(expected)
+ _, _, err := UnmarshalNew(simpleTableWithY, nil, output)
+ if err != nil {
+ t.Fatalf("unmarshal: failed: %s", err)
+ }
+ if !reflect.DeepEqual(expected, output) {
+ t.Fatalf("unmarshal: expected: %v, got: %v", expected, output)
}
}
}
@@ -980,7 +958,7 @@
func TestFailuresUnmarshalNoHandles(t *testing.T) {
for _, ex := range allErrorCasesUnmarshal {
t.Run(ex.name, func(t *testing.T) {
- err := UnmarshalNew(ex.input, nil, ex.message)
+ _, _, err := UnmarshalNew(ex.input, nil, ex.message)
validationErr, ok := err.(ValidationError)
if !ok {
t.Fatalf("expected ValidationError, was %v", err)
@@ -993,19 +971,17 @@
}
func benchmarkMarshal(b *testing.B, ex example) {
- for _, m := range marshalFuncs {
- b.Run(fmt.Sprintf("%s-%s", m.name, ex.name), func(b *testing.B) {
- b.StopTimer()
- var respb [zx.ChannelMaxMessageBytes]byte
- var resph [zx.ChannelMaxMessageHandles]zx.Handle
- b.StartTimer()
- for n := 0; n < b.N; n++ {
- if _, _, err := m.fn(ex.input, respb[:], resph[:]); err != nil {
- b.Fail()
- }
+ b.Run(ex.name, func(b *testing.B) {
+ b.StopTimer()
+ var respb [zx.ChannelMaxMessageBytes]byte
+ var resph [zx.ChannelMaxMessageHandles]zx.Handle
+ b.StartTimer()
+ for n := 0; n < b.N; n++ {
+ if _, _, err := MarshalNew(ex.input, respb[:], resph[:]); err != nil {
+ b.Fail()
}
- })
- }
+ }
+ })
}
func BenchmarkMarshal(b *testing.B) {
@@ -1018,24 +994,22 @@
}
func benchmarkUnmarshal(b *testing.B, ex example) {
- for _, u := range unmarshalFuncs {
- b.Run(fmt.Sprintf("%s-%s", u.name, ex.name), func(b *testing.B) {
- b.StopTimer()
- var respb [zx.ChannelMaxMessageBytes]byte
- var resph [zx.ChannelMaxMessageHandles]zx.Handle
- nb, nh, err := Marshal(ex.input, respb[:], resph[:])
- if err != nil {
+ b.Run(ex.name, func(b *testing.B) {
+ b.StopTimer()
+ var respb [zx.ChannelMaxMessageBytes]byte
+ var resph [zx.ChannelMaxMessageHandles]zx.Handle
+ nb, nh, err := MarshalNew(ex.input, respb[:], resph[:])
+ if err != nil {
+ b.Fail()
+ }
+ output := makeDefault(ex.input)
+ b.StartTimer()
+ for n := 0; n < b.N; n++ {
+ if _, _, err := UnmarshalNew(respb[:nb], resph[:nh], output); err != nil {
b.Fail()
}
- output := makeDefault(ex.input)
- b.StartTimer()
- for n := 0; n < b.N; n++ {
- if err := u.fn(respb[:nb], resph[:nh], output); err != nil {
- b.Fail()
- }
- }
- })
- }
+ }
+ })
}
func BenchmarkUnmarshal(b *testing.B) {
diff --git a/src/syscall/zx/fidl/fidl_test/encoding_test.go b/src/syscall/zx/fidl/fidl_test/encoding_test.go
deleted file mode 100644
index d9f03e2..0000000
--- a/src/syscall/zx/fidl/fidl_test/encoding_test.go
+++ /dev/null
@@ -1,231 +0,0 @@
-// 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_test
-
-import (
- "reflect"
- "syscall/zx"
- "testing"
-
- . "syscall/zx/fidl"
- . "syscall/zx/fidl/bindingstest"
-)
-
-func testIdentity(t *testing.T, input Payload, expectSize int, output Payload) {
- t.Helper()
- var respb [zx.ChannelMaxMessageBytes]byte
- var resph [zx.ChannelMaxMessageHandles]zx.Handle
- nb, nh, err := Marshal(input, respb[:], resph[:])
- if err != nil {
- t.Fatal(err)
- }
- if nb != expectSize {
- t.Fatalf("expected size %d but got %d: %v", expectSize, nb, respb[:nb])
- }
- if err := Unmarshal(respb[:nb], resph[:nh], output); err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(input, output) {
- t.Fatalf("expected: %v, got: %v", input, output)
- }
-}
-
-func TestEncodingIdentity(t *testing.T) {
- t.Run("Header", func(t *testing.T) {
- var buf [MessageHeaderSize]byte
- m := MessageHeader{
- Txid: 1215,
- Reserved: 0,
- Flags: 111111,
- Ordinal: 889,
- }
- MarshalHeader(&m, buf[:])
-
- var header MessageHeader
- if err := UnmarshalHeader(buf[:], &header); err != nil {
- t.Fatal(err)
- }
- if m != header {
- t.Fatalf("expected: %v, got: %v", m, header)
- }
- })
- t.Run("Simple", func(t *testing.T) {
- testIdentity(t, &TestSimple{X: 124}, 8, &TestSimple{})
- testIdentity(t, &TestSimpleBool{X: true}, 8, &TestSimpleBool{})
- })
- t.Run("Alignment", func(t *testing.T) {
- testIdentity(t, &TestAlignment1{X: -36, Y: -10, Z: 51}, 8, &TestAlignment1{})
- testIdentity(t, &TestAlignment2{
- A: 1212141,
- B: 908935,
- C: -1,
- D: 125,
- E: -22,
- F: 111,
- G: 1515,
- H: 65535,
- I: 1515,
- }, 24, &TestAlignment2{})
- })
- t.Run("Floats", func(t *testing.T) {
- testIdentity(t, &TestFloat1{A: -36.0}, 8, &TestFloat1{})
- testIdentity(t, &TestFloat2{A: -1254918271.0}, 8, &TestFloat2{})
- testIdentity(t, &TestFloat3{A: 1241.1, B: 0.2141, C: 20, D: 0.0}, 32, &TestFloat3{})
- })
- t.Run("Arrays", func(t *testing.T) {
- testIdentity(t, &TestArray1{A: [5]int8{1, 77, 2, 4, 5}}, 8, &TestArray1{})
- testIdentity(t, &TestArray2{A: -1.0, B: [1]float32{0.2}}, 16, &TestArray2{})
- testIdentity(t, &TestArray3{
- A: -999,
- B: [3]uint16{11, 12, 13},
- C: 1021,
- }, 24, &TestArray3{})
- testIdentity(t, &TestArray4{
- A: [9]bool{true, false, false, true, false, true, true, true, true},
- }, 16, &TestArray4{})
- })
- t.Run("Strings", func(t *testing.T) {
- testIdentity(t, &TestString1{A: "str", B: nil}, 40, &TestString1{})
- testIdentity(t, &TestString2{A: [2]string{"hello", "g"}}, 48, &TestString2{})
- s := "bye"
- testIdentity(t, &TestString3{
- A: [2]string{"boop", "g"},
- B: [2]*string{&s, nil},
- }, 88, &TestString3{})
- })
- t.Run("Vectors", func(t *testing.T) {
- v1 := []int64{-1}
- testIdentity(t, &TestVector1{
- A: []int8{1, 2, 3, 4},
- B: nil,
- C: []int32{99},
- D: &v1,
- }, 88, &TestVector1{})
- v2 := []string{"x", "hello"}
- testIdentity(t, &TestVector2{
- A: [2][]int8{{9, -1}, {}},
- B: [][]int8{{-111, 41}, {-1, -1, -1, -1}},
- C: []*[]string{nil, &v2},
- }, 200, &TestVector2{})
- })
- t.Run("Structs", func(t *testing.T) {
- testIdentity(t, &TestStruct1{
- A: TestSimple{
- X: -9999,
- },
- B: &TestSimple{
- X: 1254125,
- },
- }, 24, &TestStruct1{})
- v1 := []int64{101010}
- testIdentity(t, &TestStruct2{
- A: TestArray1{
- A: [5]int8{1, 77, 2, 4, 5},
- },
- B: TestFloat1{
- A: 2.81212,
- },
- C: TestVector1{
- A: []int8{1, 2, 3, 4},
- B: nil,
- C: []int32{99},
- D: &v1,
- },
- D: &TestString1{
- A: "str",
- B: nil,
- },
- }, 152, &TestStruct2{})
- })
- t.Run("Unions", func(t *testing.T) {
- u1 := Union1{}
- u1.SetB(TestSimple{X: 555})
- testIdentity(t, &TestUnion1{
- A: u1,
- B: nil,
- }, 24, &TestUnion1{})
- testIdentity(t, &TestUnion2{
- A: []Union1{u1, u1, u1},
- B: []*Union1{&u1, nil, nil},
- }, 120, &TestUnion2{})
- })
- t.Run("Uninitialized unions", func(t *testing.T) {
- u1 := Union1{}
- // Intentionally don't set any members of the union.
- tu1 := TestUnion1{
- A: u1,
- B: nil,
- }
- var respb [zx.ChannelMaxMessageBytes]byte
- var resph [zx.ChannelMaxMessageHandles]zx.Handle
- _, _, err := Marshal(&tu1, respb[:], resph[:])
- if err == nil {
- t.Errorf("got %v when marshalling invalid union but expected ErrInvalidUnionTag", err)
- }
- })
- t.Run("Handles", func(t *testing.T) {
- vmo, err := zx.NewVMO(10, 0)
- if err != nil {
- t.Fatalf("failed to create vmo: %v", err)
- }
- testIdentity(t, &TestHandle1{
- A: zx.Handle(22),
- B: zx.HandleInvalid,
- C: vmo,
- D: zx.VMO(zx.HandleInvalid),
- }, 16, &TestHandle1{})
- testIdentity(t, &TestHandle2{
- A: []zx.Handle{zx.Handle(vmo)},
- B: []zx.VMO{zx.VMO(zx.HandleInvalid)},
- }, 48, &TestHandle2{})
- vmo.Close()
- })
- t.Run("Interfaces", func(t *testing.T) {
- h0, h1, err := zx.NewChannel(0)
- defer h0.Close()
- defer h1.Close()
- if err != nil {
- t.Fatalf("failed to create vmo: %v", err)
- }
- testIdentity(t, &TestInterface1{
- A: Test1Interface(ChannelProxy{Channel: h0}),
- B: Test1Interface(ChannelProxy{Channel: zx.Channel(zx.HandleInvalid)}),
- C: Test1InterfaceRequest(InterfaceRequest{Channel: h1}),
- D: Test1InterfaceRequest(InterfaceRequest{
- Channel: zx.Channel(zx.HandleInvalid),
- }),
- }, 16, &TestInterface1{})
- })
- t.Run("Tables", func(t *testing.T) {
- st := SimpleTable{}
- st.SetX(5)
- st.SetY(7)
- testIdentity(t, &TestSimpleTable{
- Table: st,
- }, 112, &TestSimpleTable{})
- })
-}
-
-func TestDontPanic(t *testing.T) {
- for _, tt := range baseErrorCasesUnmarshal {
- t.Run(tt.name, func(t *testing.T) {
- defer func() {
- if r := recover(); r != nil {
- t.Fatalf("code did panic: %v", r)
- }
- }()
- err := Unmarshal(tt.input, nil, tt.message)
- validationErr, ok := err.(ValidationError)
- if !ok {
- t.Fatalf("expected ValidationError, was %v", err)
- }
- if validationErr.Code() != tt.errorCode {
- t.Fatalf("expected %s, was %s", tt.errorCode, validationErr.Code())
- }
- })
- }
-}
diff --git a/src/syscall/zx/fidl/interface.go b/src/syscall/zx/fidl/interface.go
index 2e83bf3..9ad96b0 100644
--- a/src/syscall/zx/fidl/interface.go
+++ b/src/syscall/zx/fidl/interface.go
@@ -42,6 +42,11 @@
// Proxy represents the client side of a FIDL interface.
type Proxy interface {
IsValid() bool
+ SendNew(ordinal uint32, req Message) error
+ RecvNew(ordinal uint32, resp Message, gen_ordinal ...uint32) error
+ CallNew(ordinal uint32, req Message, resp Message) error
+
+ // TODO(pascallouis): delete when migration is over
Send(ordinal uint32, req Payload) error
Recv(ordinal uint32, resp Payload, gen_ordinal ...uint32) error
Call(ordinal uint32, req Payload, resp Payload) error
@@ -59,6 +64,9 @@
// It also takes the data as bytes and transforms it into arguments usable by
// the method implementation. It then optionally returns a response if the
// method has a response.
+ DispatchNew(ordinal uint32, bytes []byte, handles []zx.Handle) (Message, error)
+
+ // TODO(pascallouis): delete post migration.
Dispatch(ordinal uint32, bytes []byte, handles []zx.Handle) (Payload, error)
}
@@ -77,9 +85,9 @@
return h.IsValid()
}
-// Send sends the request payload over the channel with the specified ordinal
+// Send sends the request over the channel with the specified ordinal
// without a response.
-func (p *ChannelProxy) Send(ordinal uint32, req Payload) error {
+func (p *ChannelProxy) SendNew(ordinal uint32, req Message) error {
respb := messageBytesPool.Get().([]byte)
resph := messageHandlesPool.Get().([]zx.Handle)
@@ -91,7 +99,7 @@
Txid: 0, // Txid == 0 for messages without a response.
Ordinal: ordinal,
}
- nb, nh, err := MarshalMessage(&header, req, respb[:], resph[:])
+ nb, nh, err := marshalHeaderThenMessage(&header, req, respb[:], resph[:])
if err != nil {
return err
}
@@ -100,8 +108,8 @@
return p.Channel.Write(respb[:nb], resph[:nh], 0)
}
-// Recv waits for an event and writes the response into the response payload.
-func (p *ChannelProxy) Recv(ordinal uint32, resp Payload, gen_ordinal ...uint32) error {
+// Recv waits for an event and writes the response into the response.
+func (p *ChannelProxy) RecvNew(ordinal uint32, resp Message, gen_ordinal ...uint32) error {
respb := messageBytesPool.Get().([]byte)
resph := messageHandlesPool.Get().([]zx.Handle)
@@ -129,7 +137,7 @@
// Unmarshal the message.
var header MessageHeader
- if err := UnmarshalMessage(respb[:nb], resph[:nh], &header, resp); err != nil {
+ if err := unmarshalHeaderThenMessage(respb[:nb], resph[:nh], &header, resp); err != nil {
return err
}
// TODO(FIDL-425): Remove temporary handling of two ordinals.
@@ -145,10 +153,10 @@
return nil
}
-// Call sends the request payload over the channel with the specified ordinal
+// Call sends the request over the channel with the specified ordinal
// and synchronously waits for a response. It then writes the response into the
-// response payload.
-func (p *ChannelProxy) Call(ordinal uint32, req Payload, resp Payload) error {
+// response.
+func (p *ChannelProxy) CallNew(ordinal uint32, req Message, resp Message) error {
respb := messageBytesPool.Get().([]byte)
resph := messageHandlesPool.Get().([]zx.Handle)
@@ -159,7 +167,7 @@
header := MessageHeader{
Ordinal: ordinal,
}
- nb, nh, err := MarshalMessage(&header, req, respb[:], resph[:])
+ nb, nh, err := marshalHeaderThenMessage(&header, req, respb[:], resph[:])
if err != nil {
return err
}
@@ -171,7 +179,7 @@
}
// Unmarshal the message.
- if err := UnmarshalMessage(respb[:cnb], resph[:cnh], &header, resp); err != nil {
+ if err := unmarshalHeaderThenMessage(respb[:cnb], resph[:cnh], &header, resp); err != nil {
return err
}
if header.Ordinal != ordinal {
@@ -195,9 +203,9 @@
return h.IsValid()
}
-// Send sends the request payload over the socket control plane with the
+// Send sends the request over the socket control plane with the
// specified ordinal without a response.
-func (p *SocketControlProxy) Send(ordinal uint32, req Payload) error {
+func (p *SocketControlProxy) SendNew(ordinal uint32, req Message) error {
respb := messageBytesPool.Get().([]byte)
defer messageBytesPool.Put(respb)
@@ -207,7 +215,7 @@
Txid: 0, // Txid == 0 for messages without a response.
Ordinal: ordinal,
}
- nb, _, err := MarshalMessage(&header, req, respb[:], nil)
+ nb, _, err := marshalHeaderThenMessage(&header, req, respb[:], nil)
if err != nil {
return err
}
@@ -217,8 +225,8 @@
return err
}
-// Recv waits for an event and writes the response into the response payload.
-func (p *SocketControlProxy) Recv(ordinal uint32, resp Payload, gen_ordinal ...uint32) error {
+// Recv waits for an event and writes the response.
+func (p *SocketControlProxy) RecvNew(ordinal uint32, resp Message, gen_ordinal ...uint32) error {
respb := messageBytesPool.Get().([]byte)
defer messageBytesPool.Put(respb)
@@ -244,7 +252,7 @@
// Unmarshal the message.
var header MessageHeader
- if err := UnmarshalMessage(respb[:nb], nil, &header, resp); err != nil {
+ if err := unmarshalHeaderThenMessage(respb[:nb], nil, &header, resp); err != nil {
return err
}
// TODO(FIDL-425): Remove temporary handling of two ordinals.
@@ -260,14 +268,13 @@
return nil
}
-// Call sends the request payload over the socket control plane with the specified
-// ordinal and waits for a response. It then writes the response into the response
-// payload.
-func (p *SocketControlProxy) Call(ordinal uint32, req Payload, resp Payload) error {
- if err := p.Send(ordinal, req); err != nil {
+// Call sends the request over the socket control plane with the specified
+// ordinal and waits for a response. It then writes the response into the response.
+func (p *SocketControlProxy) CallNew(ordinal uint32, req Message, resp Message) error {
+ if err := p.SendNew(ordinal, req); err != nil {
return err
}
- if err := p.Recv(ordinal, resp); err != nil {
+ if err := p.RecvNew(ordinal, resp); err != nil {
return err
}
return nil
diff --git a/src/syscall/zx/fidl/message.go b/src/syscall/zx/fidl/message.go
index f4f16c0..3931b90 100644
--- a/src/syscall/zx/fidl/message.go
+++ b/src/syscall/zx/fidl/message.go
@@ -28,49 +28,44 @@
},
}
-// MessageHeaderSize is the size of the encoded message header.
-const MessageHeaderSize int = 16
-
-// MessageHeader is a header information for a message. This struct
-// corresponds to the C type fidl_message_header_t.
+// MessageHeader represents a transactional message header.
type MessageHeader struct {
+ _ struct{} `fidl2:"s,16,4"`
Txid uint32
Reserved uint32
Flags uint32
Ordinal uint32
}
-// Payload is an interface implemented by every FIDL structure.
-type Payload interface {
- // InlineSize returns the inline size of the structure, without any
- // out-of-line structures.
- InlineSize() int
+var mMessageHeader = MustCreateMarshaler(MessageHeader{})
- // InlineAlignment returns the alignment of the structure.
- InlineAlignment() int
+func (msg *MessageHeader) Marshaler() Marshaler {
+ return mMessageHeader
}
-// MarshalMessage is a convenience function for marshalling a full FIDL message,
-// that is, header and payload.
-func MarshalMessage(header *MessageHeader, s Payload, data []byte, handles []zx.Handle) (int, int, error) {
- MarshalHeader(header, data[:MessageHeaderSize])
- if s == nil {
+func marshalHeaderThenMessage(header *MessageHeader, msg Message, data []byte, handles []zx.Handle) (int, int, error) {
+ if _, _, err := MarshalNew(header, data[:MessageHeaderSize], nil); err != nil {
+ return 0, 0, err
+ }
+ if msg == nil {
return MessageHeaderSize, 0, nil
}
- // TODO(pascallouis): switch to MarshalNew when ready.
- nb, nh, err := Marshal(s, data[MessageHeaderSize:], handles[:])
- return nb + MessageHeaderSize, nh, err
+ nb, nh, err := MarshalNew(msg, data[MessageHeaderSize:], handles[:])
+ if err != nil {
+ return 0, 0, err
+ }
+ return nb + MessageHeaderSize, nh, nil
}
-// UnmarshalMessage is a convenience function for unmarshalling a full FIDL message,
-// that is, header and payload.
-func UnmarshalMessage(data []byte, handles []zx.Handle, header *MessageHeader, s Payload) error {
- if err := UnmarshalHeader(data[:MessageHeaderSize], header); err != nil {
+func unmarshalHeaderThenMessage(data []byte, handles []zx.Handle, header *MessageHeader, msg Message) error {
+ if _, _, err := UnmarshalNew(data[:MessageHeaderSize], nil, header); err != nil {
return err
}
- if s == nil {
+ if msg == nil {
return nil
}
- // TODO(pascallouis): switch to MarshalNew when ready.
- return Unmarshal(data[MessageHeaderSize:], handles[:], s)
+ if _, _, err := UnmarshalNew(data[MessageHeaderSize:], handles[:], msg); err != nil {
+ return err
+ }
+ return nil
}