[fidl][go] Support unknown data for tables (1.5/4)

Between step 2 and step 3, createMarshaler must be able
to handle tables that may or may not have an I_unknownData
field.

This also defines the fidl.UnknownData type which is
referenced in codegen added in step 2.

Step 1: Ib6f9dce7cea743ce50f61fc5cba8b5e6c4c49242
Step 2: I3e839ceb761820299238f19c3bebdcd95fd1eb2f
Step 3: Ifd7ebe3db2544f4a07add78600d008a072c1d1a2

Test: fx test -v go-fidl-tests
Change-Id: I98b63518ad640d56029d9cbc1b07cd773a37015e
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/go/+/439059
Commit-Queue: Felix Zhu <fcz@google.com>
Reviewed-by: Pascal Perez <pascallouis@google.com>
diff --git a/api/fuchsia.txt b/api/fuchsia.txt
index 66f6a34..882578d 100644
--- a/api/fuchsia.txt
+++ b/api/fuchsia.txt
@@ -1093,6 +1093,9 @@
 pkg syscall/zx/fidl, type ServiceRequest interface, ToChannel() zx.Channel
 pkg syscall/zx/fidl, type Stub interface { Dispatch }
 pkg syscall/zx/fidl, type Stub interface, Dispatch(DispatchArgs) (Message, bool, error)
+pkg syscall/zx/fidl, type UnknownData struct
+pkg syscall/zx/fidl, type UnknownData struct, Bytes []uint8
+pkg syscall/zx/fidl, type UnknownData struct, Handles []zx.HandleInfo
 pkg syscall/zx/fidl, type ValidationError interface { Code, Error }
 pkg syscall/zx/fidl, type ValidationError interface, Code() ErrorCode
 pkg syscall/zx/fidl, type ValidationError interface, Error() string
diff --git a/src/syscall/zx/fidl/encoding_new.go b/src/syscall/zx/fidl/encoding_new.go
index c7f9c6f..ae4782e 100644
--- a/src/syscall/zx/fidl/encoding_new.go
+++ b/src/syscall/zx/fidl/encoding_new.go
@@ -307,10 +307,20 @@
 		presenceOffsets []uintptr
 	)
 
+	// If a table has an I_unknownData field, then all presence fields must be
+	// odd-numbered. If there is no I_unknownData field, then all presence
+	// fields must be even numbered
+	presenceFieldOddness := 1
+
 	// - structs, unions, and xunions have fields one after the other;
 	// - tables have a field, followed by a bool presence indicator, etc.
 	for index, field := range dataFields(typ) {
-		if kind == tableTag && index%2 == 1 {
+		if (kind == xunionTag || kind == tableTag) && isUnknownDataField(field.Name) {
+			presenceFieldOddness = 0
+			continue
+		}
+
+		if kind == tableTag && index%2 == presenceFieldOddness {
 			// Presence field
 			if field.Type.Kind() != reflect.Bool {
 				return nil, errors.New("incorrect presence field on " + nicefmt(typ))
@@ -319,10 +329,6 @@
 			continue
 		}
 
-		if (kind == xunionTag || kind == tableTag) && isUnknownDataField(field.Name) {
-			continue
-		}
-
 		fieldBounds := readBoundsTag(field)
 		handleRights, handleSubtype, err := readHandleRightsAndSubtype(field)
 		if err != nil {
diff --git a/src/syscall/zx/fidl/types.go b/src/syscall/zx/fidl/types.go
index ea78174..1bacc86 100644
--- a/src/syscall/zx/fidl/types.go
+++ b/src/syscall/zx/fidl/types.go
@@ -4,6 +4,8 @@
 
 package fidl
 
+import "syscall/zx"
+
 // Enum is implemented by any value that is a FIDL enum.
 //
 // During code generation, `fidlgen_go` ensures that all domain types generated
@@ -34,3 +36,9 @@
 	// GetUnknownBits returns a uint64 containing only the unknown bits
 	GetUnknownBits() uint64
 }
+
+// UnknownData represents the raw bytes and handles of an unknown payload
+type UnknownData struct {
+	Bytes   []byte
+	Handles []zx.HandleInfo
+}