Add a PackRemoteMessage method which doesn't malloc

This lets us pack directly into the buffer to write to disk, removing
mallocs and allowing us to fix heap fragmentation by reducing memory
churn.  A future patch will use it.

Change-Id: I05b312a4e4bf87f6da22f1d9a84ab71ec3ebab3d
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/aos/events/logging/logfile_utils_test.cc b/aos/events/logging/logfile_utils_test.cc
index b7cf90b..c8d16e0 100644
--- a/aos/events/logging/logfile_utils_test.cc
+++ b/aos/events/logging/logfile_utils_test.cc
@@ -25,6 +25,7 @@
 namespace testing {
 namespace chrono = std::chrono;
 using aos::testing::ArtifactPath;
+using aos::message_bridge::RemoteMessage;
 
 // Adapter class to make it easy to test DetachedBufferWriter without adding
 // test only boilerplate to DetachedBufferWriter.
@@ -2874,6 +2875,42 @@
     return context;
   }
 
+  aos::monotonic_clock::time_point RandomMonotonic() {
+    std::uniform_int_distribution<int64_t> time_distribution(
+        0, std::numeric_limits<int64_t>::max());
+    return aos::monotonic_clock::epoch() +
+           chrono::nanoseconds(time_distribution(random_number_generator_));
+  }
+
+  aos::SizePrefixedFlatbufferDetachedBuffer<message_bridge::RemoteMessage>
+  RandomRemoteMessage() {
+    std::uniform_int_distribution<uint8_t> uint8_distribution(
+        std::numeric_limits<uint8_t>::min(),
+        std::numeric_limits<uint8_t>::max());
+
+    std::uniform_int_distribution<int64_t> time_distribution(
+        std::numeric_limits<int64_t>::min(),
+        std::numeric_limits<int64_t>::max());
+
+    flatbuffers::FlatBufferBuilder fbb;
+    message_bridge::RemoteMessage::Builder builder(fbb);
+    builder.add_queue_index(uint8_distribution(random_number_generator_));
+
+    builder.add_monotonic_sent_time(
+        time_distribution(random_number_generator_));
+    builder.add_realtime_sent_time(time_distribution(random_number_generator_));
+    builder.add_monotonic_remote_time(
+        time_distribution(random_number_generator_));
+    builder.add_realtime_remote_time(
+        time_distribution(random_number_generator_));
+
+    builder.add_remote_queue_index(
+        uint8_distribution(random_number_generator_));
+
+    fbb.FinishSizePrefixed(builder.Finish());
+    return fbb.Release();
+  }
+
   std::vector<uint8_t> RandomData() {
     std::vector<uint8_t> result;
     std::uniform_int_distribution<int> length_distribution(1, 32);
@@ -2966,8 +3003,50 @@
   }
 }
 
-// TODO(austin): I need a method to cpoy the RemoteMessage without mallocing
-// too.
+// Tests that all variations of PackMessage are equivilent to the inline
+// PackMessage used to avoid allocations.
+TEST_F(InlinePackMessage, RemoteEquivilent) {
+  aos::FlatbufferVector<reflection::Schema> schema =
+      FileToFlatbuffer<reflection::Schema>(
+          ArtifactPath("aos/events/logging/logger.bfbs"));
+  std::uniform_int_distribution<uint8_t> uint8_distribution(
+      std::numeric_limits<uint8_t>::min(), std::numeric_limits<uint8_t>::max());
+
+  for (int i = 0; i < 100; ++i) {
+    aos::SizePrefixedFlatbufferDetachedBuffer<RemoteMessage> random_msg =
+        RandomRemoteMessage();
+    const size_t channel_index = uint8_distribution(random_number_generator_);
+    const monotonic_clock::time_point monotonic_timestamp_time =
+        RandomMonotonic();
+
+    flatbuffers::FlatBufferBuilder fbb;
+    fbb.ForceDefaults(true);
+    fbb.FinishSizePrefixed(PackRemoteMessage(
+        &fbb, &random_msg.message(), channel_index, monotonic_timestamp_time));
+
+    VLOG(1) << absl::BytesToHexString(std::string_view(
+        reinterpret_cast<const char *>(fbb.GetBufferSpan().data()),
+        fbb.GetBufferSpan().size()));
+
+    // Make sure that both the builder and inline method agree on sizes.
+    ASSERT_EQ(fbb.GetSize(), PackRemoteMessageSize());
+
+    // Initialize the buffer to something nonzer to make sure all the padding
+    // bytes are set to 0.
+    std::vector<uint8_t> repacked_message(PackRemoteMessageSize(), 67);
+
+    // And verify packing inline works as expected.
+    EXPECT_EQ(
+        repacked_message.size(),
+        PackRemoteMessageInline(repacked_message.data(), &random_msg.message(),
+                                channel_index, monotonic_timestamp_time));
+    EXPECT_EQ(absl::Span<uint8_t>(repacked_message),
+              absl::Span<uint8_t>(fbb.GetBufferSpan().data(),
+                                  fbb.GetBufferSpan().size()))
+        << AnnotateBinaries(schema, "aos/events/logging/logger.bfbs",
+                            fbb.GetBufferSpan());
+  }
+}
 
 }  // namespace testing
 }  // namespace logger