Tidy up multi_buffer:

* Improved tests
* Refactor some declaration material
* basic_multi_buffer::clear is public

and

* Fix flat_buffer::reserve
* flat_buffer::clear is public
This commit is contained in:
Vinnie Falco
2018-12-15 18:48:32 -08:00
parent 74293fb8a5
commit 594a92e515
10 changed files with 386 additions and 389 deletions

View File

@@ -16,6 +16,7 @@ Version 200
* Fix ostream prepare calculation for low limits * Fix ostream prepare calculation for low limits
* Tidy up flat_static_buffer tests * Tidy up flat_static_buffer tests
* Add more tests for dynamic buffers * Add more tests for dynamic buffers
* Tidy up multi_buffer
API Changes: API Changes:

View File

@@ -83,7 +83,7 @@ class basic_flat_buffer
static static
std::size_t std::size_t
dist(char const* first, char const* last) dist(char const* first, char const* last) noexcept
{ {
return static_cast<std::size_t>(last - first); return static_cast<std::size_t>(last - first);
} }
@@ -370,6 +370,23 @@ public:
void void
shrink_to_fit(); shrink_to_fit();
/** Deallocate the internal buffer and reduce capacity to zero.
This function deallocates the dynamically allocated
internal buffer, and reduces the capacity to zero without
affecting the maximum size. The readable and writable
bytes will be empty after the object is cleared.
Buffer sequences previously obtained using @ref data or
@ref prepare become invalid.
@par Exception Safety
No-throw guarantee.
*/
void
clear() noexcept;
/// Exchange two dynamic buffers /// Exchange two dynamic buffers
template<class Alloc> template<class Alloc>
friend friend
@@ -506,7 +523,6 @@ private:
void swap(basic_flat_buffer&, std::true_type); void swap(basic_flat_buffer&, std::true_type);
void swap(basic_flat_buffer&, std::false_type); void swap(basic_flat_buffer&, std::false_type);
char* alloc(std::size_t n); char* alloc(std::size_t n);
void clear();
}; };
/// A flat buffer which uses the default allocator. /// A flat buffer which uses the default allocator.

View File

@@ -254,7 +254,7 @@ reserve(std::size_t n)
{ {
if(max_ < n) if(max_ < n)
max_ = n; max_ = n;
if(capacity() < n) if(n > capacity())
prepare(n - size()); prepare(n - size());
} }
@@ -287,6 +287,20 @@ shrink_to_fit()
end_ = out_; end_ = out_;
} }
template<class Allocator>
void
basic_flat_buffer<Allocator>::
clear() noexcept
{
alloc_traits::deallocate(
this->get(), begin_, size());
begin_ = nullptr;
in_ = nullptr;
out_ = nullptr;
last_ = nullptr;
end_ = nullptr;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class Allocator> template<class Allocator>
@@ -310,7 +324,11 @@ prepare(std::size_t n) ->
// after a memmove, // after a memmove,
// existing capacity is sufficient // existing capacity is sufficient
if(len > 0) if(len > 0)
{
BOOST_ASSERT(begin_);
BOOST_ASSERT(in_);
std::memmove(begin_, in_, len); std::memmove(begin_, in_, len);
}
in_ = begin_; in_ = begin_;
out_ = in_ + len; out_ = in_ + len;
last_ = out_ + n; last_ = out_ + n;
@@ -375,7 +393,11 @@ copy_from(
in_ = begin_; in_ = begin_;
out_ = begin_ + n; out_ = begin_ + n;
last_ = begin_ + n; last_ = begin_ + n;
std::memcpy(begin_, other.begin_, n); if(begin_)
{
BOOST_ASSERT(other.begin_);
std::memcpy(begin_, other.begin_, n);
}
} }
template<class Allocator> template<class Allocator>
@@ -495,20 +517,6 @@ alloc(std::size_t n)
return alloc_traits::allocate(this->get(), n); return alloc_traits::allocate(this->get(), n);
} }
template<class Allocator>
void
basic_flat_buffer<Allocator>::
clear()
{
alloc_traits::deallocate(
this->get(), begin_, size());
begin_ = nullptr;
in_ = nullptr;
out_ = nullptr;
last_ = nullptr;
end_ = nullptr;
}
} // beast } // beast
} // boost } // boost

