Store UUIDs as 16 bytes of data

This makes them much more efficient to write over shared memory to solve
the boot UUID logging problem when we add them.

Change-Id: Idf361d6b096bfa52cbc98f555c90bf1f6b90d3e0
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index 586e932..a3513d1 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -384,7 +384,9 @@
     target_compatible_with = ["@platforms//os:linux"],
     visibility = ["//visibility:public"],
     deps = [
+        "@com_github_google_flatbuffers//:flatbuffers",
         "@com_github_google_glog//:glog",
+        "@com_google_absl//absl/types:span",
     ],
 )
 
diff --git a/aos/events/logging/log_namer.cc b/aos/events/logging/log_namer.cc
index 44e8f5a..18138d9 100644
--- a/aos/events/logging/log_namer.cc
+++ b/aos/events/logging/log_namer.cc
@@ -20,11 +20,10 @@
     aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
     const UUID &uuid, int parts_index) const {
   header->mutable_message()->mutate_parts_index(parts_index);
-  CHECK_EQ(uuid.string_view().size(),
+  CHECK_EQ(UUID::kStringSize,
            header->mutable_message()->mutable_parts_uuid()->size());
-  std::copy(uuid.string_view().begin(), uuid.string_view().end(),
-            reinterpret_cast<char *>(
-                header->mutable_message()->mutable_parts_uuid()->Data()));
+  uuid.CopyTo(reinterpret_cast<char *>(
+      header->mutable_message()->mutable_parts_uuid()->Data()));
 }
 
 void LocalLogNamer::WriteHeader(
diff --git a/aos/events/logging/log_reader.cc b/aos/events/logging/log_reader.cc
index 6e92568..881f9c1 100644
--- a/aos/events/logging/log_reader.cc
+++ b/aos/events/logging/log_reader.cc
@@ -1172,7 +1172,7 @@
     flatbuffers::FlatBufferBuilder fbb;
     fbb.ForceDefaults(true);
     flatbuffers::Offset<flatbuffers::String> boot_uuid_offset =
-        fbb.CreateString(event_loop_->boot_uuid().string_view());
+        event_loop_->boot_uuid().PackString(&fbb);
 
     RemoteMessage::Builder message_header_builder(fbb);
 
diff --git a/aos/events/logging/log_writer.cc b/aos/events/logging/log_writer.cc
index 96995e9..400ad8d 100644
--- a/aos/events/logging/log_writer.cc
+++ b/aos/events/logging/log_writer.cc
@@ -432,7 +432,7 @@
     // There are no offsets to compute for ourself, so always succeed.
     SetStartTime(node_index, monotonic_start_time, realtime_start_time,
                  monotonic_start_time, realtime_start_time);
-    node_state_[node_index].SetBootUUID(event_loop_->boot_uuid().string_view());
+    node_state_[node_index].SetBootUUID(event_loop_->boot_uuid());
     return true;
   } else if (server_statistics_fetcher_.get() != nullptr) {
     // We must be a remote node now.  Look for the connection and see if it is
@@ -500,10 +500,10 @@
 
   CHECK(log_event_uuid_ != UUID::Zero());
   const flatbuffers::Offset<flatbuffers::String> log_event_uuid_offset =
-      fbb.CreateString(log_event_uuid_.string_view());
+      log_event_uuid_.PackString(&fbb);
 
   const flatbuffers::Offset<flatbuffers::String> logger_instance_uuid_offset =
-      fbb.CreateString(logger_instance_uuid_.string_view());
+      logger_instance_uuid_.PackString(&fbb);
 
   flatbuffers::Offset<flatbuffers::String> log_start_uuid_offset;
   if (!log_start_uuid_.empty()) {
@@ -516,10 +516,10 @@
   }
 
   const flatbuffers::Offset<flatbuffers::String> logger_node_boot_uuid_offset =
-      fbb.CreateString(event_loop_->boot_uuid().string_view());
+      event_loop_->boot_uuid().PackString(&fbb);
 
   const flatbuffers::Offset<flatbuffers::String> source_node_boot_uuid_offset =
-      fbb.CreateString(event_loop_->boot_uuid().string_view());
+      event_loop_->boot_uuid().PackString(&fbb);
 
   const flatbuffers::Offset<flatbuffers::String> parts_uuid_offset =
       fbb.CreateString("00000000-0000-4000-8000-000000000000");
diff --git a/aos/events/logging/log_writer.h b/aos/events/logging/log_writer.h
index 4c7d39f..eb08bb7 100644
--- a/aos/events/logging/log_writer.h
+++ b/aos/events/logging/log_writer.h
@@ -209,6 +209,18 @@
     bool header_valid = false;
 
     // Sets the source_node_boot_uuid, properly updating everything.
+    void SetBootUUID(const UUID &new_source_node_boot_uuid) {
+      new_source_node_boot_uuid.CopyTo(source_node_boot_uuid.data());
+      header_valid = false;
+      has_source_node_boot_uuid = true;
+
+      flatbuffers::String *source_node_boot_uuid_string =
+          log_file_header.mutable_message()->mutable_source_node_boot_uuid();
+      CHECK_EQ(source_node_boot_uuid.size(),
+               source_node_boot_uuid_string->size());
+      memcpy(source_node_boot_uuid_string->data(), source_node_boot_uuid.data(),
+             source_node_boot_uuid.size());
+    }
     void SetBootUUID(std::string_view new_source_node_boot_uuid) {
       source_node_boot_uuid = new_source_node_boot_uuid;
       header_valid = false;
diff --git a/aos/events/logging/logger_test.cc b/aos/events/logging/logger_test.cc
index cf8a373..6ffa16c 100644
--- a/aos/events/logging/logger_test.cc
+++ b/aos/events/logging/logger_test.cc
@@ -1901,7 +1901,7 @@
 
           ASSERT_TRUE(header.has_boot_uuid());
           EXPECT_EQ(header.boot_uuid()->string_view(),
-                    pi2_event_loop->boot_uuid().string_view());
+                    pi2_event_loop->boot_uuid().ToString());
 
           EXPECT_EQ(pi1_context->queue_index, header.remote_queue_index());
           EXPECT_EQ(pi2_context->remote_queue_index,
@@ -1981,7 +1981,7 @@
 
           ASSERT_TRUE(header.has_boot_uuid());
           EXPECT_EQ(header.boot_uuid()->string_view(),
-                    pi1_event_loop->boot_uuid().string_view());
+                    pi1_event_loop->boot_uuid().ToString());
 
           EXPECT_EQ(pi2_context->queue_index, header.remote_queue_index());
           EXPECT_EQ(pi1_context->remote_queue_index,
@@ -2090,7 +2090,7 @@
   {
     pi2_boot1 = event_loop_factory_.GetNodeEventLoopFactory(pi2_)
                     ->boot_uuid()
-                    .string_view();
+                    .ToString();
     LoggerState pi1_logger = MakeLogger(pi1_);
 
     event_loop_factory_.RunFor(chrono::milliseconds(95));
@@ -2103,7 +2103,7 @@
 
     pi2_boot2 = event_loop_factory_.GetNodeEventLoopFactory(pi2_)
                     ->boot_uuid()
-                    .string_view();
+                    .ToString();
 
     event_loop_factory_.RunFor(chrono::milliseconds(20000));
   }
diff --git a/aos/events/logging/uuid.cc b/aos/events/logging/uuid.cc
index 3914740..ce3411d 100644
--- a/aos/events/logging/uuid.cc
+++ b/aos/events/logging/uuid.cc
@@ -11,64 +11,141 @@
 
 namespace aos {
 namespace {
-char ToHex(int val) {
-  if (val < 10) {
-    return val + '0';
-  } else {
-    return val - 10 + 'a';
+void ToHex(const uint8_t *val, char *result, size_t count) {
+  while (count > 0) {
+    int upper = ((*val) >> 4) & 0xf;
+    if (upper < 10) {
+      result[0] = upper + '0';
+    } else {
+      result[0] = upper - 10 + 'a';
+    }
+
+    int lower = (*val) & 0xf;
+    if (lower < 10) {
+      result[1] = lower + '0';
+    } else {
+      result[1] = lower - 10 + 'a';
+    }
+
+    ++val;
+    result += 2;
+    --count;
   }
 }
+
+void FromHex(const char *val, uint8_t *result, size_t count) {
+  while (count > 0) {
+    CHECK((val[0] >= '0' && val[0] <= '9') || (val[0] >= 'a' && val[0] <= 'f'))
+        << ": Invalid hex '" << val[0] << "'";
+    CHECK((val[1] >= '0' && val[1] <= '9') || (val[1] >= 'a' && val[1] <= 'f'))
+        << ": Invalid hex '" << val[1] << "'";
+
+    uint8_t converted = 0;
+    if (val[0] < 'a') {
+      converted |= static_cast<uint8_t>(val[0] - '0') << 4;
+    } else {
+      converted |= (static_cast<uint8_t>(val[0] - 'a') + 0xa) << 4;
+    }
+    if (val[1] < 'a') {
+      converted |= static_cast<uint8_t>(val[1] - '0');
+    } else {
+      converted |= (static_cast<uint8_t>(val[1] - 'a') + 0xa);
+    }
+    *result = converted;
+
+    val += 2;
+    ++result;
+    --count;
+  }
+}
+
 }  // namespace
 
 UUID UUID::Random() {
   std::random_device rd;
   std::mt19937 gen(rd());
 
-  std::uniform_int_distribution<> dis(0, 15);
+  std::uniform_int_distribution<> dis(0, 255);
   std::uniform_int_distribution<> dis2(8, 11);
-
   UUID result;
+  for (size_t i = 0; i < kDataSize; ++i) {
+    result.data_[i] = dis(gen);
+  }
 
-  // UUID4 is implemented per https://www.cryptosys.net/pki/uuid-rfc4122.html
-  int i;
-  for (i = 0; i < 8; i++) {
-    result.data_[i] = ToHex(dis(gen));
-  }
-  result.data_[i] = '-';
-  ++i;
-  for (; i < 13; i++) {
-    result.data_[i] = ToHex(dis(gen));
-  }
-  result.data_[i] = '-';
-  ++i;
-  result.data_[i] = '4';
-  ++i;
-  for (; i < 18; i++) {
-    result.data_[i] = ToHex(dis(gen));
-  }
-  result.data_[i] = '-';
-  ++i;
-  result.data_[i] = ToHex(dis2(gen));
-  ++i;
-  for (; i < 23; i++) {
-    result.data_[i] = ToHex(dis(gen));
-  }
-  result.data_[i] = '-';
-  ++i;
-  for (; i < 36; i++) {
-    result.data_[i] = ToHex(dis(gen));
-  }
+  // Mark the reserved bits in the data that this is a uuid4, a random UUID.
+  result.data_[6] = (result.data_[6] & 0x0f) | 0x40;
+  result.data_[8] = (result.data_[6] & 0x3f) | 0x80;
 
   return result;
 }
 
-UUID UUID::Zero() { return FromString("00000000-0000-0000-0000-000000000000"); }
+std::string UUID::ToString() const {
+  std::string out;
+  out.resize(UUID::kStringSize);
+  CopyTo(out.data());
+  return out;
+}
+
+std::ostream &operator<<(std::ostream &os, const UUID &uuid) {
+  return os << uuid.ToString();
+}
+
+flatbuffers::Offset<flatbuffers::String> UUID::PackString(
+    flatbuffers::FlatBufferBuilder *fbb) const {
+  std::array<char, kStringSize> data;
+  CopyTo(data.data());
+
+  return fbb->CreateString(data.data(), data.size());
+}
+
+flatbuffers::Offset<flatbuffers::Vector<uint8_t>> UUID::PackVector(
+    flatbuffers::FlatBufferBuilder *fbb) const {
+  return fbb->CreateVector(data_.data(), data_.size());
+}
+
+void UUID::CopyTo(char *result) const {
+  ToHex(&data_[0], result, 4);
+  result[8] = '-';
+  ToHex(&data_[4], result + 9, 2);
+  result[13] = '-';
+  ToHex(&data_[6], result + 14, 2);
+  result[18] = '-';
+  ToHex(&data_[8], result + 19, 2);
+  result[23] = '-';
+  ToHex(&data_[10], result + 24, 6);
+}
+
+UUID UUID::Zero() {
+  UUID result;
+  std::memset(result.data_.data(), 0, result.data_.size());
+  return result;
+}
+
+UUID UUID::FromString(const flatbuffers::String *str) {
+  return FromString(str->string_view());
+}
+
+UUID UUID::FromVector(const flatbuffers::Vector<uint8_t> *data) {
+  CHECK(data != nullptr);
+  CHECK_EQ(data->size(), kDataSize);
+
+  UUID result;
+  std::memcpy(result.data_.data(), data->Data(), kDataSize);
+  return result;
+}
 
 UUID UUID::FromString(std::string_view str) {
-  UUID result;
-  CHECK_EQ(str.size(), kSize);
+  CHECK_EQ(str.size(), kStringSize);
 
-  std::copy(str.begin(), str.end(), result.data_.begin());
+  UUID result;
+  FromHex(str.data(), result.data_.data(), 4);
+  CHECK(str.data()[8] == '-' && str.data()[13] == '-' &&
+        str.data()[18] == '-' && str.data()[23] == '-')
+      << ": Invalid uuid.";
+  FromHex(str.data() + 9, result.data_.data() + 4, 2);
+  FromHex(str.data() + 14, result.data_.data() + 6, 2);
+  FromHex(str.data() + 19, result.data_.data() + 8, 2);
+  FromHex(str.data() + 24, result.data_.data() + 10, 6);
   return result;
 }
 
@@ -76,11 +153,12 @@
   int fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY);
   PCHECK(fd != -1);
 
-  UUID result;
-  CHECK_EQ(static_cast<ssize_t>(kSize), read(fd, result.data_.begin(), kSize));
+  std::array<char, kStringSize> data;
+  CHECK_EQ(static_cast<ssize_t>(kStringSize),
+           read(fd, data.begin(), kStringSize));
   close(fd);
 
-  return result;
+  return UUID::FromString(std::string_view(data.data(), data.size()));
 }
 
 }  // namespace aos
diff --git a/aos/events/logging/uuid.h b/aos/events/logging/uuid.h
index d7ec33a..1379369 100644
--- a/aos/events/logging/uuid.h
+++ b/aos/events/logging/uuid.h
@@ -2,45 +2,77 @@
 #define AOS_EVENTS_LOGGING_UUID_H_
 
 #include <array>
-#include <random>
-#include <string_view>
+#include <ostream>
+#include <string>
+
+#include "absl/types/span.h"
+#include "flatbuffers/flatbuffers.h"
 
 namespace aos {
 
 // Class to generate and hold a UUID.
 class UUID {
  public:
+  // Size of a UUID both as a string and the raw data.
+  static constexpr size_t kStringSize = 36;
+  static constexpr size_t kDataSize = 16;
+
   // Returns a randomly generated UUID.  This is known as a UUID4.
   static UUID Random();
 
-  // Returns a uuid with all '0' characters.
+  // Returns a uuid with all '0's.
   static UUID Zero();
 
-  static UUID FromString(std::string_view);
+  // Converts a string UUID of the form 00000000-0000-0000-0000-000000000000 to
+  // a UUID.
+  static UUID FromString(std::string_view string);
+  static UUID FromString(const flatbuffers::String *string);
 
+  // Converts a 16 byte vector (128 bits) to a UUID.  This requires no
+  // transformation.
+  static UUID FromVector(const flatbuffers::Vector<uint8_t> *data);
+
+  // Returns the boot UUID for the current linux computer.
   static UUID BootUUID();
 
-  // Size of a UUID.
-  static constexpr size_t kSize = 36;
+  // Default constructor which builds an uninitialized UUID.  Use one of the
+  // static methods if you want something more useful.
+  UUID() {}
 
-  std::string_view string_view() const {
-    return std::string_view(data_.data(), data_.size());
+  // Packs this UUID into a flatbuffer as a string.
+  flatbuffers::Offset<flatbuffers::String> PackString(
+      flatbuffers::FlatBufferBuilder *fbb) const;
+  // Copies this UUID as a string into the memory pointed by result.  Result
+  // must be at least kStringSize long.
+  void CopyTo(char *result) const;
+  // Returns this UUID as a string.
+  std::string ToString() const;
+
+  // Packs the UUID bytes directly into a vector.
+  flatbuffers::Offset<flatbuffers::Vector<uint8_t>> PackVector(
+      flatbuffers::FlatBufferBuilder *fbb) const;
+
+  // Returns the underlying UUID data.
+  absl::Span<const uint8_t> span() const {
+    return absl::Span<const uint8_t>(data_.data(), data_.size());
   }
 
   bool operator==(const UUID &other) const {
-    return other.string_view() == string_view();
+    return other.span() == span();
   }
   bool operator!=(const UUID &other) const {
-    return other.string_view() != string_view();
+    return other.span() != span();
   }
 
  private:
-  UUID() {}
+  friend std::ostream &operator<<(std::ostream &os, const UUID &uuid);
 
-  // Fixed size storage for the data.  Non-null terminated.
-  std::array<char, kSize> data_;
+  // Encoded storage for the data.
+  std::array<uint8_t, kDataSize> data_;
 };
 
+std::ostream &operator<<(std::ostream &os, const UUID &uuid);
+
 }  // namespace aos
 
 #endif  // AOS_EVENTS_LOGGING_UUID_H_
diff --git a/aos/events/logging/uuid_test.cc b/aos/events/logging/uuid_test.cc
index 4ea351c..5ec8f83 100644
--- a/aos/events/logging/uuid_test.cc
+++ b/aos/events/logging/uuid_test.cc
@@ -9,12 +9,44 @@
 // Tests that random UUIDs are actually random, and we can convert them to a
 // string.  Not very exhaustive, but it is a good smoke test.
 TEST(UUIDTest, GetOne) {
-  LOG(INFO) << UUID::Random().string_view();
+  LOG(INFO) << UUID::Random();
+
+  UUID r = UUID::Random();
+
+  std::stringstream ss;
+  ss << r;
+
+  UUID r2 = UUID::FromString(ss.str());
+  EXPECT_EQ(r2, r);
 
   EXPECT_NE(UUID::Random(), UUID::Random());
   EXPECT_NE(UUID::Random(), UUID::Zero());
   EXPECT_EQ(UUID::Zero(), UUID::Zero());
 }
 
+// Tests that converting to and from various formats produces the same UUID.
+TEST(UUIDTest, FromString) {
+  std::string_view str = "4b88ab00-556a-455b-a395-17d1a0c6f906";
+  std::array<uint8_t, UUID::kDataSize> data = {
+      0x4b, 0x88, 0xab, 0x00, 0x55, 0x6a, 0x45, 0x5b,
+      0xa3, 0x95, 0x17, 0xd1, 0xa0, 0xc6, 0xf9, 0x06};
+
+  const UUID u = UUID::FromString(str);
+
+  EXPECT_EQ(u.span(), absl::Span<uint8_t>(data.data(), data.size()));
+  EXPECT_EQ(u.ToString(), str);
+
+  flatbuffers::FlatBufferBuilder fbb;
+  flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data_offset =
+      fbb.CreateVector(data.data(), data.size());
+
+  const flatbuffers::Vector<uint8_t> *data_vector =
+      flatbuffers::GetTemporaryPointer(fbb, data_offset);
+
+  const UUID u2 = UUID::FromVector(data_vector);
+
+  EXPECT_EQ(u, u2);
+}
+
 }  // namespace testing
 }  // namespace aos
diff --git a/aos/events/simulated_event_loop_test.cc b/aos/events/simulated_event_loop_test.cc
index 43833fd..60731c1 100644
--- a/aos/events/simulated_event_loop_test.cc
+++ b/aos/events/simulated_event_loop_test.cc
@@ -653,7 +653,7 @@
           EXPECT_EQ(header.boot_uuid()->string_view(),
                     simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
                         ->boot_uuid()
-                        .string_view());
+                        .ToString());
 
           const aos::monotonic_clock::time_point header_monotonic_sent_time(
               chrono::nanoseconds(header.monotonic_sent_time()));
@@ -1433,7 +1433,7 @@
         EXPECT_EQ(header.boot_uuid()->string_view(),
                   simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
                       ->boot_uuid()
-                      .string_view());
+                      .ToString());
         VLOG(1) << aos::FlatbufferToJson(&header);
         if (header.channel_index() == reliable_channel_index) {
           ++reliable_timestamp_count;
@@ -1488,7 +1488,7 @@
   std::string expected_boot_uuid(
       simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
           ->boot_uuid()
-          .string_view());
+          .ToString());
 
   int timestamp_count = 0;
   pi1_remote_timestamp->MakeWatcher(
@@ -1530,12 +1530,12 @@
   EXPECT_NE(expected_boot_uuid,
             simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
                 ->boot_uuid()
-                .string_view());
+                .ToString());
 
   expected_boot_uuid =
       simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
           ->boot_uuid()
