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. |
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(n: difference); |
220 | return *this; |
221 | } |
222 | |
223 | /// Subtraction operator. |
224 | buffers_iterator& operator-=(std::ptrdiff_t difference) |
225 | { |
226 | advance(n: -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 | |
300 | private: |
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. |
463 | template <typename BufferSequence> |
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> |
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 | |