Skip to content

Commit

Permalink
Double buffering no more
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Jul 11, 2020
1 parent 3640650 commit a2c4fed
Show file tree
Hide file tree
Showing 12 changed files with 109 additions and 112 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-Wold-style-cast -Wundef
-Wredundant-decls -Wwrite-strings -Wpointer-arith
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
-Wcast-align -Wnon-virtual-dtor
-Wcast-align
-Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual
-Wconversion -Wswitch-enum
Expand Down
3 changes: 1 addition & 2 deletions include/fmt/compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -587,8 +587,7 @@ template <typename CompiledFormat, typename... Args,
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
basic_memory_buffer<Char> buffer;
detail::buffer<Char>& base = buffer;
cf.format(std::back_inserter(base), args...);
cf.format(detail::buffer_appender<Char>(buffer), args...);
return to_string(buffer);
}

Expand Down
50 changes: 24 additions & 26 deletions include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -629,18 +629,18 @@ template <typename T> class buffer {
T* ptr_;
size_t size_;
size_t capacity_;
bool fixed_;

protected:
// Don't initialize ptr_ since it is not accessed to save a few cycles.
FMT_SUPPRESS_MSC_WARNING(26495)
buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz), fixed_(false) {}
buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}

buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0,
bool fixed = false) FMT_NOEXCEPT : ptr_(p),
size_(sz),
capacity_(cap),
fixed_(fixed) {}
buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT
: ptr_(p),
size_(sz),
capacity_(cap) {}

~buffer() = default;