-          .string_view();
+          .ToString();
   timestamp_count = 0;
   pi1_server_statistics_count = 0;
 
diff --git a/aos/events/simulated_network_bridge.cc b/aos/events/simulated_network_bridge.cc
index 8b29b23..e5eb4cb 100644
--- a/aos/events/simulated_network_bridge.cc
+++ b/aos/events/simulated_network_bridge.cc
@@ -148,20 +148,18 @@
 
     if (timestamp_logger_) {
       flatbuffers::FlatBufferBuilder fbb;
-        fbb.ForceDefaults(true);
+      fbb.ForceDefaults(true);
       // Reset the filter every time the UUID changes.  There's probably a more
       // clever way to do this, but that means a better concept of rebooting.
       if (server_status_->BootUUID(destination_node_index_) !=
-          send_node_factory_->boot_uuid().string_view()) {
+          send_node_factory_->boot_uuid()) {
         server_status_->ResetFilter(destination_node_index_);
-        server_status_->SetBootUUID(
-            destination_node_index_,
-            send_node_factory_->boot_uuid().string_view());
+        server_status_->SetBootUUID(destination_node_index_,
+                                    send_node_factory_->boot_uuid());
       }
 
       flatbuffers::Offset<flatbuffers::String> boot_uuid_offset =
-          fbb.CreateString(
-              send_node_factory_->boot_uuid().string_view());
+          send_node_factory_->boot_uuid().PackString(&fbb);
 
       RemoteMessage::Builder message_header_builder(fbb);
 
@@ -343,8 +341,7 @@
         auto client_event_loop = event_loop_map_.find(client_node);
         it->second.server_status.ResetFilter(node_index);
         it->second.server_status.SetBootUUID(
-            node_index,
-            client_event_loop->second.event_loop->boot_uuid().string_view());
+            node_index, client_event_loop->second.event_loop->boot_uuid());
       }
       ++node_index;
     }
