Make buffer size configurable

This commit is contained in:
Victor Zverovich 2020-08-01 10:25:39 -07:00
parent f0b84da5ff
commit 7c4c5c79d2
3 changed files with 74 additions and 12 deletions

View File

@ -18,6 +18,7 @@
#include <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#include <memory>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
@ -29,7 +30,7 @@
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__)) && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // 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 <typename... T>
inline void get_params(ostream_params& op, int oflag, T... params) {
op.oflag = oflag;
get_params(op, params...);
}
template <typename... T>
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<char> {
private:
file file_;
char buffer_[BUFSIZ];
size_t buffer_size_;
std::unique_ptr<char[]> 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<char>(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<char>(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 <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
@ -381,9 +428,16 @@ class ostream : private detail::buffer<char> {
}
};
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`:
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size (``BUFSIZ`` by default)
*/
template <typename... T>
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

View File

@ -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

View File

@ -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());