[fidl][golang] Encoding/decoding for Tables
Test: fx run-test go_fidl_test
Change-Id: I65d0eee9f5a30becc8e1e0a890a6cc8d879743e7
diff --git a/BUILD.gn b/BUILD.gn
index fbe3cbb..8edfdbe 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -88,9 +88,10 @@
#
# Not being able to depend on automated generation is due to where code is
# placed, and how this relates to the tree structure of the toolchain.
- deps = [
- "src/syscall/zx/fidl/bindingstest($go_toolchain)",
- ]
+ # Disabling while fidlgen table generation is pending.
+ #deps = [
+ # "src/syscall/zx/fidl/bindingstest($go_toolchain)",
+ #]
}
go_test("go_zxwait_test") {
diff --git a/src/syscall/zx/fidl/bindingstest/impl.go b/src/syscall/zx/fidl/bindingstest/impl.go
index 71fcbe5..398b3ef 100644
--- a/src/syscall/zx/fidl/bindingstest/impl.go
+++ b/src/syscall/zx/fidl/bindingstest/impl.go
@@ -539,6 +539,90 @@
return 16
}
+type TestSimpleTable struct {
+ _ struct{} `fidl2:"s,16,8"`
+ Table SimpleTable
+}
+
+var _mTestSimpleTable = _bindings.CreateLazyMarshaler(TestSimpleTable{})
+
+func (msg *TestSimpleTable) Marshaler() _bindings.Marshaler {
+ return _mTestSimpleTable
+}
+
+// Implements Payload.
+func (_ *TestSimpleTable) InlineAlignment() int {
+ return 8
+}
+
+// Implements Payload.
+func (_ *TestSimpleTable) InlineSize() int {
+ return 16
+}
+
+type TestOlderSimpleTable struct {
+ _ struct{} `fidl2:"s,16,8"`
+ Table OlderSimpleTable
+}
+
+var _mTestOlderSimpleTable = _bindings.CreateLazyMarshaler(TestOlderSimpleTable{})
+
+func (msg *TestOlderSimpleTable) Marshaler() _bindings.Marshaler {
+ return _mTestOlderSimpleTable
+}
+
+// Implements Payload.
+func (_ *TestOlderSimpleTable) InlineAlignment() int {
+ return 8
+}
+
+// Implements Payload.
+func (_ *TestOlderSimpleTable) InlineSize() int {
+ return 16
+}
+
+type TestOptSimpleTable struct {
+ _ struct{} `fidl2:"s,8,8"`
+ OptTable *SimpleTable
+}
+
+var _mTestOptSimpleTable = _bindings.CreateLazyMarshaler(TestOptSimpleTable{})
+
+func (msg *TestOptSimpleTable) Marshaler() _bindings.Marshaler {
+ return _mTestOptSimpleTable
+}
+
+// Implements Payload.
+func (_ *TestOptSimpleTable) InlineAlignment() int {
+ return 8
+}
+
+// Implements Payload.
+func (_ *TestOptSimpleTable) InlineSize() int {
+ return 8
+}
+
+type TestNewerSimpleTable struct {
+ _ struct{} `fidl2:"s,16,8"`
+ Table NewerSimpleTable
+}
+
+var _mTestNewerSimpleTable = _bindings.CreateLazyMarshaler(TestNewerSimpleTable{})
+
+func (msg *TestNewerSimpleTable) Marshaler() _bindings.Marshaler {
+ return _mTestNewerSimpleTable
+}
+
+// Implements Payload.
+func (_ *TestNewerSimpleTable) InlineAlignment() int {
+ return 8
+}
+
+// Implements Payload.
+func (_ *TestNewerSimpleTable) InlineSize() int {
+ return 16
+}
+
type TestFuchsiaIoReadAtResponse struct {
_ struct{} `fidl2:"s,24,8"`
S int32
@@ -636,6 +720,162 @@
u.D = d
}
+type SimpleTable struct {
+ _ struct{} `fidl2:"t,16,8"`
+ X int64 `fidl:"1" fidl2:"1"`
+ XPresent bool
+ Y int64 `fidl:"5" fidl2:"5"`
+ YPresent bool
+}
+
+// Implements Payload.
+func (_ *SimpleTable) InlineAlignment() int {
+ return 8
+}
+
+// Implements Payload.
+func (_ *SimpleTable) InlineSize() int {
+ return 16
+}
+
+func (u *SimpleTable) SetX(x int64) {
+ u.X = x
+ u.XPresent = true
+}
+
+func (u *SimpleTable) GetX() int64 {
+ return u.X
+}
+
+func (u *SimpleTable) HasX() bool {
+ return u.XPresent
+}
+
+func (u *SimpleTable) ClearX() {
+ u.XPresent = false
+}
+
+func (u *SimpleTable) SetY(y int64) {
+ u.Y = y
+ u.YPresent = true
+}
+
+func (u *SimpleTable) GetY() int64 {
+ return u.Y
+}
+
+func (u *SimpleTable) HasY() bool {
+ return u.YPresent
+}
+
+func (u *SimpleTable) ClearY() {
+ u.YPresent = false
+}
+
+type OlderSimpleTable struct {
+ _ struct{} `fidl2:"t,16,8"`
+ X int64 `fidl:"1" fidl2:"1"`
+ XPresent bool
+}
+
+// Implements Payload.
+func (_ *OlderSimpleTable) InlineAlignment() int {
+ return 8
+}
+
+// Implements Payload.
+func (_ *OlderSimpleTable) InlineSize() int {
+ return 16
+}
+
+func (u *OlderSimpleTable) SetX(x int64) {
+ u.X = x
+ u.XPresent = true
+}
+
+func (u *OlderSimpleTable) GetX() int64 {
+ return u.X
+}
+
+func (u *OlderSimpleTable) HasX() bool {
+ return u.XPresent
+}
+
+func (u *OlderSimpleTable) ClearX() {
+ u.XPresent = false
+}
+
+type NewerSimpleTable struct {
+ _ struct{} `fidl2:"t,16,8"`
+ X int64 `fidl:"1" fidl2:"1"`
+ XPresent bool
+ Y int64 `fidl:"5" fidl2:"5"`
+ YPresent bool
+ Z int64 `fidl:"6" fidl2:"6"`
+ ZPresent bool
+}
+
+// Implements Payload.
+func (_ *NewerSimpleTable) InlineAlignment() int {
+ return 8
+}
+
+// Implements Payload.
+func (_ *NewerSimpleTable) InlineSize() int {
+ return 16
+}
+
+func (u *NewerSimpleTable) SetX(x int64) {
+ u.X = x
+ u.XPresent = true
+}
+
+func (u *NewerSimpleTable) GetX() int64 {
+ return u.X
+}
+
+func (u *NewerSimpleTable) HasX() bool {
+ return u.XPresent
+}
+
+func (u *NewerSimpleTable) ClearX() {
+ u.XPresent = false
+}
+
+func (u *NewerSimpleTable) SetY(y int64) {
+ u.Y = y
+ u.YPresent = true
+}
+
+func (u *NewerSimpleTable) GetY() int64 {
+ return u.Y
+}
+
+func (u *NewerSimpleTable) HasY() bool {
+ return u.YPresent
+}
+
+func (u *NewerSimpleTable) ClearY() {
+ u.YPresent = false
+}
+
+func (u *NewerSimpleTable) SetZ(z int64) {
+ u.Z = z
+ u.ZPresent = true
+}
+
+func (u *NewerSimpleTable) GetZ() int64 {
+ return u.Z
+}
+
+func (u *NewerSimpleTable) HasZ() bool {
+ return u.ZPresent
+}
+
+func (u *NewerSimpleTable) ClearZ() {
+ u.ZPresent = false
+}
+
const (
Test1EchoOrdinal uint32 = 10
Test1SurpriseOrdinal uint32 = 11
diff --git a/src/syscall/zx/fidl/bindingstest/test.fidl b/src/syscall/zx/fidl/bindingstest/test.fidl
index 45a4e99..982a1a6 100644
--- a/src/syscall/zx/fidl/bindingstest/test.fidl
+++ b/src/syscall/zx/fidl/bindingstest/test.fidl
@@ -146,6 +146,49 @@
request<Test1>? d;
};
+table SimpleTable {
+ 1: int64 x;
+ 2: reserved;
+ 3: reserved;
+ 4: reserved;
+ 5: int64 y;
+};
+
+struct TestSimpleTable {
+ SimpleTable table;
+};
+
+// A variant of SimpleTable that has just the first few fields.
+// Think of this as an older variant of that type!
+table OlderSimpleTable {
+ 1: int64 x;
+ 2: reserved;
+};
+
+struct TestOlderSimpleTable {
+ OlderSimpleTable table;
+};
+
+struct TestOptSimpleTable {
+ SimpleTable? opt_table;
+};
+
+// A variant of SimpleTable that has some additional new fields.
+// Think of this as an newer variant of that type!
+table NewerSimpleTable {
+ 1: int64 x;
+ 2: reserved;
+ 3: reserved;
+ 4: reserved;
+ 5: int64 y;
+ 6: int64 z;
+ 7: reserved;
+};
+
+struct TestNewerSimpleTable {
+ NewerSimpleTable table;
+};
+
// examples from fuchsia world.
const uint64 kMaxBuf = 8192;
diff --git a/src/syscall/zx/fidl/encoding.go b/src/syscall/zx/fidl/encoding.go
index 448b33c..e531421 100644
--- a/src/syscall/zx/fidl/encoding.go
+++ b/src/syscall/zx/fidl/encoding.go
@@ -41,14 +41,30 @@
proxyType = reflect.TypeOf(Proxy{})
)
-// isUnionType returns true if the reflected type is a FIDL union type.
-func isUnionType(t reflect.Type) bool {
+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.
- return t.Kind() == reflect.Struct && t.NumField() > 1 && t.Field(0).Tag.Get("fidl") == "tag"
+ 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.
@@ -116,7 +132,7 @@
case reflect.String:
return 16, nil
case reflect.Struct:
- // Handles both structs and unions.
+ // Handles structs, unions, and tables.
return 8, nil
}
return 0, newValueError(ErrInvalidPointerType, t.Name())
@@ -125,7 +141,7 @@
case reflect.Slice:
return 16, nil
case reflect.Struct:
- // Handles both structs and unions.
+ // Handles structs, unions, and tables.
return getPayloadSize(t, v)
}
return 0, newValueError(ErrInvalidInlineType, t.Name())
@@ -179,6 +195,9 @@
// 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
@@ -194,7 +213,7 @@
// FromTag derives metadata from data serialized into a golang struct field
// tag.
-func (n *nestedTypeData) FromTag(tag reflect.StructTag) error {
+func (n *nestedTypeData) FromTag(tag reflect.StructTag, isTable bool) error {
raw, ok := tag.Lookup("fidl")
if !ok {
return nil
@@ -217,6 +236,11 @@
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
}
@@ -292,10 +316,13 @@
return err
}
e.head = align(e.head, a)
- if isUnionType(t) {
+ switch whichStructKind(t) {
+ case aUnion:
err = e.marshalUnion(t, v, a)
- } else {
+ case aStruct:
err = e.marshalStructFields(t, v)
+ case aTable:
+ err = e.marshalTable(t, v)
}
if err != nil {
return err
@@ -323,10 +350,13 @@
}
oldHead := e.head
e.head = e.newObject(align(payload.InlineSize(), 8))
- if isUnionType(et) {
+ switch whichStructKind(et) {
+ case aUnion:
err = e.marshalUnion(et, ev, payload.InlineAlignment())
- } else {
+ case aStruct:
err = e.marshalStructFields(et, ev)
+ case aTable:
+ err = e.marshalTable(et, ev)
}
if err != nil {
return err
@@ -348,7 +378,7 @@
continue
}
var n nestedTypeData
- if err := n.FromTag(f.Tag); err != nil {
+ if err := n.FromTag(f.Tag, false); err != nil {
return err
}
if err := e.marshal(f.Type, v.Field(i), n); err != nil {
@@ -376,7 +406,7 @@
f := t.Field(fieldIndex)
var n nestedTypeData
- if err := n.FromTag(f.Tag); err != nil {
+ if err := n.FromTag(f.Tag, false); err != nil {
return err
}
// Re-align to the union's alignment before writing its field.
@@ -392,6 +422,14 @@
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()
@@ -684,10 +722,13 @@
return err
}
d.head = align(d.head, a)
- if isUnionType(t) {
+ switch whichStructKind(t) {
+ case aUnion:
err = d.unmarshalUnion(t, v, a)
- } else {
+ case aStruct:
err = d.unmarshalStructFields(t, v)
+ case aTable:
+ err = d.unmarshalTable(t, v)
}
if err != nil {
return err
@@ -720,10 +761,13 @@
d.nextObject += align(payload.InlineSize(), 8)
// Unmarshal the value itself out-of-line.
- if isUnionType(et) {
+ switch whichStructKind(et) {
+ case aUnion:
err = d.unmarshalUnion(et, ev, payload.InlineAlignment())
- } else {
+ case aStruct:
err = d.unmarshalStructFields(et, ev)
+ case aTable:
+ err = d.unmarshalTable(et, ev)
}
if err != nil {
return err
@@ -746,7 +790,7 @@
continue
}
var n nestedTypeData
- if err := n.FromTag(f.Tag); err != nil {
+ if err := n.FromTag(f.Tag, false); err != nil {
return err
}
if err := d.unmarshal(f.Type, v.Field(i), n); err != nil {
@@ -775,7 +819,7 @@
f := t.Field(fieldIndex)
var n nestedTypeData
- if err := n.FromTag(f.Tag); err != nil {
+ if err := n.FromTag(f.Tag, false); err != nil {
return err
}
d.head = align(d.head, alignment)
@@ -790,6 +834,14 @@
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 {
diff --git a/src/syscall/zx/fidl/encoding_new.go b/src/syscall/zx/fidl/encoding_new.go
index 5a84446..e22a560 100644
--- a/src/syscall/zx/fidl/encoding_new.go
+++ b/src/syscall/zx/fidl/encoding_new.go
@@ -147,6 +147,7 @@
_ = iota
structTag tagKind = iota
unionTag
+ tableTag
boundsTag
)
@@ -168,26 +169,37 @@
}
func createMarshaler(typ reflect.Type) (Marshaler, error) {
+ // field 0 holds the tag
+ marshalerKind, marshalerBounds := readTag(typ.Field(0))
var (
- kind tagKind
- size int
- alignment int
+ kind = marshalerKind
+ size = marshalerBounds[0]
+ alignment = marshalerBounds[1]
fields []mField
+ ordinals []int
)
- for index := 0; index < typ.NumField(); index++ {
+ // field 1 and up are the actual data fields
+ // - structs and unions have fields one after the other;
+ // - tables have a field, followed by a bool presence indicator, etc.
+ for index := 1; index < typ.NumField(); index++ {
field := typ.Field(index)
- fieldKind, fieldBounds := readTag(field)
- if fieldKind == structTag || fieldKind == unionTag {
- kind = fieldKind
- size = fieldBounds[0]
- alignment = fieldBounds[1]
- continue
+ _, fieldBounds := readTag(field)
+ if kind == tableTag {
+ ordinal, actualBounds := fieldBounds.pop()
+ ordinals = append(ordinals, ordinal)
+ fieldBounds = actualBounds
}
fieldMarshaler, err := createMarshalerForField(field.Type, fieldBounds)
if err != nil {
return nil, err
}
fields = append(fields, mField{fieldMarshaler, index})
+ if kind == tableTag {
+ if typ.Field(index+1).Type.Kind() != reflect.Bool {
+ return nil, errors.New("incorrect presence field on " + nicefmt(typ))
+ }
+ index++ // i.e. skip presence field.
+ }
}
switch kind {
@@ -203,6 +215,15 @@
size: size,
alignment: alignment,
}, nil
+ case tableTag:
+ return mTable{
+ mStruct: mStruct{
+ fields: fields,
+ size: size,
+ alignment: alignment,
+ },
+ ordinals: ordinals,
+ }, nil
default:
return nil, errors.New("missing kind marker on " + nicefmt(typ))
}
@@ -211,7 +232,8 @@
// readTag reads a fidl tag which can be one of
//
// s,size,alignement -- marking a struct, with its size, and alignment
-// s,size,alignement -- marking a union, with its size, and alignment
+// u,size,alignement -- marking a union, with its size, and alignment
+// t,size,alignement -- marking a table, with its size, and alignment
// num1, num2, ... -- recording bounds of the types present on the field
//
// When handles or interfaces are present, the bounds indicate nullability,
@@ -227,6 +249,8 @@
return structTag, toBounds(elems[1:])
case "u":
return unionTag, toBounds(elems[1:])
+ case "t":
+ return tableTag, toBounds(elems[1:])
default:
return boundsTag, toBounds(elems)
}
@@ -330,12 +354,17 @@
case mVector:
return mOptVector(m), nil
case mStruct:
- return mOptStructOrUnion{
+ return mOptStructUnionTable{
Marshaler: m,
elemTyp: typ,
}, nil
case mUnion:
- return mOptStructOrUnion{
+ return mOptStructUnionTable{
+ Marshaler: m,
+ elemTyp: typ,
+ }, nil
+ case mTable:
+ return mOptStructUnionTable{
Marshaler: m,
elemTyp: typ,
}, nil
@@ -360,7 +389,8 @@
var _ = []Marshaler{
mStruct{},
mUnion{},
- mOptStructOrUnion{},
+ mTable{},
+ mOptStructUnionTable{},
mArray{},
mVector{},
mBool{},
@@ -467,16 +497,146 @@
return nil
}
-type mOptStructOrUnion struct {
+type mTable struct {
+ mStruct
+ ordinals []int
+}
+
+func (m mTable) marshal(v reflect.Value, out *encoder) error {
+ // Vector of envelopes header.
+ numKnown := len(m.ordinals)
+ maxOrdinal := m.ordinals[numKnown-1]
+ out.writeUint(uint64(maxOrdinal), 8)
+ out.writeUint(allocPresent, 8)
+
+ // Sizing.
+ numPresent := 0
+ for i := 0; i < len(m.fields); i++ {
+ if v.Field(2 * (i + 1)).Bool() {
+ numPresent++
+ }
+ }
+ numBytesPerElement := m.fields[0].Marshaler.getSize()
+
+ // Encode in the out-of-line object.
+ oldHead := out.head
+ out.head = out.newObject(maxOrdinal*16 + numPresent*numBytesPerElement)
+
+ // Envelopes.
+ var (
+ ordinal = 1
+ index, fieldIndex, presenceIndex = 0, 1, 2
+ nextFieldHead = out.head + maxOrdinal*16
+ )
+ for ordinal <= maxOrdinal {
+ fieldKnown := index < numKnown && ordinal == m.ordinals[index]
+ fieldPresent := fieldKnown && v.Field(presenceIndex).Bool()
+
+ if fieldPresent {
+ numHandles := len(out.handles)
+ savedHead := out.head
+ out.head = nextFieldHead
+ if err := m.fields[index].marshal(v.Field(fieldIndex), out); err != nil {
+ return err
+ }
+ numHandles = len(out.handles) - numHandles
+ out.head = savedHead
+ nextFieldHead += numBytesPerElement
+ out.writeUint(uint64(numBytesPerElement), 4)
+ out.writeUint(uint64(numHandles), 4)
+ out.writeUint(allocPresent, 8)
+ } else {
+ // Write both num bytes and num handles at once.
+ out.writeUint(0, 8)
+ out.writeUint(noAlloc, 8)
+ }
+
+ ordinal++
+ if fieldKnown {
+ index++
+ fieldIndex += 2 // i.e. skip presenece field
+ presenceIndex += 2 // i.e. skip field
+ }
+ }
+
+ // Re-position head.
+ out.head = oldHead
+
+ return nil
+}
+
+func (m mTable) unmarshal(in *decoder, v reflect.Value) error {
+ maxOrdinal := int(in.readUint(8))
+ allocPtr := in.readUint(8)
+ switch allocPtr {
+ case allocPresent:
+ // good
+ case noAlloc:
+ return newValueError(ErrUnexpectedNullRef, v)
+ default:
+ return newValueError(ErrBadRefEncoding, v)
+ }
+
+ // Envelopes.
+ var (
+ numKnown = len(m.ordinals)
+ ordinal = 1
+ index, fieldIndex, presenceIndex = 0, 1, 2
+ nextFieldHead = in.head + maxOrdinal*16
+ )
+ for ordinal <= maxOrdinal {
+ fieldKnown := index < numKnown && ordinal == m.ordinals[index]
+
+ numBytes := int(in.readUint(4))
+ numHandles := int(in.readUint(4))
+ fieldPresent := in.readUint(8)
+ switch fieldPresent {
+ case allocPresent:
+ if fieldKnown {
+ savedHead := in.head
+ in.head = nextFieldHead
+ if err := m.fields[index].unmarshal(in, v.Field(fieldIndex)); err != nil {
+ return err
+ }
+ in.head = savedHead
+ v.Field(presenceIndex).SetBool(true)
+ } else {
+ for i := 0; i < numHandles; i++ {
+ in.handles[0].Close() // best effort
+ in.handles = in.handles[1:]
+ }
+ }
+ nextFieldHead += numBytes
+ case noAlloc:
+ // TODO(FIDL-237): We should check that numBytes and numHandles
+ // are 0, and reject messages where this is not the case. This
+ // requires all other bindings from properly memseting to 0
+ // all bytes of the buffer.
+ default:
+ return newValueError(ErrBadRefEncoding, v)
+ }
+
+ ordinal++
+ if fieldKnown {
+ index++
+ fieldIndex += 2 // i.e skip presence field
+ presenceIndex += 2 // i.e skip field
+ }
+ }
+
+ return nil
+}
+
+type mOptStructUnionTable struct {
Marshaler
elemTyp reflect.Type
}
-func (m mOptStructOrUnion) getSize() int {
+func (m mOptStructUnionTable) getSize() int {
return 8
}
-func (m mOptStructOrUnion) marshal(v reflect.Value, out *encoder) error {
+func (m mOptStructUnionTable) marshal(v reflect.Value, out *encoder) error {
// Nil?
if v.IsNil() {
out.writeUint(noAlloc, 8)
@@ -501,7 +661,7 @@
return nil
}
-func (m mOptStructOrUnion) unmarshal(in *decoder, v reflect.Value) error {
+func (m mOptStructUnionTable) unmarshal(in *decoder, v reflect.Value) error {
// Nil?
ptr, err := in.safeReadUint(8)
if err != 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 4d84f0c1..424bf17 100644
--- a/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
+++ b/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
@@ -80,6 +80,10 @@
panic(fmt.Sprintf("failed to create vmo: %v", err))
}
+ st1 := SimpleTable{}
+ st1.SetX(42)
+ st1.SetY(67)
+
return []example{
// simple (to get started)
{"simple", &TestSimple{X: 124}, 8, &TestSimple{}},
@@ -202,6 +206,14 @@
Channel: zx.Channel(zx.HandleInvalid),
}),
}, 16, &TestInterface1{}},
+
+ // tables
+ {"table1", &TestSimpleTable{
+ Table: st1,
+ }, 112, &TestSimpleTable{}},
+ {"opt-table1", &TestOptSimpleTable{
+ OptTable: &st1,
+ }, 120, &TestOptSimpleTable{}},
}
}
@@ -337,7 +349,168 @@
}
}
-func TestNegativeMarshal(t *testing.T) {
+func makeDefault(msg Message) Message {
+ typ := reflect.TypeOf(msg)
+ if typ.Kind() != reflect.Ptr && typ.Elem().Kind() != reflect.Struct {
+ panic("expecting *struct")
+ }
+ return reflect.New(typ.Elem()).Interface().(Message)
+}
+
+// successCase represents a golden test for a success case, where encoding
+// and decoding should succceed.
+type successCase struct {
+ name string
+ input Message
+ bytes []byte
+}
+
+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) {
+ 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 nh != 0 {
+ t.Fatalf("no handles expected, got %d", nh)
+ }
+ if !reflect.DeepEqual(ex.bytes, respb[:nb]) {
+ t.Fatalf("expected %v, got %v", ex.bytes, respb[:nb])
+ }
+ 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)
+ }
+ })
+ }
+ }
+}
+
+var simpleTableWithXY = []byte{
+ 5, 0, 0, 0, 0, 0, 0, 0, // max ordinal
+ 255, 255, 255, 255, 255, 255, 255, 255, // alloc present
+ 8, 0, 0, 0, 0, 0, 0, 0, // envelope 1: num bytes / num handles
+ 255, 255, 255, 255, 255, 255, 255, 255, // alloc present
+ 0, 0, 0, 0, 0, 0, 0, 0, // envelope 2: num bytes / num handles
+ 0, 0, 0, 0, 0, 0, 0, 0, // no alloc
+ 0, 0, 0, 0, 0, 0, 0, 0, // envelope 3: num bytes / num handles
+ 0, 0, 0, 0, 0, 0, 0, 0, // no alloc
+ 0, 0, 0, 0, 0, 0, 0, 0, // envelope 4: num bytes / num handles
+ 0, 0, 0, 0, 0, 0, 0, 0, // no alloc
+ 8, 0, 0, 0, 0, 0, 0, 0, // envelope 5: num bytes / num handles
+ 255, 255, 255, 255, 255, 255, 255, 255, // alloc present
+ 42, 0, 0, 0, 0, 0, 0, 0, // field X
+ 67, 0, 0, 0, 0, 0, 0, 0, // field Y
+}
+
+var simpleTableWithY = []byte{
+ 5, 0, 0, 0, 0, 0, 0, 0, // max ordinal
+ 255, 255, 255, 255, 255, 255, 255, 255, // alloc present
+ 0, 0, 0, 0, 0, 0, 0, 0, // envelope 1: num bytes / num handles
+ 0, 0, 0, 0, 0, 0, 0, 0, // no alloc
+ 0, 0, 0, 0, 0, 0, 0, 0, // envelope 2: num bytes / num handles
+ 0, 0, 0, 0, 0, 0, 0, 0, // no alloc
+ 0, 0, 0, 0, 0, 0, 0, 0, // envelope 3: num bytes / num handles
+ 0, 0, 0, 0, 0, 0, 0, 0, // no alloc
+ 0, 0, 0, 0, 0, 0, 0, 0, // envelope 4: num bytes / num handles
+ 0, 0, 0, 0, 0, 0, 0, 0, // no alloc
+ 8, 0, 0, 0, 0, 0, 0, 0, // envelope 5: num bytes / num handles
+ 255, 255, 255, 255, 255, 255, 255, 255, // alloc present
+ 67, 0, 0, 0, 0, 0, 0, 0, // field Y
+}
+
+func TestAllSuccessCases(t *testing.T) {
+ st1 := SimpleTable{}
+ st1.SetX(42)
+ st1.SetY(67)
+ successCase{
+ name: "simpletable-x-and-y",
+ input: &TestSimpleTable{
+ Table: st1,
+ },
+ bytes: simpleTableWithXY,
+ }.check(t)
+
+ st2 := SimpleTable{}
+ st2.SetY(67)
+ successCase{
+ name: "simpletable-just-y",
+ input: &TestSimpleTable{
+ Table: st2,
+ },
+ bytes: simpleTableWithY,
+ }.check(t)
+
+ st3 := SimpleTable{}
+ st3.SetX(42)
+ st3.SetY(67)
+ successCase{
+ name: "opt-simpletable-x-and-y",
+ input: &TestOptSimpleTable{
+ OptTable: &st3,
+ },
+ bytes: append([]byte{
+ 255, 255, 255, 255, 255, 255, 255, 255, // alloc present,
+ },
+ simpleTableWithXY...),
+ }.check(t)
+}
+
+func TestTableCompatibilityWithXY(t *testing.T) {
+ ost := OlderSimpleTable{}
+ ost.SetX(42)
+ st := SimpleTable{}
+ st.SetX(42)
+ st.SetY(67)
+ nst := NewerSimpleTable{}
+ nst.SetX(42)
+ nst.SetY(67)
+ cases := []Message{
+ &TestOlderSimpleTable{Table: ost},
+ &TestSimpleTable{Table: st},
+ &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)
+ }
+ }
+ }
+}
+
+func TestTableCompatibilityWithY(t *testing.T) {
+ ost := OlderSimpleTable{}
+ st := SimpleTable{}
+ st.SetY(67)
+ nst := NewerSimpleTable{}
+ nst.SetY(67)
+ cases := []Message{
+ &TestOlderSimpleTable{Table: ost},
+ &TestSimpleTable{Table: st},
+ &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)
+ }
+ }
+ }
+}
+
+func TestFailuresMarshal(t *testing.T) {
v1 := []int64{1, 2, 3}
cases := []struct {
name string
@@ -387,7 +560,7 @@
})
}
}
-func TestNegativeUnmarshalNoHandles(t *testing.T) {
+func TestFailuresUnmarshalNoHandles(t *testing.T) {
cases := []struct {
name string
input []byte
diff --git a/src/syscall/zx/fidl/fidl_test/encoding_test.go b/src/syscall/zx/fidl/fidl_test/encoding_test.go
index 097ce20..54537eb 100644
--- a/src/syscall/zx/fidl/fidl_test/encoding_test.go
+++ b/src/syscall/zx/fidl/fidl_test/encoding_test.go
@@ -200,4 +200,12 @@
}),
}, 16, &TestInterface1{})
})
+ t.Run("Tables", func(t *testing.T) {
+ st := SimpleTable{}
+ st.SetX(5)
+ st.SetY(7)
+ testIdentity(t, &TestSimpleTable{
+ Table: st,
+ }, 112, &TestSimpleTable{})
+ })
}