diff --git a/aos/network/BUILD b/aos/network/BUILD
index 1d4b634..8e7d476 100644
--- a/aos/network/BUILD
+++ b/aos/network/BUILD
@@ -148,6 +148,7 @@
         "//aos:configuration",
         "//aos:flatbuffer_merge",
         "//aos:flatbuffers",
+        "//aos/events/logging:uuid",
         "@com_github_google_flatbuffers//:flatbuffers",
     ],
 )
diff --git a/aos/network/message_bridge_client_lib.cc b/aos/network/message_bridge_client_lib.cc
index 8a0ec34..97058f1 100644
--- a/aos/network/message_bridge_client_lib.cc
+++ b/aos/network/message_bridge_client_lib.cc
@@ -98,9 +98,9 @@
     std::vector<SctpClientChannelState> *channels, int client_index,
     MessageBridgeClientStatus *client_status)
     : event_loop_(event_loop),
-      connect_message_(
-          MakeConnectMessage(event_loop->configuration(), my_node, remote_name,
-                             event_loop->boot_uuid().string_view())),
+      connect_message_(MakeConnectMessage(event_loop->configuration(), my_node,
+                                          remote_name,
+                                          event_loop->boot_uuid())),
       message_reception_reply_(MakeMessageHeaderReply()),
       remote_node_(CHECK_NOTNULL(
           configuration::GetNode(event_loop->configuration(), remote_name))),
