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/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