Replace virtual dispatch with normal functions in buffers
This commit is contained in:
parent
74ffea0dcf
commit
63ce170853
@ -8,12 +8,12 @@
|
|||||||
#ifndef FMT_CORE_H_
|
#ifndef FMT_CORE_H_
|
||||||
#define FMT_CORE_H_
|
#define FMT_CORE_H_
|
||||||
|
|
||||||
#include <cstddef> // std::byte
|
#include <cstddef> // std::byte
|
||||||
#include <cstdio> // std::FILE
|
#include <cstdio> // std::FILE
|
||||||
#include <cstring> // std::strlen
|
#include <cstring> // std::strlen
|
||||||
#include <iterator>
|
#include <iterator> // std::back_insert_iterator
|
||||||
#include <limits>
|
#include <limits> // std::numeric_limits
|
||||||
#include <memory> // std::addressof
|
#include <memory> // std::addressof
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
@ -821,13 +821,18 @@ template <typename T> class buffer {
|
|||||||
size_t size_;
|
size_t size_;
|
||||||
size_t capacity_;
|
size_t capacity_;
|
||||||
|
|
||||||
|
using grow_fun = void (*)(buffer& buf, size_t capacity);
|
||||||
|
grow_fun grow_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Don't initialize ptr_ since it is not accessed to save a few cycles.
|
// Don't initialize ptr_ since it is not accessed to save a few cycles.
|
||||||
FMT_MSC_WARNING(suppress : 26495)
|
FMT_MSC_WARNING(suppress : 26495)
|
||||||
FMT_CONSTEXPR buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
|
FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept
|
||||||
|
: size_(sz), capacity_(sz), grow_(grow) {}
|
||||||
|
|
||||||
FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
|
FMT_CONSTEXPR20 buffer(grow_fun grow, T* p = nullptr, size_t sz = 0,
|
||||||
: ptr_(p), size_(sz), capacity_(cap) {}
|
size_t cap = 0) noexcept
|
||||||
|
: ptr_(p), size_(sz), capacity_(cap), grow_(grow) {}
|
||||||
|
|
||||||
FMT_CONSTEXPR20 ~buffer() = default;
|
FMT_CONSTEXPR20 ~buffer() = default;
|
||||||
buffer(buffer&&) = default;
|
buffer(buffer&&) = default;
|
||||||
@ -838,10 +843,6 @@ template <typename T> class buffer {
|
|||||||
capacity_ = buf_capacity;
|
capacity_ = buf_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Increases the buffer capacity to hold at least *capacity* elements. */
|
|
||||||
// DEPRECATED!
|
|
||||||
virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using const_reference = const T&;
|
using const_reference = const T&;
|
||||||
@ -880,7 +881,7 @@ template <typename T> class buffer {
|
|||||||
// for at least one additional element either by increasing the capacity or by
|
// for at least one additional element either by increasing the capacity or by
|
||||||
// flushing the buffer if it is full.
|
// flushing the buffer if it is full.
|
||||||
FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
|
FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
|
||||||
if (new_capacity > capacity_) grow(new_capacity);
|
if (new_capacity > capacity_) grow_(*this, new_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR20 void push_back(const T& value) {
|
FMT_CONSTEXPR20 void push_back(const T& value) {
|
||||||
@ -929,9 +930,8 @@ class iterator_buffer final : public Traits, public buffer<T> {
|
|||||||
enum { buffer_size = 256 };
|
enum { buffer_size = 256 };
|
||||||
T data_[buffer_size];
|
T data_[buffer_size];
|
||||||
|
|
||||||
protected:
|
static FMT_CONSTEXPR20 void grow(buffer<T>& buf, size_t) {
|
||||||
FMT_CONSTEXPR20 void grow(size_t) override {
|
if (buf.size() == buffer_size) static_cast<iterator_buffer&>(buf).flush();
|
||||||
if (this->size() == buffer_size) flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
@ -942,9 +942,11 @@ class iterator_buffer final : public Traits, public buffer<T> {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
|
explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
|
||||||
: Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {}
|
: Traits(n), buffer<T>(grow, data_, 0, buffer_size), out_(out) {}
|
||||||
iterator_buffer(iterator_buffer&& other)
|
iterator_buffer(iterator_buffer&& other)
|
||||||
: Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
|
: Traits(other),
|
||||||
|
buffer<T>(grow, data_, 0, buffer_size),
|
||||||
|
out_(other.out_) {}
|
||||||
~iterator_buffer() { flush(); }
|
~iterator_buffer() { flush(); }
|
||||||
|
|
||||||
auto out() -> OutputIt {
|
auto out() -> OutputIt {
|
||||||
@ -963,9 +965,9 @@ class iterator_buffer<T*, T, fixed_buffer_traits> final
|
|||||||
enum { buffer_size = 256 };
|
enum { buffer_size = 256 };
|
||||||
T data_[buffer_size];
|
T data_[buffer_size];
|
||||||
|
|
||||||
protected:
|
static FMT_CONSTEXPR20 void grow(buffer<T>& buf, size_t) {
|
||||||
FMT_CONSTEXPR20 void grow(size_t) override {
|
if (buf.size() == buf.capacity())
|
||||||
if (this->size() == this->capacity()) flush();
|
static_cast<iterator_buffer&>(buf).flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
@ -979,7 +981,7 @@ class iterator_buffer<T*, T, fixed_buffer_traits> final
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit iterator_buffer(T* out, size_t n = buffer_size)
|
explicit iterator_buffer(T* out, size_t n = buffer_size)
|
||||||
: fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out) {}
|
: fixed_buffer_traits(n), buffer<T>(grow, out, 0, n), out_(out) {}
|
||||||
iterator_buffer(iterator_buffer&& other)
|
iterator_buffer(iterator_buffer&& other)
|
||||||
: fixed_buffer_traits(other),
|
: fixed_buffer_traits(other),
|
||||||
buffer<T>(std::move(other)),
|
buffer<T>(std::move(other)),
|
||||||
@ -1001,11 +1003,9 @@ class iterator_buffer<T*, T, fixed_buffer_traits> final
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
|
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
|
||||||
protected:
|
|
||||||
FMT_CONSTEXPR20 void grow(size_t) override {}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
|
explicit iterator_buffer(T* out, size_t = 0)
|
||||||
|
: buffer<T>([](buffer<T>&, size_t) {}, out, 0, ~size_t()) {}
|
||||||
|
|
||||||
auto out() -> T* { return &*this->end(); }
|
auto out() -> T* { return &*this->end(); }
|
||||||
};
|
};
|
||||||
@ -1017,17 +1017,18 @@ class iterator_buffer<std::back_insert_iterator<Container>,
|
|||||||
typename Container::value_type>>
|
typename Container::value_type>>
|
||||||
final : public buffer<typename Container::value_type> {
|
final : public buffer<typename Container::value_type> {
|
||||||
private:
|
private:
|
||||||
|
using value_type = typename Container::value_type;
|
||||||
Container& container_;
|
Container& container_;
|
||||||
|
|
||||||
protected:
|
static FMT_CONSTEXPR20 void grow(buffer<value_type>& buf, size_t capacity) {
|
||||||
FMT_CONSTEXPR20 void grow(size_t capacity) override {
|
auto& self = static_cast<iterator_buffer&>(buf);
|
||||||
container_.resize(capacity);
|
self.container_.resize(capacity);
|
||||||
this->set(&container_[0], capacity);
|
self.set(&self.container_[0], capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit iterator_buffer(Container& c)
|
explicit iterator_buffer(Container& c)
|
||||||
: buffer<typename Container::value_type>(c.size()), container_(c) {}
|
: buffer<value_type>(grow, c.size()), container_(c) {}
|
||||||
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
|
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
|
||||||
: iterator_buffer(get_container(out)) {}
|
: iterator_buffer(get_container(out)) {}
|
||||||
|
|
||||||
@ -1043,15 +1044,14 @@ template <typename T = char> class counting_buffer final : public buffer<T> {
|
|||||||
T data_[buffer_size];
|
T data_[buffer_size];
|
||||||
size_t count_ = 0;
|
size_t count_ = 0;
|
||||||
|
|
||||||
protected:
|
static FMT_CONSTEXPR20 void grow(buffer<T>& buf, size_t) {
|
||||||
FMT_CONSTEXPR20 void grow(size_t) override {
|
if (buf.size() != buffer_size) return;
|
||||||
if (this->size() != buffer_size) return;
|
static_cast<counting_buffer&>(buf).count_ += buf.size();
|
||||||
count_ += this->size();
|
buf.clear();
|
||||||
this->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
|
counting_buffer() : buffer<T>(grow, data_, 0, buffer_size) {}
|
||||||
|
|
||||||
auto count() -> size_t { return count_ + this->size(); }
|
auto count() -> size_t { return count_ + this->size(); }
|
||||||
};
|
};
|
||||||
|
@ -880,27 +880,29 @@ class basic_memory_buffer final : public detail::buffer<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FMT_CONSTEXPR20 void grow(size_t size) override {
|
static FMT_CONSTEXPR20 void grow(detail::buffer<T>& buf, size_t size) {
|
||||||
detail::abort_fuzzing_if(size > 5000);
|
detail::abort_fuzzing_if(size > 5000);
|
||||||
const size_t max_size = std::allocator_traits<Allocator>::max_size(alloc_);
|
auto& self = static_cast<basic_memory_buffer&>(buf);
|
||||||
size_t old_capacity = this->capacity();
|
const size_t max_size =
|
||||||
|
std::allocator_traits<Allocator>::max_size(self.alloc_);
|
||||||
|
size_t old_capacity = buf.capacity();
|
||||||
size_t new_capacity = old_capacity + old_capacity / 2;
|
size_t new_capacity = old_capacity + old_capacity / 2;
|
||||||
if (size > new_capacity)
|
if (size > new_capacity)
|
||||||
new_capacity = size;
|
new_capacity = size;
|
||||||
else if (new_capacity > max_size)
|
else if (new_capacity > max_size)
|
||||||
new_capacity = size > max_size ? size : max_size;
|
new_capacity = size > max_size ? size : max_size;
|
||||||
T* old_data = this->data();
|
T* old_data = buf.data();
|
||||||
T* new_data =
|
T* new_data =
|
||||||
std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
|
std::allocator_traits<Allocator>::allocate(self.alloc_, new_capacity);
|
||||||
// Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481).
|
// Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481).
|
||||||
detail::assume(this->size() <= new_capacity);
|
detail::assume(buf.size() <= new_capacity);
|
||||||
// The following code doesn't throw, so the raw pointer above doesn't leak.
|
// The following code doesn't throw, so the raw pointer above doesn't leak.
|
||||||
std::uninitialized_copy_n(old_data, this->size(), new_data);
|
std::uninitialized_copy_n(old_data, buf.size(), new_data);
|
||||||
this->set(new_data, new_capacity);
|
self.set(new_data, new_capacity);
|
||||||
// deallocate must not throw according to the standard, but even if it does,
|
// deallocate must not throw according to the standard, but even if it does,
|
||||||
// the buffer already uses the new storage and will deallocate it in
|
// the buffer already uses the new storage and will deallocate it in
|
||||||
// destructor.
|
// destructor.
|
||||||
if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
|
if (old_data != self.store_) self.alloc_.deallocate(old_data, old_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -909,7 +911,7 @@ class basic_memory_buffer final : public detail::buffer<T> {
|
|||||||
|
|
||||||
FMT_CONSTEXPR20 explicit basic_memory_buffer(
|
FMT_CONSTEXPR20 explicit basic_memory_buffer(
|
||||||
const Allocator& alloc = Allocator())
|
const Allocator& alloc = Allocator())
|
||||||
: alloc_(alloc) {
|
: detail::buffer<T>(grow), alloc_(alloc) {
|
||||||
this->set(store_, SIZE);
|
this->set(store_, SIZE);
|
||||||
if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T());
|
if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T());
|
||||||
}
|
}
|
||||||
@ -941,7 +943,8 @@ class basic_memory_buffer final : public detail::buffer<T> {
|
|||||||
of the other object to it.
|
of the other object to it.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept {
|
FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept
|
||||||
|
: detail::buffer<T>(grow) {
|
||||||
move(other);
|
move(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,9 +377,10 @@ struct ostream_params {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class file_buffer final : public buffer<char> {
|
class file_buffer final : public buffer<char> {
|
||||||
|
private:
|
||||||
file file_;
|
file file_;
|
||||||
|
|
||||||
FMT_API void grow(size_t) override;
|
FMT_API static void grow(buffer<char>& buf, size_t);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FMT_API file_buffer(cstring_view path, const ostream_params& params);
|
FMT_API file_buffer(cstring_view path, const ostream_params& params);
|
||||||
|
11
src/os.cc
11
src/os.cc
@ -370,18 +370,17 @@ long getpagesize() {
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
void file_buffer::grow(size_t) {
|
void file_buffer::grow(buffer<char>& buf, size_t) {
|
||||||
if (this->size() == this->capacity()) flush();
|
if (buf.size() == buf.capacity()) static_cast<file_buffer&>(buf).flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
file_buffer::file_buffer(cstring_view path,
|
file_buffer::file_buffer(cstring_view path, const ostream_params& params)
|
||||||
const detail::ostream_params& params)
|
: buffer<char>(grow), file_(path, params.oflag) {
|
||||||
: file_(path, params.oflag) {
|
|
||||||
set(new char[params.buffer_size], params.buffer_size);
|
set(new char[params.buffer_size], params.buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
file_buffer::file_buffer(file_buffer&& other)
|
file_buffer::file_buffer(file_buffer&& other)
|
||||||
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
: buffer<char>(grow, other.data(), other.size(), other.capacity()),
|
||||||
file_(std::move(other.file_)) {
|
file_(std::move(other.file_)) {
|
||||||
other.clear();
|
other.clear();
|
||||||
other.set(nullptr, 0);
|
other.set(nullptr, 0);
|
||||||
|
@ -143,11 +143,12 @@ TEST(buffer_test, indestructible) {
|
|||||||
template <typename T> struct mock_buffer final : buffer<T> {
|
template <typename T> struct mock_buffer final : buffer<T> {
|
||||||
MOCK_METHOD(size_t, do_grow, (size_t));
|
MOCK_METHOD(size_t, do_grow, (size_t));
|
||||||
|
|
||||||
void grow(size_t capacity) override {
|
static void grow(buffer<T>& buf, size_t capacity) {
|
||||||
this->set(this->data(), do_grow(capacity));
|
auto& self = static_cast<mock_buffer&>(buf);
|
||||||
|
self.set(buf.data(), self.do_grow(capacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_buffer(T* data = nullptr, size_t buf_capacity = 0) {
|
mock_buffer(T* data = nullptr, size_t buf_capacity = 0) : buffer<T>(grow) {
|
||||||
this->set(data, buf_capacity);
|
this->set(data, buf_capacity);
|
||||||
ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) {
|
ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) {
|
||||||
return capacity;
|
return capacity;
|
||||||
@ -443,8 +444,9 @@ struct check_custom {
|
|||||||
-> test_result {
|
-> test_result {
|
||||||
struct test_buffer final : fmt::detail::buffer<char> {
|
struct test_buffer final : fmt::detail::buffer<char> {
|
||||||
char data[10];
|
char data[10];
|
||||||
test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {}
|
test_buffer()
|
||||||
void grow(size_t) override {}
|
: fmt::detail::buffer<char>([](buffer<char>&, size_t) {}, data, 0,
|
||||||
|
10) {}
|
||||||
} buffer;
|
} buffer;
|
||||||
auto parse_ctx = fmt::format_parse_context("");
|
auto parse_ctx = fmt::format_parse_context("");
|
||||||
auto ctx = fmt::format_context(fmt::detail::buffer_appender<char>(buffer),
|
auto ctx = fmt::format_context(fmt::detail::buffer_appender<char>(buffer),
|
||||||
|
@ -135,8 +135,8 @@ TEST(ostream_test, write_to_ostream_max_size) {
|
|||||||
|
|
||||||
struct test_buffer final : fmt::detail::buffer<char> {
|
struct test_buffer final : fmt::detail::buffer<char> {
|
||||||
explicit test_buffer(size_t size)
|
explicit test_buffer(size_t size)
|
||||||
: fmt::detail::buffer<char>(nullptr, size, size) {}
|
: fmt::detail::buffer<char>([](buffer<char>&, size_t) {}, nullptr, size,
|
||||||
void grow(size_t) override {}
|
size) {}
|
||||||
} buffer(max_size);
|
} buffer(max_size);
|
||||||
|
|
||||||
struct mock_streambuf : std::streambuf {
|
struct mock_streambuf : std::streambuf {
|
||||||
@ -292,8 +292,7 @@ TEST(ostream_test, closed_ofstream) {
|
|||||||
|
|
||||||
struct unlocalized {};
|
struct unlocalized {};
|
||||||
|
|
||||||
auto operator<<(std::ostream& os, unlocalized)
|
auto operator<<(std::ostream& os, unlocalized) -> std::ostream& {
|
||||||
-> std::ostream& {
|
|
||||||
return os << 12345;
|
return os << 12345;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user