diff --git a/aos/network/message_bridge_protocol.cc b/aos/network/message_bridge_protocol.cc
index 53102c2..87114ed 100644
--- a/aos/network/message_bridge_protocol.cc
+++ b/aos/network/message_bridge_protocol.cc
@@ -13,14 +13,14 @@
 
 aos::FlatbufferDetachedBuffer<aos::message_bridge::Connect> MakeConnectMessage(
     const Configuration *config, const Node *my_node,
-    std::string_view remote_name, std::string_view boot_uuid) {
+    std::string_view remote_name, const UUID &boot_uuid) {
   CHECK(config->has_nodes()) << ": Config must have nodes to transfer.";
 
   flatbuffers::FlatBufferBuilder fbb;
   fbb.ForceDefaults(true);
 
   flatbuffers::Offset<flatbuffers::String> boot_uuid_offset =
-      fbb.CreateString(boot_uuid);
+      boot_uuid.PackString(&fbb);
 
   flatbuffers::Offset<Node> node_offset =
       RecursiveCopyFlatBuffer<Node>(my_node, &fbb);
diff --git a/aos/network/message_bridge_protocol.h b/aos/network/message_bridge_protocol.h
index 5759a29..fed859b 100644
--- a/aos/network/message_bridge_protocol.h
+++ b/aos/network/message_bridge_protocol.h
@@ -4,6 +4,7 @@
 #include <string_view>
 
 #include "aos/configuration.h"
+#include "aos/events/logging/uuid.h"
 #include "aos/network/connect_generated.h"
 
 namespace aos {
@@ -33,7 +34,7 @@
 // Builds up a subscription request for my_node to remote_name.
 aos::FlatbufferDetachedBuffer<aos::message_bridge::Connect> MakeConnectMessage(
     const Configuration *config, const Node *my_node,
-    std::string_view remote_name, std::string_view boot_uuid);
+    std::string_view remote_name, const UUID &boot_uuid);
 
 }  // namespace message_bridge
 }  // namespace aos