View File

@@ -148,10 +148,11 @@ class basic_multi_buffer<Allocator>::readable_bytes
} }
public: public:
using value_type = typename std::conditional< using value_type = typename
isMutable, std::conditional<
net::mutable_buffer, isMutable,
net::const_buffer>::type; net::mutable_buffer,
net::const_buffer>::type;
class const_iterator; class const_iterator;
@@ -228,9 +229,7 @@ public:
std::bidirectional_iterator_tag; std::bidirectional_iterator_tag;
const_iterator() = default; const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default; const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default; const_iterator& operator=(const_iterator const& other) = default;
const_iterator( const_iterator(
@@ -314,7 +313,7 @@ class basic_multi_buffer<Allocator>::mutable_buffers_type
} }
public: public:
using value_type = mutable_buffer; using value_type = net::mutable_buffer;
class const_iterator; class const_iterator;
@@ -335,8 +334,8 @@ class basic_multi_buffer<Allocator>::mutable_buffers_type::const_iterator
typename list_type::const_iterator it_; typename list_type::const_iterator it_;
public: public:
using value_type = using value_type = typename
typename mutable_buffers_type::value_type; mutable_buffers_type::value_type;
using pointer = value_type const*; using pointer = value_type const*;
using reference = value_type; using reference = value_type;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
@@ -344,9 +343,7 @@ public:
std::bidirectional_iterator_tag; std::bidirectional_iterator_tag;
const_iterator() = default; const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default; const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default; const_iterator& operator=(const_iterator const& other) = default;
const_iterator( const_iterator(
@@ -468,6 +465,23 @@ basic_multi_buffer<Allocator>::
delete_list(); delete_list();
} }
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer() noexcept(default_nothrow)
: max_(alloc_traits::max_size(this->get()))
, out_(list_.end())
{
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
std::size_t limit) noexcept(default_nothrow)
: max_(limit)
, out_(list_.end())
{
}
template<class Allocator> template<class Allocator>
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
basic_multi_buffer( basic_multi_buffer(
@@ -672,6 +686,8 @@ void
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
reserve(std::size_t n) reserve(std::size_t n)
{ {
// VFALCO The amount needs to be adjusted for
// the sizeof(element) plus padding
if(n > alloc_traits::max_size(this->get())) if(n > alloc_traits::max_size(this->get()))
BOOST_THROW_EXCEPTION(std::length_error( BOOST_THROW_EXCEPTION(std::length_error(
"A basic_multi_buffer exceeded the allocator's maximum size")); "A basic_multi_buffer exceeded the allocator's maximum size"));
@@ -683,16 +699,17 @@ reserve(std::size_t n)
total += out_->size() - out_pos_; total += out_->size() - out_pos_;
if(n <= total) if(n <= total)
return; return;
auto it = out_; for(auto it = out_;;)
while(++it != list_.end())
{ {
if(++it == list_.end())
break;
total += it->size(); total += it->size();
if(n <= total) if(n <= total)
return; return;
} }
} }
BOOST_ASSERT(n > total); BOOST_ASSERT(n > total);
(void)prepare(n - total); (void)prepare(n - size());
} }
template<class Allocator> template<class Allocator>
@@ -717,15 +734,29 @@ shrink_to_fit() noexcept
} }
} }
template<class Allocator>
void
basic_multi_buffer<Allocator>::
clear() noexcept
{
delete_list();
list_.clear();
out_ = list_.end();
in_size_ = 0;
in_pos_ = 0;
out_pos_ = 0;
out_end_ = 0;
}
template<class Allocator> template<class Allocator>
auto auto
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
prepare(size_type n) -> prepare(size_type n) ->
mutable_buffers_type mutable_buffers_type
{ {
if(in_size_ + n > max_) if(in_size_ > max_ || n > (max_ - in_size_))
BOOST_THROW_EXCEPTION(std::length_error{ BOOST_THROW_EXCEPTION(std::length_error{
"A basic_multi_buffer exceeded its maximum size"}); "basic_multi_buffer too long"});
list_type reuse; list_type reuse;
std::size_t total = in_size_; std::size_t total = in_size_;
// put all empty buffers on reuse list // put all empty buffers on reuse list
@@ -943,22 +974,6 @@ copy_from(basic_multi_buffer<OtherAlloc> const& other)
prepare(other.size()), other.data())); prepare(other.size()), other.data()));
} }
template<class Allocator>
void
basic_multi_buffer<Allocator>::
move_assign(basic_multi_buffer& other, std::false_type)
{
if(this->get() != other.get())
{
copy_from(other);
other.clear();
}
else
{
move_assign(other, std::true_type{});
}
}
template<class Allocator> template<class Allocator>
void void
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
@@ -983,6 +998,22 @@ move_assign(basic_multi_buffer& other, std::true_type) noexcept
other.out_end_ = 0; other.out_end_ = 0;
} }
template<class Allocator>
void
basic_multi_buffer<Allocator>::
move_assign(basic_multi_buffer& other, std::false_type)
{
if(this->get() != other.get())
{
copy_from(other);
other.clear();
}
else
{
move_assign(other, std::true_type{});
}
}
template<class Allocator> template<class Allocator>
void void
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
@@ -1093,20 +1124,6 @@ alloc(std::size_t n)
return alloc_traits::allocate(this->get(), n); return alloc_traits::allocate(this->get(), n);
} }
template<class Allocator>
void
basic_multi_buffer<Allocator>::
clear() noexcept
{
delete_list();
list_.clear();
out_ = list_.end();
in_size_ = 0;
in_pos_ = 0;
out_pos_ = 0;
out_end_ = 0;
}
template<class Allocator> template<class Allocator>
void void
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::

