diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index 0a749badf..e29fcb22e 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -153,6 +153,9 @@ True + + True + diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index 9f47c1f21..0c505c235 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -615,6 +615,9 @@ beast\asio\tests + + beast\asio\tests + beast diff --git a/src/beast/beast/asio/Asio.unity.cpp b/src/beast/beast/asio/Asio.unity.cpp index ed38fcdd1..fbc81d76f 100644 --- a/src/beast/beast/asio/Asio.unity.cpp +++ b/src/beast/beast/asio/Asio.unity.cpp @@ -23,4 +23,5 @@ #include #include +#include diff --git a/src/beast/beast/asio/streambuf.h b/src/beast/beast/asio/streambuf.h index 66930661e..2e4a56c9d 100644 --- a/src/beast/beast/asio/streambuf.h +++ b/src/beast/beast/asio/streambuf.h @@ -20,9 +20,11 @@ #ifndef BEAST_ASIO_STREAMBUF_H_INCLUDED #define BEAST_ASIO_STREAMBUF_H_INCLUDED +#include #include #include #include +#include #include #include #include @@ -36,6 +38,7 @@ namespace asio { /** Implements asio::streambuf interface using multiple buffers. */ template class basic_streambuf + : private empty_base_optimization { public: using size_type = typename std::allocator_traits::size_type; @@ -53,25 +56,32 @@ private: /* These diagrams illustrate the layout and state variables. - Input and output sequences are contained entirely in one element: + Input and output contained entirely in one element: - out_ - |<-----+----------+-------------+-------->| - 0 in_pos_ out_pos_ out_end_ + 0 out_ + |<-------------+------------------------------------------->| + in_pos_ out_pos_ out_end_ - Output sequence is entirely contained in the second element: + Output contained in first and second elements: + + out_ + |<------+----------+------->| |<----------+-------------->| + in_pos_ out_pos_ out_end_ + + + Output contained in the second element: out_ - |<------------+------------>| |<----+----------+--------->| - 0 in_pos_ out_pos_ out_end_ + |<------------+------------>| |<----+-------------------->| + in_pos_ out_pos_ out_end_ - Output sequence occupies the second and third elements: + Output contained in second and third elements: out_ - |<-----+-------->| |<-------+------>| |<-----+--------->| - 0 in_pos_ out_pos_ out_end_ + |<-----+-------->| |<-------+------>| |<--------------->| + in_pos_ out_pos_ out_end_ Input sequence is empty: @@ -90,20 +100,25 @@ private: out_end_ - Normally if the output is empty but there is an element in out_, - out_pos_ and out_end_ will be set to zero. Except after comitting - everything, and causing the output sequence to start at the - last element. In this case out_pos_ becomes out_->size(), - and the result looks like this: + 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_ + 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 */ list_type list_; - Allocator alloc_; size_type block_size_; size_type block_size_next_; size_type in_size_ = 0; // size of the input sequence @@ -195,15 +210,10 @@ public: } char* - data() - { - return reinterpret_cast(this+1); - } - - char const* data() const { - return reinterpret_cast(this+1); + return const_cast( + reinterpret_cast(this+1)); } }; @@ -225,21 +235,20 @@ private: transform() : buffers_ (nullptr) - { } + { + } explicit transform (const_buffers_type const& buffers) - : buffers_(&buffers) - { } + : buffers_ (&buffers) + { + } - value_type + value_type const operator() (element const& e) const; }; - typename list_type::const_iterator begin_; - typename list_type::const_iterator end_; - size_type in_pos_ = 0; - size_type out_pos_ = 0; + basic_streambuf const* streambuf_ = nullptr; public: using const_iterator = boost::transform_iterator< @@ -249,42 +258,44 @@ public: const_iterator begin() const { - return const_iterator(begin_,transform(*this)); + return const_iterator (streambuf_->list_.begin(), + transform(*this)); } const_iterator end() const { - return const_iterator(end_,transform(*this)); + return const_iterator (streambuf_->out_ == + streambuf_->list_.end() ? streambuf_->list_.end() : + std::next(streambuf_->out_), transform(*this)); } private: friend class basic_streambuf; - const_buffers_type (typename list_type::const_iterator first, - typename list_type::const_iterator last, size_type in_pos, - size_type out_pos); + + explicit + const_buffers_type (basic_streambuf const& streambuf); }; template basic_streambuf::const_buffers_type::const_buffers_type ( - typename list_type::const_iterator first, - typename list_type::const_iterator last, size_type in_pos, - size_type out_pos) - : begin_(first) - , end_(last) - , in_pos_(in_pos) - , out_pos_(out_pos) - { } + basic_streambuf const& streambuf) + : streambuf_ (&streambuf) +{ +} template auto basic_streambuf::const_buffers_type:: transform::operator() (element const& e) const -> - value_type + value_type const { - return value_type(e.data(), - (&e == &*std::prev(buffers_->end_)) ? buffers_->out_pos_ : e.size()) + - ((&e == &*buffers_->begin_) ? buffers_->in_pos_ : 0); + basic_streambuf const& streambuf = *buffers_->streambuf_; + return value_type (e.data(), + (streambuf.out_ == streambuf.list_.end() || + &e != &*streambuf.out_) ? e.size() : streambuf.out_pos_) + + (&e == &*streambuf.list_.begin() ? + streambuf.in_pos_ : 0); } //------------------------------------------------------------------------------ @@ -305,66 +316,62 @@ private: transform() : buffers_ (nullptr) - { } + { + } explicit transform (mutable_buffers_type const& buffers) - : buffers_(&buffers) - { } + : buffers_ (&buffers) + { + } - value_type - operator() (element& e) const; + value_type const + operator() (element const& e) const; }; - typename list_type::iterator begin_; - typename list_type::iterator end_; - size_type out_pos_ = 0; - size_type out_end_ = 0; + basic_streambuf const* streambuf_; public: using const_iterator = boost::transform_iterator< - transform, typename list_type::iterator, + transform, typename list_type::const_iterator, value_type, value_type>; const_iterator begin() const { - return const_iterator(begin_,transform(*this)); + return const_iterator (streambuf_->out_, + transform(*this)); } const_iterator end() const { - return const_iterator(end_,transform(*this)); + return const_iterator (streambuf_->list_.end(), + transform(*this)); } private: friend class basic_streambuf; - mutable_buffers_type (typename list_type::iterator first, - typename list_type::iterator last, size_type out_pos, - size_type out_end); + mutable_buffers_type (basic_streambuf const& streambuf); }; template basic_streambuf::mutable_buffers_type::mutable_buffers_type ( - typename list_type::iterator first, - typename list_type::iterator last, size_type out_pos, - size_type out_end) - : begin_(first) - , end_(last) - , out_pos_(out_pos) - , out_end_(out_end) - { } + basic_streambuf const& streambuf) + : streambuf_ (&streambuf) +{ +} template auto basic_streambuf::mutable_buffers_type:: - transform::operator() (element& e) const -> - value_type + transform::operator() (element const& e) const -> + value_type const { - return value_type(e.data(), - (&e == &*std::prev(buffers_->end_)) ? buffers_->out_end_ : e.size()) + - ((&e == &*buffers_->begin_) ? buffers_->out_pos_ : 0); + basic_streambuf const& streambuf = *buffers_->streambuf_; + return value_type (e.data(), &e == &*std::prev(streambuf.list_.end()) ? + streambuf.out_end_ : e.size()) + (&e == &*streambuf.out_ ? + streambuf.out_pos_ : 0); } //------------------------------------------------------------------------------ @@ -376,8 +383,8 @@ basic_streambuf::~basic_streambuf() { auto& e = *iter++; size_type const n = e.alloc_size(); - alloc_traits::destroy(alloc_, &e); - alloc_traits::deallocate(alloc_, + alloc_traits::destroy(this->member(), &e); + alloc_traits::deallocate(this->member(), reinterpret_cast(&e), n); } } @@ -385,10 +392,10 @@ basic_streambuf::~basic_streambuf() template basic_streambuf::basic_streambuf(std::size_t block_size, Allocator const& alloc) - : alloc_(alloc) - , block_size_(block_size) - , block_size_next_(block_size) - , out_(list_.end()) + : empty_base_optimization(alloc) + , block_size_ (block_size) + , block_size_next_ (block_size) + , out_ (list_.end()) { if (! (block_size > 0)) throw std::invalid_argument( @@ -397,15 +404,15 @@ basic_streambuf::basic_streambuf(std::size_t block_size, template basic_streambuf::basic_streambuf (basic_streambuf&& other) - : list_(std::move(other.list_)) - , alloc_(std::move(other.alloc_)) - , block_size_(other.block_size_) - , block_size_next_(other.block_size_next_) - , in_size_(other.in_size_) - , out_(other.out_) - , in_pos_(other.in_pos_) - , out_pos_(other.out_pos_) - , out_end_(other.out_end_) + : empty_base_optimization(other.member()) + , list_ (std::move(other.list_)) + , block_size_ (other.block_size_) + , block_size_next_ (other.block_size_next_) + , in_size_ (other.in_size_) + , out_ (other.out_) + , in_pos_ (other.in_pos_) + , out_pos_ (other.out_pos_) + , out_end_ (other.out_end_) { other.in_size_ = 0; other.out_ = other.list_.end(); @@ -419,47 +426,43 @@ auto basic_streambuf::prepare (size_type n) -> mutable_buffers_type { - debug_check(); - - iterator last = out_; - - if (last != list_.end()) + iterator pos = out_; + if (pos != list_.end()) { - size_type const avail = last->size() - out_pos_; + auto const avail = pos->size() - out_pos_; if (n > avail) { n -= avail; - while (++last != list_.end()) + while (++pos != list_.end()) { - if (n <= last->size()) + if (n < pos->size()) { - ++last; out_end_ = n; n = 0; - debug_check(); + ++pos; break; } - n -= last->size(); + n -= pos->size(); } } else { - ++last; + ++pos; out_end_ = out_pos_ + n; n = 0; - debug_check(); } + debug_check(); } if (n > 0) { - assert(last == list_.end()); + assert(pos == list_.end()); for(;;) { - size_type const avail = block_size_next_; - element& e = *reinterpret_cast(alloc_traits::allocate( - alloc_, avail + sizeof(element))); - alloc_traits::construct(alloc_, &e, avail); + auto const avail = block_size_next_; + auto& e = *reinterpret_cast(alloc_traits::allocate( + this->member(), avail + sizeof(element))); + alloc_traits::construct(this->member(), &e, avail); list_.push_back(e); if (out_ == list_.end()) { @@ -470,67 +473,65 @@ basic_streambuf::prepare (size_type n) -> { out_end_ = n; debug_check(); - n = 0; break; } n -= avail; } - last = list_.end(); } else { - while (last != list_.end()) + while (pos != list_.end()) { - element& e = *last++; + auto& e = *pos++; list_.erase(list_.iterator_to(e)); - size_type const len = e.alloc_size(); - alloc_traits::destroy(alloc_, &e); - alloc_traits::deallocate(alloc_, + auto const len = e.alloc_size(); + alloc_traits::destroy(this->member(), &e); + alloc_traits::deallocate(this->member(), reinterpret_cast(&e), len); - // do we set out_ to list_.end() if empty? } + debug_check(); } - return mutable_buffers_type(out_, last, out_pos_, out_end_); + return mutable_buffers_type (*this); } template void basic_streambuf::commit (size_type n) { - debug_check(); - - if (! list_.empty()) + if (list_.empty()) + return; + if (out_ == list_.end()) + return; + auto const last = std::prev(list_.end()); + while (out_ != last) { - auto const last = std::prev(list_.end()); - while(out_ != last) + auto const avail = + out_->size() - out_pos_; + if (n < avail) { - size_type const avail = - out_->size() - out_pos_; - if (n < avail) - { - in_size_ += n; - out_pos_ += n; - debug_check(); - return; - } - n -= avail; - in_size_ += avail; - out_pos_ = 0; - ++out_; + out_pos_ += n; + in_size_ += n; debug_check(); + return; } - - assert(out_ != list_.end()); - size_type const avail = - out_end_ - out_pos_; - if (n > avail) - n = avail; - // out_pos_ can become out_->size() here (*) - in_size_ += n; - out_pos_ += n; + ++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 @@ -538,28 +539,22 @@ auto basic_streambuf::data() const -> const_buffers_type { - debug_check(); - if (out_ == list_.end()) - return const_buffers_type(list_.begin(), list_.end(), - in_pos_, out_pos_); - return const_buffers_type(list_.begin(), std::next(out_), - in_pos_, out_pos_); + return const_buffers_type(*this); } template void basic_streambuf::consume (size_type n) { - debug_check(); - if (out_ == list_.end()) + if (list_.empty()) return; - auto iter = list_.begin(); + auto pos = list_.begin(); for(;;) { - if (iter != out_) + if (pos != out_) { - size_type const avail = iter->size() - in_pos_; + auto const avail = pos->size() - in_pos_; if (n < avail) { in_size_ -= n; @@ -572,50 +567,39 @@ basic_streambuf::consume (size_type n) in_pos_ = 0; debug_check(); - element& e = *iter++; + element& e = *pos++; list_.erase(list_.iterator_to(e)); size_type const len = e.alloc_size(); - alloc_traits::destroy(alloc_, &e); - alloc_traits::deallocate(alloc_, + alloc_traits::destroy(this->member(), &e); + alloc_traits::deallocate(this->member(), reinterpret_cast(&e), len); } else { - size_type const avail = out_pos_ - in_pos_; + auto const avail = out_pos_ - in_pos_; if (n < avail) { in_size_ -= n; in_pos_ += n; - debug_check(); } else { - auto const back = list_.iterator_to(list_.back()); - if (out_ != back && out_pos_ == out_->size()) + in_size_ -= avail; + if (out_pos_ != out_end_|| + out_ != list_.iterator_to(list_.back())) { - element& e = *out_++; - list_.erase(list_.iterator_to(e)); - size_type const len = e.alloc_size(); - alloc_traits::destroy(alloc_, &e); - alloc_traits::deallocate(alloc_, - reinterpret_cast(&e), len); - out_pos_ = 0; + in_pos_ = out_pos_; } - else if (out_ == back && out_pos_ == out_end_) + else { - element& e = *out_++; - list_.erase(list_.iterator_to(e)); - size_type const len = e.alloc_size(); - alloc_traits::destroy(alloc_, &e); - alloc_traits::deallocate(alloc_, - reinterpret_cast(&e), len); + // Use the whole buffer now. + // Alternatively we could deallocate it. + in_pos_ = 0; out_pos_ = 0; out_end_ = 0; } - in_size_ -= avail; - in_pos_ = out_pos_; - debug_check(); } + debug_check(); break; } } @@ -626,28 +610,36 @@ void basic_streambuf::debug_check() const { #ifndef NDEBUG - if (out_ == list_.end()) + if (list_.empty()) { - assert(in_size_ == 0); assert(in_pos_ == 0); + assert(in_size_ == 0); assert(out_pos_ == 0); assert(out_end_ == 0); + assert(out_ == list_.end()); return; } - assert(! list_.empty()); - - auto const& out = *out_; - auto const& back = list_.back(); auto const& front = list_.front(); assert(in_pos_ < front.size()); - assert(out_end_ <= back.size()); - assert(&out != &front || out_pos_ >= in_pos_); - assert(&out != &back || out_end_ <= back.size()); - assert(&out != &back || out_pos_ <= back.size()); - assert(&out == &back || out_pos_ < back.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 } @@ -676,8 +668,8 @@ to_string (basic_streambuf const& buf) { std::string s; s.resize(buf.size()); - boost::asio::buffer_copy(boost::asio::buffer(s), - buf.data()); + boost::asio::buffer_copy(boost::asio::buffer( + &s[0], s.size()), buf.data()); return s; } diff --git a/src/beast/beast/asio/tests/streambuf.test.cpp b/src/beast/beast/asio/tests/streambuf.test.cpp new file mode 100644 index 000000000..9a69d85a1 --- /dev/null +++ b/src/beast/beast/asio/tests/streambuf.test.cpp @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include + +namespace beast { +namespace asio { + +class streambuf_test : public unit_test::suite +{ +public: + // Convert a ConstBufferSequence to a string + template + static + std::string + to_str (ConstBufferSequence const& b) + { + std::string s; + auto const n = boost::asio::buffer_size(b); + s.resize(n); + boost::asio::buffer_copy( + boost::asio::buffer(&s[0], n), b); + return s; + } + + void run() + { + { + beast::asio::streambuf b(10); + std::string const s = "1234567890"; + b << s; + expect (to_str(b.data()) == s); + b.prepare(5); + } + + { + beast::asio::streambuf b(10); + b.prepare(10); + b.commit(10); + b.consume(10); + } + + { + beast::asio::streambuf b(5); + boost::asio::buffer_copy(b.prepare(14), + boost::asio::buffer(std::string("1234567890ABCD"))); + b.commit(4); + expect(to_str(b.data()) == "1234"); + b.consume(4); + b.commit(10); + expect(to_str(b.data()) == "567890ABCD"); + } + + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(streambuf,asio,beast); + +} +}