diff --git a/aos/network/message_bridge_server_lib.cc b/aos/network/message_bridge_server_lib.cc
index a96d08e..7328f6f 100644
--- a/aos/network/message_bridge_server_lib.cc
+++ b/aos/network/message_bridge_server_lib.cc
@@ -113,8 +113,7 @@
             peer.timestamp_logger->MakeBuilder();
 
         flatbuffers::Offset<flatbuffers::String> boot_uuid_offset =
-            builder.fbb()->CreateString(
-                server_status.BootUUID(peer.node_index));
+            server_status.BootUUID(peer.node_index).PackString(builder.fbb());
 
         RemoteMessage::Builder message_header_builder =
             builder.MakeBuilder<RemoteMessage>();
@@ -279,7 +278,7 @@
                            configuration::GetNode(event_loop->configuration(),
                                                   destination_node_name),
                            event_loop->node()->name()->string_view(),
-                           UUID::Zero().string_view())
+                           UUID::Zero())
             .span()
             .size());
     VLOG(1) << "Connection to " << destination_node_name << " has size "
@@ -405,7 +404,7 @@
                    ->name()
                    ->string_view();
     server_status_.ResetFilter(node_index);
-    server_status_.SetBootUUID(node_index, "");
+    server_status_.ClearBootUUID(node_index);
   }
 }
 
