forked from boostorg/beast
Fixes and tidying for flat_buffer:
* Fix reserve() size calculation * Improved test coverage
This commit is contained in:
@@ -12,6 +12,7 @@ Version 200
|
||||
* Use new buffer traits, remove old unused traits
|
||||
* Optimize for size on buffers_cat preconditions
|
||||
* Refactor buffers_suffix
|
||||
* Tidy up flat_buffer tests
|
||||
|
||||
API Changes:
|
||||
|
||||
|
@@ -253,7 +253,7 @@ reserve(std::size_t n)
|
||||
if(max_ < n)
|
||||
max_ = n;
|
||||
if(capacity() < n)
|
||||
prepare(n - capacity());
|
||||
prepare(n - size());
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
@@ -293,13 +293,16 @@ basic_flat_buffer<Allocator>::
|
||||
prepare(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
auto const len = size();
|
||||
if(len > max_ || n > (max_ - len))
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"basic_flat_buffer too long"});
|
||||
if(n <= dist(out_, end_))
|
||||
{
|
||||
// existing capacity is sufficient
|
||||
last_ = out_ + n;
|
||||
return{out_, n};
|
||||
}
|
||||
auto const len = size();
|
||||
if(n <= capacity() - len)
|
||||
{
|
||||
// after a memmove,
|
||||
@@ -311,10 +314,6 @@ prepare(std::size_t n) ->
|
||||
last_ = out_ + n;
|
||||
return {out_, n};
|
||||
}
|
||||
// enforce maximum capacity
|
||||
if(n > max_ - len)
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"basic_flat_buffer overflow"});
|
||||
// allocate a new buffer
|
||||
auto const new_size = (std::min<std::size_t>)(
|
||||
max_,
|
||||
|
@@ -18,7 +18,6 @@
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/buffers_iterator.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
@@ -347,6 +346,24 @@ struct is_mutable_dynamic_buffer<T, detail::void_t<decltype(
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
void
|
||||
buffers_fill(
|
||||
MutableBufferSequence const& buffers,
|
||||
char c)
|
||||
{
|
||||
auto const end =
|
||||
net::buffer_sequence_end(buffers);
|
||||
for(auto it = net::buffer_sequence_begin(buffers);
|
||||
it != end; ++it)
|
||||
{
|
||||
net::mutable_buffer b(*it);
|
||||
std::fill(
|
||||
static_cast<char*>(b.data()),
|
||||
static_cast<char*>(b.data()) + b.size(), c);
|
||||
}
|
||||
}
|
||||
|
||||
template<class MutableDynamicBuffer>
|
||||
void
|
||||
test_mutable_dynamic_buffer(
|
||||
@@ -369,6 +386,11 @@ test_mutable_dynamic_buffer(
|
||||
net::is_mutable_buffer_sequence<typename
|
||||
MutableDynamicBuffer::mutable_data_type>::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(
|
||||
std::is_convertible<
|
||||
typename MutableDynamicBuffer::mutable_data_type,
|
||||
typename MutableDynamicBuffer::const_buffers_type>::value);
|
||||
|
||||
string_view src = "Hello, world!";
|
||||
if(src.size() > b0.max_size())
|
||||
src = {src.data(), b0.max_size()};
|
||||
@@ -377,11 +399,8 @@ test_mutable_dynamic_buffer(
|
||||
{
|
||||
MutableDynamicBuffer b(b0);
|
||||
auto const mb = b.prepare(src.size());
|
||||
using iter_type = net::buffers_iterator<decltype(mb)>;
|
||||
SUITE_EXPECT(test, buffer_size(mb) == src.size());
|
||||
std::fill(
|
||||
iter_type::begin(mb),
|
||||
iter_type::end(mb), '*');
|
||||
buffers_fill(mb, '*');
|
||||
b.commit(src.size());
|
||||
SUITE_EXPECT(test, b.size() == src.size());
|
||||
SUITE_EXPECT(test,
|
||||
|
@@ -17,7 +17,6 @@
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/test/test_allocator.hpp>
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
#include <boost/asio/buffers_iterator.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
@@ -27,55 +26,16 @@ namespace beast {
|
||||
class flat_buffer_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
BOOST_STATIC_ASSERT(
|
||||
net::is_dynamic_buffer<
|
||||
flat_buffer>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
net::is_const_buffer_sequence<
|
||||
flat_buffer::const_buffers_type>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
net::is_mutable_buffer_sequence<
|
||||
flat_buffer::mutable_data_type>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
net::is_mutable_buffer_sequence<
|
||||
flat_buffer::mutable_buffers_type>::value);
|
||||
BOOST_STATIC_ASSERT(std::is_convertible<
|
||||
flat_buffer::mutable_data_type,
|
||||
flat_buffer::const_buffers_type>::value);
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
testMutableData()
|
||||
testDynamicBuffer()
|
||||
{
|
||||
DynamicBuffer b;
|
||||
DynamicBuffer const& cb = b;
|
||||
ostream(b) << "Hello";
|
||||
BOOST_STATIC_ASSERT(
|
||||
net::is_const_buffer_sequence<
|
||||
decltype(cb.data())>::value &&
|
||||
! net::is_mutable_buffer_sequence<
|
||||
decltype(cb.data())>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
net::is_const_buffer_sequence<
|
||||
decltype(cb.cdata())>::value &&
|
||||
! net::is_mutable_buffer_sequence<
|
||||
decltype(cb.cdata())>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
net::is_mutable_buffer_sequence<
|
||||
decltype(b.data())>::value);
|
||||
std::for_each(
|
||||
net::buffers_iterator<decltype(b.data())>::begin(b.data()),
|
||||
net::buffers_iterator<decltype(b.data())>::end(b.data()),
|
||||
[](char& c)
|
||||
{
|
||||
c = static_cast<char>(std::toupper(c));
|
||||
});
|
||||
BEAST_EXPECT(buffers_to_string(b.data()) == "HELLO");
|
||||
BEAST_EXPECT(buffers_to_string(b.cdata()) == "HELLO");
|
||||
flat_buffer b(30);
|
||||
BEAST_EXPECT(b.max_size() == 30);
|
||||
test_dynamic_buffer(*this, b);
|
||||
}
|
||||
|
||||
void
|
||||
testBuffer()
|
||||
testSpecialMembers()
|
||||
{
|
||||
using namespace test;
|
||||
|
||||
@@ -181,6 +141,16 @@ public:
|
||||
BEAST_EXPECT(buffers_to_string(b1.data()) == "Hello");
|
||||
BEAST_EXPECT(buffers_to_string(b2.data()) == "Hello");
|
||||
}
|
||||
{
|
||||
flat_buffer b1;
|
||||
ostream(b1) << "Hello";
|
||||
basic_flat_buffer<a_t> b2;
|
||||
b2.reserve(1);
|
||||
BEAST_EXPECT(b2.capacity() == 1);
|
||||
b2 = b1;
|
||||
BEAST_EXPECT(buffers_to_string(b2.data()) == "Hello");
|
||||
BEAST_EXPECT(b2.capacity() == b2.size());
|
||||
}
|
||||
|
||||
// move assignment
|
||||
{
|
||||
@@ -319,6 +289,24 @@ public:
|
||||
BEAST_EXPECT(b.max_size() == 32);
|
||||
}
|
||||
|
||||
// allocator max_size
|
||||
{
|
||||
basic_flat_buffer<a_t> b;
|
||||
auto a = b.get_allocator();
|
||||
BOOST_STATIC_ASSERT(
|
||||
! std::is_const<decltype(a)>::value);
|
||||
a->max_size = 30;
|
||||
try
|
||||
{
|
||||
b.prepare(1000);
|
||||
fail("", __FILE__, __LINE__);
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
// read_size
|
||||
{
|
||||
flat_buffer b{10};
|
||||
@@ -392,6 +380,11 @@ public:
|
||||
b.commit(20);
|
||||
b.reserve(50);
|
||||
BEAST_EXPECT(b.capacity() == 50);
|
||||
|
||||
b.max_size(b.capacity());
|
||||
b.reserve(b.max_size() + 20);
|
||||
BEAST_EXPECT(b.capacity() == 70);
|
||||
BEAST_EXPECT(b.max_size() == 70);
|
||||
}
|
||||
|
||||
// shrink to fit
|
||||
@@ -406,14 +399,20 @@ public:
|
||||
BEAST_EXPECT(b.capacity() >= 125);
|
||||
b.shrink_to_fit();
|
||||
BEAST_EXPECT(b.capacity() == b.size());
|
||||
b.shrink_to_fit();
|
||||
BEAST_EXPECT(b.capacity() == b.size());
|
||||
b.consume(b.size());
|
||||
BEAST_EXPECT(b.size() == 0);
|
||||
b.shrink_to_fit();
|
||||
BEAST_EXPECT(b.capacity() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testBuffer();
|
||||
testMutableData<flat_buffer>();
|
||||
testDynamicBuffer();
|
||||
testSpecialMembers();
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
@@ -26,6 +27,8 @@ struct test_allocator_info
|
||||
std::size_t nmassign = 0;
|
||||
std::size_t ncpassign = 0;
|
||||
std::size_t nselect = 0;
|
||||
std::size_t max_size = (
|
||||
std::numeric_limits<std::size_t>::max)();
|
||||
|
||||
test_allocator_info()
|
||||
: id([]
|
||||
@@ -37,28 +40,55 @@ struct test_allocator_info
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, bool Equal, bool Assign, bool Move, bool Swap, bool Select>
|
||||
template<
|
||||
class T,
|
||||
bool Equal,
|
||||
bool Assign,
|
||||
bool Move,
|
||||
bool Swap,
|
||||
bool Select>
|
||||
class test_allocator;
|
||||
|
||||
template<class T, bool Equal, bool Assign, bool Move, bool Swap, bool Select>
|
||||
template<
|
||||
class T,
|
||||
bool Equal,
|
||||
bool Assign,
|
||||
bool Move,
|
||||
bool Swap,
|
||||
bool Select>
|
||||
struct test_allocator_base
|
||||
{
|
||||
};
|
||||
|
||||
template<class T, bool Equal, bool Assign, bool Move, bool Swap>
|
||||
struct test_allocator_base<T, Equal, Assign, Move, Swap, true>
|
||||
// Select == true
|
||||
template<
|
||||
class T,
|
||||
bool Equal,
|
||||
bool Assign,
|
||||
bool Move,
|
||||
bool Swap>
|
||||
struct test_allocator_base<
|
||||
T, Equal, Assign, Move, Swap, true>
|
||||
{
|
||||
static
|
||||
test_allocator<T, Equal, Assign, Move, Swap, true>
|
||||
select_on_container_copy_construction(test_allocator<
|
||||
T, Equal, Assign, Move, Swap, true> const&)
|
||||
{
|
||||
return test_allocator<T, Equal, Assign, Move, Swap, true>{};
|
||||
return test_allocator<T,
|
||||
Equal, Assign, Move, Swap, true>{};
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, bool Equal, bool Assign, bool Move, bool Swap, bool Select>
|
||||
class test_allocator : public test_allocator_base<
|
||||
template<
|
||||
class T,
|
||||
bool Equal,
|
||||
bool Assign,
|
||||
bool Move,
|
||||
bool Swap,
|
||||
bool Select>
|
||||
class test_allocator
|
||||
: public test_allocator_base<
|
||||
T, Equal, Assign, Move, Swap, Select>
|
||||
{
|
||||
std::shared_ptr<test_allocator_info> info_;
|
||||
@@ -85,7 +115,8 @@ public:
|
||||
};
|
||||
|
||||
test_allocator()
|
||||
: info_(std::make_shared<test_allocator_info>())
|
||||
: info_(std::make_shared<
|
||||
test_allocator_info>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -138,6 +169,18 @@ public:
|
||||
::operator delete(p);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return info_->max_size;
|
||||
}
|
||||
|
||||
void
|
||||
max_size(std::size_t n)
|
||||
{
|
||||
info_->max_size = n;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(test_allocator const& other) const
|
||||
{
|
||||
@@ -156,13 +199,110 @@ public:
|
||||
return info_->id;
|
||||
}
|
||||
|
||||
test_allocator_info const*
|
||||
test_allocator_info*
|
||||
operator->() const
|
||||
{
|
||||
return info_.get();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if 0
|
||||
struct allocator_info
|
||||
{
|
||||
std::size_t const id;
|
||||
|
||||
allocator_info()
|
||||
: id([]
|
||||
{
|
||||
static std::atomic<std::size_t> sid(0);
|
||||
return ++sid;
|
||||
}())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct allocator_defaults
|
||||
{
|
||||
static std::size_t constexpr max_size =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
};
|
||||
|
||||
template<
|
||||
class T,
|
||||
class Traits = allocator_defaults>
|
||||
struct allocator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
#if 0
|
||||
template<class U>
|
||||
struct rebind
|
||||
{
|
||||
using other =
|
||||
test_allocator<U, Traits>;
|
||||
};
|
||||
#endif
|
||||
|
||||
allocator() = default;
|
||||
allocator(allocator&& t) = default;
|
||||
allocator(allocator const& u) = default;
|
||||
|
||||
template<
|
||||
class U,
|
||||
class = typename std::enable_if<
|
||||
! std::is_same<U, T>::value>::type>
|
||||
allocator(
|
||||
allocator<U, Traits> const& u) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
allocator&
|
||||
operator=(allocator&& u) noexcept
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator&
|
||||
operator=(allocator const& u) noexcept
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type*
|
||||
allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<value_type*>(
|
||||
::operator new(n * sizeof(value_type)));
|
||||
}
|
||||
|
||||
void
|
||||
deallocate(value_type* p, std::size_t) noexcept
|
||||
{
|
||||
::operator delete(p);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(test_allocator const& other) const
|
||||
{
|
||||
return id() == other.id() || Equal;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(test_allocator const& other) const
|
||||
{
|
||||
return ! this->operator==(other);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // test
|
||||
} // beast
|
||||
} // boost
|
||||
|
Reference in New Issue
Block a user