[fidl] Add support for vector to simple C bindings

Vectors are supported for primtives and handles when bounded.

Test: spaceship_tests.c
Change-Id: Id2400e9141b95e5a58a6945c1baaf1e2fa9dd677
diff --git a/system/host/fidl/include/fidl/c_generator.h b/system/host/fidl/include/fidl/c_generator.h
index 39f1e3e..04b688b 100644
--- a/system/host/fidl/include/fidl/c_generator.h
+++ b/system/host/fidl/include/fidl/c_generator.h
@@ -39,6 +39,7 @@
         flat::Type::Kind kind;
         std::string type;
         std::string name;
+        std::string element_type;
         std::vector<uint32_t> array_counts;
     };
 
diff --git a/system/host/fidl/lib/c_generator.cpp b/system/host/fidl/lib/c_generator.cpp
index 5a472e2..494d528 100644
--- a/system/host/fidl/lib/c_generator.cpp
+++ b/system/host/fidl/lib/c_generator.cpp
@@ -18,7 +18,7 @@
 constexpr const char* kIndent = "    ";
 
 CGenerator::Member MessageHeader() {
-    return {flat::Type::Kind::kIdentifier, "fidl_message_header_t", "hdr", {}};
+    return {flat::Type::Kind::kIdentifier, "fidl_message_header_t", "hdr", {}, {}};
 }
 
 // Functions named "Emit..." are called to actually emit to an std::ostream
@@ -69,6 +69,10 @@
             *file << "[" << array_count << "]";
         }
         break;
+    case flat::Type::Kind::kVector:
+        *file << "const " << member.element_type << "* " << member.name << "_data, "
+              << "size_t " << member.name << "_count";
+        break;
     case flat::Type::Kind::kString:
         *file << "const char* " << member.name << "_data, "
               << "size_t " << member.name << "_size";
@@ -81,9 +85,6 @@
     case flat::Type::Kind::kIdentifier:
         *file << "const " << member.type << "* " << member.name;
         break;
-    default:
-        assert(false && "bad type simple interface");
-        break;
     }
 }
 
@@ -94,6 +95,11 @@
         for (uint32_t array_count : member.array_counts) {
             *file << "[" << array_count << "]";
         }
+    case flat::Type::Kind::kVector:
+        *file << member.element_type << "* " << member.name << "_data, "
+              << "size_t " << member.name << "_capacity, "
+              << "size_t* out_" << member.name << "_count";
+        break;
     case flat::Type::Kind::kString:
         *file << "char* " << member.name << "_data, "
               << "size_t " << member.name << "_capacity, "
@@ -105,9 +111,6 @@
     case flat::Type::Kind::kIdentifier:
         *file << member.type << "* " << member.name;
         break;
-    default:
-        assert(false && "bad type simple interface");
-        break;
     }
 }
 
@@ -158,28 +161,35 @@
 
 void EmitMeasureParams(std::ostream* file,
                        const std::vector<CGenerator::Member>& params,
-                       StringView suffix) {
+                       StringView vector_suffix,
+                       StringView string_suffix) {
     for (const auto& member : params) {
-        if (member.kind != flat::Type::Kind::kString)
-            continue;
-        *file << " + FIDL_ALIGN(" << member.name << suffix << ")";
+        if (member.kind == flat::Type::Kind::kVector)
+            *file << " + FIDL_ALIGN(sizeof(*" << member.name << "_data) * " << member.name << vector_suffix << ")";
+        else if (member.kind == flat::Type::Kind::kString)
+            *file << " + FIDL_ALIGN(" << member.name << string_suffix << ")";
     }
 }
 
