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
27namespace boost {
28namespace asio {
29
30namespace 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.
73template <typename BufferSequence, typename ByteType = char>
74class buffers_iterator
75{
76private:
77 typedef typename detail::buffers_iterator_types<
78 BufferSequence, ByteType>::buffer_type buffer_type;
79
80public:
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.
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.
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.
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.
167 reference operator*() const
168 {
169 return dereference();
170 }
171
172 /// Dereference an iterator.
173 pointer operator->() const
174 {
175 return &dereference();
176 }
177
178 /// Access an individual element.
179 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).
187 buffers_iterator& operator++()
188 {
189 increment();
190 return *this;
191 }
192
193 /// Increment operator (postfix).
194 buffers_iterator operator++(int)
195 {
196 buffers_iterator tmp(*this);
197 ++*this;
198 return tmp;
199 }
200
201 /// Decrement operator (prefix).
202 buffers_iterator& operator--()
203 {
204 decrement();
205 return *this;
206 }
207
208 /// Decrement operator (postfix).
209 buffers_iterator operator--(int)
210 {
211 buffers_iterator tmp(*this);
212 --*this;
213 return tmp;
214 }
215
216 /// Addition operator.
217 buffers_iterator& operator+=(std::ptrdiff_t difference)
218 {
219 advance(difference);
220 return *this;
221 }
222
223 /// Subtraction operator.
224 buffers_iterator& operator-=(std::ptrdiff_t difference)
225 {
226 advance(-difference);
227 return *this;
228 }
229
230 /// Addition operator.
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.
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.
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.
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.
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.
271 friend bool operator!=(const buffers_iterator& a, const buffers_iterator& b)
272 {
273 return !a.equal(b);
274 }
275
276 /// Compare two iterators.
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.
283 friend bool operator<=(const buffers_iterator& a, const buffers_iterator& b)
284 {
285 return !(b < a);
286 }
287
288 /// Compare two iterators.
289 friend bool operator>(const buffers_iterator& a, const buffers_iterator& b)
290 {
291 return b < a;
292 }
293
294 /// Compare two iterators.
295 friend bool operator>=(const buffers_iterator& a, const buffers_iterator& b)
296 {
297 return !(a < b);
298 }
299
300private:
301 // Dereference the iterator.
302 reference dereference() const
303 {
304 return buffer_cast<pointer>(current_buffer_)[current_buffer_position_];
305 }
306
307 // Compare two iterators for equality.
308 bool equal(const buffers_iterator& other) const
309 {
310 return position_ == other.position_;
311 }
312
313 // Increment the iterator.
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.
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.
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.
449 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.
463template <typename BufferSequence>
464inline 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.
471template <typename BufferSequence>
472inline 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