/** Sets the buffer data and capacity. */
void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
Expand All @@ -657,7 +657,6 @@ template <typename T> class buffer {

buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
virtual ~buffer() = default;

T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
Expand All @@ -677,24 +676,26 @@ template <typename T> class buffer {
/** Returns a pointer to the buffer data. */
const T* data() const FMT_NOEXCEPT { return ptr_; }

/**
Resizes the buffer. If T is a POD type new elements may not be initialized.
*/
void resize(size_t new_size) {
reserve(new_size);
size_ = new_size;
}

/** Clears this buffer. */
void clear() { size_ = 0; }

/** Reserves space to store at least *capacity* elements. */
void reserve(size_t new_capacity) {
// Tries resizing the buffer to contain *count* elements. If T is a POD type
// the new elements may not be initialized.
void try_resize(size_t count) {
try_reserve(count);
size_ = count <= capacity_ ? count : capacity_;
}

// Tries increasing the buffer capacity to *new_capacity*. It can increase the
// capacity by a smaller amount than requested but guarantees there is space
// for at least one additional element either by increasing the capacity or by
// flushing the buffer if it is full.
void try_reserve(size_t new_capacity) {
if (new_capacity > capacity_) grow(new_capacity);
}

void push_back(const T& value) {
reserve(size_ + 1);
try_reserve(size_ + 1);
ptr_[size_++] = value;
}

Expand All @@ -707,12 +708,6 @@ template <typename T> class buffer {
}
};

// A fixed capacity buffer.
template <typename T> class fixed_buffer : buffer<T> {
public:
fixed_buffer(T* data, size_t capacity) : buffer<T>(data, 0, capacity, true) {}
};

// A container-backed buffer.
template <typename Container>
class container_buffer : public buffer<typename Container::value_type> {
Expand Down Expand Up @@ -1815,7 +1810,10 @@ OutputIt vformat_to(
OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto& c = detail::get_container(out);
detail::container_buffer<remove_reference_t<decltype(c)>> buf(c);
using container = remove_reference_t<decltype(c)>;
typename std::conditional<
std::is_same<container, detail::buffer<Char>>::value,
detail::buffer<Char>&, detail::container_buffer<container>>::type buf(c);
detail::vformat_to(buf, to_string_view(format_str), args);
return out;
}
Expand Down
28 changes: 15 additions & 13 deletions include/fmt/format-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
// Report error code making sure that the output fits into
// inline_buffer_size to avoid dynamic memory allocation and potential
// bad_alloc.
out.resize(0);
out.try_resize(0);
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
Expand All @@ -156,7 +156,7 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
++error_code_size;
}
error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
auto it = std::back_inserter(out);
auto it = buffer_appender<char>(out);
if (message.size() <= inline_buffer_size - error_code_size)
format_to(it, "{}{}", message, SEP);
format_to(it, "{}{}", ERROR_STR, error_code);
Expand Down Expand Up @@ -1051,7 +1051,7 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
if (result > 0 || (result == 0 && (digit % 2) != 0))
++data[num_digits - 1];
}
buf.resize(to_unsigned(num_digits));
buf.try_resize(to_unsigned(num_digits));
exp10 -= num_digits - 1;
return;
}
Expand All @@ -1075,7 +1075,7 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
buf.push_back('0');
return 0;
}
buf.resize(to_unsigned(precision));
buf.try_resize(to_unsigned(precision));
std::uninitialized_fill_n(buf.data(), precision, '0');
return -precision;
}
Expand Down Expand Up @@ -1113,7 +1113,7 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
fallback_format(value, buf, exp);
return exp;
}
buf.resize(to_unsigned(handler.size));
buf.try_resize(to_unsigned(handler.size));
} else {
if (precision > 17) return snprintf_float(value, precision, specs, buf);
fp normalized = normalize(fp(value));
Expand All @@ -1131,7 +1131,7 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
++exp;
}
}
buf.resize(to_unsigned(num_digits));
buf.try_resize(to_unsigned(num_digits));
}
return exp - cached_exp10;
}
Expand Down Expand Up @@ -1182,19 +1182,20 @@ int snprintf_float(T value, int precision, float_specs specs,
? snprintf_ptr(begin, capacity, format, precision, value)
: snprintf_ptr(begin, capacity, format, value);
if (result < 0) {
buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
// The buffer will grow exponentially.
buf.try_reserve(buf.capacity() + 1);
continue;
}
auto size = to_unsigned(result);
// Size equal to capacity means that the last character was truncated.
if (size >= capacity) {
buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'.
buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'.
continue;
}
auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
if (specs.format == float_format::fixed) {
if (precision == 0) {
buf.resize(size);
buf.try_resize(size);
return 0;
}
// Find and remove the decimal point.
Expand All @@ -1204,11 +1205,11 @@ int snprintf_float(T value, int precision, float_specs specs,
} while (is_digit(*p));
int fraction_size = static_cast<int>(end - p - 1);
std::memmove(p, p + 1, to_unsigned(fraction_size));
buf.resize(size - 1);
buf.try_resize(size - 1);
return -fraction_size;
}
if (specs.format == float_format::hex) {
buf.resize(size + offset);
buf.try_resize(size + offset);
return 0;
}
// Find and parse the exponent.
Expand All @@ -1234,7 +1235,7 @@ int snprintf_float(T value, int precision, float_specs specs,
fraction_size = static_cast<int>(fraction_end - begin - 1);
std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size));
}
buf.resize(to_unsigned(fraction_size) + offset + 1);
buf.try_resize(to_unsigned(fraction_size) + offset + 1);
return exp - fraction_size;
}
}
Expand Down Expand Up @@ -1373,7 +1374,8 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
int result =
detail::safe_strerror(error_code, system_message, buf.size());
if (result == 0) {
format_to(std::back_inserter(out), "{}: {}", message, system_message);
format_to(detail::buffer_appender<char>(out), "{}: {}", message,
system_message);
return;
}
if (result != ERANGE)
Expand Down
17 changes: 13 additions & 4 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ reserve(std::back_insert_iterator<Container> it, size_t n) {
template <typename T>
inline buffer_appender<T> reserve(buffer_appender<T> it, size_t n) {
buffer<T>& buf = get_container(it);
buf.reserve(buf.size() + n);
buf.try_reserve(buf.size() + n);
return it;
}

Expand Down Expand Up @@ -572,7 +572,7 @@ template <typename T>
template <typename U>
void buffer<T>::append(const U* begin, const U* end) {
size_t new_size = size_ + to_unsigned(end - begin);
reserve(new_size);
try_reserve(new_size);
std::uninitialized_copy(begin, end,
make_checked(ptr_ + size_, capacity_ - size_));
size_ = new_size;
Expand Down Expand Up @@ -628,7 +628,7 @@ class basic_memory_buffer : public detail::buffer<T> {
}

protected:
void grow(size_t size) FMT_OVERRIDE;
void grow(size_t size) final;

public:
using value_type = T;
Expand All @@ -638,7 +638,7 @@ class basic_memory_buffer : public detail::buffer<T> {
: alloc_(alloc) {
this->set(store_, SIZE);
}
~basic_memory_buffer() FMT_OVERRIDE { deallocate(); }
~basic_memory_buffer() { deallocate(); }

private:
// Move data from other to this buffer.
Expand Down Expand Up @@ -682,6 +682,15 @@ class basic_memory_buffer : public detail::buffer<T> {

// Returns a copy of the allocator associated with this buffer.
Allocator get_allocator() const { return alloc_; }

/**
Resizes the buffer to contain *count* elements. If T is a POD type new
elements may not be initialized.
*/
void resize(size_t count) { this->try_resize(count); }

/** Increases the buffer capacity to *new_capacity*. */
void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
};

template <typename T, size_t SIZE, typename Allocator>
Expand Down
37 changes: 12 additions & 25 deletions include/fmt/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,25 +349,26 @@ template <typename S, typename... Args>
void print(direct_buffered_file& f, const S& format_str, const Args&... args);

// A buffered file with a direct buffer access and no synchronization.
class direct_buffered_file {
class direct_buffered_file : private detail::buffer<char> {
private:
file file_;

enum { buffer_size = 4096 };
char buffer_[buffer_size];
int pos_;
char buffer_[BUFSIZ];

void flush() {
if (pos_ == 0) return;
file_.write(buffer_, pos_);
pos_ = 0;
if (size() == 0) return;
file_.write(buffer_, size());
clear();
}

int free_capacity() const { return buffer_size - pos_; }
int free_capacity() const { return static_cast<int>(BUFSIZ - size()); }

protected:
void grow(size_t) final;

public:
direct_buffered_file(cstring_view path, int oflag)
: file_(path, oflag), pos_(0) {}
direct_buffered_file(const char* path, int oflag)
: buffer<char>(buffer_, 0, BUFSIZ), file_(path, oflag) {}

~direct_buffered_file() { flush(); }

Expand All @@ -379,21 +380,7 @@ class direct_buffered_file {
template <typename S, typename... Args>
friend void print(direct_buffered_file& f, const S& format_str,
const Args&... args) {
// We could avoid double buffering.
auto buf = fmt::memory_buffer();
fmt::format_to(std::back_inserter(buf), format_str, args...);
auto remaining_pos = 0;
auto remaining_size = buf.size();
while (remaining_size > detail::to_unsigned(f.free_capacity())) {
auto size = f.free_capacity();
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size);
f.pos_ += size;
f.flush();
remaining_pos += size;
remaining_size -= size;
}
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size);
f.pos_ += static_cast<int>(remaining_size);
fmt::format_to(detail::buffer_appender<char>(f), format_str, args...);
}
};
#endif // FMT_USE_FCNTL
Expand Down
2 changes: 1 addition & 1 deletion include/fmt/ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ void format_value(buffer<Char>& buf, const T& value,
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.resize(buf.size());
buf.try_resize(buf.size());
}

// Formats an object of type T that has an overloaded ostream operator<<.
Expand Down
4 changes: 2 additions & 2 deletions include/fmt/printf.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ template <typename Char> class printf_width_handler {
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
Context(buffer_appender<Char>(buf), format, args).format();
}
} // namespace detail

Expand Down Expand Up @@ -598,7 +598,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {

template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
basic_printf_context<detail::buffer_appender<Char>, Char>;

using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
Expand Down
6 changes: 5 additions & 1 deletion src/os.cc
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
format_to(std::back_inserter(out), "{}: {}", message, utf8_message);
format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
return;
}
break;
Expand Down Expand Up @@ -313,5 +313,9 @@ long getpagesize() {
return size;
# endif
}

void direct_buffered_file::grow(size_t) {
if (this->size() == BUFSIZ) flush();
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE
Loading

0 comments on commit a2c4fed

Please sign in to comment.