@@ -480,7 +479,8 @@
       }
     }
     server_status_.ResetFilter(node_index);
-    server_status_.SetBootUUID(node_index, connect->boot_uuid()->string_view());
+    server_status_.SetBootUUID(
+        node_index, UUID::FromString(connect->boot_uuid()->string_view()));
     VLOG(1) << "Resetting filters for " << node_index << " "
             << event_loop_->configuration()
                    ->nodes()
diff --git a/aos/network/message_bridge_server_status.cc b/aos/network/message_bridge_server_status.cc
index 6c58dd1..7788f4a 100644
--- a/aos/network/message_bridge_server_status.cc
+++ b/aos/network/message_bridge_server_status.cc
@@ -85,11 +85,8 @@
       statistics_.message().connections()->size());
 
   filters_.resize(event_loop->configuration()->nodes()->size());
-  boot_uuids_.resize(event_loop->configuration()->nodes()->size());
-  for (std::string &boot_uuid : boot_uuids_) {
-    // Make sure the memory gets allocated.
-    boot_uuid.reserve(UUID::kSize);
-  }
+  boot_uuids_.resize(event_loop->configuration()->nodes()->size(), UUID::Zero());
+  has_boot_uuids_.resize(event_loop->configuration()->nodes()->size(), false);
   timestamp_fetchers_.resize(event_loop->configuration()->nodes()->size());
   server_connection_.resize(event_loop->configuration()->nodes()->size());
 
