multi_buffer implementation change (API Change):

fix #440

WARNING

multi_buffer constructor now takes a maximum size instead of the
"allocation size". This is a breaking change. The allocation size
feature is removed. To update calling code, remove the allocation
size parameter from call sites.

* multi_buffer now uses a geometric growth algorithm for better performance.
This commit is contained in:
Vinnie Falco
2017-06-08 11:07:37 -07:00
parent 74c26a8869
commit 196d9c60fa
9 changed files with 235 additions and 270 deletions

View File

@ -10,6 +10,12 @@ Version 51
API Changes:
* Tune up static_buffer
* multi_buffer implementation change
Actions Required:
* Call sites passing a number to multi_buffer's constructor
will need to be adjusted, see the corresponding commit message.
--------------------------------------------------------------------------------

View File

@ -26,12 +26,12 @@ read_size_helper(DynamicBuffer 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)
return std::min<std::size_t>(
std::max<std::size_t>(512, buffer.capacity() - size),
std::min<std::size_t>(max_size, limit));
BOOST_THROW_EXCEPTION(std::length_error{
"dynamic buffer overflow"});
if(limit <= 0)
BOOST_THROW_EXCEPTION(std::length_error{
"dynamic buffer overflow"});
return std::min<std::size_t>(
std::max<std::size_t>(512, buffer.capacity() - size),
std::min<std::size_t>(max_size, limit));
}
} // detail

View File

@ -416,7 +416,7 @@ buffers_adapter<MutableBufferSequence>::prepare(std::size_t n) ->
}
if(n > 0)
BOOST_THROW_EXCEPTION(std::length_error{
"no space"});
"buffer overflow"});
return mutable_buffers_type{*this};
}

View File

