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