-bool HasSecondaryObjects(const std::vector<CGenerator::Member>& params) {
+// This function assumes the |params| are part of a [Layout="Simple"] interface.
+// In particular, simple interfaces don't have nullable structs or nested
+// vectors. The only secondary objects they contain are top-level vectors and
+// strings.
+size_t CountSecondaryObjects(const std::vector<CGenerator::Member>& params) {
+    size_t count = 0u;
     for (const auto& member : params) {
-        if (member.kind == flat::Type::Kind::kString ||
-            member.kind == flat::Type::Kind::kVector)
-            return true;
+        if (member.kind == flat::Type::Kind::kVector ||
+            member.kind == flat::Type::Kind::kString)
+            ++count;
     }
-    return false;
+    return count;
 }
 
 void EmitLinearizeMessage(std::ostream* file,
                           StringView receiver,
                           StringView bytes,
                           const std::vector<CGenerator::Member>& request) {
-    if (HasSecondaryObjects(request))
+    if (CountSecondaryObjects(request) > 0)
         *file << kIndent << "uint32_t _next = sizeof(*" << receiver << ");\n";
     for (const auto& member : request) {
         const auto& name = member.name;
@@ -188,6 +198,12 @@
             *file << kIndent << "memcpy(" << receiver << "->" << name << ", "
                   << name << ", sizeof(" << receiver << "->" << name << "));\n";
             break;
+        case flat::Type::Kind::kVector:
+            *file << kIndent << receiver << "->" << name << ".data = &" << bytes << "[_next];\n";
+            *file << kIndent << receiver << "->" << name << ".count = " << name << "_count;\n";
+            *file << kIndent << "memcpy(" << receiver << "->" << name << ".data, " << name << "_data, sizeof(*" << name << "_data) * " << name << "_count);\n";
+            *file << kIndent << "_next += FIDL_ALIGN(sizeof(*" << name << "_data) * " << name << "_count);\n";
+            break;
         case flat::Type::Kind::kString:
             *file << kIndent << receiver << "->" << name << ".data = &" << bytes << "[_next];\n";
             *file << kIndent << receiver << "->" << name << ".size = " << name << "_size;\n";
@@ -202,9 +218,6 @@
         case flat::Type::Kind::kIdentifier:
             *file << kIndent << receiver << "->" << name << " = *" << name << ";\n";
             break;
-        default:
-            assert(false && "bad type simple interface");
-            break;
         }
     }
 }
@@ -327,7 +340,18 @@
     const flat::Type* type = decl.type.get();
     auto type_name = NameFlatCType(library, type);
     std::vector<uint32_t> array_counts = ArrayCounts(library, type);
-    return CGenerator::Member{type->kind, type_name, name, std::move(array_counts)};
+    std::string element_type;
+    if (type->kind == flat::Type::Kind::kVector) {
+        auto vector_type = static_cast<const flat::VectorType*>(type);
+        element_type = NameFlatCType(library, vector_type->element_type.get());
+    }
+    return CGenerator::Member{
+        type->kind,
+        std::move(type_name),
+        std::move(name),
+        std::move(element_type),
+        std::move(array_counts),
+    };
 }
 
 std::vector<CGenerator::Member>
