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