Support read-write mmapped flatbuffers
Change-Id: I005788775d6fca5660b911d59078a0d75c0bb545
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/flatbuffers.h b/aos/flatbuffers.h
index 74d74ba..cdf5339 100644
--- a/aos/flatbuffers.h
+++ b/aos/flatbuffers.h
@@ -507,8 +507,9 @@
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) {
- span_ = util::MMapFile(flatbuffer_path);
+ 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.
@@ -519,10 +520,7 @@
FlatbufferMMap(FlatbufferMMap &&) = default;
FlatbufferMMap &operator=(FlatbufferMMap<T> &&other) = default;
- absl::Span<uint8_t> span() override {
- LOG(FATAL) << "Unimplemented. A flatbuffer is immutable.";
- return *span_;
- }
+ absl::Span<uint8_t> span() override { return *span_; }
absl::Span<const uint8_t> span() const override { return *span_; }
private:
diff --git a/aos/flatbuffers_test.cc b/aos/flatbuffers_test.cc
index f37af53..ae8d6e6 100644
--- a/aos/flatbuffers_test.cc
+++ b/aos/flatbuffers_test.cc
@@ -55,5 +55,26 @@
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/file.cc b/aos/util/file.cc
index cec5a6e..317206e 100644
--- a/aos/util/file.cc
+++ b/aos/util/file.cc
@@ -152,18 +152,24 @@
}
}
-std::shared_ptr<absl::Span<uint8_t>> MMapFile(const std::string &path) {
- int fd = open(path.c_str(), O_RDONLY);
+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, PROT_READ, MAP_PRIVATE, fd, 0));
+ 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;
});
diff --git a/aos/util/file.h b/aos/util/file.h
index 7c06ece..2d37ec2 100644
--- a/aos/util/file.h
+++ b/aos/util/file.h
@@ -35,8 +35,11 @@
// 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);
+std::shared_ptr<absl::Span<uint8_t>> MMapFile(
+ const std::string &path, FileOptions options = FileOptions::kReadable);
} // namespace util
} // namespace aos