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