@@ -455,7 +479,7 @@
         NamedInterface named_interface;
         named_interface.c_name = NameInterface(*interface_info);
         if (interface_info->HasAttribute("Discoverable")) {
-          named_interface.discoverable_name = NameDiscoverable(*interface_info);
+            named_interface.discoverable_name = NameDiscoverable(*interface_info);
         }
         for (const auto& method : interface_info->methods) {
             NamedMethod named_method;
@@ -534,7 +558,7 @@
 
 void CGenerator::ProduceInterfaceForwardDeclaration(const NamedInterface& named_interface) {
     if (!named_interface.discoverable_name.empty()) {
-      file_ << "#define " << named_interface.c_name << "_Name \"" << named_interface.discoverable_name << "\"\n";
+        file_ << "#define " << named_interface.c_name << "_Name \"" << named_interface.discoverable_name << "\"\n";
     }
     for (const auto& method_info : named_interface.methods) {
         file_ << "#define " << method_info.ordinal_name << " ((uint32_t)"
@@ -647,7 +671,7 @@
         EmitClientMethodDecl(&file_, method_info.c_name, request, response);
         file_ << " {\n";
         file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.request->c_name << ")";
-        EmitMeasureParams(&file_, request, "_size");
+        EmitMeasureParams(&file_, request, "_count", "_size");
         file_ << ";\n";
         file_ << kIndent << "FIDL_ALIGNDECL char _wr_bytes[_wr_num_bytes];\n";
         file_ << kIndent << method_info.request->c_name << "* _request = (" << method_info.request->c_name << "*)_wr_bytes;\n";
@@ -665,7 +689,7 @@
             file_ << kIndent << "return zx_channel_write(_channel, 0u, _wr_bytes, _wr_num_bytes, _handles, _wr_num_handles);\n";
         } else {
             file_ << kIndent << "uint32_t _rd_num_bytes = sizeof(" << method_info.response->c_name << ")";
-            EmitMeasureParams(&file_, response, "_capacity");
+            EmitMeasureParams(&file_, response, "_capacity", "_capacity");
             file_ << ";\n";
             file_ << kIndent << "FIDL_ALIGNDECL char _rd_bytes[_rd_num_bytes];\n";
             if (!response.empty())
@@ -691,10 +715,26 @@
             // before decoding the message so that we can close the handles
             // using |_handles| rather than trying to find them in the decoded
             // message.
-            for (const auto& member : response) {
-                if (member.kind != flat::Type::Kind::kString)
-                    continue;
-                file_ << kIndent << "if (_response->" << member.name << ".size > " << member.name << "_capacity) {\n";
+            size_t count = CountSecondaryObjects(response);
+            if (count > 0u) {
+                file_ << kIndent << "if ";
+                if (count > 1u)
+                    file_ << "(";
+                size_t i = 0;
+                for (const auto& member : response) {
+                    if (member.kind == flat::Type::Kind::kVector) {
+                        if (i++ > 0u)
+                            file_ << " || ";
+                        file_ << "(_response->" << member.name << ".count > " << member.name << "_capacity)";
+                    } else if (member.kind == flat::Type::Kind::kString) {
+                        if (i++ > 0u)
+                            file_ << " || ";
+                        file_ << "(_response->" << member.name << ".size > " << member.name << "_capacity)";
+                    }
+                }
+                if (count > 1u)
+                    file_ << ")";
+                file_ << " {\n";
                 file_ << kIndent << kIndent << "zx_handle_close_many(_handles, _actual_num_handles);\n";
                 file_ << kIndent << kIndent << "return ZX_ERR_BUFFER_TOO_SMALL;\n";
                 file_ << kIndent << "}\n";
@@ -712,6 +752,10 @@
                 case flat::Type::Kind::kArray:
                     file_ << kIndent << "memcpy(out_" << name << ", _response->" << name << ", sizeof(out_" << name << "));\n";
                     break;
+                case flat::Type::Kind::kVector:
+                    file_ << kIndent << "memcpy(" << name << "_data, _response->" << name << ".data, sizeof(*" << name << "_data) * _response->" << name << ".count);\n";
+                    file_ << kIndent << "*out_" << name << "_count = _response->" << name << ".count;\n";
+                    break;
                 case flat::Type::Kind::kString:
                     file_ << kIndent << "memcpy(" << name << "_data, _response->" << name << ".data, _response->" << name << ".size);\n";
                     file_ << kIndent << "*out_" << name << "_size = _response->" << name << ".size;\n";
@@ -722,9 +766,6 @@
                 case flat::Type::Kind::kIdentifier:
                     file_ << kIndent << "*" << name << " = _response->" << name << ";\n";
                     break;
-                default:
-                    assert(false && "bad type simple interface");
-                    break;
                 }
             }
 
@@ -792,6 +833,10 @@
             case flat::Type::Kind::kPrimitive:
                 file_ << ", request->" << member.name;
                 break;
+            case flat::Type::Kind::kVector:
+                file_ << ", (" << member.element_type << "*)request->" << member.name << ".data"
+                      << ", request->" << member.name << ".count";
+                break;
             case flat::Type::Kind::kString:
                 file_ << ", request->" << member.name << ".data"
                       << ", request->" << member.name << ".size";
@@ -799,9 +844,6 @@
             case flat::Type::Kind::kIdentifier:
                 file_ << ", &(request->" << member.name << ")";
                 break;
-            default:
-                assert(false && "bad type simple interface");
-                break;
             }
         }
         if (method_info.response != nullptr)
@@ -827,7 +869,7 @@
         EmitServerReplyDecl(&file_, method_info.c_name, response);
         file_ << " {\n";
         file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.response->c_name << ")";
-        EmitMeasureParams(&file_, response, "_size");
+        EmitMeasureParams(&file_, response, "_count", "_size");
         file_ << ";\n";
         file_ << kIndent << "char _wr_bytes[_wr_num_bytes];\n";
         file_ << kIndent << method_info.response->c_name << "* _response = (" << method_info.response->c_name << "*)_wr_bytes;\n";
diff --git a/system/host/fidl/lib/flat_ast.cpp b/system/host/fidl/lib/flat_ast.cpp
index 6a22aa0..3ec2049 100644
--- a/system/host/fidl/lib/flat_ast.cpp
+++ b/system/host/fidl/lib/flat_ast.cpp
@@ -188,6 +188,21 @@
     if (type->kind == Type::Kind::kString) {
         auto string_type = static_cast<StringType*>(type.get());
         return string_type->max_size.Value() < Size::Max().Value();
+    } else if (type->kind == Type::Kind::kVector) {
+        auto vector_type = static_cast<VectorType*>(type.get());
+        if (vector_type->element_count.Value() == Size::Max().Value())
+            return false;
+        switch (vector_type->element_type->kind) {
+        case Type::Kind::kHandle:
+        case Type::Kind::kRequestHandle:
+        case Type::Kind::kPrimitive:
+            return true;
+        case Type::Kind::kArray:
+        case Type::Kind::kVector:
+        case Type::Kind::kString:
+        case Type::Kind::kIdentifier:
+            return false;
+        }
     }
     return fieldshape.Depth() == 0u;
 }