@@ -144,12 +141,19 @@
 }
 
 void MessageBridgeServerStatus::SetBootUUID(int node_index,
-                                            std::string_view boot_uuid) {
+                                            const UUID &boot_uuid) {
+  has_boot_uuids_[node_index] = true;
   boot_uuids_[node_index] = boot_uuid;
   SendStatistics();
   last_statistics_send_time_ = event_loop_->monotonic_now();
 }
 
+void MessageBridgeServerStatus::ClearBootUUID(int node_index) {
+  has_boot_uuids_[node_index] = false;
+  SendStatistics();
+  last_statistics_send_time_ = event_loop_->monotonic_now();
+}
+
 void MessageBridgeServerStatus::ResetFilter(int node_index) {
   filters_[node_index].Reset();
   server_connection_[node_index]->mutate_monotonic_offset(0);
@@ -175,9 +179,9 @@
     flatbuffers::Offset<Node> node_offset = node_builder.Finish();
 
     flatbuffers::Offset<flatbuffers::String> boot_uuid_offset;
-    if (!boot_uuids_[node_index].empty() &&
-        connection->state() == State::CONNECTED) {
-      boot_uuid_offset = builder.fbb()->CreateString(boot_uuids_[node_index]);
+    if (connection->state() == State::CONNECTED &&
+        has_boot_uuids_[node_index]) {
+      boot_uuid_offset = boot_uuids_[node_index].PackString(builder.fbb());
     }
 
     ServerConnection::Builder server_connection_builder =
diff --git a/aos/network/message_bridge_server_status.h b/aos/network/message_bridge_server_status.h
index 680c189..52fc174 100644
--- a/aos/network/message_bridge_server_status.h
+++ b/aos/network/message_bridge_server_status.h
@@ -41,13 +41,13 @@
   // Resets the filter and clears the entry from the server statistics.
   void ResetFilter(int node_index);
   // Sets the boot UUID for the provided node.
-  void SetBootUUID(int node_index, std::string_view boot_uuid);
+  void SetBootUUID(int node_index, const UUID &boot_uuid);
+  // Clears the boot UUID for the provided node.
+  void ClearBootUUID(int node_index);
 
   // Returns the boot UUID for a node, or an empty string_view if there isn't
   // one.
-  std::string_view BootUUID(int node_index) const {
-    return boot_uuids_[node_index];
-  }
+  const UUID &BootUUID(int node_index) const { return boot_uuids_[node_index]; }
 
   // Returns the ServerConnection message which is updated by the server.
   ServerConnection *FindServerConnection(std::string_view node_name);
@@ -93,7 +93,8 @@
   std::vector<ClippedAverageFilter> filters_;
 
   // List of UUIDs for each node.
-  std::vector<std::string> boot_uuids_;
+  std::vector<UUID> boot_uuids_;
+  std::vector<bool> has_boot_uuids_;
 
   // Sender for the timestamps that we are forwarding over the network.
   aos::Sender<Timestamp> timestamp_sender_;