[go: nahoru, domu]

Optimize ReadFileToStringWithMaxSize function

GetFileSize can be used to estimate buffer size if file size is
available. So use it as a hint for buffer size for file reading.
Also avoid latest fread syscall in reading loop using feof.

Bug: 821262
Change-Id: I7dc30483a2f95d4da2c0d3cb0049941d2634a281
Reviewed-on: https://chromium-review.googlesource.com/958861
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#559168}
diff --git a/base/files/file_util.cc b/base/files/file_util.cc
index 6fd5dd8..109cb229c 100644
--- a/base/files/file_util.cc
+++ b/base/files/file_util.cc
@@ -136,27 +136,52 @@
     return false;
   }
 
-  const size_t kBufferSize = 1 << 16;
-  std::unique_ptr<char[]> buf(new char[kBufferSize]);
-  size_t len;
-  size_t size = 0;
-  bool read_status = true;
-
   // Many files supplied in |path| have incorrect size (proc files etc).
-  // Hence, the file is read sequentially as opposed to a one-shot read.
-  while ((len = fread(buf.get(), 1, kBufferSize, file)) > 0) {
-    if (contents)
-      contents->append(buf.get(), std::min(len, max_size - size));
+  // Hence, the file is read sequentially as opposed to a one-shot read, using
+  // file size as a hint for chunk size if available.
+  constexpr int64_t kDefaultChunkSize = 1 << 16;
+  int64_t chunk_size;
+#if !defined(OS_NACL_NONSFI)
+  if (!GetFileSize(path, &chunk_size) || chunk_size <= 0)
+    chunk_size = kDefaultChunkSize - 1;
+  // We need to attempt to read at EOF for feof flag to be set so here we
+  // use |chunk_size| + 1.
+  chunk_size = std::min<uint64_t>(chunk_size, max_size) + 1;
+#else
+  chunk_size = kDefaultChunkSize;
+#endif  // !defined(OS_NACL_NONSFI)
+  size_t bytes_read_this_pass;
+  size_t bytes_read_so_far = 0;
+  bool read_status = true;
+  std::string local_contents;
+  local_contents.resize(chunk_size);
 
-    if ((max_size - size) < len) {
+  while ((bytes_read_this_pass = fread(&local_contents[bytes_read_so_far], 1,
+                                       chunk_size, file)) > 0) {
+    if ((max_size - bytes_read_so_far) < bytes_read_this_pass) {
+      // Read more than max_size bytes, bail out.
+      bytes_read_so_far = max_size;
       read_status = false;
       break;
     }
+    // In case EOF was not reached, iterate again but revert to the default
+    // chunk size.
+    if (bytes_read_so_far == 0)
+      chunk_size = kDefaultChunkSize;
 
-    size += len;
+    bytes_read_so_far += bytes_read_this_pass;
+    // Last fread syscall (after EOF) can be avoided via feof, which is just a
+    // flag check.
+    if (feof(file))
+      break;
+    local_contents.resize(bytes_read_so_far + chunk_size);
   }
   read_status = read_status && !ferror(file);
   CloseFile(file);
+  if (contents) {
+    contents->swap(local_contents);
+    contents->resize(bytes_read_so_far);
+  }
 
   return read_status;
 }