1 //
2 // buffers_iterator.hpp
3 // ~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #ifndef BOOST_ASIO_BUFFERS_ITERATOR_HPP
12 #define BOOST_ASIO_BUFFERS_ITERATOR_HPP
13 
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
15 # pragma once
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17 
18 #include <boost/asio/detail/config.hpp>
19 #include <cstddef>
20 #include <iterator>
21 #include <boost/asio/buffer.hpp>
22 #include <boost/asio/detail/assert.hpp>
23 #include <boost/asio/detail/type_traits.hpp>
24 
25 #include <boost/asio/detail/push_options.hpp>
26 
27 namespace boost {
28 namespace asio {
29 
30 namespace detail
31 {
32   template <bool IsMutable>
33   struct buffers_iterator_types_helper;
34 
35   template <>
36   struct buffers_iterator_types_helper<false>
37   {
38     typedef const_buffer buffer_type;
39     template <typename ByteType>
40     struct byte_type
41     {
42       typedef typename add_const<ByteType>::type type;
43     };
44   };
45 
46   template <>
47   struct buffers_iterator_types_helper<true>
48   {
49     typedef mutable_buffer buffer_type;
50     template <typename ByteType>
51     struct byte_type
52     {
53       typedef ByteType type;
54     };
55   };
56 
57   template <typename BufferSequence, typename ByteType>
58   struct buffers_iterator_types
59   {
60     enum
61     {
62       is_mutable = is_convertible<
63           typename BufferSequence::value_type,
64           mutable_buffer>::value
65     };
66     typedef buffers_iterator_types_helper<is_mutable> helper;
67     typedef typename helper::buffer_type buffer_type;
68     typedef typename helper::template byte_type<ByteType>::type byte_type;
69   };
70 }
71 
72 /// A random access iterator over the bytes in a buffer sequence.
73 template <typename BufferSequence, typename ByteType = char>
74 class buffers_iterator
75 {
76 private:
77   typedef typename detail::buffers_iterator_types<
78       BufferSequence, ByteType>::buffer_type buffer_type;
79 
80 public:
81   /// The type used for the distance between two iterators.
82   typedef std::ptrdiff_t difference_type;
83 
84   /// The type of the value pointed to by the iterator.
85   typedef ByteType value_type;
86 
87 #if defined(GENERATING_DOCUMENTATION)
88   /// The type of the result of applying operator->() to the iterator.
89   /**
90    * If the buffer sequence stores buffer objects that are convertible to
91    * mutable_buffer, this is a pointer to a non-const ByteType. Otherwise, a
92    * pointer to a const ByteType.
93    */
94   typedef const_or_non_const_ByteType* pointer;
95 #else // defined(GENERATING_DOCUMENTATION)
96   typedef typename detail::buffers_iterator_types<
97       BufferSequence, ByteType>::byte_type* pointer;
98 #endif // defined(GENERATING_DOCUMENTATION)
99 
100 #if defined(GENERATING_DOCUMENTATION)
101   /// The type of the result of applying operator*() to the iterator.
102   /**
103    * If the buffer sequence stores buffer objects that are convertible to
104    * mutable_buffer, this is a reference to a non-const ByteType. Otherwise, a
105    * reference to a const ByteType.
106    */
107   typedef const_or_non_const_ByteType& reference;
108 #else // defined(GENERATING_DOCUMENTATION)
109   typedef typename detail::buffers_iterator_types<
110       BufferSequence, ByteType>::byte_type& reference;
111 #endif // defined(GENERATING_DOCUMENTATION)
112 
113   /// The iterator category.
114   typedef std::random_access_iterator_tag iterator_category;
115 
116   /// Default constructor. Creates an iterator in an undefined state.
buffers_iterator()117   buffers_iterator()
118     : current_buffer_(),
119       current_buffer_position_(0),
120       begin_(),
121       current_(),
122       end_(),
123       position_(0)
124   {
125   }
126 
127   /// Construct an iterator representing the beginning of the buffers' data.
begin(const BufferSequence & buffers)128   static buffers_iterator begin(const BufferSequence& buffers)
129 #if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
130     __attribute__ ((__noinline__))
131 #endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
132   {
133     buffers_iterator new_iter;
134     new_iter.begin_ = buffers.begin();
135     new_iter.current_ = buffers.begin();
136     new_iter.end_ = buffers.end();
137     while (new_iter.current_ != new_iter.end_)
138     {
139       new_iter.current_buffer_ = *new_iter.current_;
140       if (boost::asio::buffer_size(new_iter.current_buffer_) > 0)
141         break;
142       ++new_iter.current_;
143     }
144     return new_iter;
145   }
146 
147   /// Construct an iterator representing the end of the buffers' data.
end(const BufferSequence & buffers)148   static buffers_iterator end(const BufferSequence& buffers)
149 #if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
150     __attribute__ ((__noinline__))
151 #endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
152   {
153     buffers_iterator new_iter;
154     new_iter.begin_ = buffers.begin();
155     new_iter.current_ = buffers.begin();
156     new_iter.end_ = buffers.end();
157     while (new_iter.current_ != new_iter.end_)
158     {
159       buffer_type buffer = *new_iter.current_;
160       new_iter.position_ += boost::asio::buffer_size(buffer);
161       ++new_iter.current_;
162     }
163     return new_iter;
164   }
165 
166   /// Dereference an iterator.
operator *() const167   reference operator*() const
168   {
169     return dereference();
170   }
171 
172   /// Dereference an iterator.
operator ->() const173   pointer operator->() const
174   {
175     return &dereference();
176   }
177 
178   /// Access an individual element.
operator [](std::ptrdiff_t difference) const179   reference operator[](std::ptrdiff_t difference) const
180   {
181     buffers_iterator tmp(*this);
182     tmp.advance(difference);
183     return *tmp;
184   }
185 
186   /// Increment operator (prefix).
operator ++()187   buffers_iterator& operator++()
188   {
189     increment();
190     return *this;
191   }
192 
193   /// Increment operator (postfix).
operator ++(int)194   buffers_iterator operator++(int)
195   {
196     buffers_iterator tmp(*this);
197     ++*this;
198     return tmp;
199   }
200 
201   /// Decrement operator (prefix).
operator --()202   buffers_iterator& operator--()
203   {
204     decrement();
205     return *this;
206   }
207 
208   /// Decrement operator (postfix).
operator --(int)209   buffers_iterator operator--(int)
210   {
211     buffers_iterator tmp(*this);
212     --*this;
213     return tmp;
214   }
215 
216   /// Addition operator.
operator +=(std::ptrdiff_t difference)217   buffers_iterator& operator+=(std::ptrdiff_t difference)
218   {
219     advance(difference);
220     return *this;
221   }
222 
223   /// Subtraction operator.
operator -=(std::ptrdiff_t difference)224   buffers_iterator& operator-=(std::ptrdiff_t difference)
225   {
226     advance(-difference);
227     return *this;
228   }
229 
230   /// Addition operator.
operator +(const buffers_iterator & iter,std::ptrdiff_t difference)231   friend buffers_iterator operator+(const buffers_iterator& iter,
232       std::ptrdiff_t difference)
233   {
234     buffers_iterator tmp(iter);
235     tmp.advance(difference);
236     return tmp;
237   }
238 
239   /// Addition operator.
operator +(std::ptrdiff_t difference,const buffers_iterator & iter)240   friend buffers_iterator operator+(std::ptrdiff_t difference,
241       const buffers_iterator& iter)
242   {
243     buffers_iterator tmp(iter);
244     tmp.advance(difference);
245     return tmp;
246   }
247 
248   /// Subtraction operator.
operator -(const buffers_iterator & iter,std::ptrdiff_t difference)249   friend buffers_iterator operator-(const buffers_iterator& iter,
250       std::ptrdiff_t difference)
251   {
252     buffers_iterator tmp(iter);
253     tmp.advance(-difference);
254     return tmp;
255   }
256 
257   /// Subtraction operator.
operator -(const buffers_iterator & a,const buffers_iterator & b)258   friend std::ptrdiff_t operator-(const buffers_iterator& a,
259       const buffers_iterator& b)
260   {
261     return b.distance_to(a);
262   }
263 
264   /// Test two iterators for equality.
operator ==(const buffers_iterator & a,const buffers_iterator & b)265   friend bool operator==(const buffers_iterator& a, const buffers_iterator& b)
266   {
267     return a.equal(b);
268   }
269 
270   /// Test two iterators for inequality.
operator !=(const buffers_iterator & a,const buffers_iterator & b)271   friend bool operator!=(const buffers_iterator& a, const buffers_iterator& b)
272   {
273     return !a.equal(b);
274   }
275 
276   /// Compare two iterators.
operator <(const buffers_iterator & a,const buffers_iterator & b)277   friend bool operator<(const buffers_iterator& a, const buffers_iterator& b)
278   {
279     return a.distance_to(b) > 0;
280   }
281 
282   /// Compare two iterators.
operator <=(const buffers_iterator & a,const buffers_iterator & b)283   friend bool operator<=(const buffers_iterator& a, const buffers_iterator& b)
284   {
285     return !(b < a);
286   }
287 
288   /// Compare two iterators.
operator >(const buffers_iterator & a,const buffers_iterator & b)289   friend bool operator>(const buffers_iterator& a, const buffers_iterator& b)
290   {
291     return b < a;
292   }
293 
294   /// Compare two iterators.
operator >=(const buffers_iterator & a,const buffers_iterator & b)295   friend bool operator>=(const buffers_iterator& a, const buffers_iterator& b)
296   {
297     return !(a < b);
298   }
299 
300 private:
301   // Dereference the iterator.
dereference() const302   reference dereference() const
303   {
304     return buffer_cast<pointer>(current_buffer_)[current_buffer_position_];
305   }
306 
307   // Compare two iterators for equality.
equal(const buffers_iterator & other) const308   bool equal(const buffers_iterator& other) const
309   {
310     return position_ == other.position_;
311   }
312 
313   // Increment the iterator.
increment()314   void increment()
315   {
316     BOOST_ASIO_ASSERT(current_ != end_ && "iterator out of bounds");
317     ++position_;
318 
319     // Check if the increment can be satisfied by the current buffer.
320     ++current_buffer_position_;
321     if (current_buffer_position_ != boost::asio::buffer_size(current_buffer_))
322       return;
323 
324     // Find the next non-empty buffer.
325     ++current_;
326     current_buffer_position_ = 0;
327     while (current_ != end_)
328     {
329       current_buffer_ = *current_;
330       if (boost::asio::buffer_size(current_buffer_) > 0)
331         return;
332       ++current_;
333     }
334   }
335 
336   // Decrement the iterator.
decrement()337   void decrement()
338   {
339     BOOST_ASIO_ASSERT(position_ > 0 && "iterator out of bounds");
340     --position_;
341 
342     // Check if the decrement can be satisfied by the current buffer.
343     if (current_buffer_position_ != 0)
344     {
345       --current_buffer_position_;
346       return;
347     }
348 
349     // Find the previous non-empty buffer.
350     typename BufferSequence::const_iterator iter = current_;
351     while (iter != begin_)
352     {
353       --iter;
354       buffer_type buffer = *iter;
355       std::size_t buffer_size = boost::asio::buffer_size(buffer);
356       if (buffer_size > 0)
357       {
358         current_ = iter;
359         current_buffer_ = buffer;
360         current_buffer_position_ = buffer_size - 1;
361         return;
362       }
363     }
364   }
365 
366   // Advance the iterator by the specified distance.
advance(std::ptrdiff_t n)367   void advance(std::ptrdiff_t n)
368   {
369     if (n > 0)
370     {
371       BOOST_ASIO_ASSERT(current_ != end_ && "iterator out of bounds");
372       for (;;)
373       {
374         std::ptrdiff_t current_buffer_balance
375           = boost::asio::buffer_size(current_buffer_)
376           - current_buffer_position_;
377 
378         // Check if the advance can be satisfied by the current buffer.
379         if (current_buffer_balance > n)
380         {
381           position_ += n;
382           current_buffer_position_ += n;
383           return;
384         }
385 
386         // Update position.
387         n -= current_buffer_balance;
388         position_ += current_buffer_balance;
389 
390         // Move to next buffer. If it is empty then it will be skipped on the
391         // next iteration of this loop.
392         if (++current_ == end_)
393         {
394           BOOST_ASIO_ASSERT(n == 0 && "iterator out of bounds");
395           current_buffer_ = buffer_type();
396           current_buffer_position_ = 0;
397           return;
398         }
399         current_buffer_ = *current_;
400         current_buffer_position_ = 0;
401       }
402     }
403     else if (n < 0)
404     {
405       std::size_t abs_n = -n;
406       BOOST_ASIO_ASSERT(position_ >= abs_n && "iterator out of bounds");
407       for (;;)
408       {
409         // Check if the advance can be satisfied by the current buffer.
410         if (current_buffer_position_ >= abs_n)
411         {
412           position_ -= abs_n;
413           current_buffer_position_ -= abs_n;
414           return;
415         }
416 
417         // Update position.
418         abs_n -= current_buffer_position_;
419         position_ -= current_buffer_position_;
420 
421         // Check if we've reached the beginning of the buffers.
422         if (current_ == begin_)
423         {
424           BOOST_ASIO_ASSERT(abs_n == 0 && "iterator out of bounds");
425           current_buffer_position_ = 0;
426           return;
427         }
428 
429         // Find the previous non-empty buffer.
430         typename BufferSequence::const_iterator iter = current_;
431         while (iter != begin_)
432         {
433           --iter;
434           buffer_type buffer = *iter;
435           std::size_t buffer_size = boost::asio::buffer_size(buffer);
436           if (buffer_size > 0)
437           {
438             current_ = iter;
439             current_buffer_ = buffer;
440             current_buffer_position_ = buffer_size;
441             break;
442           }
443         }
444       }
445     }
446   }
447 
448   // Determine the distance between two iterators.
distance_to(const buffers_iterator & other) const449   std::ptrdiff_t distance_to(const buffers_iterator& other) const
450   {
451     return other.position_ - position_;
452   }
453 
454   buffer_type current_buffer_;
455   std::size_t current_buffer_position_;
456   typename BufferSequence::const_iterator begin_;
457   typename BufferSequence::const_iterator current_;
458   typename BufferSequence::const_iterator end_;
459   std::size_t position_;
460 };
461 
462 /// Construct an iterator representing the beginning of the buffers' data.
463 template <typename BufferSequence>
buffers_begin(const BufferSequence & buffers)464 inline buffers_iterator<BufferSequence> buffers_begin(
465     const BufferSequence& buffers)
466 {
467   return buffers_iterator<BufferSequence>::begin(buffers);
468 }
469 
470 /// Construct an iterator representing the end of the buffers' data.
471 template <typename BufferSequence>
buffers_end(const BufferSequence & buffers)472 inline buffers_iterator<BufferSequence> buffers_end(
473     const BufferSequence& buffers)
474 {
475   return buffers_iterator<BufferSequence>::end(buffers);
476 }
477 
478 } // namespace asio
479 } // namespace boost
480 
481 #include <boost/asio/detail/pop_options.hpp>
482 
483 #endif // BOOST_ASIO_BUFFERS_ITERATOR_HPP
484