Move basic_streambuf to streambuf.hpp (API Change):

* Also change allocator element type to char
This commit is contained in:
Vinnie Falco 2017-02-05 10:02:53 -05:00
parent 765cb22b48
commit 7ef72b6ebc
9 changed files with 789 additions and 822 deletions

View File

@ -3,6 +3,7 @@
API Changes:
* Invoke callback on pings and pongs
* Move basic_streambuf to streambuf.hpp
--------------------------------------------------------------------------------

View File

@ -9,7 +9,6 @@
#define BEAST_CORE_HPP
#include <beast/core/async_completion.hpp>
#include <beast/core/basic_streambuf.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/buffer_cat.hpp>
#include <beast/core/buffer_concepts.hpp>

View File

@ -1,333 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_BASIC_STREAMBUF_HPP
#define BEAST_BASIC_STREAMBUF_HPP
#include <beast/core/detail/empty_base_optimization.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/intrusive/list.hpp>
#include <iterator>
#include <limits>
#include <memory>
#include <type_traits>
namespace beast {
/** A @b `DynamicBuffer` that uses multiple buffers internally.
The implementation uses a sequence of one or more character arrays
of varying sizes. Additional character array objects are appended to
the sequence to accommodate changes in the size of the character
sequence.
@note Meets the requirements of @b DynamicBuffer.
@tparam Allocator The allocator to use for managing memory.
*/
template<class Allocator>
class basic_streambuf
#if ! GENERATING_DOCS
: private detail::empty_base_optimization<
typename std::allocator_traits<Allocator>::
template rebind_alloc<std::uint8_t>>
#endif
{
public:
#if GENERATING_DOCS
/// The type of allocator used.
using allocator_type = Allocator;
#else
using allocator_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<std::uint8_t>;
#endif
private:
// Storage for the list of buffers representing the input
// and output sequences. The allocation for each element
// contains `element` followed by raw storage bytes.
class element;
using alloc_traits = std::allocator_traits<allocator_type>;
using list_type = typename boost::intrusive::make_list<element,
boost::intrusive::constant_time_size<true>>::type;
using iterator = typename list_type::iterator;
using const_iterator = typename list_type::const_iterator;
using size_type = typename std::allocator_traits<Allocator>::size_type;
using const_buffer = boost::asio::const_buffer;
using mutable_buffer = boost::asio::mutable_buffer;
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
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_
size_type out_end_ = 0; // output end offset in list_.back()
public:
#if GENERATING_DOCS
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = implementation_defined;
#else
class const_buffers_type;
class mutable_buffers_type;
#endif
/// Destructor.
~basic_streambuf();
/** 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_streambuf(basic_streambuf&&);
/** 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_streambuf(basic_streambuf&&,
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_streambuf&
operator=(basic_streambuf&&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
basic_streambuf(basic_streambuf 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_streambuf(basic_streambuf 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_streambuf& operator=(basic_streambuf const&);
/** 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_streambuf(basic_streambuf<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_streambuf(basic_streambuf<OtherAlloc> 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.
*/
template<class OtherAlloc>
basic_streambuf& operator=(basic_streambuf<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_streambuf(std::size_t alloc_size = 1024,
Allocator const& alloc = allocator_type{});
/// Returns a copy of the associated allocator.
allocator_type
get_allocator() const
{
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
{
return in_size_;
}
/// Returns the permitted maximum sum of the sizes of the input and output sequence.
size_type
max_size() const
{
return (std::numeric_limits<std::size_t>::max)();
}
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
std::size_t
capacity() const;
/** Get a list of buffers that represents the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type
data() const;
/** Get a list of buffers that represents the output sequence, with the given size.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
mutable_buffers_type
prepare(size_type n);
/** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void
commit(size_type n);
/// Remove bytes from the input sequence.
void
consume(size_type n);
// Helper for boost::asio::read_until
template<class OtherAllocator>
friend
std::size_t
read_size_helper(basic_streambuf<
OtherAllocator> const& streambuf, std::size_t max_size);
private:
void
clear();
void
move_assign(basic_streambuf& other, std::false_type);
void
move_assign(basic_streambuf& other, std::true_type);
void
copy_assign(basic_streambuf const& other, std::false_type);
void
copy_assign(basic_streambuf const& other, std::true_type);
void
delete_list();
void
debug_check() const;
};
/** Format output to a @ref basic_streambuf.
@param streambuf The @ref basic_streambuf to write to.
@param t The object to write.
@return A reference to the @ref basic_streambuf.
*/
template<class Allocator, class T>
basic_streambuf<Allocator>&
operator<<(basic_streambuf<Allocator>& streambuf, T const& t);
} // beast
#include <beast/core/impl/basic_streambuf.ipp>
#endif

View File

@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP
#define BEAST_IMPL_BASIC_STREAMBUF_IPP
#ifndef BEAST_IMPL_STREAMBUF_IPP
#define BEAST_IMPL_STREAMBUF_IPP
#include <beast/core/detail/type_traits.hpp>
#include <beast/core/detail/write_dynabuf.hpp>
@ -624,7 +624,7 @@ basic_streambuf<Allocator>::prepare(size_type n) ->
auto const len = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<std::uint8_t*>(&e), len);
reinterpret_cast<char*>(&e), len);
}
return mutable_buffers_type(*this);
}
@ -696,7 +696,7 @@ basic_streambuf<Allocator>::consume(size_type n)
auto const len = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<std::uint8_t*>(&e), len);
reinterpret_cast<char*>(&e), len);
debug_check();
}
else
@ -808,7 +808,7 @@ basic_streambuf<Allocator>::delete_list()
auto const n = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<std::uint8_t*>(&e), n);
reinterpret_cast<char*>(&e), n);
}
}

