Added arrays to queues.

Change-Id: Ifcb5ff0ecdbd47e7fa445275195cd7a87e96c20a
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index fb410b7..9a63e48 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -109,6 +109,7 @@
         '<(EXTERNALS):gtest',
         'test_queue',
         '<(AOS)/build/aos.gyp:logging',
+        'queue_testutils',
       ],
     },
     {
diff --git a/aos/common/queue_types.cc b/aos/common/queue_types.cc
index 42dec0c..f04e365 100644
--- a/aos/common/queue_types.cc
+++ b/aos/common/queue_types.cc
@@ -43,6 +43,8 @@
   for (int i = 0; i < number_fields; ++i) {
     to_network(&fields[i]->type, buffer);
     buffer += sizeof(fields[i]->type);
+    to_network(&fields[i]->length, buffer);
+    buffer += sizeof(fields[i]->length);
     length = fields[i]->name.size();
     to_network(&length, buffer);
     buffer += sizeof(length);
@@ -53,7 +55,8 @@
   return buffer - buffer_start;
 }
 
-MessageType *MessageType::Deserialize(const char *buffer, size_t *bytes) {
+MessageType *MessageType::Deserialize(const char *buffer, size_t *bytes,
+                                      bool deserialize_length) {
   uint16_t name_length;
   decltype(MessageType::super_size) super_size;
   decltype(MessageType::id) id;
@@ -94,6 +97,10 @@
 
     to_host(buffer, &fields[i]->type);
     buffer += sizeof(fields[i]->type);
+    if (deserialize_length) {
+      to_host(buffer, &fields[i]->length);
+      buffer += sizeof(fields[i]->length);
+    }
     to_host(buffer, &field_name_length);
     buffer += sizeof(field_name_length);
 
@@ -108,6 +115,50 @@
   return r.release();
 }
 
+bool PrintArray(char *output, size_t *output_bytes, const void *input,
+                size_t *input_bytes, uint32_t type_id, uint32_t length) {
+  if (*output_bytes < 1) return false;
+  *output_bytes -= 1;
+  *(output++) = '[';
+
+  bool first = true;
+  for (uint32_t i = 0; i < length; ++i) {
+    if (first) {
+      first = false;
+    } else {
+      if (*output_bytes < 2) return false;
+      *output_bytes -= 2;
+      *(output++) = ',';
+      *(output++) = ' ';
+    }
+
+    const size_t output_bytes_before = *output_bytes,
+                 input_bytes_before = *input_bytes;
+    if (MessageType::IsPrimitive(type_id)) {
+      if (!PrintField(output, output_bytes, input, input_bytes, type_id)) {
+        return false;
+      }
+    } else {
+      if (!PrintMessage(output, output_bytes, input, input_bytes,
+                        type_cache::Get(type_id))) {
+        return false;
+      }
+      // Ignore the trailing '\0' that the subcall put on.
+      *output_bytes += 1;
+    }
+
+    // Update the input and output pointers.
+    output += output_bytes_before - *output_bytes;
+    input =
+        static_cast<const char *>(input) + input_bytes_before - *input_bytes;
+  }
+  if (*output_bytes < 2) return false;
+  *output_bytes -= 2;
+  *(output++) = ']';
+  *(output++) = '\0';
+  return true;
+}
+
 bool PrintMessage(char *output, size_t *output_bytes, const void *input,
                   size_t *input_bytes, const MessageType &type) {
   *input_bytes -= type.super_size;
@@ -138,14 +189,21 @@
 
     const size_t output_bytes_before = *output_bytes,
                  input_bytes_before = *input_bytes;
-    if (MessageType::IsPrimitive(type.fields[i]->type)) {
-      if (!PrintField(output, output_bytes, input, input_bytes,
-                      type.fields[i]->type)) {
+    const uint32_t type_id = type.fields[i]->type;
+    if (type.fields[i]->length > 0) {
+      if (!PrintArray(output, output_bytes, input, input_bytes, type_id,
+                      type.fields[i]->length)) {
+        return false;
+      }
+      // Ignore the trailing '\0' that the subcall put on.
+      *output_bytes += 1;
+    } else if (MessageType::IsPrimitive(type_id)) {
+      if (!PrintField(output, output_bytes, input, input_bytes, type_id)) {
         return false;
       }
     } else {
       if (!PrintMessage(output, output_bytes, input, input_bytes,
-                        type_cache::Get(type.fields[i]->type))) {
+                        type_cache::Get(type_id))) {
         return false;
       }
       // Ignore the trailing '\0' that the subcall put on.
diff --git a/aos/common/queue_types.h b/aos/common/queue_types.h
index 7381029..be943c1 100644
--- a/aos/common/queue_types.h
+++ b/aos/common/queue_types.h
@@ -21,6 +21,8 @@
   struct Field {
     // The type ID for the type of this field.
     uint32_t type;
+    // The length of the array if it is one or 0.
+    uint32_t length;
     ::std::string name;
   };
 
@@ -47,8 +49,11 @@
   ssize_t Serialize(char *buffer, size_t max_bytes) const;
   // bytes should start out as the number of bytes available in buffer and gets
   // reduced by the number actually read before returning.
+  // deserialize_length is whether to look for a length field in the serialized
+  // data.
   // Returns a new instance allocated with new or nullptr for error.
-  static MessageType *Deserialize(const char *buffer, size_t *bytes);
+  static MessageType *Deserialize(const char *buffer, size_t *bytes,
+                                  bool deserialize_length = true);
 
   static bool IsPrimitive(uint32_t type_id) {
     return (type_id & 0x2000) != 0;
diff --git a/aos/common/queue_types_test.cc b/aos/common/queue_types_test.cc
index 514499b..868820f 100644
--- a/aos/common/queue_types_test.cc
+++ b/aos/common/queue_types_test.cc
@@ -10,10 +10,12 @@
 #include "aos/common/byteorder.h"
 #include "aos/queue_primitives.h"
 #include "aos/common/logging/logging.h"
+#include "aos/common/queue_testutils.h"
 
 using ::aos::common::testing::Structure;
 using ::aos::common::testing::MessageWithStructure;
 using ::aos::common::testing::OtherTestingMessage;
+using ::aos::common::testing::MessageWithArrays;
 
 namespace aos {
 namespace testing {
@@ -21,12 +23,16 @@
 typedef MessageType::Field Field;
 
 static const MessageType kTestType1(5, 0x1234, "TestType1",
-                                    {new Field{0, "field1"},
-                                     new Field{0, "field2"},
-                                     new Field{0, "field3"}});
+                                    {new Field{0, 0, "field1"},
+                                     new Field{0, 0, "field2"},
+                                     new Field{0, 0, "field3"}});
 
 class QueueTypesTest : public ::testing::Test {
  public:
+  QueueTypesTest() {
+    ::aos::common::testing::EnableTestLogging();
+  }
+
   ::testing::AssertionResult Equal(const MessageType &l, const MessageType &r) {
     using ::testing::AssertionFailure;
     if (l.id != r.id) {
@@ -83,7 +89,7 @@
 
 class PrintMessageTest : public ::testing::Test {
  public:
-  char input[128], output[128];
+  char input[128], output[256];
   size_t input_bytes, output_bytes;
 };
 
@@ -194,6 +200,15 @@
 static const ::std::string kTestStructure1String =
     ".aos.common.testing.Structure{struct_bool:f, struct_int:973"
     ", struct_float:8.560000}";
+static const ::aos::common::testing::Structure kStructureValue(true, 973, 3.14);
+static const MessageWithArrays kTestMessageWithArrays({{971, 254, 1678}},
+                                                      {{kStructureValue,
+                                                        kStructureValue}});
+static const ::std::string kTestMessageWithArraysString =
+    ".aos.common.testing.MessageWithArrays{test_int:[971, 254, 1678], "
+    "test_struct:[.aos.common.testing.Structure{struct_bool:T, struct_int:973, "
+    "struct_float:3.140000}, .aos.common.testing.Structure{struct_bool:T, "
+    "struct_int:973, struct_float:3.140000}]}";
 
 TEST_F(PrintMessageTest, Basic) {
   CHECK_GE(sizeof(input), kTestMessage1.Size());
@@ -244,5 +259,16 @@
   EXPECT_EQ(kOutput.size() + 1, sizeof(output) - output_bytes);
 }
 
+TEST_F(PrintMessageTest, Array) {
+  CHECK_GE(sizeof(input), kTestMessageWithArrays.Size());
+  input_bytes = kTestMessageWithArrays.Serialize(input);
+  output_bytes = sizeof(output);
+  ASSERT_TRUE(PrintMessage(output, &output_bytes, input, &input_bytes,
+                           *kTestMessageWithArrays.GetType()));
+  EXPECT_EQ(kTestMessageWithArraysString, ::std::string(output));
+  EXPECT_EQ(kTestMessageWithArraysString.size() + 1,
+            sizeof(output) - output_bytes);
+}
+
 }  // namespace testing
 }  // namespace aos
diff --git a/aos/common/test_queue.q b/aos/common/test_queue.q
index d603802..827b607 100644
--- a/aos/common/test_queue.q
+++ b/aos/common/test_queue.q
@@ -23,6 +23,11 @@
   double test_double;
 };
 
+message MessageWithArrays {
+  uint16_t[3] test_int;
+  Structure[2] test_struct;
+};
+
 queue TestingMessage test_queue;
 
 queue_group TwoQueues {