From d870468159f2d9a4b8ad1ba5244d1c850a8abb8c Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 11 Jul 2020 06:53:25 -0700 Subject: [PATCH] Make append work with fixed-size buffer --- include/fmt/core.h | 6 +++--- include/fmt/format.h | 14 +++++++++----- test/core-test.cc | 31 +++++++++++++++++++++++-------- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 3a7b1433..80d93dff 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1811,9 +1811,9 @@ OutputIt vformat_to( basic_format_args>> args) { auto& c = detail::get_container(out); using container = remove_reference_t; - typename std::conditional< - std::is_same>::value, - detail::buffer&, detail::container_buffer>::type buf(c); + conditional_t>::value, + detail::buffer&, detail::container_buffer> + buf(c); detail::vformat_to(buf, to_string_view(format_str), args); return out; } diff --git a/include/fmt/format.h b/include/fmt/format.h index 3fd7769d..0e2a8da1 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -571,11 +571,15 @@ template constexpr bool use_grisu() { template template void buffer::append(const U* begin, const U* end) { - size_t new_size = size_ + to_unsigned(end - begin); - try_reserve(new_size); - std::uninitialized_copy(begin, end, - make_checked(ptr_ + size_, capacity_ - size_)); - size_ = new_size; + do { + auto count = to_unsigned(end - begin); + try_reserve(size_ + count); + auto free_cap = capacity_ - size_; + if (free_cap < count) count = free_cap; + std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); + size_ += count; + begin += count; + } while (begin != end); } } // namespace detail diff --git a/test/core-test.cc b/test/core-test.cc index d8157359..78d54bb1 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -35,6 +35,7 @@ using fmt::detail::make_arg; using fmt::detail::value; using testing::_; +using testing::Invoke; using testing::Return; using testing::StrictMock; @@ -84,9 +85,9 @@ template struct mock_buffer : buffer { mock_buffer(T* data = nullptr, size_t capacity = 0) { this->set(data, capacity); - ON_CALL(*this, do_grow(_)) - .WillByDefault( - testing::Invoke([](size_t capacity) { return capacity; })); + ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) { + return capacity; + })); } }; @@ -178,6 +179,20 @@ TEST(BufferTest, Append) { EXPECT_EQ(12u, buffer.size()); } +TEST(BufferTest, AppendPartial) { + char data[10]; + mock_buffer buffer(data, sizeof(data)); + testing::InSequence seq; + EXPECT_CALL(buffer, do_grow(15)).WillOnce(Return(10)); + EXPECT_CALL(buffer, do_grow(15)).WillOnce(Invoke([&buffer](size_t) { + EXPECT_EQ(fmt::string_view(buffer.data(), buffer.size()), "0123456789"); + buffer.clear(); + return 10; + })); + auto test = "0123456789abcde"; + buffer.append(test, test + 15); +} + TEST(BufferTest, AppendAllocatesEnoughStorage) { char data[19]; mock_buffer buffer(data, 10); @@ -292,10 +307,10 @@ VISIT_TYPE(unsigned long, unsigned long long); template class NumericArgTest : public testing::Test {}; -using types = ::testing::Types; +using types = + ::testing::Types; TYPED_TEST_CASE(NumericArgTest, types); template @@ -372,7 +387,7 @@ TEST(ArgTest, CustomArg) { using visitor = mock_visitor::handle>; testing::StrictMock v; - EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom())); + EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom())); fmt::visit_format_arg(v, make_arg(test)); }