diff --git a/system/utest/fidl-simple/ldsvc_tests.c b/system/utest/fidl-simple/ldsvc_tests.c
index 286e1d7..c5a5c56 100644
--- a/system/utest/fidl-simple/ldsvc_tests.c
+++ b/system/utest/fidl-simple/ldsvc_tests.c
@@ -206,6 +206,7 @@
     }
 
     ASSERT_EQ(ZX_OK, fuchsia_ldsvc_LoaderDone(client), "");
+    ASSERT_EQ(ZX_OK, zx_handle_close(client), "");
 
     int result = 0;
     rv = thrd_join(thread, &result);
diff --git a/system/utest/fidl-simple/rules.mk b/system/utest/fidl-simple/rules.mk
index e160f58..290fdb9 100644
--- a/system/utest/fidl-simple/rules.mk
+++ b/system/utest/fidl-simple/rules.mk
@@ -4,6 +4,24 @@
 
 LOCAL_DIR := $(GET_LOCAL_DIR)
 
+#
+# fidl.test.spaceship
+#
+
+MODULE := $(LOCAL_DIR).spaceship
+
+MODULE_TYPE := fidl
+
+MODULE_FIDL_LIBRARY := fidl.test.spaceship
+
+MODULE_SRCS += $(LOCAL_DIR)/spaceship.fidl
+
+include make/module.mk
+
+#
+# fidl-simple-test
+#
+
 MODULE := $(LOCAL_DIR)
 
 MODULE_TYPE := usertest
@@ -13,12 +31,14 @@
     $(LOCAL_DIR)/ldsvc_tests.c \
     $(LOCAL_DIR)/main.c \
     $(LOCAL_DIR)/server_tests.c \
+    $(LOCAL_DIR)/spaceship_tests.c \
 
 MODULE_NAME := fidl-simple-test
 
 MODULE_FIDL_LIBS := \
     system/fidl/fuchsia-crash \
     system/fidl/fuchsia-ldsvc \
+    system/utest/fidl-simple.spaceship \
 
 MODULE_STATIC_LIBS := \
     system/ulib/fidl \
