Reorganize source files

This commit is contained in:
Vinnie Falco
2016-04-19 20:29:16 -04:00
parent 54f6f0ceba
commit f07cd8ceb4
239 changed files with 2692 additions and 19127 deletions

View File

@@ -0,0 +1,835 @@
//
// 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_BASIC_STREAMBUF_IPP
#define BEAST_IMPL_BASIC_STREAMBUF_IPP
#include <algorithm>
#include <cassert>
#include <exception>
#include <sstream>
#include <string>
#include <utility>
namespace beast {
/* These diagrams illustrate the layout and state variables.
Input and output contained entirely in one element:
0 out_
|<-------------+------------------------------------------->|
in_pos_ out_pos_ out_end_
Output contained in first and second elements:
out_
|<------+----------+------->| |<----------+-------------->|
in_pos_ out_pos_ out_end_
Output contained in the second element:
out_
|<------------+------------>| |<----+-------------------->|
in_pos_ out_pos_ out_end_
Output contained in second and third elements:
out_
|<-----+-------->| |<-------+------>| |<--------------->|
in_pos_ out_pos_ out_end_
Input sequence is empty:
out_
|<------+------------------>| |<-----------+------------->|
out_pos_ out_end_
in_pos_
Output sequence is empty:
out_
|<------+------------------>| |<------+------------------>|
in_pos_ out_pos_
out_end_
The end of output can point to the end of an element.
But out_pos_ should never point to the end:
out_
|<------+------------------>| |<------+------------------>|
in_pos_ out_pos_ out_end_
When the input sequence entirely fills the last element and
the output sequence is empty, out_ will point to the end of
the list of buffers, and out_pos_ and out_end_ will be 0:
|<------+------------------>| out_ == list_.end()
in_pos_ out_pos_ == 0
out_end_ == 0
*/
template<class Allocator>
class basic_streambuf<Allocator>::element
: public boost::intrusive::list_base_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>
{
using size_type = typename std::allocator_traits<Allocator>::size_type;
size_type const size_;
public:
element(element const&) = delete;
element& operator=(element const&) = delete;
explicit
element(size_type n)
: size_(n)
{
}
size_type
size() const
{
return size_;
}
char*
data() const
{
return const_cast<char*>(
reinterpret_cast<char const*>(this+1));
}
};
//------------------------------------------------------------------------------
template<class Allocator>
class basic_streambuf<Allocator>::const_buffers_type::const_iterator
{
basic_streambuf const* sb_ = nullptr;
typename list_type::const_iterator it_;
public:
using value_type =
typename const_buffers_type::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
const_iterator(basic_streambuf const& sb,
typename list_type::const_iterator const& it)
: sb_(&sb)
, it_(it)
{
}
bool
operator==(const_iterator const& other) const
{
return sb_ == other.sb_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
auto const& e = *it_;
return value_type{e.data(),
(sb_->out_ == sb_->list_.end() ||
&e != &*sb_->out_) ? e.size() : sb_->out_pos_} +
(&e == &*sb_->list_.begin() ? sb_->in_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
};
template<class Allocator>
basic_streambuf<Allocator>::const_buffers_type::const_buffers_type(
basic_streambuf const& sb)
: sb_(&sb)
{
}
template<class Allocator>
auto
basic_streambuf<Allocator>::const_buffers_type::begin() const ->
const_iterator
{
return const_iterator{*sb_, sb_->list_.begin()};
}
template<class Allocator>
auto
basic_streambuf<Allocator>::const_buffers_type::end() const ->
const_iterator
{
return const_iterator{*sb_, sb_->out_ ==
sb_->list_.end() ? sb_->list_.end() :
std::next(sb_->out_)};
}
//------------------------------------------------------------------------------
template<class Allocator>
class basic_streambuf<Allocator>::mutable_buffers_type::const_iterator
{
basic_streambuf const* sb_ = nullptr;
typename list_type::const_iterator it_;
public:
using value_type =
typename mutable_buffers_type::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
const_iterator(basic_streambuf const& sb,
typename list_type::const_iterator const& it)
: sb_(&sb)
, it_(it)
{
}
bool
operator==(const_iterator const& other) const
{
return sb_ == other.sb_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
auto const& e = *it_;
return value_type{e.data(),
&e == &*std::prev(sb_->list_.end()) ?
sb_->out_end_ : e.size()} +
(&e == &*sb_->out_ ? sb_->out_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
};
template<class Allocator>
basic_streambuf<Allocator>::mutable_buffers_type::mutable_buffers_type(
basic_streambuf const& sb)
: sb_(&sb)
{
}
template<class Allocator>
auto
basic_streambuf<Allocator>::mutable_buffers_type::begin() const ->
const_iterator
{
return const_iterator{*sb_, sb_->out_};
}
template<class Allocator>
auto
basic_streambuf<Allocator>::mutable_buffers_type::end() const ->
const_iterator
{
return const_iterator{*sb_, sb_->list_.end()};
}
//------------------------------------------------------------------------------
template<class Allocator>
basic_streambuf<Allocator>::~basic_streambuf()
{
delete_list();
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf&& other)
: detail::empty_base_optimization<allocator_type>(
std::move(other.member()))
, alloc_size_(other.alloc_size_)
, in_size_(other.in_size_)
, in_pos_(other.in_pos_)
, out_pos_(other.out_pos_)
, out_end_(other.out_end_)
{
auto const at_end =
other.out_ == other.list_.end();
list_ = std::move(other.list_);
out_ = at_end ? list_.end() : other.out_;
other.in_size_ = 0;
other.out_ = other.list_.end();
other.in_pos_ = 0;
other.out_pos_ = 0;
other.out_end_ = 0;
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf&& other,
allocator_type const& alloc)
: basic_streambuf(other.alloc_size_, alloc)
{
using boost::asio::buffer_copy;
if(this->member() != other.member())
commit(buffer_copy(prepare(other.size()), other.data()));
else
move_assign(other, std::true_type{});
}
template<class Allocator>
auto
basic_streambuf<Allocator>::operator=(
basic_streambuf&& other) -> basic_streambuf&
{
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_streambuf<Allocator>::
basic_streambuf(basic_streambuf const& other)
: basic_streambuf(other.alloc_size_,
alloc_traits::select_on_container_copy_construction(other.member()))
{
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf const& other,
allocator_type const& alloc)
: basic_streambuf(other.alloc_size_, alloc)
{
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
auto
basic_streambuf<Allocator>::operator=(
basic_streambuf const& other) ->
basic_streambuf&
{
if(this == &other)
return *this;
using boost::asio::buffer_copy;
clear();
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_streambuf<Allocator>::basic_streambuf(
basic_streambuf<OtherAlloc> const& other)
: basic_streambuf(other.alloc_size_)
{
using boost::asio::buffer_copy;
commit(buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
template<class OtherAlloc>
basic_streambuf<Allocator>::basic_streambuf(
basic_streambuf<OtherAlloc> const& other,
allocator_type const& alloc)
: basic_streambuf(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_streambuf<Allocator>::operator=(
basic_streambuf<OtherAlloc> const& other) ->
basic_streambuf&
{
using boost::asio::buffer_copy;
clear();
commit(buffer_copy(prepare(other.size()), other.data()));
return *this;
}
template<class Allocator>
basic_streambuf<Allocator>::basic_streambuf(
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)
throw std::invalid_argument(
"basic_streambuf: invalid alloc_size");
}
template<class Allocator>
auto
basic_streambuf<Allocator>::prepare(size_type n) ->
mutable_buffers_type
{
iterator pos = out_;
if(pos != list_.end())
{
auto const avail = pos->size() - out_pos_;
if(n > avail)
{
n -= avail;
out_end_ = pos->size();
while(++pos != list_.end())
{
if(n < pos->size())
{
out_end_ = n;
n = 0;
++pos;
break;
}
n -= pos->size();
}
}
else
{
++pos;
out_end_ = out_pos_ + n;
n = 0;
}
}
if(n > 0)
{
assert(pos == list_.end());
for(;;)
{
auto const size = std::max(alloc_size_, n);
auto& e = *reinterpret_cast<element*>(
alloc_traits::allocate(this->member(),
size + sizeof(element)));
alloc_traits::construct(this->member(), &e, size);
list_.push_back(e);
if(out_ == list_.end())
{
out_ = list_.iterator_to(e);
debug_check();
}
if(n <= size)
{
out_end_ = n;
debug_check();
break;
}
n -= size;
debug_check();
}
}
else
{
while(pos != list_.end())
{
auto& e = *pos++;
list_.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<std::uint8_t*>(&e), len);
}
debug_check();
}
return mutable_buffers_type(*this);
}
template<class Allocator>
void
basic_streambuf<Allocator>::commit(size_type n)
{
if(list_.empty())
return;
if(out_ == list_.end())
return;
auto const last = std::prev(list_.end());
while(out_ != last)
{
auto const avail =
out_->size() - out_pos_;
if(n < avail)
{
out_pos_ += n;
in_size_ += n;
debug_check();
return;
}
++out_;
n -= avail;
out_pos_ = 0;
in_size_ += avail;
debug_check();
}
n = std::min(n, out_end_ - out_pos_);
out_pos_ += n;
in_size_ += n;
if(out_pos_ == out_->size())
{
++out_;
out_pos_ = 0;
out_end_ = 0;
}
debug_check();
}
template<class Allocator>
auto
basic_streambuf<Allocator>::data() const ->
const_buffers_type
{
return const_buffers_type(*this);
}
template<class Allocator>
void
basic_streambuf<Allocator>::consume(size_type n)
{
if(list_.empty())
return;
auto pos = list_.begin();
for(;;)
{
if(pos != out_)
{
auto const avail = pos->size() - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
debug_check();
break;
}
n -= avail;
in_size_ -= avail;
in_pos_ = 0;
debug_check();
element& e = *pos++;
list_.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<std::uint8_t*>(&e), len);
}
else
{
auto const avail = out_pos_ - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
}
else
{
in_size_ -= avail;
if(out_pos_ != out_end_||
out_ != list_.iterator_to(list_.back()))
{
in_pos_ = out_pos_;
}
else
{
// Use the whole buffer now.
// Alternatively we could deallocate it.
in_pos_ = 0;
out_pos_ = 0;
out_end_ = 0;
}
}
debug_check();
break;
}
}
}
template<class Allocator>
void
basic_streambuf<Allocator>::clear()
{
delete_list();
list_.clear();
out_ = list_.begin();
in_size_ = 0;
in_pos_ = 0;
out_pos_ = 0;
out_end_ = 0;
}
template<class Allocator>
void
basic_streambuf<Allocator>::
move_assign(basic_streambuf& other, std::false_type)
{
using boost::asio::buffer_copy;
if(this->member() != other.member())
{
commit(buffer_copy(prepare(other.size()), other.data()));
other.clear();
}
else
move_assign(other, std::true_type{});
}
template<class Allocator>
void
basic_streambuf<Allocator>::
move_assign(basic_streambuf& other, std::true_type)
{
this->member() = std::move(other.member());
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>
void
basic_streambuf<Allocator>::
copy_assign(basic_streambuf const& other, std::false_type)
{
}
template<class Allocator>
void
basic_streambuf<Allocator>::
copy_assign(basic_streambuf const& other, std::true_type)
{
this->member() = other.member();
}
template<class Allocator>
void
basic_streambuf<Allocator>::delete_list()
{
for(auto iter = list_.begin(); iter != list_.end();)
{
auto& e = *iter++;
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);
}
}
// Returns the number of bytes which can be
// prepared without causing a memory allocation.
template<class Allocator>
std::size_t
basic_streambuf<Allocator>::prepare_size() const
{
auto pos = out_;
if(pos == list_.end())
return 0;
auto n = pos->size() - out_pos_;
while(++pos != list_.end())
n += pos->size();
return n;
}
template<class Allocator>
void
basic_streambuf<Allocator>::debug_check() const
{
#ifndef NDEBUG
if(list_.empty())
{
assert(in_pos_ == 0);
assert(in_size_ == 0);
assert(out_pos_ == 0);
assert(out_end_ == 0);
assert(out_ == list_.end());
return;
}
auto const& front = list_.front();
assert(in_pos_ < front.size());
if(out_ == list_.end())
{
assert(out_pos_ == 0);
assert(out_end_ == 0);
}
else
{
auto const& out = *out_;
auto const& back = list_.back();
assert(out_end_ <= back.size());
assert(out_pos_ < out.size());
assert(&out != &front || out_pos_ >= in_pos_);
assert(&out != &front || out_pos_ - in_pos_ == in_size_);
assert(&out != &back || out_pos_ <= out_end_);
}
#endif
}
template<class Alloc, class T>
basic_streambuf<Alloc>&
operator<<(basic_streambuf<Alloc>& buf, T const& t)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
std::stringstream ss;
ss << t;
auto const& s = ss.str();
buf.commit(buffer_copy(buf.prepare(s.size()),
boost::asio::buffer(s)));
return buf;
}
//------------------------------------------------------------------------------
template<class Allocator>
std::size_t
read_size_helper(basic_streambuf<
Allocator> const& streambuf, std::size_t max_size)
{
return std::min<std::size_t>(max_size,
std::max<std::size_t>(512, streambuf.prepare_size()));
}
template<class Allocator>
std::string
to_string(basic_streambuf<Allocator> const& streambuf)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
std::string s;
s.resize(streambuf.size());
buffer_copy(
buffer(&s[0], s.size()), streambuf.data());
return s;
}
} // beast
#endif

View File

@@ -0,0 +1,253 @@
//
// 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_STREAMBUF_READSTREAM_IPP
#define BEAST_IMPL_STREAMBUF_READSTREAM_IPP
#include <beast/async_completion.hpp>
#include <beast/bind_handler.hpp>
#include <beast/handler_alloc.hpp>
#include <beast/type_check.hpp>
namespace beast {
template<class Stream, class Streambuf>
template<class MutableBufferSequence, class Handler>
class streambuf_readstream<
Stream, Streambuf>::read_some_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
streambuf_readstream& brs;
MutableBufferSequence bs;
Handler h;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_,
streambuf_readstream& brs_,
MutableBufferSequence const& bs_)
: brs(brs_)
, bs(bs_)
, h(std::forward<DeducedHandler>(h_))
{
}
};
std::shared_ptr<data> d_;
public:
read_some_op(read_some_op&&) = default;
read_some_op(read_some_op const&) = default;
template<class DeducedHandler, class... Args>
read_some_op(DeducedHandler&& h, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h),
std::forward<Args>(args)...))
{
(*this)(error_code{}, 0);
}
void
operator()(error_code const& ec,
std::size_t bytes_transferred);
friend
auto asio_handler_allocate(
std::size_t size, read_some_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, read_some_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(read_some_op* op)
{
return boost_asio_handler_cont_helpers::
is_continuation(op->d_->h);
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, read_some_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class Stream, class Streambuf>
template<class MutableBufferSequence, class Handler>
void
streambuf_readstream<Stream, Streambuf>::
read_some_op<MutableBufferSequence, Handler>::operator()(
error_code const& ec, std::size_t bytes_transferred)
{
auto& d = *d_;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
if(d.brs.sb_.size() == 0)
{
d.state =
d.brs.size_ > 0 ? 2 : 1;
break;
}
d.state = 4;
d.brs.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
return;
case 1:
// read (unbuffered)
d.state = 99;
d.brs.next_layer_.async_read_some(
d.bs, std::move(*this));
return;
case 2:
// read
d.state = 3;
d.brs.next_layer_.async_read_some(
d.brs.sb_.prepare(d.brs.size_),
std::move(*this));
return;
// got data
case 3:
d.state = 4;
d.brs.sb_.commit(bytes_transferred);
break;
// copy
case 4:
bytes_transferred =
boost::asio::buffer_copy(
d.bs, d.brs.sb_.data());
d.brs.sb_.consume(bytes_transferred);
// call handler
d.state = 99;
break;
}
}
d.h(ec, bytes_transferred);
}
//------------------------------------------------------------------------------
template<class Stream, class Streambuf>
template<class... Args>
streambuf_readstream<Stream, Streambuf>::
streambuf_readstream(Args&&... args)
: next_layer_(std::forward<Args>(args)...)
{
static_assert(is_Stream<next_layer_type>::value,
"Stream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
}
template<class Stream, class Streambuf>
template<class ConstBufferSequence, class WriteHandler>
auto
streambuf_readstream<Stream, Streambuf>::
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
static_assert(is_Handler<WriteHandler,
void(error_code, std::size_t)>::value,
"WriteHandler requirements not met");
return next_layer_.async_write_some(buffers,
std::forward<WriteHandler>(handler));
}
template<class Stream, class Streambuf>
template<class MutableBufferSequence>
std::size_t
streambuf_readstream<Stream, Streambuf>::
read_some(
MutableBufferSequence const& buffers)
{
static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
error_code ec;
auto n = read_some(buffers, ec);
if(ec)
throw boost::system::system_error{ec};
return n;
}
template<class Stream, class Streambuf>
template<class MutableBufferSequence>
std::size_t
streambuf_readstream<Stream, Streambuf>::
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
using boost::asio::buffer_size;
using boost::asio::buffer_copy;
if(buffer_size(buffers) == 0)
return 0;
if(size_ == 0)
return next_layer_.read_some(buffers, ec);
if(sb_.size() == 0)
{
sb_.commit(next_layer_.read_some(
sb_.prepare(size_), ec));
if(ec)
return 0;
}
auto bytes_transferred =
buffer_copy(buffers, sb_.data());
sb_.consume(bytes_transferred);
return bytes_transferred;
}
template<class Stream, class Streambuf>
template<class MutableBufferSequence, class ReadHandler>
auto
streambuf_readstream<Stream, Streambuf>::
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
beast::async_completion<
ReadHandler, void(error_code, std::size_t)
> completion(handler);
read_some_op<MutableBufferSequence,
decltype(completion.handler)>{
completion.handler, *this, buffers};
return completion.result.get();
}
} // beast
#endif