multi_buffer members and coverage

fix #458
This commit is contained in:
Vinnie Falco
2017-06-10 07:48:17 -07:00
parent 5d6d486da4
commit 260b506520
4 changed files with 721 additions and 280 deletions

View File

@@ -2,6 +2,7 @@ Version 54:
* static_buffer coverage * static_buffer coverage
* flat_buffer coverage * flat_buffer coverage
* multi_buffer coverage
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@@ -425,6 +425,34 @@ basic_multi_buffer()
{ {
} }
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(std::size_t limit)
: max_(limit)
, out_(list_.end())
{
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(Allocator const& alloc)
: detail::empty_base_optimization<
allocator_type>(alloc)
, out_(list_.end())
{
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(std::size_t limit,
Allocator const& alloc)
: detail::empty_base_optimization<
allocator_type>(alloc)
, 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&& other) basic_multi_buffer(basic_multi_buffer&& other)
@@ -450,34 +478,54 @@ basic_multi_buffer(basic_multi_buffer&& other)
template<class Allocator> template<class Allocator>
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer&& other, basic_multi_buffer(basic_multi_buffer&& other,
allocator_type const& alloc) Allocator const& alloc)
: basic_multi_buffer(other.max_, alloc) : detail::empty_base_optimization<allocator_type>(alloc)
, max_(other.max_)
{ {
using boost::asio::buffer_copy;
if(this->member() != other.member()) if(this->member() != other.member())
commit(buffer_copy(prepare(other.size()), other.data())); {
out_ = list_.end();
copy_from(other);
other.reset();
}
else else
move_assign(other, std::true_type{}); {
auto const at_end =
other.out_ == other.list_.end();
list_ = std::move(other.list_);
out_ = at_end ? list_.end() : other.out_;
in_size_ = other.in_size_;
in_pos_ = other.in_pos_;
out_pos_ = other.out_pos_;
out_end_ = other.out_end_;
other.in_size_ = 0;
other.out_ = other.list_.end();
other.in_pos_ = 0;
other.out_pos_ = 0;
other.out_end_ = 0;
}
} }
template<class Allocator> template<class Allocator>
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer const& other) basic_multi_buffer(basic_multi_buffer const& other)
: basic_multi_buffer(other.max_, : detail::empty_base_optimization<allocator_type>(
alloc_traits::select_on_container_copy_construction(other.member())) alloc_traits::select_on_container_copy_construction(other.member()))
, max_(other.max_)
, out_(list_.end())
{ {
commit(boost::asio::buffer_copy( copy_from(other);
prepare(other.size()), other.data()));
} }
template<class Allocator> template<class Allocator>
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer const& other, basic_multi_buffer(basic_multi_buffer const& other,
allocator_type const& alloc) Allocator const& alloc)
: basic_multi_buffer(other.max_, alloc) : detail::empty_base_optimization<allocator_type>(alloc)
, max_(other.max_)
, out_(list_.end())
{ {
commit(boost::asio::buffer_copy( copy_from(other);
prepare(other.size()), other.data()));
} }
template<class Allocator> template<class Allocator>
@@ -485,10 +533,9 @@ template<class OtherAlloc>
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
basic_multi_buffer( basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other) basic_multi_buffer<OtherAlloc> const& other)
: basic_multi_buffer(other.max_) : out_(list_.end())
{ {
using boost::asio::buffer_copy; copy_from(other);
commit(buffer_copy(prepare(other.size()), other.data()));
} }
template<class Allocator> template<class Allocator>
@@ -497,35 +544,11 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer( basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other, basic_multi_buffer<OtherAlloc> const& other,
allocator_type const& alloc) allocator_type const& alloc)
: basic_multi_buffer(other.max_, alloc) : detail::empty_base_optimization<allocator_type>(alloc)
{ , max_(other.max_)
using boost::asio::buffer_copy;
commit(buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(std::size_t limit)
: max_(limit)
, out_(list_.end()) , out_(list_.end())
{ {
if(max_ <= 0) copy_from(other);
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid limit"});
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(std::size_t limit,
Allocator const& alloc)
: detail::empty_base_optimization<
allocator_type>(alloc)
, max_(limit)
, out_(list_.end())
{
if(max_ <= 0)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid limit"});
} }
template<class Allocator> template<class Allocator>
@@ -536,11 +559,10 @@ operator=(basic_multi_buffer&& other) ->
{ {
if(this == &other) if(this == &other)
return *this; return *this;
// VFALCO If any memory allocated we could use it first? reset();
clear();
max_ = other.max_; max_ = other.max_;
move_assign(other, std::integral_constant<bool, move_assign(other, typename
alloc_traits::propagate_on_container_move_assignment::value>{}); alloc_traits::propagate_on_container_move_assignment{});
return *this; return *this;
} }
@@ -552,12 +574,8 @@ basic_multi_buffer&
{ {
if(this == &other) if(this == &other)
return *this; return *this;
using boost::asio::buffer_copy; copy_assign(other, typename
clear(); alloc_traits::propagate_on_container_copy_assignment{});
max_ = other.max_;
copy_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_copy_assignment::value>{});
commit(buffer_copy(prepare(other.size()), other.data()));
return *this; return *this;
} }
@@ -569,10 +587,9 @@ operator=(
basic_multi_buffer<OtherAlloc> const& other) -> basic_multi_buffer<OtherAlloc> const& other) ->
basic_multi_buffer& basic_multi_buffer&
{ {
using boost::asio::buffer_copy; reset();
clear();
max_ = other.max_; max_ = other.max_;
commit(buffer_copy(prepare(other.size()), other.data())); copy_from(other);
return *this; return *this;
} }
@@ -609,8 +626,11 @@ prepare(size_type n) ->
BOOST_THROW_EXCEPTION(std::length_error{ BOOST_THROW_EXCEPTION(std::length_error{
"dynamic buffer overflow"}); "dynamic buffer overflow"});
list_type reuse; list_type reuse;
std::size_t total = in_size_;
// put all empty buffers on reuse list
if(out_ != list_.end()) if(out_ != list_.end())
{ {
total += out_->size() - out_pos_;
if(out_ != list_.iterator_to(list_.back())) if(out_ != list_.iterator_to(list_.back()))
{ {
out_end_ = out_->size(); out_end_ = out_->size();
@@ -635,11 +655,13 @@ prepare(size_type n) ->
debug_check(); debug_check();
#endif #endif
} }
// get space from reuse buffers
while(n > 0 && ! reuse.empty()) while(n > 0 && ! reuse.empty())
{ {
auto& e = reuse.front(); auto& e = reuse.front();
reuse.erase(reuse.iterator_to(e)); reuse.erase(reuse.iterator_to(e));
list_.push_back(e); list_.push_back(e);
total += e.size();
if(n > e.size()) if(n > e.size())
{ {
out_end_ = e.size(); out_end_ = e.size();
@@ -654,16 +676,23 @@ prepare(size_type n) ->
debug_check(); debug_check();
#endif #endif
} }
while(n > 0) BOOST_ASSERT(total <= max_);
for(auto it = reuse.begin(); it != reuse.end();)
{
auto& e = *it++;
reuse.erase(list_.iterator_to(e));
delete_element(e);
}
if(n > 0)
{ {
static auto const growth_factor = 2.0f; static auto const growth_factor = 2.0f;
auto const size = auto const size =
std::min<std::size_t>( std::min<std::size_t>(
max_ - in_size_, max_ - total,
std::max<std::size_t>({ std::max<std::size_t>({
static_cast<std::size_t>( static_cast<std::size_t>(
in_size_ * growth_factor - in_size_), in_size_ * growth_factor - in_size_),
1024, 512,
n})); n}));
auto& e = *reinterpret_cast<element*>(static_cast< auto& e = *reinterpret_cast<element*>(static_cast<
void*>(alloc_traits::allocate(this->member(), void*>(alloc_traits::allocate(this->member(),
@@ -672,29 +701,11 @@ prepare(size_type n) ->
list_.push_back(e); list_.push_back(e);
if(out_ == list_.end()) if(out_ == list_.end())
out_ = list_.iterator_to(e); out_ = list_.iterator_to(e);
if(n >= e.size()) out_end_ = n;
{
out_end_ = e.size();
n -= e.size();
}
else
{
out_end_ = n;
n = 0;
}
#if BEAST_MULTI_BUFFER_DEBUG_CHECK #if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check(); debug_check();
#endif #endif
} }
for(auto it = reuse.begin(); it != reuse.end();)
{
auto& e = *it++;
reuse.erase(list_.iterator_to(e));
auto const len = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<char*>(&e), len);
}
return mutable_buffers_type(*this); return mutable_buffers_type(*this);
} }
@@ -752,12 +763,12 @@ consume(size_type n)
{ {
if(list_.empty()) if(list_.empty())
return; return;
for(;;) for(;;)
{ {
if(list_.begin() != out_) if(list_.begin() != out_)
{ {
auto const avail = list_.front().size() - in_pos_; auto const avail =
list_.front().size() - in_pos_;
if(n < avail) if(n < avail)
{ {
in_size_ -= n; in_size_ -= n;
@@ -772,10 +783,7 @@ consume(size_type n)
in_pos_ = 0; in_pos_ = 0;
auto& e = list_.front(); auto& e = list_.front();
list_.erase(list_.iterator_to(e)); list_.erase(list_.iterator_to(e));
auto const len = e.size() + sizeof(e); delete_element(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<char*>(&e), len);
#if BEAST_MULTI_BUFFER_DEBUG_CHECK #if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check(); debug_check();
#endif #endif
@@ -814,13 +822,36 @@ consume(size_type n)
} }
template<class Allocator> template<class Allocator>
inline
void void
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
clear() delete_element(element& e)
{
auto const len = sizeof(e) + e.size();
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<char*>(&e), len);
}
template<class Allocator>
inline
void
basic_multi_buffer<Allocator>::
delete_list()
{
for(auto iter = list_.begin(); iter != list_.end();)
delete_element(*iter++);
}
template<class Allocator>
inline
void
basic_multi_buffer<Allocator>::
reset()
{ {
delete_list(); delete_list();
list_.clear(); list_.clear();
out_ = list_.begin(); out_ = list_.end();
in_size_ = 0; in_size_ = 0;
in_pos_ = 0; in_pos_ = 0;
out_pos_ = 0; out_pos_ = 0;
@@ -828,21 +859,38 @@ clear()
} }
template<class Allocator> template<class Allocator>
template<class DynamicBuffer>
inline
void
basic_multi_buffer<Allocator>::
copy_from(DynamicBuffer const& buffer)
{
if(buffer.size() == 0)
return;
using boost::asio::buffer_copy;
commit(buffer_copy(
prepare(buffer.size()), buffer.data()));
}
template<class Allocator>
inline
void void
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
move_assign(basic_multi_buffer& other, std::false_type) move_assign(basic_multi_buffer& other, std::false_type)
{ {
using boost::asio::buffer_copy;
if(this->member() != other.member()) if(this->member() != other.member())
{ {
commit(buffer_copy(prepare(other.size()), other.data())); copy_from(other);
other.clear(); other.reset();
} }
else else
{
move_assign(other, std::true_type{}); move_assign(other, std::true_type{});
}
} }
template<class Allocator> template<class Allocator>
inline
void void
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
move_assign(basic_multi_buffer& other, std::true_type) move_assign(basic_multi_buffer& other, std::true_type)
@@ -866,36 +914,95 @@ move_assign(basic_multi_buffer& other, std::true_type)
} }
template<class Allocator> template<class Allocator>
inline
void void
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
copy_assign( copy_assign(
basic_multi_buffer const& other, std::false_type) basic_multi_buffer const& other, std::false_type)
{ {
beast::detail::ignore_unused(other); reset();
max_ = other.max_;
copy_from(other);
} }
template<class Allocator> template<class Allocator>
inline
void void
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
copy_assign( copy_assign(
basic_multi_buffer const& other, std::true_type) basic_multi_buffer const& other, std::true_type)
{ {
reset();
max_ = other.max_;
this->member() = other.member(); this->member() = other.member();
copy_from(other);
}
template<class Allocator>
inline
void
basic_multi_buffer<Allocator>::
swap(basic_multi_buffer& other)
{
swap(other, typename
alloc_traits::propagate_on_container_swap{});
}
template<class Allocator>
inline
void
basic_multi_buffer<Allocator>::
swap(basic_multi_buffer& other, std::true_type)
{
using std::swap;
auto const at_end0 =
out_ == list_.end();
auto const at_end1 =
other.out_ == other.list_.end();
swap(this->member(), other.member());
swap(list_, other.list_);
swap(out_, other.out_);
if(at_end1)
out_ = list_.end();
if(at_end0)
other.out_ = other.list_.end();
swap(in_size_, other.in_size_);
swap(in_pos_, other.in_pos_);
swap(out_pos_, other.out_pos_);
swap(out_end_, other.out_end_);
}
template<class Allocator>
inline
void
basic_multi_buffer<Allocator>::
swap(basic_multi_buffer& other, std::false_type)
{
BOOST_ASSERT(this->member() == other.member());
using std::swap;
auto const at_end0 =
out_ == list_.end();
auto const at_end1 =
other.out_ == other.list_.end();
swap(list_, other.list_);
swap(out_, other.out_);
if(at_end1)
out_ = list_.end();
if(at_end0)
other.out_ = other.list_.end();
swap(in_size_, other.in_size_);
swap(in_pos_, other.in_pos_);
swap(out_pos_, other.out_pos_);
swap(out_end_, other.out_end_);
} }
template<class Allocator> template<class Allocator>
void void
basic_multi_buffer<Allocator>:: swap(
delete_list() basic_multi_buffer<Allocator>& lhs,
basic_multi_buffer<Allocator>& rhs)
{ {
for(auto iter = list_.begin(); iter != list_.end();) lhs.swap(rhs);
{
auto& e = *iter++;
auto const n = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<char*>(&e), n);
}
} }
template<class Allocator> template<class Allocator>
@@ -939,30 +1046,6 @@ debug_check() const
#endif #endif
} }
#if 0
template<class Allocator>
std::size_t
read_size_helper(
basic_multi_buffer<Allocator> const& buffer,
std::size_t max_size)
{
BOOST_ASSERT(max_size >= 1);
auto const size = buffer.size();
auto const limit = buffer.max_size() - size;
if(limit <= 0)
BOOST_THROW_EXCEPTION(std::length_error{
"dynamic buffer overflow"});
auto const avail = std::min<std::size_t>(
buffer.capacity() - size, max_size);
if(avail > 0)
return avail; // avoid allocation
return std::min<std::size_t>(
std::min<std::size_t>(max_size, buffer.max_size() - size),
avail + buffer.alloc_size());
}
#endif
} // beast } // beast
#endif #endif

View File

@@ -96,120 +96,121 @@ public:
#endif #endif
/// Destructor. /// Destructor
~basic_multi_buffer(); ~basic_multi_buffer();
/// Default constructor. /** Constructor
Upon construction, capacity will be zero.
*/
basic_multi_buffer(); basic_multi_buffer();
/** Move constructor.
The new object will have the input sequence of
the other stream buffer, and an empty output sequence.
@note After the move, the moved-from object will have
an empty input and output sequence, with no internal
buffers allocated.
*/
basic_multi_buffer(basic_multi_buffer&&);
/** Move constructor.
The new object will have the input sequence of
the other stream buffer, and an empty output sequence.
@note After the move, the moved-from object will have
an empty input and output sequence, with no internal
buffers allocated.
@param alloc The allocator to associate with the
stream buffer.
*/
basic_multi_buffer(basic_multi_buffer&&,
allocator_type const& alloc);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
basic_multi_buffer(basic_multi_buffer const&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param alloc The allocator to associate with the
stream buffer.
*/
basic_multi_buffer(basic_multi_buffer const&,
allocator_type const& alloc);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
template<class OtherAlloc>
basic_multi_buffer(basic_multi_buffer<OtherAlloc> const&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param alloc The allocator to associate with the
stream buffer.
*/
template<class OtherAlloc>
basic_multi_buffer(basic_multi_buffer<OtherAlloc> const&,
allocator_type const& alloc);
/** Constructor. /** Constructor.
@param limit The maximum allowed sum of the input and @param limit The setting for @ref max_size.
output sequence sizes.
*/ */
explicit explicit
basic_multi_buffer(std::size_t limit); basic_multi_buffer(std::size_t limit);
/** Constructor. /** Constructor.
@param limit The maximum allowed sum of the input and @param alloc The allocator to use.
output sequence sizes. */
basic_multi_buffer(Allocator const& alloc);
/** Constructor.
@param limit The setting for @ref max_size.
@param alloc The allocator to use. @param alloc The allocator to use.
*/ */
basic_multi_buffer( basic_multi_buffer(
std::size_t limit, Allocator const& alloc); std::size_t limit, Allocator const& alloc);
/** Move assignment. /** Move constructor
This object will have the input sequence of After the move, `*this` will have an empty output sequence.
the other stream buffer, and an empty output sequence.
@note After the move, the moved-from object will have @param other The object to move from. After the move,
an empty input and output sequence, with no internal The object's state will be as if constructed using
buffers allocated. its current allocator and limit.
*/ */
basic_multi_buffer& basic_multi_buffer(basic_multi_buffer&& other);
operator=(basic_multi_buffer&&);
/** Copy assignment. /** Move constructor
This object will have a copy of the other stream After the move, `*this` will have an empty output sequence.
buffer's input sequence, and an empty output sequence.
@param other The object to move from. After the move,
The object's state will be as if constructed using
its current allocator and limit.
@param alloc The allocator to use.
*/ */
basic_multi_buffer& operator=(basic_multi_buffer const&); basic_multi_buffer(basic_multi_buffer&& other,
Allocator const& alloc);
/** Copy assignment. /** Copy constructor.
This object will have a copy of the other stream @param other The object to copy from.
buffer's input sequence, and an empty output sequence. */
basic_multi_buffer(basic_multi_buffer const& other);
/** Copy constructor
@param other The object to copy from.
@param alloc The allocator to use.
*/
basic_multi_buffer(basic_multi_buffer const& other,
Allocator const& alloc);
/** Copy constructor.
@param other The object to copy from.
*/ */
template<class OtherAlloc> template<class OtherAlloc>
basic_multi_buffer& operator=(basic_multi_buffer<OtherAlloc> const&); basic_multi_buffer(basic_multi_buffer<
OtherAlloc> const& other);
/** Copy constructor.
@param other The object to copy from.
@param alloc The allocator to use.
*/
template<class OtherAlloc>
basic_multi_buffer(basic_multi_buffer<
OtherAlloc> const& other, allocator_type const& alloc);
/** Move assignment
After the move, `*this` will have an empty output sequence.
@param other The object to move from. After the move,
The object's state will be as if constructed using
its current allocator and limit.
*/
basic_multi_buffer&
operator=(basic_multi_buffer&& other);
/** Copy assignment
After the copy, `*this` will have an empty output sequence.
@param other The object to copy from.
*/
basic_multi_buffer& operator=(basic_multi_buffer const& other);
/** Copy assignment
After the copy, `*this` will have an empty output sequence.
@param other The object to copy from.
*/
template<class OtherAlloc>
basic_multi_buffer& operator=(
basic_multi_buffer<OtherAlloc> const& other);
/// Returns a copy of the associated allocator. /// Returns a copy of the associated allocator.
allocator_type allocator_type
@@ -263,9 +264,29 @@ public:
void void
consume(size_type n); consume(size_type n);
private: template<class Alloc>
friend
void void
clear(); swap(
basic_multi_buffer<Alloc>& lhs,
basic_multi_buffer<Alloc>& rhs);
private:
template<class OtherAlloc>
friend class basic_multi_buffer;
void
delete_element(element& e);
void
delete_list();
void
reset();
template<class DynamicBuffer>
void
copy_from(DynamicBuffer const& other);
void void
move_assign(basic_multi_buffer& other, std::false_type); move_assign(basic_multi_buffer& other, std::false_type);
@@ -280,21 +301,18 @@ private:
copy_assign(basic_multi_buffer const& other, std::true_type); copy_assign(basic_multi_buffer const& other, std::true_type);
void void
delete_list(); swap(basic_multi_buffer&);
void
swap(basic_multi_buffer&, std::true_type);
void
swap(basic_multi_buffer&, std::false_type);
void void
debug_check() const; debug_check() const;
}; };
#if 0
/// Helper for boost::asio::read_until
template<class Allocator>
std::size_t
read_size_helper(
basic_multi_buffer<Allocator> const& buffer,
std::size_t max_size);
#endif
/// A typical multi buffer /// A typical multi buffer
using multi_buffer = basic_multi_buffer<std::allocator<char>>; using multi_buffer = basic_multi_buffer<std::allocator<char>>;

View File

@@ -9,7 +9,9 @@
#include <beast/core/multi_buffer.hpp> #include <beast/core/multi_buffer.hpp>
#include "buffer_test.hpp" #include "buffer_test.hpp"
#include <beast/core/ostream.hpp> #include <beast/core/ostream.hpp>
#include <beast/core/string_view.hpp>
#include <beast/core/type_traits.hpp> #include <beast/core/type_traits.hpp>
#include <beast/test/test_allocator.hpp> #include <beast/test/test_allocator.hpp>
#include <beast/unit_test/suite.hpp> #include <beast/unit_test/suite.hpp>
@@ -26,22 +28,14 @@ BOOST_STATIC_ASSERT(is_dynamic_buffer<multi_buffer>::value);
class multi_buffer_test : public beast::unit_test::suite class multi_buffer_test : public beast::unit_test::suite
{ {
public: public:
template<class ConstBufferSequence>
static
std::string
to_string(ConstBufferSequence const& bs)
{
return boost::lexical_cast<
std::string>(buffers(bs));
}
template<class Alloc1, class Alloc2> template<class Alloc1, class Alloc2>
static static
bool bool
eq(basic_multi_buffer<Alloc1> const& sb1, eq(basic_multi_buffer<Alloc1> const& mb1,
basic_multi_buffer<Alloc2> const& sb2) basic_multi_buffer<Alloc2> const& mb2)
{ {
return to_string(sb1.data()) == to_string(sb2.data()); return test::to_string(mb1.data()) ==
test::to_string(mb2.data());
} }
template<class ConstBufferSequence> template<class ConstBufferSequence>
@@ -62,8 +56,10 @@ public:
u = std::forward<V>(v); u = std::forward<V>(v);
} }
void testSpecialMembers() void
testMatrix1()
{ {
using namespace test;
using boost::asio::buffer; using boost::asio::buffer;
std::string const s = "Hello, world"; std::string const s = "Hello, world";
BEAST_EXPECT(s.size() == 12); BEAST_EXPECT(s.size() == 12);
@@ -72,27 +68,27 @@ public:
for(std::size_t y = 1; y < 4; ++y) { for(std::size_t y = 1; y < 4; ++y) {
std::size_t z = s.size() - (x + y); std::size_t z = s.size() - (x + y);
{ {
multi_buffer b;//(i); multi_buffer b;
b.commit(buffer_copy(b.prepare(x), buffer(s.data(), x))); 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(y), buffer(s.data()+x, y)));
b.commit(buffer_copy(b.prepare(z), buffer(s.data()+x+y, z))); b.commit(buffer_copy(b.prepare(z), buffer(s.data()+x+y, z)));
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
{ {
multi_buffer sb2(b); multi_buffer mb2{b};
BEAST_EXPECT(eq(b, sb2)); BEAST_EXPECT(eq(b, mb2));
} }
{ {
multi_buffer sb2; multi_buffer mb2;
sb2 = b; mb2 = b;
BEAST_EXPECT(eq(b, sb2)); BEAST_EXPECT(eq(b, mb2));
} }
{ {
multi_buffer sb2(std::move(b)); multi_buffer mb2{std::move(b)};
BEAST_EXPECT(to_string(sb2.data()) == s); BEAST_EXPECT(to_string(mb2.data()) == s);
expect_size(0, b.data()); expect_size(0, b.data());
b = std::move(sb2); b = std::move(mb2);
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
expect_size(0, sb2.data()); expect_size(0, mb2.data());
} }
self_assign(b, b); self_assign(b, b);
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
@@ -100,40 +96,12 @@ public:
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
} }
}}} }}}
try
{
multi_buffer sb0(0);
fail();
}
catch(std::exception const&)
{
pass();
}
} }
void void
testAllocator() testMatrix2()
{
using test::test_allocator;
// VFALCO This needs work
{
using alloc_type =
test_allocator<char, false, false, false, false, false>;
using type = basic_multi_buffer<alloc_type>;
type b;
}
{
using alloc_type =
test_allocator<char, false, false, false, false, false>;
using type = basic_multi_buffer<alloc_type>;
type b;
type b2(b);
type b3(b, alloc_type{});
}
}
void testMatrix()
{ {
using namespace test;
using boost::asio::buffer; using boost::asio::buffer;
using boost::asio::buffer_size; using boost::asio::buffer_size;
std::string const s = "Hello, world"; std::string const s = "Hello, world";
@@ -146,7 +114,7 @@ public:
std::size_t z = s.size() - (x + y); std::size_t z = s.size() - (x + y);
std::size_t v = s.size() - (t + u); std::size_t v = s.size() - (t + u);
{ {
multi_buffer b;//(i); multi_buffer b;
{ {
auto d = b.prepare(z); auto d = b.prepare(z);
BEAST_EXPECT(buffer_size(d) == z); BEAST_EXPECT(buffer_size(d) == z);
@@ -226,7 +194,8 @@ public:
}}}}} }}}}}
} }
void testIterators() void
testIterators()
{ {
using boost::asio::buffer_size; using boost::asio::buffer_size;
multi_buffer b; multi_buffer b;
@@ -240,14 +209,384 @@ public:
b.commit(2); b.commit(2);
} }
void run() override void
testMembers()
{
using namespace test;
// compare equal
using equal_t = test::test_allocator<char,
true, true, true, true, true>;
// compare not equal
using unequal_t = test::test_allocator<char,
false, true, true, true, true>;
// construction
{
{
multi_buffer b;
BEAST_EXPECT(b.capacity() == 0);
}
{
multi_buffer b{500};
BEAST_EXPECT(b.capacity() == 0);
BEAST_EXPECT(b.max_size() == 500);
}
{
unequal_t a1;
basic_multi_buffer<unequal_t> b{a1};
BEAST_EXPECT(b.get_allocator() == a1);
BEAST_EXPECT(b.get_allocator() != unequal_t{});
}
}
// move construction
{
{
basic_multi_buffer<equal_t> b1{30};
BEAST_EXPECT(b1.get_allocator()->nmove == 0);
ostream(b1) << "Hello";
basic_multi_buffer<equal_t> b2{std::move(b1)};
BEAST_EXPECT(b2.get_allocator()->nmove == 1);
BEAST_EXPECT(b1.size() == 0);
BEAST_EXPECT(b1.capacity() == 0);
BEAST_EXPECT(to_string(b2.data()) == "Hello");
BEAST_EXPECT(b1.max_size() == b2.max_size());
}
// allocators equal
{
basic_multi_buffer<equal_t> b1{30};
ostream(b1) << "Hello";
equal_t a;
basic_multi_buffer<equal_t> b2{std::move(b1), a};
BEAST_EXPECT(b1.size() == 0);
BEAST_EXPECT(b1.capacity() == 0);
BEAST_EXPECT(to_string(b2.data()) == "Hello");
BEAST_EXPECT(b1.max_size() == b2.max_size());
}
{
// allocators unequal
basic_multi_buffer<unequal_t> b1{30};
ostream(b1) << "Hello";
unequal_t a;
basic_multi_buffer<unequal_t> b2{std::move(b1), a};
BEAST_EXPECT(b1.size() == 0);
BEAST_EXPECT(b1.capacity() == 0);
BEAST_EXPECT(to_string(b2.data()) == "Hello");
BEAST_EXPECT(b1.max_size() == b2.max_size());
}
}
// copy construction
{
{
basic_multi_buffer<equal_t> b1;
ostream(b1) << "Hello";
basic_multi_buffer<equal_t> b2{b1};
BEAST_EXPECT(b1.get_allocator() == b2.get_allocator());
BEAST_EXPECT(to_string(b1.data()) == "Hello");
BEAST_EXPECT(to_string(b2.data()) == "Hello");
}
{
basic_multi_buffer<unequal_t> b1;
ostream(b1) << "Hello";
unequal_t a;
basic_multi_buffer<unequal_t> b2(b1, a);
BEAST_EXPECT(b1.get_allocator() != b2.get_allocator());
BEAST_EXPECT(to_string(b1.data()) == "Hello");
BEAST_EXPECT(to_string(b2.data()) == "Hello");
}
{
basic_multi_buffer<equal_t> b1;
ostream(b1) << "Hello";
basic_multi_buffer<unequal_t> b2(b1);
BEAST_EXPECT(to_string(b1.data()) == "Hello");
BEAST_EXPECT(to_string(b2.data()) == "Hello");
}
{
basic_multi_buffer<unequal_t> b1;
ostream(b1) << "Hello";
equal_t a;
basic_multi_buffer<equal_t> b2(b1, a);
BEAST_EXPECT(b2.get_allocator() == a);
BEAST_EXPECT(to_string(b1.data()) == "Hello");
BEAST_EXPECT(to_string(b2.data()) == "Hello");
}
}
// move assignment
{
{
multi_buffer b1;
ostream(b1) << "Hello";
multi_buffer b2;
b2 = std::move(b1);
BEAST_EXPECT(b1.size() == 0);
BEAST_EXPECT(b1.capacity() == 0);
BEAST_EXPECT(to_string(b2.data()) == "Hello");
}
{
// propagate_on_container_move_assignment : true
using pocma_t = test::test_allocator<char,
true, true, true, true, true>;
basic_multi_buffer<pocma_t> b1;
ostream(b1) << "Hello";
basic_multi_buffer<pocma_t> b2;
b2 = std::move(b1);
BEAST_EXPECT(b1.size() == 0);
BEAST_EXPECT(to_string(b2.data()) == "Hello");
}
{
// propagate_on_container_move_assignment : false
using pocma_t = test::test_allocator<char,
true, true, false, true, true>;
basic_multi_buffer<pocma_t> b1;
ostream(b1) << "Hello";
basic_multi_buffer<pocma_t> b2;
b2 = std::move(b1);
BEAST_EXPECT(b1.size() == 0);
BEAST_EXPECT(to_string(b2.data()) == "Hello");
}
}
// copy assignment
{
{
multi_buffer b1;
ostream(b1) << "Hello";
multi_buffer b2;
b2 = b1;
BEAST_EXPECT(to_string(b1.data()) == "Hello");
BEAST_EXPECT(to_string(b2.data()) == "Hello");
basic_multi_buffer<equal_t> b3;
b3 = b2;
BEAST_EXPECT(to_string(b3.data()) == "Hello");
}
{
// propagate_on_container_copy_assignment : true
using pocca_t = test::test_allocator<char,
true, true, true, true, true>;
basic_multi_buffer<pocca_t> b1;
ostream(b1) << "Hello";
basic_multi_buffer<pocca_t> b2;
b2 = b1;
BEAST_EXPECT(to_string(b2.data()) == "Hello");
}
{
// propagate_on_container_copy_assignment : false
using pocca_t = test::test_allocator<char,
true, false, true, true, true>;
basic_multi_buffer<pocca_t> b1;
ostream(b1) << "Hello";
basic_multi_buffer<pocca_t> b2;
b2 = b1;
BEAST_EXPECT(to_string(b2.data()) == "Hello");
}
}
// prepare
{
{
multi_buffer b{100};
try
{
b.prepare(b.max_size() + 1);
fail("", __FILE__, __LINE__);
}
catch(std::length_error const&)
{
pass();
}
}
{
string_view const s = "Hello, world!";
multi_buffer b1{64};
BEAST_EXPECT(b1.size() == 0);
BEAST_EXPECT(b1.max_size() == 64);
BEAST_EXPECT(b1.capacity() == 0);
ostream(b1) << s;
BEAST_EXPECT(to_string(b1.data()) == s);
{
multi_buffer b2{b1};
BEAST_EXPECT(to_string(b2.data()) == s);
b2.consume(7);
BEAST_EXPECT(to_string(b2.data()) == s.substr(7));
}
{
multi_buffer b2{64};
b2 = b1;
BEAST_EXPECT(to_string(b2.data()) == s);
b2.consume(7);
BEAST_EXPECT(to_string(b2.data()) == s.substr(7));
}
}
{
multi_buffer b;
b.prepare(1000);
BEAST_EXPECT(b.capacity() >= 1000);
b.commit(1);
BEAST_EXPECT(b.size() == 1);
BEAST_EXPECT(b.capacity() >= 1000);
b.prepare(1000);
BEAST_EXPECT(b.size() == 1);
BEAST_EXPECT(b.capacity() >= 1000);
b.prepare(1500);
BEAST_EXPECT(b.capacity() >= 1000);
}
{
multi_buffer b;
b.prepare(1000);
BEAST_EXPECT(b.capacity() >= 1000);
b.commit(1);
BEAST_EXPECT(b.capacity() >= 1000);
b.prepare(1000);
BEAST_EXPECT(b.capacity() >= 1000);
b.prepare(2000);
BEAST_EXPECT(b.capacity() >= 2000);
b.commit(2);
}
{
multi_buffer b;
b.prepare(1000);
BEAST_EXPECT(b.capacity() >= 1000);
b.prepare(2000);
BEAST_EXPECT(b.capacity() >= 2000);
b.prepare(4000);
BEAST_EXPECT(b.capacity() >= 4000);
b.prepare(50);
BEAST_EXPECT(b.capacity() >= 50);
}
}
// commit
{
multi_buffer b;
b.prepare(1000);
BEAST_EXPECT(b.capacity() >= 1000);
b.commit(1000);
BEAST_EXPECT(b.size() == 1000);
BEAST_EXPECT(b.capacity() >= 1000);
b.consume(1000);
BEAST_EXPECT(b.size() == 0);
BEAST_EXPECT(b.capacity() == 0);
b.prepare(1000);
b.commit(650);
BEAST_EXPECT(b.size() == 650);
BEAST_EXPECT(b.capacity() >= 1000);
b.prepare(1000);
BEAST_EXPECT(b.capacity() >= 1650);
b.commit(100);
BEAST_EXPECT(b.size() == 750);
BEAST_EXPECT(b.capacity() >= 1000);
b.prepare(1000);
BEAST_EXPECT(b.capacity() >= 2000);
b.commit(500);
}
// consume
{
multi_buffer b;
b.prepare(1000);
BEAST_EXPECT(b.capacity() >= 1000);
b.commit(1000);
BEAST_EXPECT(b.size() == 1000);
BEAST_EXPECT(b.capacity() >= 1000);
b.prepare(1000);
BEAST_EXPECT(b.capacity() >= 2000);
b.commit(750);
BEAST_EXPECT(b.size() == 1750);
b.consume(500);
BEAST_EXPECT(b.size() == 1250);
b.consume(500);
BEAST_EXPECT(b.size() == 750);
b.prepare(250);
b.consume(750);
BEAST_EXPECT(b.size() == 0);
b.prepare(1000);
b.commit(800);
BEAST_EXPECT(b.size() == 800);
b.prepare(1000);
b.commit(600);
BEAST_EXPECT(b.size() == 1400);
b.consume(1400);
BEAST_EXPECT(b.size() == 0);
}
// swap
{
{
// propagate_on_container_swap : true
using pocs_t = test::test_allocator<char,
false, true, true, true, true>;
pocs_t a1, a2;
BEAST_EXPECT(a1 != a2);
basic_multi_buffer<pocs_t> b1{a1};
ostream(b1) << "Hello";
basic_multi_buffer<pocs_t> b2{a2};
BEAST_EXPECT(b1.get_allocator() == a1);
BEAST_EXPECT(b2.get_allocator() == a2);
swap(b1, b2);
BEAST_EXPECT(b1.get_allocator() == a2);
BEAST_EXPECT(b2.get_allocator() == a1);
BEAST_EXPECT(b1.size() == 0);
BEAST_EXPECT(to_string(b2.data()) == "Hello");
swap(b1, b2);
BEAST_EXPECT(b1.get_allocator() == a1);
BEAST_EXPECT(b2.get_allocator() == a2);
BEAST_EXPECT(to_string(b1.data()) == "Hello");
BEAST_EXPECT(b2.size() == 0);
}
{
// propagate_on_container_swap : false
using pocs_t = test::test_allocator<char,
true, true, true, false, true>;
pocs_t a1, a2;
BEAST_EXPECT(a1 == a2);
BEAST_EXPECT(a1.id() != a2.id());
basic_multi_buffer<pocs_t> b1{a1};
ostream(b1) << "Hello";
basic_multi_buffer<pocs_t> b2{a2};
BEAST_EXPECT(b1.get_allocator() == a1);
BEAST_EXPECT(b2.get_allocator() == a2);
swap(b1, b2);
BEAST_EXPECT(b1.get_allocator().id() == a1.id());
BEAST_EXPECT(b2.get_allocator().id() == a2.id());
BEAST_EXPECT(b1.size() == 0);
BEAST_EXPECT(to_string(b2.data()) == "Hello");
swap(b1, b2);
BEAST_EXPECT(b1.get_allocator().id() == a1.id());
BEAST_EXPECT(b2.get_allocator().id() == a2.id());
BEAST_EXPECT(to_string(b1.data()) == "Hello");
BEAST_EXPECT(b2.size() == 0);
}
}
// read_size_helper
{
using detail::read_size_helper;
multi_buffer b{10};
BEAST_EXPECT(read_size_helper(b, 512) == 10);
b.prepare(4);
b.commit(4);
BEAST_EXPECT(read_size_helper(b, 512) == 6);
b.consume(2);
BEAST_EXPECT(read_size_helper(b, 512) == 8);
b.prepare(8);
b.commit(8);
BEAST_EXPECT(read_size_helper(b, 512) == 0);
}
}
void
run() override
{ {
test::check_read_size_helper<multi_buffer>(); test::check_read_size_helper<multi_buffer>();
testSpecialMembers(); testMatrix1();
testAllocator(); testMatrix2();
testMatrix();
testIterators(); testIterators();
testMembers();
} }
}; };