// // 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_CONSUMING_BUFFERS_HPP #define BEAST_CONSUMING_BUFFERS_HPP #include #include #include #include #include #include namespace beast { /** Adapter to trim the front of a `BufferSequence`. This adapter wraps a buffer sequence to create a new sequence which may be incrementally consumed. Bytes consumed are removed from the front of the buffer. The underlying memory is not changed, instead the adapter efficiently iterates through a subset of the buffers wrapped. The wrapped buffer is not modified, a copy is made instead. Ownership of the underlying memory is not transferred, the application is still responsible for managing its lifetime. @tparam Buffers The buffer sequence to wrap. @ptaram ValueType The type of buffer of the final buffer sequence. This can be different from the buffer type of the wrapped sequence. For example, a `MutableBufferSequence` can be transformed into a consumable `ConstBufferSequence`. Violations of buffer const safety are not permitted, and will result in a compile error. */ template class consuming_buffers { using iter_type = typename Buffers::const_iterator; static_assert(std::is_constructible::value_type>::value, "ValueType requirements not met"); Buffers bs_; iter_type begin_; std::size_t skip_ = 0; template consuming_buffers(Deduced&& other, std::size_t nbegin) : bs_(std::forward(other).bs_) , begin_(std::next(bs_.begin(), nbegin)) , skip_(other.skip_) { } public: /// The type for each element in the list of buffers. using value_type = ValueType; class const_iterator; /// Move constructor. consuming_buffers(consuming_buffers&&); /// Copy constructor. consuming_buffers(consuming_buffers const&); /// Move assignment. consuming_buffers& operator=(consuming_buffers&&); /// Copy assignment. consuming_buffers& operator=(consuming_buffers const&); /** Construct to represent a buffer sequence. A copy of the buffer sequence is made. Ownership of the underlying memory is not transferred or copied. */ explicit consuming_buffers(Buffers const& buffers); /// Get a bidirectional iterator to the first element. const_iterator begin() const; /// Get a bidirectional iterator for one past the last element. const_iterator end() const; /** Remove bytes from the beginning of the sequence. @param n The number of bytes to remove. If this is larger than the number of bytes remaining, all the bytes remaining are removed. */ void consume(std::size_t n); }; /// A bidirectional iterator type that may be used to read elements. template class consuming_buffers::const_iterator { friend class consuming_buffers; using iter_type = typename Buffers::const_iterator; iter_type it_; consuming_buffers const* b_; public: using value_type = typename std::iterator_traits::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; bool operator==(const_iterator const& other) const { return b_ == other.b_ && it_ == other.it_; } bool operator!=(const_iterator const& other) const { return !(*this == other); } reference operator*() const { if(it_ == b_->begin_) return *it_ + b_->skip_; return *it_; } 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; } private: const_iterator(consuming_buffers const& b, iter_type it) : it_(it) , b_(&b) { } }; template consuming_buffers:: consuming_buffers(consuming_buffers&& other) : consuming_buffers(std::move(other), std::distance( other.bs_.begin(), other.begin_)) { } template consuming_buffers:: consuming_buffers(consuming_buffers const& other) : consuming_buffers(other, std::distance( other.bs_.begin(), other.begin_)) { } template auto consuming_buffers:: operator=(consuming_buffers&& other) -> consuming_buffers& { auto const nbegin = std::distance( other.bs_.begin(), other.begin_); bs_ = std::move(other.bs_); begin_ = std::next(bs_.begin(), nbegin); skip_ = other.skip_; return *this; } template auto consuming_buffers:: operator=(consuming_buffers const& other) -> consuming_buffers& { auto const nbegin = std::distance( other.bs_.begin(), other.begin_); bs_ = other.bs_; begin_ = std::next(bs_.begin(), nbegin); skip_ = other.skip_; return *this; } template consuming_buffers:: consuming_buffers(Buffers const& bs) : bs_(bs) , begin_(bs_.begin()) { } template auto consuming_buffers::begin() const -> const_iterator { return const_iterator{*this, begin_}; } template auto consuming_buffers::end() const -> const_iterator { return const_iterator{*this, bs_.end()}; } template void consuming_buffers::consume(std::size_t n) { using boost::asio::buffer_size; for(;n > 0 && begin_ != bs_.end(); ++begin_) { auto const len = buffer_size(*begin_) - skip_; if(n < len) { skip_ += n; break; } n -= len; skip_ = 0; } } /// Returns a consumed buffer template consuming_buffers consumed_buffers(Buffers const& bs, std::size_t n) { consuming_buffers cb(bs); cb.consume(n); return cb; } } // beast #endif