Add malloc-free file contents reader
If you have a file that can be read safely in realtime code, this
provides a convenient wrapper for grabbing the entire contents of
said file. This should also just be a bit faster than
ReadFileToStringOrDie just by virtue of avoiding all the malloc's in
std::string and not having to re-open the file.
Change-Id: I90a3fa9433ac3a8773027327bde245ebf0c13b10
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/util/BUILD b/aos/util/BUILD
index 5a87251..19a9172 100644
--- a/aos/util/BUILD
+++ b/aos/util/BUILD
@@ -333,6 +333,7 @@
target_compatible_with = ["@platforms//os:linux"],
deps = [
":file",
+ "//aos:realtime",
"//aos/testing:googletest",
],
)
diff --git a/aos/util/file.h b/aos/util/file.h
index 2d37ec2..d2fb9fa 100644
--- a/aos/util/file.h
+++ b/aos/util/file.h
@@ -1,13 +1,17 @@
#ifndef AOS_UTIL_FILE_H_
#define AOS_UTIL_FILE_H_
+#include <fcntl.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <memory>
#include <string>
#include <string_view>
+#include "absl/strings/numbers.h"
#include "absl/types/span.h"
+#include "aos/scoped/scoped_fd.h"
#include "glog/logging.h"
namespace aos {
@@ -41,6 +45,41 @@
std::shared_ptr<absl::Span<uint8_t>> MMapFile(
const std::string &path, FileOptions options = FileOptions::kReadable);
+// Wrapper to handle reading the contents of a file into a buffer. Meant for
+// situations where the malloc'ing of ReadFileToStringOrDie is inappropriate,
+// but where you still want to read a file.
+template <int kBufferSize = 1024>
+class FileReader {
+ public:
+ FileReader(std::string_view filename)
+ : file_(open(::std::string(filename).c_str(), O_RDONLY)) {
+ PCHECK(file_.get() != -1) << ": opening " << filename;
+ memset(buffer_, 0, kBufferSize);
+ }
+ // Reads the entire contents of the file into the internal buffer and returns
+ // a string_view of it.
+ // Note: The result may not be null-terminated.
+ std::string_view ReadContents() {
+ PCHECK(0 == lseek(file_.get(), 0, SEEK_SET));
+ const ssize_t result = read(file_.get(), buffer_, sizeof(buffer_));
+ PCHECK(result >= 0);
+ return {buffer_, static_cast<size_t>(result)};
+ }
+ // Calls ReadContents() and attempts to convert the result into an integer, or
+ // dies trying.
+ int ReadInt() {
+ int result;
+ std::string_view contents = ReadContents();
+ CHECK(absl::SimpleAtoi(contents, &result))
+ << "Failed to parse \"" << contents << "\" as int.";
+ return result;
+ }
+
+ private:
+ aos::ScopedFD file_;
+ char buffer_[kBufferSize];
+};
+
} // namespace util
} // namespace aos
diff --git a/aos/util/file_test.cc b/aos/util/file_test.cc
index 7c93fb3..8e76154 100644
--- a/aos/util/file_test.cc
+++ b/aos/util/file_test.cc
@@ -4,6 +4,9 @@
#include <string>
#include "gtest/gtest.h"
+#include "aos/realtime.h"
+
+DECLARE_bool(die_on_malloc);
namespace aos {
namespace util {
@@ -38,6 +41,21 @@
EXPECT_TRUE(PathExists(test_file));
}
+// Basic test of reading a normal file.
+TEST(FileTest, ReadNormalFileNoMalloc) {
+ const ::std::string tmpdir(getenv("TEST_TMPDIR"));
+ const ::std::string test_file = tmpdir + "/test_file";
+ ASSERT_EQ(0, system(("echo 971 > " + test_file).c_str()));
+
+ FileReader reader(test_file);
+
+ FLAGS_die_on_malloc = true;
+ RegisterMallocHook();
+ aos::ScopedRealtime realtime;
+ EXPECT_EQ("971\n", reader.ReadContents());
+ EXPECT_EQ(971, reader.ReadInt());
+}
+
} // namespace testing
} // namespace util
} // namespace aos