From 7c4c5c79d2f51924e7beec7af195bc21e53376d9 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 1 Aug 2020 10:25:39 -0700 Subject: [PATCH] Make buffer size configurable --- include/fmt/os.h | 76 +++++++++++++++++++++++++++++++++++++++++------- src/os.cc | 2 +- test/os-test.cc | 8 +++++ 3 files changed, 74 insertions(+), 12 deletions(-) diff --git a/include/fmt/os.h b/include/fmt/os.h index 80261784..24e51052 100644 --- a/include/fmt/os.h +++ b/include/fmt/os.h @@ -18,6 +18,7 @@ #include #include #include // for strtod_l +#include #if defined __APPLE__ || defined(__FreeBSD__) # include // for LC_NUMERIC_MASK on OS X @@ -29,7 +30,7 @@ #if FMT_HAS_INCLUDE("winapifamily.h") # include #endif -#if FMT_HAS_INCLUDE("fcntl.h") && \ +#if (FMT_HAS_INCLUDE() || defined(__APPLE__)) && \ (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) # include // for O_RDONLY # define FMT_USE_FCNTL 1 @@ -343,32 +344,78 @@ class file { // Returns the memory page size. long getpagesize(); +namespace detail { + +struct buffer_size { + size_t value = 0; + buffer_size operator=(size_t val) const { + auto bs = buffer_size(); + bs.value = val; + return bs; + } +}; + +struct ostream_params { + int oflag = file::WRONLY | file::CREATE; + size_t buffer_size = BUFSIZ; +}; + +inline void get_params(ostream_params&) {} + +template +inline void get_params(ostream_params& op, int oflag, T... params) { + op.oflag = oflag; + get_params(op, params...); +} + +template +inline void get_params(ostream_params& op, buffer_size bs, T... params) { + op.buffer_size = bs.value; + get_params(op, params...); +} +} // namespace detail + +static constexpr detail::buffer_size buffer_size; + // A fast output stream which is not thread-safe. class ostream : private detail::buffer { private: file file_; - char buffer_[BUFSIZ]; + size_t buffer_size_; + std::unique_ptr buffer_; + + char* move_buffer(ostream&& other) { + buffer_ = std::move(other.buffer_); + buffer_size_ = other.buffer_size_; + return buffer_.get(); + } void flush() { if (size() == 0) return; - file_.write(buffer_, size()); + file_.write(buffer_.get(), size()); clear(); } void grow(size_t) final; - ostream(cstring_view path, int oflag) - : buffer(buffer_, 0, BUFSIZ), file_(path, oflag) {} + ostream(cstring_view path, const detail::ostream_params& params) + : file_(path, params.oflag), + buffer_size_(params.buffer_size), + buffer_(new char[params.buffer_size]) { + set(buffer_.get(), params.buffer_size); + } public: ostream(ostream&& other) - : buffer(buffer_, 0, BUFSIZ), file_(std::move(other.file_)) { - append(other.begin(), other.end()); + : file_(std::move(other.file_)), + buffer_size_(other.buffer_size_), + buffer_(std::move(other.buffer_)) { other.clear(); } ~ostream() { flush(); } - friend ostream output_file(cstring_view path, int oflag); + template + friend ostream output_file(cstring_view path, T... params); void close() { flush(); @@ -381,9 +428,16 @@ class ostream : private detail::buffer { } }; -inline ostream output_file(cstring_view path, - int oflag = file::WRONLY | file::CREATE) { - return {path, oflag}; +/** + Opens a file for writing. Supported parameters passed in `params`: + * ````: Output flags (``file::WRONLY | file::CREATE`` by default) + * ``buffer_size=``: Output buffer size (``BUFSIZ`` by default) + */ +template +inline ostream output_file(cstring_view path, T... params) { + auto op = detail::ostream_params(); + get_params(op, params...); + return {path, op}; } #endif // FMT_USE_FCNTL diff --git a/src/os.cc b/src/os.cc index 5931702c..411fcea8 100644 --- a/src/os.cc +++ b/src/os.cc @@ -315,7 +315,7 @@ long getpagesize() { } void ostream::grow(size_t) { - if (this->size() == BUFSIZ) flush(); + if (this->size() == buffer_size_) flush(); } #endif // FMT_USE_FCNTL FMT_END_NAMESPACE diff --git a/test/os-test.cc b/test/os-test.cc index 4cf76f2a..4a54c063 100644 --- a/test/os-test.cc +++ b/test/os-test.cc @@ -305,6 +305,14 @@ TEST(OStreamTest, BufferBoundary) { EXPECT_READ(in, str + str); } +TEST(OStreamTest, BufferSize) { + fmt::ostream out = fmt::output_file("test-file", fmt::buffer_size=1); + out.print("{}", "foo"); + out.close(); + file in("test-file", file::RDONLY); + EXPECT_READ(in, "foo"); +} + TEST(FileTest, DefaultCtor) { file f; EXPECT_EQ(-1, f.descriptor());