View File

@ -8,10 +8,312 @@
#ifndef BEAST_STREAMBUF_HPP
#define BEAST_STREAMBUF_HPP
#include <beast/core/basic_streambuf.hpp>
#include <beast/core/detail/empty_base_optimization.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/intrusive/list.hpp>
#include <iterator>
#include <limits>
#include <memory>
#include <type_traits>
namespace beast {
/** A @b `DynamicBuffer` that uses multiple buffers internally.
The implementation uses a sequence of one or more character arrays
of varying sizes. Additional character array objects are appended to
the sequence to accommodate changes in the size of the character
sequence.
@note Meets the requirements of @b DynamicBuffer.
@tparam Allocator The allocator to use for managing memory.
*/
template<class Allocator>
class basic_streambuf
#if ! GENERATING_DOCS
: private detail::empty_base_optimization<
typename std::allocator_traits<Allocator>::
template rebind_alloc<char>>
#endif
{
public:
#if GENERATING_DOCS
/// The type of allocator used.
using allocator_type = Allocator;
#else
using allocator_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<char>;
#endif
private:
// Storage for the list of buffers representing the input
// and output sequences. The allocation for each element
// contains `element` followed by raw storage bytes.
class element;
using alloc_traits = std::allocator_traits<allocator_type>;
using list_type = typename boost::intrusive::make_list<element,
boost::intrusive::constant_time_size<true>>::type;
using iterator = typename list_type::iterator;
using const_iterator = typename list_type::const_iterator;
using size_type = typename std::allocator_traits<Allocator>::size_type;
using const_buffer = boost::asio::const_buffer;
using mutable_buffer = boost::asio::mutable_buffer;
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
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_
size_type out_end_ = 0; // output end offset in list_.back()
public:
#if GENERATING_DOCS
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = implementation_defined;
#else
class const_buffers_type;
class mutable_buffers_type;
#endif
/// Destructor.
~basic_streambuf();
/** 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_streambuf(basic_streambuf&&);
/** 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_streambuf(basic_streambuf&&,
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_streambuf&
operator=(basic_streambuf&&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
basic_streambuf(basic_streambuf 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_streambuf(basic_streambuf 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_streambuf& operator=(basic_streambuf const&);
/** 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_streambuf(basic_streambuf<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_streambuf(basic_streambuf<OtherAlloc> 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.
*/
template<class OtherAlloc>
basic_streambuf& operator=(basic_streambuf<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_streambuf(std::size_t alloc_size = 1024,
Allocator const& alloc = allocator_type{});
/// Returns a copy of the associated allocator.
allocator_type
get_allocator() const
{
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
{
return in_size_;
}
/// Returns the permitted maximum sum of the sizes of the input and output sequence.
size_type
max_size() const
{
return (std::numeric_limits<std::size_t>::max)();
}
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
std::size_t
capacity() const;
/** Get a list of buffers that represents the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type
data() const;
/** Get a list of buffers that represents the output sequence, with the given size.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
mutable_buffers_type
prepare(size_type n);
/** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void
commit(size_type n);
/// Remove bytes from the input sequence.
void
consume(size_type n);
// Helper for boost::asio::read_until
template<class OtherAllocator>
friend
std::size_t
read_size_helper(basic_streambuf<
OtherAllocator> const& streambuf, std::size_t max_size);
private:
void
clear();
void
move_assign(basic_streambuf& other, std::false_type);
void
move_assign(basic_streambuf& other, std::true_type);
void
copy_assign(basic_streambuf const& other, std::false_type);
void
copy_assign(basic_streambuf const& other, std::true_type);
void
delete_list();
void
debug_check() const;
};
/** A @b `DynamicBuffer` that uses multiple buffers internally.
The implementation uses a sequence of one or more character arrays
@ -23,6 +325,20 @@ namespace beast {
*/
using streambuf = basic_streambuf<std::allocator<char>>;
/** Format output to a @ref basic_streambuf.
@param streambuf The @ref basic_streambuf to write to.
@param t The object to write.
@return A reference to the @ref basic_streambuf.
*/
template<class Allocator, class T>
basic_streambuf<Allocator>&
operator<<(basic_streambuf<Allocator>& streambuf, T const& t);
} // beast
#include <beast/core/impl/streambuf.ipp>
#endif

View File

@ -16,7 +16,6 @@ compile zlib.cpp : : ;
unit-test core-tests :
../extras/beast/unit_test/main.cpp
core/async_completion.cpp
core/basic_streambuf.cpp
core/bind_handler.cpp
core/buffer_cat.cpp
core/buffer_concepts.cpp

View File

@ -10,7 +10,6 @@ add_executable (core-tests
../../extras/beast/unit_test/main.cpp
buffer_test.hpp
async_completion.cpp
basic_streambuf.cpp
bind_handler.cpp
buffer_cat.cpp
buffer_concepts.cpp

View File

@ -1,480 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/core/basic_streambuf.hpp>
#include "buffer_test.hpp"
#include <beast/core/streambuf.hpp>
#include <beast/core/to_string.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <atomic>
#include <memory>
#include <string>
namespace beast {
struct test_allocator_info
{
std::size_t ncopy = 0;
std::size_t nmove = 0;
std::size_t nselect = 0;
};
template<class T,
bool Assign, bool Move, bool Swap, bool Select>
class test_allocator;
template<class T,
bool Assign, bool Move, bool Swap, bool Select>
struct test_allocator_base
{
};
template<class T,
bool Assign, bool Move, bool Swap>
struct test_allocator_base<T, Assign, Move, Swap, true>
{
static
test_allocator<T, Assign, Move, Swap, true>
select_on_container_copy_construction(
test_allocator<T, Assign, Move, Swap, true> const& a)
{
return test_allocator<T, Assign, Move, Swap, true>{};
}
};
template<class T,
bool Assign, bool Move, bool Swap, bool Select>
class test_allocator : public test_allocator_base<
T, Assign, Move, Swap, Select>
{
std::size_t id_;
std::shared_ptr<test_allocator_info> info_;
template<class, bool, bool, bool, bool>
friend class test_allocator;
public:
using value_type = T;
using propagate_on_container_copy_assignment =
std::integral_constant<bool, Assign>;
using propagate_on_container_move_assignment =
std::integral_constant<bool, Move>;
using propagate_on_container_swap =
std::integral_constant<bool, Swap>;
template<class U>
struct rebind
{
using other = test_allocator<
U, Assign, Move, Swap, Select>;
};
test_allocator()
: id_([]
{
static std::atomic<
std::size_t> sid(0);
return ++sid;
}())
, info_(std::make_shared<test_allocator_info>())
{
}
test_allocator(test_allocator const& u) noexcept
: id_(u.id_)
, info_(u.info_)
{
++info_->ncopy;
}
template<class U>
test_allocator(test_allocator<
U, Assign, Move, Swap, Select> const& u) noexcept
: id_(u.id_)
, info_(u.info_)
{
++info_->ncopy;
}
test_allocator(test_allocator&& t)
: id_(t.id_)
, info_(t.info_)
{
++info_->nmove;
}
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
id() const
{
return id_;
}
test_allocator_info const*
operator->() const
{
return info_.get();
}
};
class basic_streambuf_test : public beast::unit_test::suite
{
public:
template<class Alloc1, class Alloc2>
static
bool
eq(basic_streambuf<Alloc1> const& sb1,
basic_streambuf<Alloc2> const& sb2)
{
return to_string(sb1.data()) == to_string(sb2.data());
}
template<class ConstBufferSequence>
void
expect_size(std::size_t n, ConstBufferSequence const& buffers)
{
BEAST_EXPECT(test::size_pre(buffers) == n);
BEAST_EXPECT(test::size_post(buffers) == n);
BEAST_EXPECT(test::size_rev_pre(buffers) == n);
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);
}
void testSpecialMembers()
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::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) {
std::size_t z = s.size() - (x + y);
{
streambuf sb(i);
sb.commit(buffer_copy(sb.prepare(x), buffer(s.data(), x)));
sb.commit(buffer_copy(sb.prepare(y), buffer(s.data()+x, y)));
sb.commit(buffer_copy(sb.prepare(z), buffer(s.data()+x+y, z)));
BEAST_EXPECT(to_string(sb.data()) == s);
{
streambuf sb2(sb);
BEAST_EXPECT(eq(sb, sb2));
}
{
streambuf sb2;
sb2 = sb;
BEAST_EXPECT(eq(sb, sb2));
}
{
streambuf sb2(std::move(sb));
BEAST_EXPECT(to_string(sb2.data()) == s);
expect_size(0, sb.data());
sb = std::move(sb2);
BEAST_EXPECT(to_string(sb.data()) == s);
expect_size(0, sb2.data());
}
self_assign(sb, sb);
BEAST_EXPECT(to_string(sb.data()) == s);
self_assign(sb, std::move(sb));
BEAST_EXPECT(to_string(sb.data()) == s);
}
}}}
try
{
streambuf sb0(0);
fail();
}
catch(std::exception const&)
{
pass();
}
}
void testAllocator()
{
// VFALCO This needs work
{
using alloc_type =
test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>;
sb_type sb;
BEAST_EXPECT(sb.get_allocator().id() == 1);
}
{
using alloc_type =
test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>;
sb_type sb;
BEAST_EXPECT(sb.get_allocator().id() == 2);
sb_type sb2(sb);
BEAST_EXPECT(sb2.get_allocator().id() == 2);
sb_type sb3(sb, alloc_type{});
}
}
void
testPrepare()
{
using boost::asio::buffer_size;
{
streambuf sb(2);
BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5);
BEAST_EXPECT(buffer_size(sb.prepare(8)) == 8);
BEAST_EXPECT(buffer_size(sb.prepare(7)) == 7);
}
{
streambuf sb(2);
sb.prepare(2);
BEAST_EXPECT(test::buffer_count(sb.prepare(5)) == 2);
BEAST_EXPECT(test::buffer_count(sb.prepare(8)) == 3);
BEAST_EXPECT(test::buffer_count(sb.prepare(4)) == 2);
}
}
void testCommit()
{
using boost::asio::buffer_size;
streambuf sb(2);
sb.prepare(2);
sb.prepare(5);
sb.commit(1);
expect_size(1, sb.data());
}
void testConsume()
{
using boost::asio::buffer_size;
streambuf sb(1);
expect_size(5, sb.prepare(5));
sb.commit(3);
expect_size(3, sb.data());
sb.consume(1);
expect_size(2, sb.data());
}
void testMatrix()
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::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);
{
streambuf sb(i);
{
auto d = sb.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = sb.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = sb.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
sb.commit(buffer_copy(d, buffer(s.data(), x)));
}
BEAST_EXPECT(sb.size() == x);
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
{
auto d = sb.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = sb.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = sb.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
sb.commit(buffer_copy(d, buffer(s.data()+x, y)));
}
sb.commit(1);
BEAST_EXPECT(sb.size() == x + y);
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
{
auto d = sb.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = sb.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = sb.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
sb.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
}
sb.commit(2);
BEAST_EXPECT(sb.size() == x + y + z);
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
BEAST_EXPECT(to_string(sb.data()) == s);
sb.consume(t);
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
BEAST_EXPECT(to_string(sb.data()) == s.substr(t, std::string::npos));
sb.consume(u);
BEAST_EXPECT(to_string(sb.data()) == s.substr(t + u, std::string::npos));
sb.consume(v);
BEAST_EXPECT(to_string(sb.data()) == "");
sb.consume(1);
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
}
}}}}}
}
void testIterators()
{
using boost::asio::buffer_size;
streambuf sb(1);
sb.prepare(1);
sb.commit(1);
sb.prepare(2);
sb.commit(2);
expect_size(3, sb.data());
sb.prepare(1);
expect_size(3, sb.prepare(3));
sb.commit(2);
BEAST_EXPECT(test::buffer_count(sb.data()) == 4);
}
void testOutputStream()
{
streambuf sb;
sb << "x";
BEAST_EXPECT(to_string(sb.data()) == "x");
}
void testCapacity()
{
using boost::asio::buffer_size;
{
streambuf sb{10};
BEAST_EXPECT(sb.alloc_size() == 10);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 10) == 10);
BEAST_EXPECT(read_size_helper(sb, 20) == 20);
BEAST_EXPECT(read_size_helper(sb, 1000) == 512);
sb.prepare(3);
sb.commit(3);
BEAST_EXPECT(read_size_helper(sb, 10) == 7);
BEAST_EXPECT(read_size_helper(sb, 1000) == 7);
}
{
streambuf sb(1000);
BEAST_EXPECT(sb.alloc_size() == 1000);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 1000);
BEAST_EXPECT(read_size_helper(sb, 2000) == 1000);
sb.prepare(3);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 1000);
BEAST_EXPECT(read_size_helper(sb, 2000) == 1000);
sb.commit(3);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 997);
BEAST_EXPECT(read_size_helper(sb, 2000) == 997);
sb.consume(2);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 997);
BEAST_EXPECT(read_size_helper(sb, 2000) == 997);
}
{
streambuf sb{2};
BEAST_EXPECT(sb.alloc_size() == 2);
BEAST_EXPECT(test::buffer_count(sb.prepare(2)) == 1);
BEAST_EXPECT(test::buffer_count(sb.prepare(3)) == 2);
BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5);
BEAST_EXPECT(read_size_helper(sb, 10) == 6);
}
{
auto avail =
[](streambuf const& sb)
{
return sb.capacity() - sb.size();
};
streambuf sb{100};
BEAST_EXPECT(sb.alloc_size() == 100);
BEAST_EXPECT(avail(sb) == 0);
sb.prepare(100);
BEAST_EXPECT(avail(sb) == 100);
sb.commit(100);
BEAST_EXPECT(avail(sb) == 0);
sb.consume(100);
BEAST_EXPECT(avail(sb) == 0);
sb.alloc_size(200);
BEAST_EXPECT(sb.alloc_size() == 200);
sb.prepare(1);
BEAST_EXPECT(avail(sb) == 200);
}
}
void run() override
{
testSpecialMembers();
testAllocator();
testPrepare();
testCommit();
testConsume();
testMatrix();
testIterators();
testOutputStream();
testCapacity();
}
};
BEAST_DEFINE_TESTSUITE(basic_streambuf,core,beast);
} // beast

