Merge "Track message_bridge client UUID and connection counts and time"
diff --git a/aos/BUILD b/aos/BUILD
index 9049e08..8a8a19a 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -409,6 +409,7 @@
     deps = [
         "//aos:macros",
         "//aos/containers:resizeable_buffer",
+        "//aos/util:file",
         "@com_github_google_flatbuffers//:flatbuffers",
         "@com_github_google_glog//:glog",
         "@com_google_absl//absl/strings",
@@ -581,6 +582,7 @@
         ":json_to_flatbuffer",
         ":json_to_flatbuffer_fbs",
         "//aos/testing:googletest",
+        "//aos/testing:tmpdir",
     ],
 )
 
diff --git a/aos/flatbuffers.h b/aos/flatbuffers.h
index 68a8ad5..cdf5339 100644
--- a/aos/flatbuffers.h
+++ b/aos/flatbuffers.h
@@ -7,6 +7,7 @@
 #include "absl/types/span.h"
 #include "aos/containers/resizeable_buffer.h"
 #include "aos/macros.h"
+#include "aos/util/file.h"
 #include "flatbuffers/flatbuffers.h"  // IWYU pragma: export
 #include "glog/logging.h"
 
@@ -501,6 +502,31 @@
                                      span.size());
 }
 
+// MMap a flatbuffer on disk.
+template <typename T>
+class FlatbufferMMap : public NonSizePrefixedFlatbuffer<T> {
+ public:
+  // Builds a Flatbuffer by mmaping the data from a flatbuffer saved on disk.
+  FlatbufferMMap(const std::string &flatbuffer_path,
+                 util::FileOptions options = util::FileOptions::kReadable) {
+    span_ = util::MMapFile(flatbuffer_path, options);
+  }
+
+  // Copies the reference to the mapped memory.
+  FlatbufferMMap(const FlatbufferMMap &) = default;
+  FlatbufferMMap &operator=(const FlatbufferMMap<T> &other) = default;
+
+  // Moves the reference to the mapped memory from one pointer to another.
+  FlatbufferMMap(FlatbufferMMap &&) = default;
+  FlatbufferMMap &operator=(FlatbufferMMap<T> &&other) = default;
+
+  absl::Span<uint8_t> span() override { return *span_; }
+  absl::Span<const uint8_t> span() const override { return *span_; }
+
+ private:
+  std::shared_ptr<absl::Span<uint8_t>> span_;
+};
+
 }  // namespace aos
 
 #endif  // AOS_FLATBUFFERS_H_
diff --git a/aos/flatbuffers_test.cc b/aos/flatbuffers_test.cc
index e3030f1..ae8d6e6 100644
--- a/aos/flatbuffers_test.cc
+++ b/aos/flatbuffers_test.cc
@@ -1,9 +1,10 @@
 #include "aos/flatbuffers.h"
 
-#include "gtest/gtest.h"
-
+#include "absl/strings/str_cat.h"
 #include "aos/json_to_flatbuffer.h"
 #include "aos/json_to_flatbuffer_generated.h"
