Clang support for constexpr StringPiece constructor
[folly.git] / folly / FileUtil.h
index 48e5e7129a5e8e800671b99e3b6b480df0d7562c..5adcd62d6c0df6a869c1d8aecb37a6fca3833132 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2015 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #ifndef FOLLY_FILEUTIL_H_
 #define FOLLY_FILEUTIL_H_
 
+#include <folly/Conv.h>
+#include <folly/Portability.h>
+#include <folly/ScopeGuard.h>
+
+#include <cassert>
+#include <limits>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <sys/uio.h>
+#include <fcntl.h>
 #include <unistd.h>
 
 namespace folly {
@@ -28,15 +37,24 @@ namespace folly {
  * until all data is written.  Note that *Full wrappers weaken the thread
  * semantics of underlying system calls.
  */
+int openNoInt(const char* name, int flags, mode_t mode = 0666);
 int closeNoInt(int fd);
+int dupNoInt(int fd);
+int dup2NoInt(int oldfd, int newfd);
+int fsyncNoInt(int fd);
+int fdatasyncNoInt(int fd);
+int ftruncateNoInt(int fd, off_t len);
+int truncateNoInt(const char* path, off_t len);
+int flockNoInt(int fd, int operation);
+int shutdownNoInt(int fd, int how);
 
 ssize_t readNoInt(int fd, void* buf, size_t n);
 ssize_t preadNoInt(int fd, void* buf, size_t n, off_t offset);
-ssize_t readvNoInt(int fd, const struct iovec* iov, int count);
+ssize_t readvNoInt(int fd, const iovec* iov, int count);
 
 ssize_t writeNoInt(int fd, const void* buf, size_t n);
 ssize_t pwriteNoInt(int fd, const void* buf, size_t n, off_t offset);
-ssize_t writevNoInt(int fd, const struct iovec* iov, int count);
+ssize_t writevNoInt(int fd, const iovec* iov, int count);
 
 /**
  * Wrapper around read() (and pread()) that, in addition to retrying on
@@ -55,10 +73,17 @@ ssize_t writevNoInt(int fd, const struct iovec* iov, int count);
  * contiguous.  You can no longer make this assumption if using readFull().
  * You should probably use pread() when reading from the same file descriptor
  * from multiple threads simultaneously, anyway.
+ *
+ * Note that readvFull and preadvFull require iov to be non-const, unlike
+ * readv and preadv.  The contents of iov after these functions return
+ * is unspecified.
  */
 ssize_t readFull(int fd, void* buf, size_t n);
 ssize_t preadFull(int fd, void* buf, size_t n, off_t offset);
-// TODO(tudorb): add readvFull if needed
+ssize_t readvFull(int fd, iovec* iov, int count);
+#if FOLLY_HAVE_PREADV
+ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset);
+#endif
 
 /**
  * Similar to readFull and preadFull above, wrappers around write() and
@@ -69,12 +94,104 @@ ssize_t preadFull(int fd, void* buf, size_t n, off_t offset);
  * a pipe), POSIX provides stronger guarantees, but not in the general case.
  * For example, Linux (even on a 64-bit platform) won't write more than 2GB in
  * one write() system call.
+ *
+ * Note that writevFull and pwritevFull require iov to be non-const, unlike
+ * writev and pwritev.  The contents of iov after these functions return
+ * is unspecified.
  */
 ssize_t writeFull(int fd, const void* buf, size_t n);
 ssize_t pwriteFull(int fd, const void* buf, size_t n, off_t offset);
-// TODO(tudorb): add writevFull if needed
+ssize_t writevFull(int fd, iovec* iov, int count);
+#if FOLLY_HAVE_PWRITEV
+ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset);
+#endif
+
+/**
+ * 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(const char* file_name, 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");
+  assert(file_name);
+
+  const auto fd = openNoInt(file_name, O_RDONLY);
+  if (fd == -1) return false;
+
+  size_t soFar = 0; // amount of bytes successfully read
+  SCOPE_EXIT {
+    assert(out.size() >= soFar); // resize better doesn't throw
+    out.resize(soFar);
+    // Ignore errors when closing the file
+    closeNoInt(fd);
+  };
+
+  // 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;
+}
+
+/**
+ * Writes container to file. The container is assumed to be
+ * contiguous, with element size equal to 1, and offering STL-like
+ * methods empty(), size(), and indexed access
+ * (e.g. std::vector<char>, std::string, fbstring, StringPiece).
+ *
+ * "flags" dictates the open flags to use. Default is to create file
+ * if it doesn't exist and truncate it.
+ *
+ * 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 writeFile(const Container& data, const char* filename,
+              int flags = O_WRONLY | O_CREAT | O_TRUNC) {
+  static_assert(sizeof(data[0]) == 1,
+                "writeFile works with element size equal to 1");
+  int fd = open(filename, flags, 0666);
+  if (fd == -1) {
+    return false;
+  }
+  bool ok = data.empty() ||
+    writeFull(fd, &data[0], data.size()) == static_cast<ssize_t>(data.size());
+  return closeNoInt(fd) == 0 && ok;
+}
 
 }  // namespaces
 
 #endif /* FOLLY_FILEUTIL_H_ */
-