+/**
+ * Read entire file (if num_bytes is defaulted) or no more than
+ * num_bytes (otherwise) into container *out. The container is assumed
+ * to be contiguous, with element size equal to 1, and offer size(),
+ * reserve(), and random access (e.g. std::vector<char>, std::string,
+ * fbstring).
+ *
+ * Returns: true on success or false on failure. In the latter case
+ * errno will be set appropriately by the failing system primitive.
+ */
+template <class Container>
+bool readFile(
+ int fd,
+ Container& out,
+ size_t num_bytes = std::numeric_limits<size_t>::max()) {
+ static_assert(sizeof(out[0]) == 1,
+ "readFile: only containers with byte-sized elements accepted");
+
+ size_t soFar = 0; // amount of bytes successfully read
+ SCOPE_EXIT {
+ DCHECK(out.size() >= soFar); // resize better doesn't throw
+ out.resize(soFar);
+ };
+
+ // Obtain file size:
+ struct stat buf;
+ if (fstat(fd, &buf) == -1) return false;
+ // Some files (notably under /proc and /sys on Linux) lie about
+ // their size, so treat the size advertised by fstat under advise
+ // but don't rely on it. In particular, if the size is zero, we
+ // should attempt to read stuff. If not zero, we'll attempt to read
+ // one extra byte.
+ constexpr size_t initialAlloc = 1024 * 4;
+ out.resize(
+ std::min(
+ buf.st_size > 0 ? folly::to<size_t>(buf.st_size + 1) : initialAlloc,
+ num_bytes));
+
+ while (soFar < out.size()) {
+ const auto actual = readFull(fd, &out[soFar], out.size() - soFar);
+ if (actual == -1) {
+ return false;
+ }
+ soFar += actual;
+ if (soFar < out.size()) {
+ // File exhausted
+ break;
+ }
+ // Ew, allocate more memory. Use exponential growth to avoid
+ // quadratic behavior. Cap size to num_bytes.
+ out.resize(std::min(out.size() * 3 / 2, num_bytes));
+ }
+
+ return true;
+}
+
+/**
+ * Same as above, but takes in a file name instead of fd
+ */
+template <class Container>
+bool readFile(
+ const char* file_name,
+ Container& out,
+ size_t num_bytes = std::numeric_limits<size_t>::max()) {
+ DCHECK(file_name);
+
+ const auto fd = openNoInt(file_name, O_RDONLY);
+ if (fd == -1) {
+ return false;
+ }
+
+ SCOPE_EXIT {
+ // Ignore errors when closing the file
+ closeNoInt(fd);
+ };
+
+ return readFile(fd, out, num_bytes);
+}