// | |
// buffers_iterator.hpp | |
// ~~~~~~~~~~~~~~~~~~~~ | |
// | |
// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff 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 BOOST_ASIO_BUFFERS_ITERATOR_HPP | |
#define BOOST_ASIO_BUFFERS_ITERATOR_HPP | |
#if defined(_MSC_VER) && (_MSC_VER >= 1200) | |
# pragma once | |
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |
#include <boost/asio/detail/config.hpp> | |
#include <cstddef> | |
#include <boost/assert.hpp> | |
#include <boost/detail/workaround.hpp> | |
#include <boost/iterator.hpp> | |
#include <boost/type_traits/is_convertible.hpp> | |
#include <boost/type_traits/add_const.hpp> | |
#include <boost/asio/buffer.hpp> | |
#include <boost/asio/detail/push_options.hpp> | |
namespace boost { | |
namespace asio { | |
namespace detail | |
{ | |
template <bool IsMutable> | |
struct buffers_iterator_types_helper; | |
template <> | |
struct buffers_iterator_types_helper<false> | |
{ | |
typedef const_buffer buffer_type; | |
template <typename ByteType> | |
struct byte_type | |
{ | |
typedef typename boost::add_const<ByteType>::type type; | |
}; | |
}; | |
template <> | |
struct buffers_iterator_types_helper<true> | |
{ | |
typedef mutable_buffer buffer_type; | |
template <typename ByteType> | |
struct byte_type | |
{ | |
typedef ByteType type; | |
}; | |
}; | |
template <typename BufferSequence, typename ByteType> | |
struct buffers_iterator_types | |
{ | |
enum | |
{ | |
is_mutable = boost::is_convertible< | |
typename BufferSequence::value_type, mutable_buffer>::value | |
}; | |
typedef buffers_iterator_types_helper<is_mutable> helper; | |
typedef typename helper::buffer_type buffer_type; | |
typedef typename helper::template byte_type<ByteType>::type byte_type; | |
}; | |
} | |
/// A random access iterator over the bytes in a buffer sequence. | |
template <typename BufferSequence, typename ByteType = char> | |
class buffers_iterator | |
: public boost::iterator< | |
std::random_access_iterator_tag, | |
typename detail::buffers_iterator_types< | |
BufferSequence, ByteType>::byte_type> | |
{ | |
private: | |
typedef typename detail::buffers_iterator_types< | |
BufferSequence, ByteType>::buffer_type buffer_type; | |
typedef typename detail::buffers_iterator_types< | |
BufferSequence, ByteType>::byte_type byte_type; | |
public: | |
/// Default constructor. Creates an iterator in an undefined state. | |
buffers_iterator() | |
: current_buffer_(), | |
current_buffer_position_(0), | |
begin_(), | |
current_(), | |
end_(), | |
position_(0) | |
{ | |
} | |
/// Construct an iterator representing the beginning of the buffers' data. | |
static buffers_iterator begin(const BufferSequence& buffers) | |
#if BOOST_WORKAROUND(__GNUC__, == 4) && BOOST_WORKAROUND(__GNUC_MINOR__, == 3) | |
__attribute__ ((noinline)) | |
#endif | |
{ | |
buffers_iterator new_iter; | |
new_iter.begin_ = buffers.begin(); | |
new_iter.current_ = buffers.begin(); | |
new_iter.end_ = buffers.end(); | |
while (new_iter.current_ != new_iter.end_) | |
{ | |
new_iter.current_buffer_ = *new_iter.current_; | |
if (boost::asio::buffer_size(new_iter.current_buffer_) > 0) | |
break; | |
++new_iter.current_; | |
} | |
return new_iter; | |
} | |
/// Construct an iterator representing the end of the buffers' data. | |
static buffers_iterator end(const BufferSequence& buffers) | |
#if BOOST_WORKAROUND(__GNUC__, == 4) && BOOST_WORKAROUND(__GNUC_MINOR__, == 3) | |
__attribute__ ((noinline)) | |
#endif | |
{ | |
buffers_iterator new_iter; | |
new_iter.begin_ = buffers.begin(); | |
new_iter.current_ = buffers.begin(); | |
new_iter.end_ = buffers.end(); | |
while (new_iter.current_ != new_iter.end_) | |
{ | |
buffer_type buffer = *new_iter.current_; | |
new_iter.position_ += boost::asio::buffer_size(buffer); | |
++new_iter.current_; | |
} | |
return new_iter; | |
} | |
/// Dereference an iterator. | |
byte_type& operator*() const | |
{ | |
return dereference(); | |
} | |
/// Dereference an iterator. | |
byte_type* operator->() const | |
{ | |
return &dereference(); | |
} | |
/// Access an individual element. | |
byte_type& operator[](std::ptrdiff_t difference) const | |
{ | |
buffers_iterator tmp(*this); | |
tmp.advance(difference); | |
return *tmp; | |
} | |
/// Increment operator (prefix). | |
buffers_iterator& operator++() | |
{ | |
increment(); | |
return *this; | |
} | |
/// Increment operator (postfix). | |
buffers_iterator operator++(int) | |
{ | |
buffers_iterator tmp(*this); | |
++*this; | |
return tmp; | |
} | |
/// Decrement operator (prefix). | |
buffers_iterator& operator--() | |
{ | |
decrement(); | |
return *this; | |
} | |
/// Decrement operator (postfix). | |
buffers_iterator operator--(int) | |
{ | |
buffers_iterator tmp(*this); | |
--*this; | |
return tmp; | |
} | |
/// Addition operator. | |
buffers_iterator& operator+=(std::ptrdiff_t difference) | |
{ | |
advance(difference); | |
return *this; | |
} | |
/// Subtraction operator. | |
buffers_iterator& operator-=(std::ptrdiff_t difference) | |
{ | |
advance(-difference); | |
return *this; | |
} | |
/// Addition operator. | |
friend buffers_iterator operator+(const buffers_iterator& iter, | |
std::ptrdiff_t difference) | |
{ | |
buffers_iterator tmp(iter); | |
tmp.advance(difference); | |
return tmp; | |
} | |
/// Addition operator. | |
friend buffers_iterator operator+(std::ptrdiff_t difference, | |
const buffers_iterator& iter) | |
{ | |
buffers_iterator tmp(iter); | |
tmp.advance(difference); | |
return tmp; | |
} | |
/// Subtraction operator. | |
friend buffers_iterator operator-(const buffers_iterator& iter, | |
std::ptrdiff_t difference) | |
{ | |
buffers_iterator tmp(iter); | |
tmp.advance(-difference); | |
return tmp; | |
} | |
/// Subtraction operator. | |
friend std::ptrdiff_t operator-(const buffers_iterator& a, | |
const buffers_iterator& b) | |
{ | |
return b.distance_to(a); | |
} | |
/// Test two iterators for equality. | |
friend bool operator==(const buffers_iterator& a, const buffers_iterator& b) | |
{ | |
return a.equal(b); | |
} | |
/// Test two iterators for inequality. | |
friend bool operator!=(const buffers_iterator& a, const buffers_iterator& b) | |
{ | |
return !a.equal(b); | |
} | |
/// Compare two iterators. | |
friend bool operator<(const buffers_iterator& a, const buffers_iterator& b) | |
{ | |
return a.distance_to(b) > 0; | |
} | |
/// Compare two iterators. | |
friend bool operator<=(const buffers_iterator& a, const buffers_iterator& b) | |
{ | |
return !(b < a); | |
} | |
/// Compare two iterators. | |
friend bool operator>(const buffers_iterator& a, const buffers_iterator& b) | |
{ | |
return b < a; | |
} | |
/// Compare two iterators. | |
friend bool operator>=(const buffers_iterator& a, const buffers_iterator& b) | |
{ | |
return !(a < b); | |
} | |
private: | |
// Dereference the iterator. | |
byte_type& dereference() const | |
{ | |
return buffer_cast<byte_type*>(current_buffer_)[current_buffer_position_]; | |
} | |
// Compare two iterators for equality. | |
bool equal(const buffers_iterator& other) const | |
{ | |
return position_ == other.position_; | |
} | |
// Increment the iterator. | |
void increment() | |
{ | |
BOOST_ASSERT(current_ != end_ && "iterator out of bounds"); | |
++position_; | |
// Check if the increment can be satisfied by the current buffer. | |
++current_buffer_position_; | |
if (current_buffer_position_ != boost::asio::buffer_size(current_buffer_)) | |
return; | |
// Find the next non-empty buffer. | |
++current_; | |
current_buffer_position_ = 0; | |
while (current_ != end_) | |
{ | |
current_buffer_ = *current_; | |
if (boost::asio::buffer_size(current_buffer_) > 0) | |
return; | |
++current_; | |
} | |
} | |
// Decrement the iterator. | |
void decrement() | |
{ | |
BOOST_ASSERT(position_ > 0 && "iterator out of bounds"); | |
--position_; | |
// Check if the decrement can be satisfied by the current buffer. | |
if (current_buffer_position_ != 0) | |
{ | |
--current_buffer_position_; | |
return; | |
} | |
// Find the previous non-empty buffer. | |
typename BufferSequence::const_iterator iter = current_; | |
while (iter != begin_) | |
{ | |
--iter; | |
buffer_type buffer = *iter; | |
std::size_t buffer_size = boost::asio::buffer_size(buffer); | |
if (buffer_size > 0) | |
{ | |
current_ = iter; | |
current_buffer_ = buffer; | |
current_buffer_position_ = buffer_size - 1; | |
return; | |
} | |
} | |
} | |
// Advance the iterator by the specified distance. | |
void advance(std::ptrdiff_t n) | |
{ | |
if (n > 0) | |
{ | |
BOOST_ASSERT(current_ != end_ && "iterator out of bounds"); | |
for (;;) | |
{ | |
std::ptrdiff_t current_buffer_balance | |
= boost::asio::buffer_size(current_buffer_) | |
- current_buffer_position_; | |
// Check if the advance can be satisfied by the current buffer. | |
if (current_buffer_balance > n) | |
{ | |
position_ += n; | |
current_buffer_position_ += n; | |
return; | |
} | |
// Update position. | |
n -= current_buffer_balance; | |
position_ += current_buffer_balance; | |
// Move to next buffer. If it is empty then it will be skipped on the | |
// next iteration of this loop. | |
if (++current_ == end_) | |
{ | |
BOOST_ASSERT(n == 0 && "iterator out of bounds"); | |
current_buffer_ = buffer_type(); | |
current_buffer_position_ = 0; | |
return; | |
} | |
current_buffer_ = *current_; | |
current_buffer_position_ = 0; | |
} | |
} | |
else if (n < 0) | |
{ | |
std::size_t abs_n = -n; | |
BOOST_ASSERT(position_ >= abs_n && "iterator out of bounds"); | |
for (;;) | |
{ | |
// Check if the advance can be satisfied by the current buffer. | |
if (current_buffer_position_ >= abs_n) | |
{ | |
position_ -= abs_n; | |
current_buffer_position_ -= abs_n; | |
return; | |
} | |
// Update position. | |
abs_n -= current_buffer_position_; | |
position_ -= current_buffer_position_; | |
// Check if we've reached the beginning of the buffers. | |
if (current_ == begin_) | |
{ | |
BOOST_ASSERT(abs_n == 0 && "iterator out of bounds"); | |
current_buffer_position_ = 0; | |
return; | |
} | |
// Find the previous non-empty buffer. | |
typename BufferSequence::const_iterator iter = current_; | |
while (iter != begin_) | |
{ | |
--iter; | |
buffer_type buffer = *iter; | |
std::size_t buffer_size = boost::asio::buffer_size(buffer); | |
if (buffer_size > 0) | |
{ | |
current_ = iter; | |
current_buffer_ = buffer; | |
current_buffer_position_ = buffer_size; | |
break; | |
} | |
} | |
} | |
} | |
} | |
// Determine the distance between two iterators. | |
std::ptrdiff_t distance_to(const buffers_iterator& other) const | |
{ | |
return other.position_ - position_; | |
} | |
buffer_type current_buffer_; | |
std::size_t current_buffer_position_; | |
typename BufferSequence::const_iterator begin_; | |
typename BufferSequence::const_iterator current_; | |
typename BufferSequence::const_iterator end_; | |
std::size_t position_; | |
}; | |
/// Construct an iterator representing the beginning of the buffers' data. | |
template <typename BufferSequence> | |
inline buffers_iterator<BufferSequence> buffers_begin( | |
const BufferSequence& buffers) | |
{ | |
return buffers_iterator<BufferSequence>::begin(buffers); | |
} | |
/// Construct an iterator representing the end of the buffers' data. | |
template <typename BufferSequence> | |
inline buffers_iterator<BufferSequence> buffers_end( | |
const BufferSequence& buffers) | |
{ | |
return buffers_iterator<BufferSequence>::end(buffers); | |
} | |
} // namespace asio | |
} // namespace boost | |
#include <boost/asio/detail/pop_options.hpp> | |
#endif // BOOST_ASIO_BUFFERS_ITERATOR_HPP |