mirror of
https://github.com/boostorg/beast.git
synced 2025-07-29 12:27:44 +02:00
Add flat_streambuf:
Objects of this type meet the requirements of DynamicBuffer and offer an additional invariant: buffer sequences returned by data() and prepare() are always of length one.
This commit is contained in:
@ -3,6 +3,7 @@
|
||||
* Add Appveyor build scripts and badge
|
||||
* Tidy up MSVC CMake configuration
|
||||
* Make close_code a proper enum
|
||||
* Add flat_streambuf
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -165,6 +165,7 @@
|
||||
<bridgehead renderas="sect3">Classes</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.async_completion">async_completion</link></member>
|
||||
<member><link linkend="beast.ref.basic_flat_streambuf">basic_flat_streambuf</link></member>
|
||||
<member><link linkend="beast.ref.basic_streambuf">basic_streambuf</link></member>
|
||||
<member><link linkend="beast.ref.buffers_adapter">buffers_adapter</link></member>
|
||||
<member><link linkend="beast.ref.consuming_buffers">consuming_buffers</link></member>
|
||||
@ -173,6 +174,7 @@
|
||||
<member><link linkend="beast.ref.error_category">error_category</link></member>
|
||||
<member><link linkend="beast.ref.error_code">error_code</link></member>
|
||||
<member><link linkend="beast.ref.error_condition">error_condition</link></member>
|
||||
<member><link linkend="beast.ref.flat_streambuf">flat_streambuf</link></member>
|
||||
<member><link linkend="beast.ref.handler_alloc">handler_alloc</link></member>
|
||||
<member><link linkend="beast.ref.handler_ptr">handler_ptr</link></member>
|
||||
<member><link linkend="beast.ref.static_streambuf">static_streambuf</link></member>
|
||||
|
@ -19,7 +19,9 @@ The interface to this concept is intended to permit the following
|
||||
implementation strategies:
|
||||
|
||||
* A single contiguous octet array, which is reallocated as necessary to
|
||||
accommodate changes in the size of the octet sequence.
|
||||
accommodate changes in the size of the octet sequence. This is the
|
||||
implementation approach currently offered by
|
||||
[link beast.ref.basic_flat_streambuf `basic_flat_streambuf`].
|
||||
|
||||
* A sequence of one or more octet arrays, where each array is of the same
|
||||
size. Additional octet array objects are appended to the sequence to
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <beast/core/buffers_adapter.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/flat_streambuf.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
|
310
include/beast/core/flat_streambuf.hpp
Normal file
310
include/beast/core/flat_streambuf.hpp
Normal file
@ -0,0 +1,310 @@
|
||||
//
|
||||
// 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_FLAT_STREAMBUF_HPP
|
||||
#define BEAST_FLAT_STREAMBUF_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A linear dynamic buffer.
|
||||
|
||||
Objects of this type meet the requirements of @b DynamicBuffer
|
||||
and offer additional invariants:
|
||||
|
||||
@li Buffer sequences returned by @ref data and @ref prepare
|
||||
will always be of length one.
|
||||
|
||||
@li A configurable maximum buffer size may be set upon
|
||||
construction. Attempts to exceed the buffer size will throw
|
||||
`std::length_error`.
|
||||
|
||||
@note This class is designed for use with algorithms that
|
||||
take dynamic buffers as parameters, and are optimized
|
||||
for the case where the input sequence or output sequence
|
||||
is stored in a single contiguous buffer.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_flat_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:
|
||||
enum
|
||||
{
|
||||
min_size = 512
|
||||
};
|
||||
|
||||
template<class OtherAlloc>
|
||||
friend class basic_flat_streambuf;
|
||||
|
||||
using alloc_traits =
|
||||
std::allocator_traits<allocator_type>;
|
||||
|
||||
static
|
||||
inline
|
||||
std::size_t
|
||||
dist(char const* first, char const* last)
|
||||
{
|
||||
return static_cast<std::size_t>(last - first);
|
||||
}
|
||||
|
||||
char* p_;
|
||||
char* in_;
|
||||
char* out_;
|
||||
char* last_;
|
||||
char* end_;
|
||||
std::size_t max_;
|
||||
|
||||
public:
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = boost::asio::const_buffers_1;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = boost::asio::mutable_buffers_1;
|
||||
|
||||
/// Copy assignment (disallowed).
|
||||
basic_flat_streambuf&
|
||||
operator=(basic_flat_streambuf const&) = delete;
|
||||
|
||||
/// Destructor.
|
||||
~basic_flat_streambuf();
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The new object will have the same input sequence
|
||||
and an empty output sequence.
|
||||
|
||||
@note After the move, the moved-from object will
|
||||
have a capacity of zero, an empty input sequence,
|
||||
and an empty output sequence.
|
||||
*/
|
||||
basic_flat_streambuf(basic_flat_streambuf&&);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The new object will have the same input sequence
|
||||
and an empty output sequence.
|
||||
|
||||
@note After the move, the moved-from object will
|
||||
have a capacity of zero, an empty input sequence,
|
||||
and an empty output sequence.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
basic_flat_streambuf(basic_flat_streambuf&&,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
The new object will have a copy of the input sequence
|
||||
and an empty output sequence.
|
||||
*/
|
||||
basic_flat_streambuf(basic_flat_streambuf const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
The new object will have a copy of the input sequence
|
||||
and an empty output sequence.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
basic_flat_streambuf(basic_flat_streambuf const&,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
The new object will have a copy of the input sequence
|
||||
and an empty output sequence.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_flat_streambuf(
|
||||
basic_flat_streambuf<OtherAlloc> const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
The new object will have a copy of the input sequence
|
||||
and an empty output sequence.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_flat_streambuf(
|
||||
basic_flat_streambuf<OtherAlloc> const&,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Construct a flat stream buffer.
|
||||
|
||||
No allocation is performed; the buffer will have
|
||||
empty input and output sequences.
|
||||
|
||||
@param limit An optional non-zero value specifying the
|
||||
maximum of the sum of the input and output sequence sizes
|
||||
that can be allocated. If unspecified, the largest
|
||||
possible value of `std::size_t` is used.
|
||||
*/
|
||||
explicit
|
||||
basic_flat_streambuf(std::size_t limit = (
|
||||
std::numeric_limits<std::size_t>::max)());
|
||||
|
||||
/** Construct a flat stream buffer.
|
||||
|
||||
No allocation is performed; the buffer will have
|
||||
empty input and output sequences.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
|
||||
@param limit An optional non-zero value specifying the
|
||||
maximum of the sum of the input and output sequence sizes
|
||||
that can be allocated. If unspecified, the largest
|
||||
possible value of `std::size_t` is used.
|
||||
*/
|
||||
basic_flat_streambuf(Allocator const& alloc,
|
||||
std::size_t limit = (
|
||||
std::numeric_limits<std::size_t>::max)());
|
||||
|
||||
/// Returns a copy of the associated allocator.
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return this->member();
|
||||
}
|
||||
|
||||
/// Returns the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return dist(in_, out_);
|
||||
}
|
||||
|
||||
/// Return the maximum sum of the input and output sequence sizes.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return max_;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return dist(p_, end_);
|
||||
}
|
||||
|
||||
/// Get a list of buffers that represent the input sequence.
|
||||
const_buffers_type
|
||||
data() const
|
||||
{
|
||||
return {in_, dist(in_, out_)};
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the output sequence, with the given size.
|
||||
|
||||
@throws std::length_error if `size() + n` exceeds `max_size()`.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@param n The number of bytes to move. If this is larger than
|
||||
the number of bytes in the output sequences, then the entire
|
||||
output sequences is moved.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
out_ += (std::min)(n, dist(out_, last_));
|
||||
}
|
||||
|
||||
/** Remove bytes from the input sequence.
|
||||
|
||||
If `n` is greater than the number of bytes in the input
|
||||
sequence, all bytes in the input sequence are removed.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t n);
|
||||
|
||||
/** Reserve space in the stream.
|
||||
|
||||
This reallocates the buffer if necessary.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
|
||||
@param n The number of bytes to reserve. Upon success,
|
||||
the capacity will be at least `n`.
|
||||
|
||||
@throws std::length_error if `n` exceeds `max_size()`.
|
||||
*/
|
||||
void
|
||||
reserve(std::size_t n);
|
||||
|
||||
/** Reallocate the buffer to fit the input sequence.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
void
|
||||
shrink_to_fit();
|
||||
|
||||
// Helper for boost::asio::read_until
|
||||
template<class OtherAlloc>
|
||||
friend
|
||||
std::size_t
|
||||
read_size_helper(basic_flat_streambuf<
|
||||
OtherAlloc> const&, std::size_t);
|
||||
|
||||
private:
|
||||
void
|
||||
move_from(basic_flat_streambuf& other);
|
||||
|
||||
template<class OtherAlloc>
|
||||
void
|
||||
copy_from(basic_flat_streambuf<
|
||||
OtherAlloc> const& other);
|
||||
};
|
||||
|
||||
using flat_streambuf =
|
||||
basic_flat_streambuf<std::allocator<char>>;
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/flat_streambuf.ipp>
|
||||
|
||||
#endif
|
316
include/beast/core/impl/flat_streambuf.ipp
Normal file
316
include/beast/core/impl/flat_streambuf.ipp
Normal file
@ -0,0 +1,316 @@
|
||||
//
|
||||
// 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_IMPL_FLAT_STREAMBUF_HPP
|
||||
#define BEAST_IMPL_FLAT_STREAMBUF_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/* Memory is laid out thusly:
|
||||
|
||||
p_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_
|
||||
*/
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
next_pow2(std::size_t x)
|
||||
{
|
||||
std::size_t n = 0;
|
||||
while(x > 0)
|
||||
{
|
||||
++n;
|
||||
x >>= 1;
|
||||
}
|
||||
return std::size_t{1} << n;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_flat_streambuf<Allocator>::
|
||||
move_from(basic_flat_streambuf& other)
|
||||
{
|
||||
p_ = other.p_;
|
||||
in_ = other.in_;
|
||||
out_ = other.out_;
|
||||
last_ = out_;
|
||||
end_ = other.end_;
|
||||
max_ = other.max_;
|
||||
other.p_ = nullptr;
|
||||
other.in_ = nullptr;
|
||||
other.out_ = nullptr;
|
||||
other.last_ = nullptr;
|
||||
other.end_ = nullptr;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
void
|
||||
basic_flat_streambuf<Allocator>::
|
||||
copy_from(basic_flat_streambuf<
|
||||
OtherAlloc> const& other)
|
||||
{
|
||||
max_ = other.max_;
|
||||
auto const n = other.size();
|
||||
if(n > 0)
|
||||
{
|
||||
p_ = alloc_traits::allocate(
|
||||
this->member(), n);
|
||||
in_ = p_;
|
||||
out_ = p_ + n;
|
||||
last_ = out_;
|
||||
end_ = out_;
|
||||
std::memcpy(in_, other.in_, n);
|
||||
return;
|
||||
}
|
||||
p_ = nullptr;
|
||||
in_ = nullptr;
|
||||
out_ = nullptr;
|
||||
last_ = nullptr;
|
||||
end_ = nullptr;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_streambuf<Allocator>::
|
||||
~basic_flat_streambuf()
|
||||
{
|
||||
if(p_)
|
||||
alloc_traits::deallocate(
|
||||
this->member(), p_, dist(p_, end_));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_streambuf<Allocator>::
|
||||
basic_flat_streambuf(basic_flat_streambuf&& other)
|
||||
: detail::empty_base_optimization<
|
||||
allocator_type>(std::move(other.member()))
|
||||
{
|
||||
move_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_streambuf<Allocator>::
|
||||
basic_flat_streambuf(basic_flat_streambuf&& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<
|
||||
allocator_type>(alloc)
|
||||
{
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
copy_from(other);
|
||||
return;
|
||||
}
|
||||
move_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_streambuf<Allocator>::
|
||||
basic_flat_streambuf(
|
||||
basic_flat_streambuf const& other)
|
||||
: detail::empty_base_optimization<allocator_type>(
|
||||
alloc_traits::select_on_container_copy_construction(
|
||||
other.member()))
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_streambuf<Allocator>::
|
||||
basic_flat_streambuf(
|
||||
basic_flat_streambuf const& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<
|
||||
allocator_type>(alloc)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_flat_streambuf<Allocator>::
|
||||
basic_flat_streambuf(
|
||||
basic_flat_streambuf<OtherAlloc> const& other)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_flat_streambuf<Allocator>::
|
||||
basic_flat_streambuf(
|
||||
basic_flat_streambuf<OtherAlloc> const& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<
|
||||
allocator_type>(alloc)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_streambuf<Allocator>::
|
||||
basic_flat_streambuf(std::size_t limit)
|
||||
: p_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(limit)
|
||||
{
|
||||
BOOST_ASSERT(limit >= 1);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_streambuf<Allocator>::
|
||||
basic_flat_streambuf(Allocator const& alloc,
|
||||
std::size_t limit)
|
||||
: detail::empty_base_optimization<
|
||||
allocator_type>(alloc)
|
||||
, p_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(limit)
|
||||
{
|
||||
BOOST_ASSERT(limit >= 1);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_flat_streambuf<Allocator>::
|
||||
prepare(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
if(n <= dist(out_, end_))
|
||||
{
|
||||
last_ = out_ + n;
|
||||
return{out_, n};
|
||||
}
|
||||
auto const len = size();
|
||||
if(n <= dist(p_, end_) - len)
|
||||
{
|
||||
if(len > 0)
|
||||
std::memmove(p_, in_, len);
|
||||
in_ = p_;
|
||||
out_ = in_ + len;
|
||||
last_ = out_ + n;
|
||||
return {out_, n};
|
||||
}
|
||||
if(n > max_ - len)
|
||||
throw std::length_error{
|
||||
"flat_streambuf overflow"};
|
||||
auto const new_size = (std::min)(max_,
|
||||
std::max<std::size_t>(
|
||||
detail::next_pow2(len + n), min_size));
|
||||
auto const p = alloc_traits::allocate(
|
||||
this->member(), new_size);
|
||||
std::memcpy(p, in_, len);
|
||||
alloc_traits::deallocate(
|
||||
this->member(), p_, dist(p_, end_));
|
||||
p_ = p;
|
||||
in_ = p_;
|
||||
out_ = in_ + len;
|
||||
last_ = out_ + n;
|
||||
end_ = p_ + new_size;
|
||||
return {out_, n};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_flat_streambuf<Allocator>::
|
||||
consume(std::size_t n)
|
||||
{
|
||||
if(n >= dist(in_, out_))
|
||||
{
|
||||
in_ = p_;
|
||||
out_ = p_;
|
||||
return;
|
||||
}
|
||||
in_ += n;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_flat_streambuf<Allocator>::
|
||||
reserve(std::size_t n)
|
||||
{
|
||||
if(n <= dist(p_, end_))
|
||||
return;
|
||||
if(n > max_)
|
||||
throw std::length_error{
|
||||
"flat_streambuf overflow"};
|
||||
auto const new_size = (std::min)(max_,
|
||||
std::max<std::size_t>(
|
||||
detail::next_pow2(n), min_size));
|
||||
auto const p = alloc_traits::allocate(
|
||||
this->member(), new_size);
|
||||
auto const len = size();
|
||||
if(len > 0)
|
||||
std::memcpy(p, in_, len);
|
||||
alloc_traits::deallocate(
|
||||
this->member(), p_, dist(p_, end_));
|
||||
p_ = p;
|
||||
in_ = p_;
|
||||
out_ = p_ + len;
|
||||
last_ = out_;
|
||||
end_ = p_ + new_size;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_flat_streambuf<Allocator>::
|
||||
shrink_to_fit()
|
||||
{
|
||||
auto const len = size();
|
||||
if(len == dist(p_, end_))
|
||||
return;
|
||||
char* p;
|
||||
if(len > 0)
|
||||
{
|
||||
p = alloc_traits::allocate(
|
||||
this->member(), len);
|
||||
std::memcpy(p, in_, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = nullptr;
|
||||
}
|
||||
alloc_traits::deallocate(
|
||||
this->member(), p_, dist(p_, end_));
|
||||
p_ = p;
|
||||
in_ = p_;
|
||||
out_ = p_ + len;
|
||||
last_ = out_;
|
||||
end_ = out_;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
read_size_helper(basic_flat_streambuf<
|
||||
Allocator> const& fb, std::size_t max_size)
|
||||
{
|
||||
BOOST_ASSERT(max_size >= 1);
|
||||
auto const len = fb.size();
|
||||
auto const avail = fb.capacity() - len;
|
||||
if (avail > 0)
|
||||
return (std::min)(avail, max_size);
|
||||
auto size = (std::min)(
|
||||
fb.capacity() * 2, fb.max_size()) - len;
|
||||
if(size == 0)
|
||||
size = 1;
|
||||
return (std::min)(size, max_size);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
@ -25,6 +25,7 @@ unit-test core-tests :
|
||||
core/consuming_buffers.cpp
|
||||
core/dynabuf_readstream.cpp
|
||||
core/error.cpp
|
||||
core/flat_streambuf.cpp
|
||||
core/handler_alloc.cpp
|
||||
core/handler_concepts.cpp
|
||||
core/handler_ptr.cpp
|
||||
|
@ -18,6 +18,7 @@ add_executable (core-tests
|
||||
consuming_buffers.cpp
|
||||
dynabuf_readstream.cpp
|
||||
error.cpp
|
||||
flat_streambuf.cpp
|
||||
handler_alloc.cpp
|
||||
handler_concepts.cpp
|
||||
handler_ptr.cpp
|
||||
|
171
test/core/flat_streambuf.cpp
Normal file
171
test/core/flat_streambuf.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
//
|
||||
// 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/flat_streambuf.hpp>
|
||||
|
||||
#include "buffer_test.hpp"
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
static_assert(is_DynamicBuffer<flat_streambuf>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
|
||||
class flat_streambuf_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
template<class Alloc1, class Alloc2>
|
||||
static
|
||||
bool
|
||||
eq(basic_flat_streambuf<Alloc1> const& sb1,
|
||||
basic_flat_streambuf<Alloc2> const& sb2)
|
||||
{
|
||||
return to_string(sb1.data()) == to_string(sb2.data());
|
||||
}
|
||||
|
||||
void
|
||||
testSpecialMembers()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
{
|
||||
flat_streambuf fb{10};
|
||||
BEAST_EXPECT(fb.max_size() == 10);
|
||||
}
|
||||
{
|
||||
flat_streambuf fb{1024};
|
||||
BEAST_EXPECT(fb.max_size() == 1024);
|
||||
}
|
||||
std::string const s = "Hello, world!";
|
||||
for(std::size_t i = 1; i < s.size() - 1; ++i)
|
||||
{
|
||||
flat_streambuf fb{1024};
|
||||
fb.commit(buffer_copy(
|
||||
fb.prepare(i), buffer(s)));
|
||||
fb.commit(buffer_copy(
|
||||
fb.prepare(s.size() - i),
|
||||
buffer(s.data() + i, s.size() - i)));
|
||||
BEAST_EXPECT(to_string(fb.data()) == s);
|
||||
{
|
||||
flat_streambuf fb2{fb};
|
||||
BEAST_EXPECT(eq(fb2, fb));
|
||||
flat_streambuf fb3{std::move(fb2)};
|
||||
BEAST_EXPECT(eq(fb3, fb));
|
||||
BEAST_EXPECT(! eq(fb2, fb3));
|
||||
BEAST_EXPECT(fb2.size() == 0);
|
||||
}
|
||||
|
||||
using alloc_type = std::allocator<double>;
|
||||
using type =
|
||||
basic_flat_streambuf<alloc_type>;
|
||||
alloc_type alloc;
|
||||
{
|
||||
type fba{alloc, 1};
|
||||
BEAST_EXPECT(fba.max_size() == 1);
|
||||
}
|
||||
{
|
||||
type fba{alloc, 1024};
|
||||
BEAST_EXPECT(fba.max_size() == 1024);
|
||||
}
|
||||
{
|
||||
type fb2{fb};
|
||||
BEAST_EXPECT(eq(fb2, fb));
|
||||
type fb3{std::move(fb2)};
|
||||
BEAST_EXPECT(eq(fb3, fb));
|
||||
BEAST_EXPECT(! eq(fb2, fb3));
|
||||
BEAST_EXPECT(fb2.size() == 0);
|
||||
}
|
||||
{
|
||||
type fb2{fb, alloc};
|
||||
BEAST_EXPECT(eq(fb2, fb));
|
||||
type fb3{std::move(fb2), alloc};
|
||||
BEAST_EXPECT(eq(fb3, fb));
|
||||
BEAST_EXPECT(! eq(fb2, fb3));
|
||||
BEAST_EXPECT(fb2.size() == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testStream()
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
|
||||
flat_streambuf fb{100};
|
||||
BEAST_EXPECT(fb.size() == 0);
|
||||
BEAST_EXPECT(fb.capacity() == 0);
|
||||
|
||||
BEAST_EXPECT(buffer_size(fb.prepare(100)) == 100);
|
||||
BEAST_EXPECT(fb.size() == 0);
|
||||
BEAST_EXPECT(fb.capacity() > 0);
|
||||
|
||||
fb.commit(20);
|
||||
BEAST_EXPECT(fb.size() == 20);
|
||||
BEAST_EXPECT(fb.capacity() == 100);
|
||||
|
||||
fb.consume(5);
|
||||
BEAST_EXPECT(fb.size() == 15);
|
||||
BEAST_EXPECT(fb.capacity() == 100);
|
||||
|
||||
fb.prepare(80);
|
||||
fb.commit(80);
|
||||
BEAST_EXPECT(fb.size() == 95);
|
||||
BEAST_EXPECT(fb.capacity() == 100);
|
||||
|
||||
fb.shrink_to_fit();
|
||||
BEAST_EXPECT(fb.size() == 95);
|
||||
BEAST_EXPECT(fb.capacity() == 95);
|
||||
}
|
||||
|
||||
void
|
||||
testPrepare()
|
||||
{
|
||||
flat_streambuf fb{100};
|
||||
fb.prepare(20);
|
||||
BEAST_EXPECT(fb.capacity() == 100);
|
||||
fb.commit(10);
|
||||
BEAST_EXPECT(fb.capacity() == 100);
|
||||
fb.consume(4);
|
||||
BEAST_EXPECT(fb.capacity() == 100);
|
||||
fb.prepare(14);
|
||||
BEAST_EXPECT(fb.size() == 6);
|
||||
BEAST_EXPECT(fb.capacity() == 100);
|
||||
fb.consume(10);
|
||||
BEAST_EXPECT(fb.size() == 0);
|
||||
BEAST_EXPECT(fb.capacity() == 100);
|
||||
}
|
||||
|
||||
void
|
||||
testMax()
|
||||
{
|
||||
flat_streambuf fb{1};
|
||||
try
|
||||
{
|
||||
fb.prepare(2);
|
||||
fail("", __FILE__, __LINE__);
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testSpecialMembers();
|
||||
testStream();
|
||||
testPrepare();
|
||||
testMax();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(flat_streambuf,core,beast);
|
||||
|
||||
} // beast
|
@ -172,8 +172,6 @@ public:
|
||||
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) {
|
||||
@ -263,7 +261,6 @@ public:
|
||||
|
||||
void testCommit()
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
streambuf sb(2);
|
||||
sb.prepare(2);
|
||||
sb.prepare(5);
|
||||
@ -273,7 +270,6 @@ public:
|
||||
|
||||
void testConsume()
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
streambuf sb(1);
|
||||
expect_size(5, sb.prepare(5));
|
||||
sb.commit(3);
|
||||
@ -285,7 +281,6 @@ public:
|
||||
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);
|
||||
|
Reference in New Issue
Block a user