View File

@@ -40,7 +40,7 @@ namespace beast {
desired size. The behavior and implementation of this desired size. The behavior and implementation of this
container is most similar to `std::deque`. container is most similar to `std::deque`.
Objects of this type meet the requirements of @b DynamicBuffer Objects of this type meet the requirements of <em>DynamicBuffer</em>
and have the following additional properties: and have the following additional properties:
@li A mutable buffer sequence representing the readable @li A mutable buffer sequence representing the readable
@@ -90,8 +90,6 @@ class basic_multi_buffer
using const_iter = typename list_type::const_iterator; using const_iter = typename list_type::const_iterator;
using size_type = typename alloc_traits::size_type; using size_type = typename alloc_traits::size_type;
using const_buffer = net::const_buffer;
using mutable_buffer = net::mutable_buffer;
using pocma = typename using pocma = typename
alloc_traits::propagate_on_container_move_assignment; alloc_traits::propagate_on_container_move_assignment;
@@ -128,11 +126,7 @@ public:
@ref max_size will return the largest value which may @ref max_size will return the largest value which may
be passed to the allocator's `allocate` function. be passed to the allocator's `allocate` function.
*/ */
basic_multi_buffer() noexcept(default_nothrow) basic_multi_buffer() noexcept(default_nothrow);
: max_(alloc_traits::max_size(this->get()))
, out_(list_.end())
{
}
/** Constructor /** Constructor
@@ -143,11 +137,7 @@ public:
*/ */
explicit explicit
basic_multi_buffer( basic_multi_buffer(
std::size_t limit) noexcept(default_nothrow) std::size_t limit) noexcept(default_nothrow);
: max_(limit)
, out_(list_.end())
{
}
/** Constructor /** Constructor
@@ -392,6 +382,23 @@ public:
void void
shrink_to_fit() noexcept; shrink_to_fit() noexcept;
/** Deallocate all buffers and reduce capacity to zero.
This function deallocates all dynamically allocated
buffers, and reduces the capacity to zero without
affecting the maximum size. The readable and writable
bytes will be empty after the object is cleared.
Buffer sequences previously obtained using @ref data or
@ref prepare become invalid.
@par Exception Safety
No-throw guarantee.
*/
void
clear() noexcept;
/// Exchange two dynamic buffers /// Exchange two dynamic buffers
template<class Alloc> template<class Alloc>
friend friend
@@ -536,7 +543,6 @@ private:
void swap(basic_multi_buffer&, std::false_type) noexcept; void swap(basic_multi_buffer&, std::false_type) noexcept;
void delete_list() noexcept; void delete_list() noexcept;
char* alloc(std::size_t n); char* alloc(std::size_t n);
void clear() noexcept;
void debug_check() const; void debug_check() const;
}; };

