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