From ca864e6a936bf9be76d415b5310577c9a0a5b963 Mon Sep 17 00:00:00 2001 From: Joel Rosdahl Date: Fri, 17 Jul 2020 13:17:25 +0200 Subject: [PATCH] Add size_hint parameter to Util::read_file MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Similar to the legacy read_file’s size_hint parameter. --- src/Util.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- src/Util.hpp | 5 +++-- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/Util.cpp b/src/Util.cpp index 337392fcf..e7323692d 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -20,6 +20,7 @@ #include "Config.hpp" #include "Context.hpp" +#include "Fd.hpp" #include "FormatNonstdStringView.hpp" #include "legacy_util.hpp" #include "logging.hpp" @@ -669,14 +670,54 @@ parse_int(const std::string& value) } std::string -read_file(const std::string& path) +read_file(const std::string& path, size_t size_hint) { - std::ifstream file(path); - if (!file) { + if (size_hint == 0) { + auto stat = Stat::stat(path, Stat::OnError::log); + if (!stat) { + throw Error(strerror(errno)); + } + size_hint = stat.size(); + } + + // +1 to be able to detect EOF in the first read call + size_hint = (size_hint < 1024) ? 1024 : size_hint + 1; + + Fd fd(open(path.c_str(), O_RDONLY | O_BINARY)); + if (!fd) { throw Error(strerror(errno)); } - return std::string(std::istreambuf_iterator(file), - std::istreambuf_iterator()); + + ssize_t ret = 0; + size_t pos = 0; + std::string result; + result.resize(size_hint); + + while (true) { + if (pos > result.size()) { + result.resize(2 * result.size()); + } + const size_t max_read = result.size() - pos; + char* data = const_cast(result.data()); // cast needed before C++17 + ret = read(*fd, data + pos, max_read); + if (ret == 0 || (ret == -1 && errno != EINTR)) { + break; + } + if (ret > 0) { + pos += ret; + if (static_cast(ret) < max_read) { + break; + } + } + } + + if (ret == -1) { + cc_log("Failed reading %s", path.c_str()); + throw Error(strerror(errno)); + } + + result.resize(pos); + return result; } #ifndef _WIN32 diff --git a/src/Util.hpp b/src/Util.hpp index 62a0ffabe..0bbb61e30 100644 --- a/src/Util.hpp +++ b/src/Util.hpp @@ -275,11 +275,12 @@ std::string normalize_absolute_path(nonstd::string_view path); // Throws Error on error. int parse_int(const std::string& value); -// Read file data as a string. +// Return `path`'s content as a string. If `size_hint` is not 0 then assume that +// `path` has this size (this saves system calls). // // Throws `Error` on error. The description contains the error message without // the path. -std::string read_file(const std::string& path); +std::string read_file(const std::string& path, size_t size_hint = 0); #ifndef _WIN32 // Like readlink(2) but returns the string (or the empty string on failure). -- 2.47.2