View File

@@ -103,88 +103,16 @@ public:
} }
}; };
//------------------------------------------------------------------------------
namespace test {
template<class DynamicBuffer>
void
write_buffer(DynamicBuffer& b, string_view s)
{
b.commit(net::buffer_copy(
b.prepare(s.size()), net::buffer(
s.data(), s.size())));
}
template<class ConstBufferSequence> template<class ConstBufferSequence>
typename std::enable_if< std::size_t
net::is_const_buffer_sequence<ConstBufferSequence>::value, buffers_length(
std::size_t>::type ConstBufferSequence const& buffers)
buffer_count(ConstBufferSequence const& buffers)
{ {
return std::distance(buffers.begin(), buffers.end()); return std::distance(
net::buffer_sequence_begin(buffers),
net::buffer_sequence_end(buffers));
} }
template<class ConstBufferSequence>
typename std::enable_if<
net::is_const_buffer_sequence<ConstBufferSequence>::value,
std::size_t>::type
size_pre(ConstBufferSequence const& buffers)
{
std::size_t n = 0;
for(auto it = buffers.begin(); it != buffers.end(); ++it)
{
typename ConstBufferSequence::const_iterator it0(std::move(it));
typename ConstBufferSequence::const_iterator it1(it0);
typename ConstBufferSequence::const_iterator it2;
it2 = it1;
n += net::buffer_size(*it2);
it = std::move(it2);
}
return n;
}
template<class ConstBufferSequence>
typename std::enable_if<
net::is_const_buffer_sequence<ConstBufferSequence>::value,
std::size_t>::type
size_post(ConstBufferSequence const& buffers)
{
std::size_t n = 0;
for(auto it = buffers.begin(); it != buffers.end(); it++)
n += net::buffer_size(*it);
return n;
}
template<class ConstBufferSequence>
typename std::enable_if<
net::is_const_buffer_sequence<ConstBufferSequence>::value,
std::size_t>::type
size_rev_pre(ConstBufferSequence const& buffers)
{
std::size_t n = 0;
for(auto it = buffers.end(); it != buffers.begin();)
n += net::buffer_size(*--it);
return n;
}
template<class ConstBufferSequence>
typename std::enable_if<
net::is_const_buffer_sequence<ConstBufferSequence>::value,
std::size_t>::type
size_rev_post(ConstBufferSequence const& buffers)
{
std::size_t n = 0;
for(auto it = buffers.end(); it != buffers.begin();)
{
it--;
n += net::buffer_size(*it);
}
return n;
}
} // test
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace detail { namespace detail {

View File

@@ -28,17 +28,6 @@ namespace beast {
class buffers_cat_test : public unit_test::suite class buffers_cat_test : public unit_test::suite
{ {
public: public:
template<class ConstBufferSequence>
static
std::size_t
buffers_length(
ConstBufferSequence const& buffers)
{
return std::distance(
net::buffer_sequence_begin(buffers),
net::buffer_sequence_end(buffers));
}
void void
testDefaultIterators() testDefaultIterators()
{ {

View File

@@ -69,7 +69,6 @@ public:
// in-place init // in-place init
{ {
using namespace test;
buffers_suffix<buffers_cat_view< buffers_suffix<buffers_cat_view<
net::const_buffer, net::const_buffer,
net::const_buffer>> cb( net::const_buffer>> cb(
@@ -106,8 +105,9 @@ public:
bool bool
eq(Buffers1 const& lhs, Buffers2 const& rhs) eq(Buffers1 const& lhs, Buffers2 const& rhs)
{ {
using namespace test; return
return buffers_to_string(lhs) == buffers_to_string(rhs); buffers_to_string(lhs) ==
buffers_to_string(rhs);
} }
void void

View File

@@ -32,20 +32,8 @@ class multi_buffer_test : public beast::unit_test::suite
{ {
public: public:
BOOST_STATIC_ASSERT( BOOST_STATIC_ASSERT(
net::is_dynamic_buffer< is_mutable_dynamic_buffer<multi_buffer>::value);
multi_buffer>::value);
BOOST_STATIC_ASSERT(
net::is_const_buffer_sequence<
multi_buffer::const_buffers_type>::value);
BOOST_STATIC_ASSERT(
net::is_mutable_buffer_sequence<
multi_buffer::mutable_data_type>::value);
BOOST_STATIC_ASSERT(
net::is_mutable_buffer_sequence<
multi_buffer::mutable_buffers_type>::value);
BOOST_STATIC_ASSERT(std::is_convertible<
multi_buffer::mutable_data_type,
multi_buffer::const_buffers_type>::value);
#if ! BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000) && \ #if ! BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000) && \
! BOOST_WORKAROUND(BOOST_MSVC, < 1910) ! BOOST_WORKAROUND(BOOST_MSVC, < 1910)
BOOST_STATIC_ASSERT(std::is_trivially_copyable< BOOST_STATIC_ASSERT(std::is_trivially_copyable<
@@ -57,214 +45,19 @@ public:
template<class Alloc1, class Alloc2> template<class Alloc1, class Alloc2>
static static
bool bool
eq(basic_multi_buffer<Alloc1> const& mb1, eq( basic_multi_buffer<Alloc1> const& mb1,
basic_multi_buffer<Alloc2> const& mb2) basic_multi_buffer<Alloc2> const& mb2)
{ {
return buffers_to_string(mb1.data()) == return buffers_to_string(mb1.data()) ==
buffers_to_string(mb2.data()); buffers_to_string(mb2.data());
} }
template<class ConstBufferSequence>
void void
expect_size(std::size_t n, ConstBufferSequence const& buffers) testDynamicBuffer()
{ {
BEAST_EXPECT(test::size_pre(buffers) == n); multi_buffer b(30);
BEAST_EXPECT(test::size_post(buffers) == n); BEAST_EXPECT(b.max_size() == 30);
BEAST_EXPECT(test::size_rev_pre(buffers) == n); test_dynamic_buffer(*this, b);
BEAST_EXPECT(test::size_rev_post(buffers) == n);
}
template<class U, class V>
static
void
self_assign(U& u, V&& v)
{
u = std::forward<V>(v);
}
template<class DynamicBuffer>
void
testMutableData()
{
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");
}
void
testMatrix1()
{
using namespace test;
using net::buffer;
std::string const s = "Hello, world";
BEAST_EXPECT(s.size() == 12);
for(std::size_t i = 1; i < 12; ++i) {
for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) {
std::size_t z = s.size() - (x + y);
{
multi_buffer b;
b.commit(buffer_copy(b.prepare(x), buffer(s.data(), x)));
b.commit(buffer_copy(b.prepare(y), buffer(s.data()+x, y)));
b.commit(buffer_copy(b.prepare(z), buffer(s.data()+x+y, z)));
BEAST_EXPECT(buffers_to_string(b.data()) == s);
{
multi_buffer mb2{b};
BEAST_EXPECT(eq(b, mb2));
}
{
multi_buffer mb2;
mb2 = b;
BEAST_EXPECT(eq(b, mb2));
}
{
multi_buffer mb2{std::move(b)};
BEAST_EXPECT(buffers_to_string(mb2.data()) == s);
expect_size(0, b.data());
b = std::move(mb2);
BEAST_EXPECT(buffers_to_string(b.data()) == s);
expect_size(0, mb2.data());
}
self_assign(b, b);
BEAST_EXPECT(buffers_to_string(b.data()) == s);
self_assign(b, std::move(b));
BEAST_EXPECT(buffers_to_string(b.data()) == s);
}
}}}
}
void
testMatrix2()
{
using namespace test;
using net::buffer;
using net::buffer_size;
std::string const s = "Hello, world";
BEAST_EXPECT(s.size() == 12);
for(std::size_t i = 1; i < 12; ++i) {
for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) {
for(std::size_t t = 1; t < 4; ++ t) {
for(std::size_t u = 1; u < 4; ++ u) {
std::size_t z = s.size() - (x + y);
std::size_t v = s.size() - (t + u);
{
multi_buffer b;
{
auto d = b.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = b.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = b.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = b.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
b.commit(buffer_copy(d, buffer(s.data(), x)));
}
BEAST_EXPECT(b.size() == x);
BEAST_EXPECT(buffer_size(b.data()) == b.size());
{
auto d = b.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = b.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = b.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = b.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
b.commit(buffer_copy(d, buffer(s.data()+x, y)));
}
b.commit(1);
BEAST_EXPECT(b.size() == x + y);
BEAST_EXPECT(buffer_size(b.data()) == b.size());
{
auto d = b.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = b.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = b.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = b.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
b.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
}
b.commit(2);
BEAST_EXPECT(b.size() == x + y + z);
BEAST_EXPECT(buffer_size(b.data()) == b.size());
BEAST_EXPECT(buffers_to_string(b.data()) == s);
b.consume(t);
{
auto d = b.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
BEAST_EXPECT(buffers_to_string(b.data()) == s.substr(t, std::string::npos));
b.consume(u);
BEAST_EXPECT(buffers_to_string(b.data()) == s.substr(t + u, std::string::npos));
b.consume(v);
BEAST_EXPECT(buffers_to_string(b.data()) == "");
b.consume(1);
{
auto d = b.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
}
}}}}}
}
void
testIterators()
{
using net::buffer_size;
multi_buffer b;
b.prepare(1);
b.commit(1);
b.prepare(2);
b.commit(2);
expect_size(3, b.data());
b.prepare(1);
expect_size(3, b.prepare(3));
b.commit(2);
} }
void void
@@ -297,6 +90,14 @@ public:
BEAST_EXPECT(b.get_allocator() == a1); BEAST_EXPECT(b.get_allocator() == a1);
BEAST_EXPECT(b.get_allocator() != unequal_t{}); BEAST_EXPECT(b.get_allocator() != unequal_t{});
} }
{
unequal_t a1;
basic_multi_buffer<unequal_t> b{500, a1};
BEAST_EXPECT(b.capacity() == 0);
BEAST_EXPECT(b.max_size() == 500);
BEAST_EXPECT(b.get_allocator() == a1);
BEAST_EXPECT(b.get_allocator() != unequal_t{});
}
} }
// move construction // move construction
@@ -384,6 +185,18 @@ public:
BEAST_EXPECT(b1.capacity() == 0); BEAST_EXPECT(b1.capacity() == 0);
BEAST_EXPECT(buffers_to_string(b2.data()) == "Hello"); BEAST_EXPECT(buffers_to_string(b2.data()) == "Hello");
} }
{
using na_t = test::test_allocator<char,
false, true, false, true, true>;
basic_multi_buffer<na_t> b1;
ostream(b1) << "Hello";
basic_multi_buffer<na_t> b2;
b2 = std::move(b1);
BEAST_EXPECT(b1.get_allocator() != b2.get_allocator());
BEAST_EXPECT(b1.size() == 0);
BEAST_EXPECT(b1.capacity() == 0);
BEAST_EXPECT(buffers_to_string(b2.data()) == "Hello");
}
{ {
// propagate_on_container_move_assignment : true // propagate_on_container_move_assignment : true
using pocma_t = test::test_allocator<char, using pocma_t = test::test_allocator<char,
@@ -451,6 +264,34 @@ public:
BEAST_EXPECT(b.max_size() == 32); BEAST_EXPECT(b.max_size() == 32);
} }
// allocator max_size
{
basic_multi_buffer<equal_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();
}
try
{
b.reserve(1000);
fail("", __FILE__, __LINE__);
}
catch(std::length_error const&)
{
pass();
}
}
// prepare // prepare
{ {
{ {
@@ -527,6 +368,23 @@ public:
// commit // commit
{ {
{
multi_buffer b;
b.prepare(16);
b.commit(16);
auto const n =
b.capacity() - b.size();
b.prepare(n);
b.commit(n);
auto const size =
b.size();
auto const capacity =
b.capacity();
b.commit(1);
BEAST_EXPECT(b.size() == size);
BEAST_EXPECT(b.capacity() == capacity);
}
multi_buffer b; multi_buffer b;
b.prepare(1000); b.prepare(1000);
BEAST_EXPECT(b.capacity() >= 1000); BEAST_EXPECT(b.capacity() >= 1000);
@@ -589,6 +447,21 @@ public:
b.commit(20); b.commit(20);
b.reserve(50); b.reserve(50);
BEAST_EXPECT(b.capacity() >= 50); BEAST_EXPECT(b.capacity() >= 50);
BEAST_EXPECT(b.size() > 1);
auto capacity = b.capacity();
b.reserve(b.size() - 1);
BEAST_EXPECT(b.capacity() == capacity);
b.reserve(b.capacity() + 1);
BEAST_EXPECT(b.capacity() > capacity);
capacity = b.capacity();
BEAST_EXPECT(buffers_length(
b.prepare(b.capacity() + 200)) > 1);
BEAST_EXPECT(b.capacity() > capacity);
b.reserve(b.capacity() + 2);
BEAST_EXPECT(b.capacity() > capacity);
capacity = b.capacity();
b.reserve(b.capacity());
BEAST_EXPECT(b.capacity() == capacity);
} }
// shrink to fit // shrink to fit
@@ -619,6 +492,23 @@ public:
} }
} }
// clear
{
multi_buffer b;
b.prepare(50);
BEAST_EXPECT(b.capacity() >= 50);
b.clear();
BEAST_EXPECT(b.size() == 0);
BEAST_EXPECT(b.capacity() == 0);
b.prepare(80);
b.commit(30);
BEAST_EXPECT(b.size() == 30);
BEAST_EXPECT(b.capacity() >= 80);
b.clear();
BEAST_EXPECT(b.size() == 0);
BEAST_EXPECT(b.capacity() == 0);
}
// swap // swap
{ {
{ {
@@ -683,14 +573,158 @@ public:
} }
} }
void
testMatrix1()
{
using net::buffer_size;
string_view s = "Hello, world";
BEAST_EXPECT(s.size() == 12);
for(std::size_t i = 1; i < 12; ++i) {
for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) {
std::size_t z = s.size() - (x + y);
{
multi_buffer b;
b.commit(net::buffer_copy(
b.prepare(x), net::buffer(s.data(), x)));
b.commit(net::buffer_copy(
b.prepare(y), net::buffer(s.data()+x, y)));
b.commit(net::buffer_copy(
b.prepare(z), net::buffer(s.data()+x+y, z)));
BEAST_EXPECT(buffers_to_string(b.data()) == s);
{
multi_buffer mb2{b};
BEAST_EXPECT(eq(b, mb2));
}
{
multi_buffer mb2;
mb2 = b;
BEAST_EXPECT(eq(b, mb2));
}
{
multi_buffer mb2{std::move(b)};
BEAST_EXPECT(buffers_to_string(mb2.data()) == s);
BEAST_EXPECT(b.size() == 0);
BEAST_EXPECT(buffer_size(b.data()) == 0);
b = std::move(mb2);
BEAST_EXPECT(buffers_to_string(b.data()) == s);
BEAST_EXPECT(mb2.size() == 0);
BEAST_EXPECT(buffer_size(mb2.data()) == 0);
}
}
}}}
}
void
testMatrix2()
{
using namespace test;
using net::buffer;
using net::buffer_size;
std::string const s = "Hello, world";
BEAST_EXPECT(s.size() == 12);
for(std::size_t i = 1; i < 12; ++i) {
for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) {
for(std::size_t t = 1; t < 4; ++ t) {
for(std::size_t u = 1; u < 4; ++ u) {
std::size_t z = s.size() - (x + y);
std::size_t v = s.size() - (t + u);
{
multi_buffer b;
{
auto d = b.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = b.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = b.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = b.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
b.commit(buffer_copy(d, buffer(s.data(), x)));
}
BEAST_EXPECT(b.size() == x);
BEAST_EXPECT(buffer_size(b.data()) == b.size());
{
auto d = b.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = b.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = b.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = b.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
b.commit(buffer_copy(d, buffer(s.data()+x, y)));
}
b.commit(1);
BEAST_EXPECT(b.size() == x + y);
BEAST_EXPECT(buffer_size(b.data()) == b.size());
{
auto d = b.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = b.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = b.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = b.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
b.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
}
b.commit(2);
BEAST_EXPECT(b.size() == x + y + z);
BEAST_EXPECT(buffer_size(b.data()) == b.size());
BEAST_EXPECT(buffers_to_string(b.data()) == s);
b.consume(t);
{
auto d = b.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
BEAST_EXPECT(buffers_to_string(b.data()) ==
s.substr(t, std::string::npos));
b.consume(u);
BEAST_EXPECT(buffers_to_string(b.data()) ==
s.substr(t + u, std::string::npos));
b.consume(v);
BEAST_EXPECT(buffers_to_string(b.data()).empty());
b.consume(1);
{
auto d = b.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
}
}}}}}
}
void void
run() override run() override
{ {
testDynamicBuffer();
testMembers();
testMatrix1(); testMatrix1();
testMatrix2(); testMatrix2();
#if 0
testIterators(); testIterators();
testMembers();
testMutableData<multi_buffer>(); testMutableData<multi_buffer>();
#endif
} }
}; };

View File

@@ -87,7 +87,6 @@ public:
void void
testStaticBuffer() testStaticBuffer()
{ {
using namespace test;
using net::buffer; using net::buffer;
using net::buffer_size; using net::buffer_size;
char buf[12]; char buf[12];
@@ -195,7 +194,6 @@ public:
void void
testBuffer() testBuffer()
{ {
using namespace test;
string_view const s = "Hello, world!"; string_view const s = "Hello, world!";
// static_buffer_base // static_buffer_base
@@ -234,9 +232,9 @@ public:
// cause memmove // cause memmove
{ {
static_buffer<10> b; static_buffer<10> b;
write_buffer(b, "12345"); ostream(b) << "12345";
b.consume(3); b.consume(3);
write_buffer(b, "67890123"); ostream(b) << "67890123";
BEAST_EXPECT(buffers_to_string(b.data()) == "4567890123"); BEAST_EXPECT(buffers_to_string(b.data()) == "4567890123");
try try
{ {