diff --git a/system/utest/fidl-simple/spaceship.fidl b/system/utest/fidl-simple/spaceship.fidl
new file mode 100644
index 0000000..e3dc07a
--- /dev/null
+++ b/system/utest/fidl-simple/spaceship.fidl
@@ -0,0 +1,11 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fidl.test.spaceship;
+
+[Layout="Simple"]
+interface SpaceShip {
+    1: AdjustHeading(vector<uint32>:64 stars) -> (int8 result);
+    2: ScanForLifeforms() -> (vector<uint32>:64 lifesigns);
+};
diff --git a/system/utest/fidl-simple/spaceship_tests.c b/system/utest/fidl-simple/spaceship_tests.c
new file mode 100644
index 0000000..dcd5820
--- /dev/null
+++ b/system/utest/fidl-simple/spaceship_tests.c
@@ -0,0 +1,129 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fidl/test/spaceship/c/fidl.h>
+#include <string.h>
+#include <threads.h>
+#include <zircon/fidl.h>
+#include <zircon/syscalls.h>
+
+#include <unittest/unittest.h>
+
+static zx_status_t SpaceShip_AdjustHeading(void* ctx, const uint32_t* stars_data, size_t stars_count, fidl_txn_t* txn) {
+    EXPECT_EQ(3u, stars_count, "");
+    EXPECT_EQ(11u, stars_data[0], "");
+    EXPECT_EQ(0u, stars_data[1], "");
+    EXPECT_EQ(UINT32_MAX, stars_data[2], "");
+    return fidl_test_spaceship_SpaceShipAdjustHeading_reply(txn, -12);
+}
+
+static zx_status_t SpaceShip_ScanForLifeforms(void* ctx, fidl_txn_t* txn) {
+    const uint32_t lifesigns[5] = { 42u, 43u, UINT32_MAX, 0u, 9u };
+    return fidl_test_spaceship_SpaceShipScanForLifeforms_reply(txn, lifesigns, 5);
+}
+
+static const fidl_test_spaceship_SpaceShip_ops_t kOps = {
+    .AdjustHeading = SpaceShip_AdjustHeading,
+    .ScanForLifeforms = SpaceShip_ScanForLifeforms,
+};
+
+typedef struct spaceship_connection {
+    fidl_txn_t txn;
+    zx_handle_t channel;
+    zx_txid_t txid;
+} spaceship_connection_t;
+
+static zx_status_t spaceship_reply(fidl_txn_t* txn, const fidl_msg_t* msg) {
+    spaceship_connection_t* conn = (spaceship_connection_t*)txn;
+    if (msg->num_bytes < sizeof(fidl_message_header_t))
+        return ZX_ERR_INVALID_ARGS;
+    fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;
+    hdr->txid = conn->txid;
+    conn->txid = 0;
+    return zx_channel_write(conn->channel, 0, msg->bytes, msg->num_bytes,
+                            msg->handles, msg->num_handles);
+}
+
+static int spaceship_server(void* ctx) {
+    spaceship_connection_t conn = {
+        .txn.reply = spaceship_reply,
+        .channel = *(zx_handle_t*)ctx,
+    };
+    zx_status_t status = ZX_OK;
+    while (status == ZX_OK) {
+        zx_signals_t observed;
+        status = zx_object_wait_one(
+            conn.channel, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
+            ZX_TIME_INFINITE, &observed);
+        if ((observed & ZX_CHANNEL_READABLE) != 0) {
+            ASSERT_EQ(ZX_OK, status, "");
+            char bytes[ZX_CHANNEL_MAX_MSG_BYTES];
+            zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
+            fidl_msg_t msg = {
+                .bytes = bytes,
+                .handles = handles,
+                .num_bytes = 0u,
+                .num_handles = 0u,
+            };
+            status = zx_channel_read(conn.channel, 0, bytes, handles,
+                                     ZX_CHANNEL_MAX_MSG_BYTES,
+                                     ZX_CHANNEL_MAX_MSG_HANDLES,
+                                     &msg.num_bytes, &msg.num_handles);
+            ASSERT_EQ(ZX_OK, status, "");
+            ASSERT_GE(msg.num_bytes, sizeof(fidl_message_header_t), "");
+            fidl_message_header_t* hdr = (fidl_message_header_t*)msg.bytes;
+            conn.txid = hdr->txid;
+            status = fidl_test_spaceship_SpaceShip_dispatch(NULL, &conn.txn, &msg, &kOps);
+            ASSERT_EQ(ZX_OK, status, "");
+        } else {
+            break;
+        }
+    }
+
+    zx_handle_close(conn.channel);
+    return 0;
+}
+
+static bool spaceship_test(void) {
+    BEGIN_TEST;
+
+    zx_handle_t client, server;
+    zx_status_t status = zx_channel_create(0, &client, &server);
+    ASSERT_EQ(ZX_OK, status, "");
+
+    thrd_t thread;
+    int rv = thrd_create(&thread, spaceship_server, &server);
+    ASSERT_EQ(thrd_success, rv, "");
+
+    {
+        const uint32_t stars[3] = { 11u, 0u, UINT32_MAX };
+        int8_t result = 0;
+        ASSERT_EQ(ZX_OK, fidl_test_spaceship_SpaceShipAdjustHeading(client, stars, 3, &result), "");
+        ASSERT_EQ(-12, result, "");
+    }
+
+    {
+        uint32_t lifesigns[64];
+        size_t actual = 0;
+        ASSERT_EQ(ZX_OK, fidl_test_spaceship_SpaceShipScanForLifeforms(client, lifesigns, 64, &actual), "");
+        ASSERT_EQ(5u, actual, "");
+        ASSERT_EQ(42u, lifesigns[0], "");
+        ASSERT_EQ(43u, lifesigns[1], "");
+        ASSERT_EQ(UINT32_MAX, lifesigns[2], "");
+        ASSERT_EQ(0u, lifesigns[3], "");
+        ASSERT_EQ(9u, lifesigns[4], "");
+    }
+
+    ASSERT_EQ(ZX_OK, zx_handle_close(client), "");
+
+    int result = 0;
+    rv = thrd_join(thread, &result);
+    ASSERT_EQ(thrd_success, rv, "");
+
+    END_TEST;
+}
+
+BEGIN_TEST_CASE(spaceship_tests)
+RUN_NAMED_TEST("fidl.test.spaceship.SpaceShip test", spaceship_test)
+END_TEST_CASE(spaceship_tests);