View File

@ -8,9 +8,475 @@
// Test that header file is self-contained.
#include <beast/core/streambuf.hpp>
#include "buffer_test.hpp"
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/to_string.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <atomic>
#include <memory>
#include <string>
namespace beast {
static_assert(is_DynamicBuffer<streambuf>::value, "");
struct test_allocator_info
{
std::size_t ncopy = 0;
std::size_t nmove = 0;
std::size_t nselect = 0;
};
template<class T,
bool Assign, bool Move, bool Swap, bool Select>
class test_allocator;
template<class T,
bool Assign, bool Move, bool Swap, bool Select>
struct test_allocator_base
{
};
template<class T,
bool Assign, bool Move, bool Swap>
struct test_allocator_base<T, Assign, Move, Swap, true>
{
static
test_allocator<T, Assign, Move, Swap, true>
select_on_container_copy_construction(
test_allocator<T, Assign, Move, Swap, true> const& a)
{
return test_allocator<T, Assign, Move, Swap, true>{};
}
};
template<class T,
bool Assign, bool Move, bool Swap, bool Select>
class test_allocator : public test_allocator_base<
T, Assign, Move, Swap, Select>
{
std::size_t id_;
std::shared_ptr<test_allocator_info> info_;
template<class, bool, bool, bool, bool>
friend class test_allocator;
public:
using value_type = T;
using propagate_on_container_copy_assignment =
std::integral_constant<bool, Assign>;
using propagate_on_container_move_assignment =
std::integral_constant<bool, Move>;
using propagate_on_container_swap =
std::integral_constant<bool, Swap>;
template<class U>
struct rebind
{
using other = test_allocator<
U, Assign, Move, Swap, Select>;
};
test_allocator()
: id_([]
{
static std::atomic<
std::size_t> sid(0);
return ++sid;
}())
, info_(std::make_shared<test_allocator_info>())
{
}
test_allocator(test_allocator const& u) noexcept
: id_(u.id_)
, info_(u.info_)
{
++info_->ncopy;
}
template<class U>
test_allocator(test_allocator<
U, Assign, Move, Swap, Select> const& u) noexcept
: id_(u.id_)
, info_(u.info_)
{
++info_->ncopy;
}
test_allocator(test_allocator&& t)
: id_(t.id_)
, info_(t.info_)
{
++info_->nmove;
}
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
id() const
{
return id_;
}
test_allocator_info const*
operator->() const
{
return info_.get();
}
};
class basic_streambuf_test : public beast::unit_test::suite
{
public:
template<class Alloc1, class Alloc2>
static
bool
eq(basic_streambuf<Alloc1> const& sb1,
basic_streambuf<Alloc2> const& sb2)
{
return to_string(sb1.data()) == to_string(sb2.data());
}
template<class ConstBufferSequence>
void
expect_size(std::size_t n, ConstBufferSequence const& buffers)
{
BEAST_EXPECT(test::size_pre(buffers) == n);
BEAST_EXPECT(test::size_post(buffers) == n);
BEAST_EXPECT(test::size_rev_pre(buffers) == n);
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);
}
void testSpecialMembers()
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::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) {
std::size_t z = s.size() - (x + y);
{
streambuf sb(i);
sb.commit(buffer_copy(sb.prepare(x), buffer(s.data(), x)));
sb.commit(buffer_copy(sb.prepare(y), buffer(s.data()+x, y)));
sb.commit(buffer_copy(sb.prepare(z), buffer(s.data()+x+y, z)));
BEAST_EXPECT(to_string(sb.data()) == s);
{
streambuf sb2(sb);
BEAST_EXPECT(eq(sb, sb2));
}
{
streambuf sb2;
sb2 = sb;
BEAST_EXPECT(eq(sb, sb2));
}
{
streambuf sb2(std::move(sb));
BEAST_EXPECT(to_string(sb2.data()) == s);
expect_size(0, sb.data());
sb = std::move(sb2);
BEAST_EXPECT(to_string(sb.data()) == s);
expect_size(0, sb2.data());
}
self_assign(sb, sb);
BEAST_EXPECT(to_string(sb.data()) == s);
self_assign(sb, std::move(sb));
BEAST_EXPECT(to_string(sb.data()) == s);
}
}}}
try
{
streambuf sb0(0);
fail();
}
catch(std::exception const&)
{
pass();
}
}
void testAllocator()
{
// VFALCO This needs work
{
using alloc_type =
test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>;
sb_type sb;
BEAST_EXPECT(sb.get_allocator().id() == 1);
}
{
using alloc_type =
test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>;
sb_type sb;
BEAST_EXPECT(sb.get_allocator().id() == 2);
sb_type sb2(sb);
BEAST_EXPECT(sb2.get_allocator().id() == 2);
sb_type sb3(sb, alloc_type{});
}
}
void
testPrepare()
{
using boost::asio::buffer_size;
{
streambuf sb(2);
BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5);
BEAST_EXPECT(buffer_size(sb.prepare(8)) == 8);
BEAST_EXPECT(buffer_size(sb.prepare(7)) == 7);
}
{
streambuf sb(2);
sb.prepare(2);
BEAST_EXPECT(test::buffer_count(sb.prepare(5)) == 2);
BEAST_EXPECT(test::buffer_count(sb.prepare(8)) == 3);
BEAST_EXPECT(test::buffer_count(sb.prepare(4)) == 2);
}
}
void testCommit()
{
using boost::asio::buffer_size;
streambuf sb(2);
sb.prepare(2);
sb.prepare(5);
sb.commit(1);
expect_size(1, sb.data());
}
void testConsume()
{
using boost::asio::buffer_size;
streambuf sb(1);
expect_size(5, sb.prepare(5));
sb.commit(3);
expect_size(3, sb.data());
sb.consume(1);
expect_size(2, sb.data());
}
void testMatrix()
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::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);
{
streambuf sb(i);
{
auto d = sb.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = sb.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = sb.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
sb.commit(buffer_copy(d, buffer(s.data(), x)));
}
BEAST_EXPECT(sb.size() == x);
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
{
auto d = sb.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = sb.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = sb.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
sb.commit(buffer_copy(d, buffer(s.data()+x, y)));
}
sb.commit(1);
BEAST_EXPECT(sb.size() == x + y);
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
{
auto d = sb.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = sb.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = sb.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
sb.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
}
sb.commit(2);
BEAST_EXPECT(sb.size() == x + y + z);
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
BEAST_EXPECT(to_string(sb.data()) == s);
sb.consume(t);
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
BEAST_EXPECT(to_string(sb.data()) == s.substr(t, std::string::npos));
sb.consume(u);
BEAST_EXPECT(to_string(sb.data()) == s.substr(t + u, std::string::npos));
sb.consume(v);
BEAST_EXPECT(to_string(sb.data()) == "");
sb.consume(1);
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
}
}}}}}
}
void testIterators()
{
using boost::asio::buffer_size;
streambuf sb(1);
sb.prepare(1);
sb.commit(1);
sb.prepare(2);
sb.commit(2);
expect_size(3, sb.data());
sb.prepare(1);
expect_size(3, sb.prepare(3));
sb.commit(2);
BEAST_EXPECT(test::buffer_count(sb.data()) == 4);
}
void testOutputStream()
{
streambuf sb;
sb << "x";
BEAST_EXPECT(to_string(sb.data()) == "x");
}
void testCapacity()
{
using boost::asio::buffer_size;
{
streambuf sb{10};
BEAST_EXPECT(sb.alloc_size() == 10);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 10) == 10);
BEAST_EXPECT(read_size_helper(sb, 20) == 20);
BEAST_EXPECT(read_size_helper(sb, 1000) == 512);
sb.prepare(3);
sb.commit(3);
BEAST_EXPECT(read_size_helper(sb, 10) == 7);
BEAST_EXPECT(read_size_helper(sb, 1000) == 7);
}
{
streambuf sb(1000);
BEAST_EXPECT(sb.alloc_size() == 1000);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 1000);
BEAST_EXPECT(read_size_helper(sb, 2000) == 1000);
sb.prepare(3);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 1000);
BEAST_EXPECT(read_size_helper(sb, 2000) == 1000);
sb.commit(3);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 997);
BEAST_EXPECT(read_size_helper(sb, 2000) == 997);
sb.consume(2);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 997);
BEAST_EXPECT(read_size_helper(sb, 2000) == 997);
}
{
streambuf sb{2};
BEAST_EXPECT(sb.alloc_size() == 2);
BEAST_EXPECT(test::buffer_count(sb.prepare(2)) == 1);
BEAST_EXPECT(test::buffer_count(sb.prepare(3)) == 2);
BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5);
BEAST_EXPECT(read_size_helper(sb, 10) == 6);
}
{
auto avail =
[](streambuf const& sb)
{
return sb.capacity() - sb.size();
};
streambuf sb{100};
BEAST_EXPECT(sb.alloc_size() == 100);
BEAST_EXPECT(avail(sb) == 0);
sb.prepare(100);
BEAST_EXPECT(avail(sb) == 100);
sb.commit(100);
BEAST_EXPECT(avail(sb) == 0);
sb.consume(100);
BEAST_EXPECT(avail(sb) == 0);
sb.alloc_size(200);
BEAST_EXPECT(sb.alloc_size() == 200);
sb.prepare(1);
BEAST_EXPECT(avail(sb) == 200);
}
}
void run() override
{
testSpecialMembers();
testAllocator();
testPrepare();
testCommit();
testConsume();
testMatrix();
testIterators();
testOutputStream();
testCapacity();
}
};
BEAST_DEFINE_TESTSUITE(basic_streambuf,core,beast);
} // beast