+#include "aos/testing/tmpdir.h"
+#include "gtest/gtest.h"
 
 namespace aos {
 namespace testing {
@@ -21,5 +22,59 @@
   EXPECT_FALSE(empty.Verify());
 }
 
+// Tests the ability to map a flatbuffer on disk to memory
+TEST(FlatbufferMMapTest, Verify) {
+  FlatbufferDetachedBuffer<Configuration> fb =
+      JsonToFlatbuffer<Configuration>("{\"foo_int\": 3}");
+
+  const std::string fb_path = absl::StrCat(TestTmpDir(), "/fb.bfbs");
+  WriteFlatbufferToFile(fb_path, fb);
+
+  FlatbufferMMap<Configuration> fb_mmap(fb_path);
+  EXPECT_TRUE(fb.Verify());
+  EXPECT_TRUE(fb_mmap.Verify());
+  ASSERT_EQ(fb_mmap.message().foo_int(), 3);
+
+  // Verify that copying works
+  {
+    FlatbufferMMap<Configuration> fb_mmap2(fb_path);
+    fb_mmap2 = fb_mmap;
+    EXPECT_TRUE(fb_mmap.Verify());
+    EXPECT_TRUE(fb_mmap2.Verify());
+    ASSERT_EQ(fb_mmap2.message().foo_int(), 3);
+    ASSERT_EQ(fb_mmap.message().foo_int(), 3);
+  }
+  EXPECT_TRUE(fb_mmap.Verify());
+  ASSERT_EQ(fb_mmap.message().foo_int(), 3);
+
+  // Verify that moving works
+  {
+    FlatbufferMMap<Configuration> fb_mmap3(fb_path);
+    fb_mmap3 = std::move(fb_mmap);
+    EXPECT_TRUE(fb_mmap3.Verify());
+    ASSERT_EQ(fb_mmap3.message().foo_int(), 3);
+  }
+}
+
+// Tests the ability to modify a flatbuffer mmaped from on disk in memory
+TEST(FlatbufferMMapTest, Writeable) {
+  FlatbufferDetachedBuffer<Configuration> fb =
+      JsonToFlatbuffer<Configuration>("{\"foo_int\": 3}");
+
+  const std::string fb_path = absl::StrCat(TestTmpDir(), "/fb.bfbs");
+  WriteFlatbufferToFile(fb_path, fb);
+
+  {
+    FlatbufferMMap<Configuration> fb_mmap(fb_path,
+                                          util::FileOptions::kWriteable);
+    fb_mmap.mutable_message()->mutate_foo_int(5);
+  }
+
+  {
+    FlatbufferMMap<Configuration> fb_mmap(fb_path);
+    EXPECT_EQ(fb_mmap.message().foo_int(), 5);
+  }
+}
+
 }  // namespace testing
 }  // namespace aos
diff --git a/aos/util/BUILD b/aos/util/BUILD
index 59dec18..daacba1 100644
--- a/aos/util/BUILD
+++ b/aos/util/BUILD
@@ -246,6 +246,7 @@
         "//aos/scoped:scoped_fd",
         "@com_github_google_glog//:glog",
         "@com_google_absl//absl/strings",
+        "@com_google_absl//absl/types:span",
     ],
 )
 
diff --git a/aos/util/file.cc b/aos/util/file.cc
index 4473d4d..317206e 100644
--- a/aos/util/file.cc
+++ b/aos/util/file.cc
@@ -2,6 +2,7 @@
 
 #include <fcntl.h>
 #include <fts.h>
+#include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -151,5 +152,30 @@
   }
 }
 
+std::shared_ptr<absl::Span<uint8_t>> MMapFile(const std::string &path,
+                                              FileOptions options) {
+  int fd =
+      open(path.c_str(), options == FileOptions::kReadable ? O_RDONLY : O_RDWR);
+  PCHECK(fd != -1) << "Unable to open file " << path;
+  struct stat sb;
+  PCHECK(fstat(fd, &sb) != -1) << ": Unable to get file size of " << path;
+  uint8_t *start = reinterpret_cast<uint8_t *>(mmap(
+      NULL, sb.st_size,
+      options == FileOptions::kReadable ? PROT_READ : (PROT_READ | PROT_WRITE),
+      MAP_SHARED, fd, 0));
+  CHECK(start != MAP_FAILED) << ": Unable to open mapping to file " << path;
+  std::shared_ptr<absl::Span<uint8_t>> span =
+      std::shared_ptr<absl::Span<uint8_t>>(
+          new absl::Span<uint8_t>(start, sb.st_size),
+          [](absl::Span<uint8_t> *span) {
+            PCHECK(msync(span->data(), span->size(), MS_SYNC) == 0)
+                << ": Failed to flush data before unmapping.";
+            PCHECK(munmap(span->data(), span->size()) != -1);
+            delete span;
+          });
+  close(fd);
+  return span;
+}
+
 }  // namespace util
 }  // namespace aos
diff --git a/aos/util/file.h b/aos/util/file.h
index 8089225..2d37ec2 100644
--- a/aos/util/file.h
+++ b/aos/util/file.h
@@ -2,9 +2,12 @@
 #define AOS_UTIL_FILE_H_
 
 #include <sys/stat.h>
+
+#include <memory>
 #include <string>
 #include <string_view>
 
+#include "absl/types/span.h"
 #include "glog/logging.h"
 
 namespace aos {
@@ -32,6 +35,12 @@
 // runs across.
 void UnlinkRecursive(std::string_view path);
 
+enum class FileOptions { kReadable, kWriteable };
+
+// Maps file from disk into memory
+std::shared_ptr<absl::Span<uint8_t>> MMapFile(
+    const std::string &path, FileOptions options = FileOptions::kReadable);
+
 }  // namespace util
 }  // namespace aos