@ -264,7 +264,9 @@ public:
};
template<class Allocator>
basic_multi_buffer<Allocator>::const_buffers_type::const_buffers_type(
basic_multi_buffer<Allocator>::
const_buffers_type::
const_buffers_type(
basic_multi_buffer const& b)
: b_(&b)
{
@ -272,7 +274,9 @@ basic_multi_buffer<Allocator>::const_buffers_type::const_buffers_type(
template<class Allocator>
auto
basic_multi_buffer<Allocator>::const_buffers_type::begin() const ->
basic_multi_buffer<Allocator>::
const_buffers_type::
begin() const ->
const_iterator
{
return const_iterator{*b_, b_->list_.begin()};
@ -280,7 +284,9 @@ basic_multi_buffer<Allocator>::const_buffers_type::begin() const ->
template<class Allocator>
auto
basic_multi_buffer<Allocator>::const_buffers_type::end() const ->
basic_multi_buffer<Allocator>::
const_buffers_type::
end() const ->
const_iterator
{
return const_iterator{*b_, b_->out_ ==
@ -375,7 +381,9 @@ public:
};
template<class Allocator>
basic_multi_buffer<Allocator>::mutable_buffers_type::mutable_buffers_type(
basic_multi_buffer<Allocator>::
mutable_buffers_type::
mutable_buffers_type(
basic_multi_buffer const& b)
: b_(&b)
{
@ -383,7 +391,9 @@ basic_multi_buffer<Allocator>::mutable_buffers_type::mutable_buffers_type(
template<class Allocator>
auto
basic_multi_buffer<Allocator>::mutable_buffers_type::begin() const ->
basic_multi_buffer<Allocator>::
mutable_buffers_type::
begin() const ->
const_iterator
{
return const_iterator{*b_, b_->out_};
@ -391,7 +401,9 @@ basic_multi_buffer<Allocator>::mutable_buffers_type::begin() const ->
template<class Allocator>
auto
basic_multi_buffer<Allocator>::mutable_buffers_type::end() const ->
basic_multi_buffer<Allocator>::
mutable_buffers_type::
end() const ->
const_iterator
{
return const_iterator{*b_, b_->list_.end()};
@ -400,17 +412,25 @@ basic_multi_buffer<Allocator>::mutable_buffers_type::end() const ->
//------------------------------------------------------------------------------
template<class Allocator>
basic_multi_buffer<Allocator>::~basic_multi_buffer()
basic_multi_buffer<Allocator>::
~basic_multi_buffer()
{
delete_list();
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer()
: out_(list_.end())
{
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer&& other)
: detail::empty_base_optimization<allocator_type>(
std::move(other.member()))
, alloc_size_(other.alloc_size_)
, max_(other.max_)
, in_size_(other.in_size_)
, in_pos_(other.in_pos_)
, out_pos_(other.out_pos_)
@ -431,7 +451,7 @@ template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer&& other,
allocator_type const& alloc)
: basic_multi_buffer(other.alloc_size_, alloc)
: basic_multi_buffer(other.max_, alloc)
{
using boost::asio::buffer_copy;
if(this->member() != other.member())
@ -440,104 +460,126 @@ basic_multi_buffer(basic_multi_buffer&& other,
move_assign(other, std::true_type{});
}
template<class Allocator>
auto
basic_multi_buffer<Allocator>::operator=(
basic_multi_buffer&& other) -> basic_multi_buffer&
{
if(this == &other)
return *this;
// VFALCO If any memory allocated we could use it first?
clear();
alloc_size_ = other.alloc_size_;
move_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_move_assignment::value>{});
return *this;
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer const& other)
: basic_multi_buffer(other.alloc_size_,
: basic_multi_buffer(other.max_,
alloc_traits::select_on_container_copy_construction(other.member()))
{
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
commit(boost::asio::buffer_copy(
prepare(other.size()), other.data()));
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer const& other,
allocator_type const& alloc)
: basic_multi_buffer(other.alloc_size_, alloc)
: basic_multi_buffer(other.max_, alloc)
{
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
commit(boost::asio::buffer_copy(
prepare(other.size()), other.data()));
}
template<class Allocator>
template<class OtherAlloc>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other)
: basic_multi_buffer(other.max_)
{
using boost::asio::buffer_copy;
commit(buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
template<class OtherAlloc>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other,
allocator_type const& alloc)
: basic_multi_buffer(other.max_, alloc)
{
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())
{
if(max_ <= 0)
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>
auto
basic_multi_buffer<Allocator>::operator=(
basic_multi_buffer const& other) ->
basic_multi_buffer&
basic_multi_buffer<Allocator>::
operator=(basic_multi_buffer&& other) ->
basic_multi_buffer&
{
if(this == &other)
return *this;
// VFALCO If any memory allocated we could use it first?
clear();
max_ = other.max_;
move_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_move_assignment::value>{});
return *this;
}
template<class Allocator>
auto
basic_multi_buffer<Allocator>::
operator=(basic_multi_buffer const& other) ->
basic_multi_buffer&
{
if(this == &other)
return *this;
using boost::asio::buffer_copy;
clear();
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;
}
template<class Allocator>
template<class OtherAlloc>
basic_multi_buffer<Allocator>::basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other)
: basic_multi_buffer(other.alloc_size_)
{
using boost::asio::buffer_copy;
commit(buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
template<class OtherAlloc>
basic_multi_buffer<Allocator>::basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other,
allocator_type const& alloc)
: basic_multi_buffer(other.alloc_size_, alloc)
{
using boost::asio::buffer_copy;
commit(buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
template<class OtherAlloc>
auto
basic_multi_buffer<Allocator>::operator=(
basic_multi_buffer<Allocator>::
operator=(
basic_multi_buffer<OtherAlloc> const& other) ->
basic_multi_buffer&
{
using boost::asio::buffer_copy;
clear();
max_ = other.max_;
commit(buffer_copy(prepare(other.size()), other.data()));
return *this;
}
template<class Allocator>
basic_multi_buffer<Allocator>::basic_multi_buffer(
std::size_t alloc_size, Allocator const& alloc)
: detail::empty_base_optimization<allocator_type>(alloc)
, out_(list_.end())
, alloc_size_(alloc_size)
{
if(alloc_size <= 0)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid alloc_size"});
}
template<class Allocator>
std::size_t
basic_multi_buffer<Allocator>::capacity() const
basic_multi_buffer<Allocator>::
capacity() const
{
auto pos = out_;
if(pos == list_.end())
@ -559,9 +601,13 @@ data() const ->
template<class Allocator>
auto
basic_multi_buffer<Allocator>::prepare(size_type n) ->
basic_multi_buffer<Allocator>::
prepare(size_type n) ->
mutable_buffers_type
{
if(in_size_ + n > max_)
BOOST_THROW_EXCEPTION(std::length_error{
"dynamic buffer overflow"});
list_type reuse;
if(out_ != list_.end())
{
@ -570,7 +616,9 @@ basic_multi_buffer<Allocator>::prepare(size_type n) ->
out_end_ = out_->size();
reuse.splice(reuse.end(), list_,
std::next(out_), list_.end());
//debug_check();
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
auto const avail = out_->size() - out_pos_;
if(n > avail)
@ -583,7 +631,9 @@ basic_multi_buffer<Allocator>::prepare(size_type n) ->
out_end_ = out_pos_ + n;
n = 0;
}
//debug_check();
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
while(n > 0 && ! reuse.empty())
{
@ -600,11 +650,21 @@ basic_multi_buffer<Allocator>::prepare(size_type n) ->
out_end_ = n;
n = 0;
}
//debug_check();
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
while(n > 0)
{
auto const size = std::max(alloc_size_, n);
static auto const growth_factor = 2.0f;
auto const size =
std::min<std::size_t>(
max_ - in_size_,
std::max<std::size_t>({
static_cast<std::size_t>(
in_size_ * growth_factor - in_size_),
1024,
n}));
auto& e = *reinterpret_cast<element*>(static_cast<
void*>(alloc_traits::allocate(this->member(),
sizeof(element) + size)));
@ -622,7 +682,9 @@ basic_multi_buffer<Allocator>::prepare(size_type n) ->
out_end_ = n;
n = 0;
}
//debug_check();
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
for(auto it = reuse.begin(); it != reuse.end();)
{
@ -638,7 +700,8 @@ basic_multi_buffer<Allocator>::prepare(size_type n) ->
template<class Allocator>
void
basic_multi_buffer<Allocator>::commit(size_type n)
basic_multi_buffer<Allocator>::
commit(size_type n)
{
if(list_.empty())
return;
@ -654,14 +717,18 @@ basic_multi_buffer<Allocator>::commit(size_type n)
{
out_pos_ += n;
in_size_ += n;
//debug_check();
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
return;
}
++out_;
n -= avail;
out_pos_ = 0;
in_size_ += avail;
//debug_check();
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
n = (std::min)(n, out_end_ - out_pos_);
@ -673,12 +740,15 @@ basic_multi_buffer<Allocator>::commit(size_type n)
out_pos_ = 0;
out_end_ = 0;
}
//debug_check();
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
template<class Allocator>
void
basic_multi_buffer<Allocator>::consume(size_type n)
basic_multi_buffer<Allocator>::
consume(size_type n)
{
if(list_.empty())
return;
@ -692,7 +762,9 @@ basic_multi_buffer<Allocator>::consume(size_type n)
{
in_size_ -= n;
in_pos_ += n;
//debug_check();
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
break;
}
n -= avail;
@ -704,7 +776,9 @@ basic_multi_buffer<Allocator>::consume(size_type n)
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<char*>(&e), len);
//debug_check();
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
else
{
@ -731,7 +805,9 @@ basic_multi_buffer<Allocator>::consume(size_type n)
out_end_ = 0;
}
}
//debug_check();
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
break;
}
}
@ -792,7 +868,8 @@ move_assign(basic_multi_buffer& other, std::true_type)
template<class Allocator>
void
basic_multi_buffer<Allocator>::
copy_assign(basic_multi_buffer const& other, std::false_type)
copy_assign(
basic_multi_buffer const& other, std::false_type)
{
beast::detail::ignore_unused(other);
}
@ -800,14 +877,16 @@ copy_assign(basic_multi_buffer const& other, std::false_type)
template<class Allocator>
void
basic_multi_buffer<Allocator>::
copy_assign(basic_multi_buffer const& other, std::true_type)
copy_assign(
basic_multi_buffer const& other, std::true_type)
{
this->member() = other.member();
}
template<class Allocator>
void
basic_multi_buffer<Allocator>::delete_list()
basic_multi_buffer<Allocator>::
delete_list()
{
for(auto iter = list_.begin(); iter != list_.end();)
{
@ -821,7 +900,8 @@ basic_multi_buffer<Allocator>::delete_list()
template<class Allocator>
void
basic_multi_buffer<Allocator>::debug_check() const
basic_multi_buffer<Allocator>::
debug_check() const
{
#ifndef NDEBUG
using boost::asio::buffer_size;
@ -859,6 +939,7 @@ basic_multi_buffer<Allocator>::debug_check() const
#endif
}
#if 0
template<class Allocator>
std::size_t
read_size_helper(
@ -867,6 +948,11 @@ read_size_helper(
{
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)
@ -875,6 +961,7 @@ read_size_helper(
std::min<std::size_t>(max_size, buffer.max_size() - size),
avail + buffer.alloc_size());
}
#endif
} // beast

View File

@ -72,9 +72,10 @@ private:
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
std::size_t max_ =
(std::numeric_limits<std::size_t>::max)();
list_type list_; // list of allocated buffers
iterator out_; // element that contains out_pos_
size_type alloc_size_; // min amount to allocate
size_type in_size_ = 0; // size of the input sequence
size_type in_pos_ = 0; // input offset in list_.front()
size_type out_pos_ = 0; // output offset in *out_
@ -98,6 +99,9 @@ public:
/// Destructor.
~basic_multi_buffer();
/// Default constructor.
basic_multi_buffer();
/** Move constructor.
The new object will have the input sequence of
@ -124,18 +128,6 @@ public:
basic_multi_buffer(basic_multi_buffer&&,
allocator_type const& alloc);
/** Move assignment.
This 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&
operator=(basic_multi_buffer&&);
/** Copy constructor.
This object will have a copy of the other stream
@ -154,13 +146,6 @@ public:
basic_multi_buffer(basic_multi_buffer const&,
allocator_type const& alloc);
/** Copy assignment.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
basic_multi_buffer& operator=(basic_multi_buffer const&);
/** Copy constructor.
This object will have a copy of the other stream
@ -181,6 +166,43 @@ public:
basic_multi_buffer(basic_multi_buffer<OtherAlloc> const&,
allocator_type const& alloc);
/** Constructor.
@param limit The maximum allowed sum of the input and
output sequence sizes.
*/
explicit
basic_multi_buffer(std::size_t limit);
/** Constructor.
@param limit The maximum allowed sum of the input and
output sequence sizes.
@param alloc The allocator to use.
*/
basic_multi_buffer(
std::size_t limit, Allocator const& alloc);
/** Move assignment.
This 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&
operator=(basic_multi_buffer&&);
/** Copy assignment.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
basic_multi_buffer& operator=(basic_multi_buffer const&);
/** Copy assignment.
This object will have a copy of the other stream
@ -189,20 +211,6 @@ public:
template<class OtherAlloc>
basic_multi_buffer& operator=(basic_multi_buffer<OtherAlloc> const&);
/** Construct a stream buffer.
@param alloc_size The size of buffer to allocate. This is a
soft limit, calls to prepare for buffers exceeding this size
will allocate the larger size. The default allocation size
is 1KB (1024 bytes).
@param alloc The allocator to use. If this parameter is
unspecified, a default constructed allocator will be used.
*/
explicit
basic_multi_buffer(std::size_t alloc_size = 1024,
Allocator const& alloc = allocator_type{});
/// Returns a copy of the associated allocator.
allocator_type
get_allocator() const
@ -210,36 +218,6 @@ public:
return this->member();
}
/** Returns the default allocation size.
This is the smallest size that the stream buffer will allocate.
The size of the allocation can influence capacity, which will
affect algorithms that use capacity to efficiently read from
streams.
*/
std::size_t
alloc_size() const
{
return alloc_size_;
}
/** Set the default allocation size.
This is the smallest size that the stream buffer will allocate.
The size of the allocation can influence capacity, which will
affect algorithms that use capacity to efficiently read from
streams.
@note This will not affect any already-existing allocations.
@param n The number of bytes.
*/
void
alloc_size(std::size_t n)
{
alloc_size_ = n;
}
/// Returns the size of the input sequence.
size_type
size() const
@ -251,7 +229,7 @@ public:
size_type
max_size() const
{
return (std::numeric_limits<std::size_t>::max)();
return max_;
}
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
@ -308,12 +286,14 @@ private:
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
using multi_buffer = basic_multi_buffer<std::allocator<char>>;

View File

@ -161,7 +161,7 @@ public:
}
{
using sb_type = beast::multi_buffer;
sb_type b(2);
sb_type b;
b.prepare(3);
buffers_adapter<
sb_type::mutable_buffers_type> ba(b.prepare(8));

View File

@ -72,7 +72,7 @@ public:
for(std::size_t y = 1; y < 4; ++y) {
std::size_t z = s.size() - (x + y);
{
multi_buffer b(i);
multi_buffer b;//(i);
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)));
@ -132,44 +132,6 @@ public:
}
}
void
testPrepare()
{
using boost::asio::buffer_size;
{
multi_buffer b(2);
BEAST_EXPECT(buffer_size(b.prepare(5)) == 5);
BEAST_EXPECT(buffer_size(b.prepare(8)) == 8);
BEAST_EXPECT(buffer_size(b.prepare(7)) == 7);
}
{
multi_buffer b(2);
b.prepare(2);
BEAST_EXPECT(test::buffer_count(b.prepare(5)) == 2);
BEAST_EXPECT(test::buffer_count(b.prepare(8)) == 3);
BEAST_EXPECT(test::buffer_count(b.prepare(4)) == 2);
}
}
void testCommit()
{
multi_buffer b(2);
b.prepare(2);
b.prepare(5);
b.commit(1);
expect_size(1, b.data());
}
void testConsume()
{
multi_buffer b(1);
expect_size(5, b.prepare(5));
b.commit(3);
expect_size(3, b.data());
b.consume(1);
expect_size(2, b.data());
}
void testMatrix()
{
using boost::asio::buffer;
@ -184,7 +146,7 @@ public:
std::size_t z = s.size() - (x + y);
std::size_t v = s.size() - (t + u);
{
multi_buffer b(i);
multi_buffer b;//(i);
{
auto d = b.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
@ -267,7 +229,7 @@ public:
void testIterators()
{
using boost::asio::buffer_size;
multi_buffer b(1);
multi_buffer b;
b.prepare(1);
b.commit(1);
b.prepare(2);
@ -276,72 +238,6 @@ public:
b.prepare(1);
expect_size(3, b.prepare(3));
b.commit(2);
BEAST_EXPECT(test::buffer_count(b.data()) == 4);
}
void testCapacity()
{
using beast::detail::read_size_helper;
using boost::asio::buffer_size;
{
multi_buffer b{10};
BEAST_EXPECT(b.alloc_size() == 10);
BEAST_EXPECT(read_size_helper(b, 1) == 1);
BEAST_EXPECT(read_size_helper(b, 10) == 10);
BEAST_EXPECT(read_size_helper(b, 20) == 10);
BEAST_EXPECT(read_size_helper(b, 1000) == 10);
b.prepare(3);
b.commit(3);
BEAST_EXPECT(read_size_helper(b, 10) == 7);
BEAST_EXPECT(read_size_helper(b, 1000) == 7);
}
{
multi_buffer b(1000);
BEAST_EXPECT(b.alloc_size() == 1000);
BEAST_EXPECT(read_size_helper(b, 1) == 1);
BEAST_EXPECT(read_size_helper(b, 1000) == 1000);
BEAST_EXPECT(read_size_helper(b, 2000) == 1000);
b.prepare(3);
BEAST_EXPECT(read_size_helper(b, 1) == 1);
BEAST_EXPECT(read_size_helper(b, 1000) == 1000);
BEAST_EXPECT(read_size_helper(b, 2000) == 1000);
b.commit(3);
BEAST_EXPECT(read_size_helper(b, 1) == 1);
BEAST_EXPECT(read_size_helper(b, 1000) == 997);
BEAST_EXPECT(read_size_helper(b, 2000) == 997);
b.consume(2);
BEAST_EXPECT(read_size_helper(b, 1) == 1);
BEAST_EXPECT(read_size_helper(b, 1000) == 997);
BEAST_EXPECT(read_size_helper(b, 2000) == 997);
}
{
multi_buffer b{2};
BEAST_EXPECT(b.alloc_size() == 2);
BEAST_EXPECT(test::buffer_count(b.prepare(2)) == 1);
BEAST_EXPECT(test::buffer_count(b.prepare(3)) == 2);
BEAST_EXPECT(buffer_size(b.prepare(5)) == 5);
BEAST_EXPECT(read_size_helper(b, 10) == 6);
}
{
auto avail =
[](multi_buffer const& b)
{
return b.capacity() - b.size();
};
multi_buffer b{100};
BEAST_EXPECT(b.alloc_size() == 100);
BEAST_EXPECT(avail(b) == 0);
b.prepare(100);
BEAST_EXPECT(avail(b) == 100);
b.commit(100);
BEAST_EXPECT(avail(b) == 0);
b.consume(100);
BEAST_EXPECT(avail(b) == 0);
b.alloc_size(200);
BEAST_EXPECT(b.alloc_size() == 200);
b.prepare(1);
BEAST_EXPECT(avail(b) == 200);
}
}
void run() override
@ -350,12 +246,8 @@ public:
testSpecialMembers();
testAllocator();
testPrepare();
testCommit();
testConsume();
testMatrix();
testIterators();
testCapacity();
}
};

View File

@ -40,7 +40,7 @@ public:
"0123456789abcdef" "0123456789abcdef" "0123456789abcdef" "0123456789abcdef"
"0123456789abcdef" "0123456789abcdef" "0123456789abcdef" "0123456789abcdef"
"0123456789abcdef" "0123456789abcdef" "0123456789abcdef" "0123456789abcdef";
multi_buffer b(512);
multi_buffer b;
ostream(b) << s;
BEAST_EXPECT(boost::lexical_cast<std::string>(
buffers(b.data())) == s);

View File

@ -379,7 +379,7 @@ public:
consuming_buffers<
boost::asio::const_buffers_1> cb{
boost::asio::const_buffers_1(s.data(), n)};
multi_buffer b{size};
multi_buffer b;
while(n)
{
auto const amount = (std::min)(n, size);