100.00% Lines (88/88) 100.00% Functions (19/19)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/capy 7   // Official repository: https://github.com/cppalliance/capy
8   // 8   //
9   9  
10   #ifndef BOOST_CAPY_BUFFERS_BUFFERS_ITERATOR_HPP 10   #ifndef BOOST_CAPY_BUFFERS_BUFFERS_ITERATOR_HPP
11   #define BOOST_CAPY_BUFFERS_BUFFERS_ITERATOR_HPP 11   #define BOOST_CAPY_BUFFERS_BUFFERS_ITERATOR_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/buffers.hpp> 14   #include <boost/capy/buffers.hpp>
15   15  
16   #include <compare> 16   #include <compare>
17   #include <cstddef> 17   #include <cstddef>
18   #include <iterator> 18   #include <iterator>
19   #include <ranges> 19   #include <ranges>
20   #include <type_traits> 20   #include <type_traits>
21   #include <utility> 21   #include <utility>
22   22  
23   namespace boost { 23   namespace boost {
24   namespace capy { 24   namespace capy {
25   25  
26   namespace detail { 26   namespace detail {
27   27  
28   // Walking back from the end needs an end iterator, not a sentinel 28   // Walking back from the end needs an end iterator, not a sentinel
29   template<class T> 29   template<class T>
30   concept common_buffer_sequence = 30   concept common_buffer_sequence =
31   ConstBufferSequence<T> && 31   ConstBufferSequence<T> &&
32   (ConstBuffer<T> || std::ranges::common_range<T const>); 32   (ConstBuffer<T> || std::ranges::common_range<T const>);
33   33  
34   } // detail 34   } // detail
35   35  
36   /** A random access iterator over the bytes in a buffer sequence. 36   /** A random access iterator over the bytes in a buffer sequence.
37   37  
38   A buffer sequence stores its bytes in several separate regions. 38   A buffer sequence stores its bytes in several separate regions.
39   This iterator presents them as one run of bytes, so standard 39   This iterator presents them as one run of bytes, so standard
40   algorithms such as `std::search` can scan across the boundaries 40   algorithms such as `std::search` can scan across the boundaries
41   between buffers without first copying the data into contiguous 41   between buffers without first copying the data into contiguous
42   storage. Empty buffers in the sequence are skipped. 42   storage. Empty buffers in the sequence are skipped.
43   43  
44   The iterator dereferences to `ByteType&` for a 44   The iterator dereferences to `ByteType&` for a
45   @ref MutableBufferSequence and to `ByteType const&` otherwise. 45   @ref MutableBufferSequence and to `ByteType const&` otherwise.
46   46  
47   Moving by `n` bytes walks the underlying buffers, so `operator+=`, 47   Moving by `n` bytes walks the underlying buffers, so `operator+=`,
48   `operator-=`, `operator+`, `operator-` with an integer, and 48   `operator-=`, `operator+`, `operator-` with an integer, and
49   `operator[]` are linear in the number of buffers crossed. Comparison 49   `operator[]` are linear in the number of buffers crossed. Comparison
50   and the difference of two iterators are constant time. 50   and the difference of two iterators are constant time.
51   51  
52   @par Preconditions 52   @par Preconditions
53   The buffer sequence and the memory it refers to outlive every 53   The buffer sequence and the memory it refers to outlive every
54   iterator obtained from it. Iterators from different sequences are 54   iterator obtained from it. Iterators from different sequences are
55   not comparable. 55   not comparable.
56   56  
57   @par Example 57   @par Example
58   @code 58   @code
59   std::array<capy::const_buffer, 2> bufs{ 59   std::array<capy::const_buffer, 2> bufs{
60   capy::make_buffer(std::string_view("GET / HTTP/1.1\r")), 60   capy::make_buffer(std::string_view("GET / HTTP/1.1\r")),
61   capy::make_buffer(std::string_view("\n\r\nbody")) }; 61   capy::make_buffer(std::string_view("\n\r\nbody")) };
62   62  
63   // Find the end of the header, which spans both buffers 63   // Find the end of the header, which spans both buffers
64   char const crlfcrlf[] = "\r\n\r\n"; 64   char const crlfcrlf[] = "\r\n\r\n";
65   auto it = std::search( 65   auto it = std::search(
66   capy::buffers_begin(bufs), capy::buffers_end(bufs), 66   capy::buffers_begin(bufs), capy::buffers_end(bufs),
67   crlfcrlf, crlfcrlf + 4); 67   crlfcrlf, crlfcrlf + 4);
68   std::size_t header_size = it - capy::buffers_begin(bufs) + 4; 68   std::size_t header_size = it - capy::buffers_begin(bufs) + 4;
69   @endcode 69   @endcode
70   70  
71   @tparam BufferSequence The buffer sequence type. It must be a single 71   @tparam BufferSequence The buffer sequence type. It must be a single
72   buffer, or a range of buffers whose begin and end have the same type. 72   buffer, or a range of buffers whose begin and end have the same type.
73   73  
74   @tparam ByteType The byte type presented by the iterator. Its size 74   @tparam ByteType The byte type presented by the iterator. Its size
75   must be 1. 75   must be 1.
76   76  
77   @see @ref buffers_begin, @ref buffers_end, 77   @see @ref buffers_begin, @ref buffers_end,
78   @ref buffer_sequence_begin 78   @ref buffer_sequence_begin
79   */ 79   */
80   template<class BufferSequence, class ByteType = char> 80   template<class BufferSequence, class ByteType = char>
81   requires detail::common_buffer_sequence<BufferSequence> 81   requires detail::common_buffer_sequence<BufferSequence>
82   class buffers_iterator 82   class buffers_iterator
83   { 83   {
84   static_assert(sizeof(ByteType) == 1, "ByteType must be a byte"); 84   static_assert(sizeof(ByteType) == 1, "ByteType must be a byte");
85   85  
86   using sequence_iterator = decltype( 86   using sequence_iterator = decltype(
87   buffer_sequence_begin(std::declval<BufferSequence const&>())); 87   buffer_sequence_begin(std::declval<BufferSequence const&>()));
88   using buffer_type = capy::buffer_type<BufferSequence>; 88   using buffer_type = capy::buffer_type<BufferSequence>;
89   using byte_type = std::conditional_t< 89   using byte_type = std::conditional_t<
90   MutableBufferSequence<BufferSequence>, 90   MutableBufferSequence<BufferSequence>,
91   ByteType, 91   ByteType,
92   ByteType const>; 92   ByteType const>;
93   93  
94   buffer_type current_buffer_{}; 94   buffer_type current_buffer_{};
95   std::size_t current_buffer_position_ = 0; 95   std::size_t current_buffer_position_ = 0;
96   sequence_iterator current_{}; 96   sequence_iterator current_{};
97   sequence_iterator end_{}; 97   sequence_iterator end_{};
98   std::size_t position_ = 0; 98   std::size_t position_ = 0;
99   99  
100   // Move to the next non-empty buffer, or to the end of the sequence 100   // Move to the next non-empty buffer, or to the end of the sequence
101   void 101   void
HITCBC 102   202 skip_forward() noexcept 102   202 skip_forward() noexcept
103   { 103   {
HITCBC 104   363 while(current_ != end_) 104   363 while(current_ != end_)
105   { 105   {
HITCBC 106   334 current_buffer_ = buffer_type(*current_); 106   334 current_buffer_ = buffer_type(*current_);
HITCBC 107   334 if(current_buffer_.size() > 0) 107   334 if(current_buffer_.size() > 0)
HITCBC 108   173 return; 108   173 return;
HITCBC 109   161 ++current_; 109   161 ++current_;
110   } 110   }
HITCBC 111   29 current_buffer_ = {}; 111   29 current_buffer_ = {};
112   } 112   }
113   113  
114   // Move to the previous non-empty buffer, positioned past its last byte 114   // Move to the previous non-empty buffer, positioned past its last byte
115   void 115   void
HITCBC 116   155 skip_backward() noexcept 116   155 skip_backward() noexcept
117   { 117   {
118   do 118   do
119   { 119   {
HITCBC 120   277 --current_; 120   277 --current_;
HITCBC 121   277 current_buffer_ = buffer_type(*current_); 121   277 current_buffer_ = buffer_type(*current_);
122   } 122   }
HITCBC 123   277 while(current_buffer_.size() == 0); 123   277 while(current_buffer_.size() == 0);
HITCBC 124   155 current_buffer_position_ = current_buffer_.size(); 124   155 current_buffer_position_ = current_buffer_.size();
HITCBC 125   155 } 125   155 }
126   126  
127   void 127   void
HITCBC 128   318 advance(std::ptrdiff_t n) noexcept 128   318 advance(std::ptrdiff_t n) noexcept
129   { 129   {
HITCBC 130   318 if(n >= 0) 130   318 if(n >= 0)
131   { 131   {
HITCBC 132   177 auto left = static_cast<std::size_t>(n); 132   177 auto left = static_cast<std::size_t>(n);
HITCBC 133   349 while(left > 0) 133   349 while(left > 0)
134   { 134   {
HITCBC 135   269 std::size_t const avail = 135   269 std::size_t const avail =
HITCBC 136   269 current_buffer_.size() - current_buffer_position_; 136   269 current_buffer_.size() - current_buffer_position_;
HITCBC 137   269 if(left < avail) 137   269 if(left < avail)
138   { 138   {
HITCBC 139   97 current_buffer_position_ += left; 139   97 current_buffer_position_ += left;
HITCBC 140   97 position_ += left; 140   97 position_ += left;
HITCBC 141   97 return; 141   97 return;
142   } 142   }
HITCBC 143   172 left -= avail; 143   172 left -= avail;
HITCBC 144   172 position_ += avail; 144   172 position_ += avail;
HITCBC 145   172 ++current_; 145   172 ++current_;
HITCBC 146   172 current_buffer_position_ = 0; 146   172 current_buffer_position_ = 0;
HITCBC 147   172 skip_forward(); 147   172 skip_forward();
148   } 148   }
149   } 149   }
150   else 150   else
151   { 151   {
HITCBC 152   141 auto left = static_cast<std::size_t>(-n); 152   141 auto left = static_cast<std::size_t>(-n);
HITCBC 153   296 while(left > 0) 153   296 while(left > 0)
154   { 154   {
HITCBC 155   296 if(left <= current_buffer_position_) 155   296 if(left <= current_buffer_position_)
156   { 156   {
HITCBC 157   141 current_buffer_position_ -= left; 157   141 current_buffer_position_ -= left;
HITCBC 158   141 position_ -= left; 158   141 position_ -= left;
HITCBC 159   141 return; 159   141 return;
160   } 160   }
HITCBC 161   155 left -= current_buffer_position_; 161   155 left -= current_buffer_position_;
HITCBC 162   155 position_ -= current_buffer_position_; 162   155 position_ -= current_buffer_position_;
HITCBC 163   155 skip_backward(); 163   155 skip_backward();
164   } 164   }
165   } 165   }
166   } 166   }
167   167  
168   public: 168   public:
169   /// The signed type of the distance between two iterators. 169   /// The signed type of the distance between two iterators.
170   using difference_type = std::ptrdiff_t; 170   using difference_type = std::ptrdiff_t;
171   171  
172   /// The type of the bytes the iterator visits. 172   /// The type of the bytes the iterator visits.
173   using value_type = ByteType; 173   using value_type = ByteType;
174   174  
175   /// The type returned by `operator->`. 175   /// The type returned by `operator->`.
176   using pointer = byte_type*; 176   using pointer = byte_type*;
177   177  
178   /// The type returned by `operator*`. 178   /// The type returned by `operator*`.
179   using reference = byte_type&; 179   using reference = byte_type&;
180   180  
181   /// The iterator category. 181   /// The iterator category.
182   using iterator_category = std::random_access_iterator_tag; 182   using iterator_category = std::random_access_iterator_tag;
183   183  
184   /// Construct a singular iterator. 184   /// Construct a singular iterator.
HITCBC 185   14 buffers_iterator() = default; 185   14 buffers_iterator() = default;
186   186  
187   /** Return an iterator to the first byte of a buffer sequence. 187   /** Return an iterator to the first byte of a buffer sequence.
188   188  
189   @param bs The buffer sequence. It must outlive the iterator. 189   @param bs The buffer sequence. It must outlive the iterator.
190   190  
191   @return An iterator to the first byte, or the end iterator if 191   @return An iterator to the first byte, or the end iterator if
192   the sequence holds no bytes. 192   the sequence holds no bytes.
193   */ 193   */
194   static 194   static
195   buffers_iterator 195   buffers_iterator
HITCBC 196   18 begin(BufferSequence const& bs) noexcept 196   18 begin(BufferSequence const& bs) noexcept
197   { 197   {
HITCBC 198   18 buffers_iterator it; 198   18 buffers_iterator it;
HITCBC 199   18 it.current_ = buffer_sequence_begin(bs); 199   18 it.current_ = buffer_sequence_begin(bs);
HITCBC 200   18 it.end_ = buffer_sequence_end(bs); 200   18 it.end_ = buffer_sequence_end(bs);
HITCBC 201   18 it.skip_forward(); 201   18 it.skip_forward();
HITCBC 202   18 return it; 202   18 return it;
203   } 203   }
204   204  
205   /** Return an iterator past the last byte of a buffer sequence. 205   /** Return an iterator past the last byte of a buffer sequence.
206   206  
207   @param bs The buffer sequence. It must outlive the iterator. 207   @param bs The buffer sequence. It must outlive the iterator.
208   208  
209   @return The end iterator. 209   @return The end iterator.
210   */ 210   */
211   static 211   static
212   buffers_iterator 212   buffers_iterator
HITCBC 213   24 end(BufferSequence const& bs) noexcept 213   24 end(BufferSequence const& bs) noexcept
214   { 214   {
HITCBC 215   24 buffers_iterator it; 215   24 buffers_iterator it;
HITCBC 216   24 it.current_ = buffer_sequence_end(bs); 216   24 it.current_ = buffer_sequence_end(bs);
HITCBC 217   24 it.end_ = it.current_; 217   24 it.end_ = it.current_;
HITCBC 218   24 it.position_ = buffer_size(bs); 218   24 it.position_ = buffer_size(bs);
HITCBC 219   24 return it; 219   24 return it;
220   } 220   }
221   221  
222   /** Return a reference to the current byte. 222   /** Return a reference to the current byte.
223   223  
224   @par Preconditions 224   @par Preconditions
225   The iterator is not the end iterator. 225   The iterator is not the end iterator.
226   */ 226   */
227   reference 227   reference
HITCBC 228   125 operator*() const noexcept 228   125 operator*() const noexcept
229   { 229   {
230   return static_cast<pointer>( 230   return static_cast<pointer>(
HITCBC 231   125 current_buffer_.data())[current_buffer_position_]; 231   125 current_buffer_.data())[current_buffer_position_];
232   } 232   }
233   233  
234   /** Return a pointer to the current byte. 234   /** Return a pointer to the current byte.
235   235  
236   @par Preconditions 236   @par Preconditions
237   The iterator is not the end iterator. 237   The iterator is not the end iterator.
238   */ 238   */
239   pointer 239   pointer
240   operator->() const noexcept 240   operator->() const noexcept
241   { 241   {
242   return &**this; 242   return &**this;
243   } 243   }
244   244  
245   /** Return a reference to the byte `n` positions away. 245   /** Return a reference to the byte `n` positions away.
246   246  
247   @par Complexity 247   @par Complexity
248   Linear in the number of buffers crossed. 248   Linear in the number of buffers crossed.
249   249  
250   @param n The offset from this iterator, in bytes. 250   @param n The offset from this iterator, in bytes.
251   */ 251   */
252   reference 252   reference
HITCBC 253   33 operator[](difference_type n) const noexcept 253   33 operator[](difference_type n) const noexcept
254   { 254   {
HITCBC 255   33 return *(*this + n); 255   33 return *(*this + n);
256   } 256   }
257   257  
258   /// Advance to the next byte. 258   /// Advance to the next byte.
259   buffers_iterator& 259   buffers_iterator&
HITCBC 260   59 operator++() noexcept 260   59 operator++() noexcept
261   { 261   {
HITCBC 262   59 ++position_; 262   59 ++position_;
HITCBC 263   59 if(++current_buffer_position_ < current_buffer_.size()) 263   59 if(++current_buffer_position_ < current_buffer_.size())
HITCBC 264   47 return *this; 264   47 return *this;
HITCBC 265   12 ++current_; 265   12 ++current_;
HITCBC 266   12 current_buffer_position_ = 0; 266   12 current_buffer_position_ = 0;
HITCBC 267   12 skip_forward(); 267   12 skip_forward();
HITCBC 268   12 return *this; 268   12 return *this;
269   } 269   }
270   270  
271   /// Advance to the next byte, returning the previous position. 271   /// Advance to the next byte, returning the previous position.
272   buffers_iterator 272   buffers_iterator
273   operator++(int) noexcept 273   operator++(int) noexcept
274   { 274   {
275   auto tmp = *this; 275   auto tmp = *this;
276   ++*this; 276   ++*this;
277   return tmp; 277   return tmp;
278   } 278   }
279   279  
280   /// Move back to the previous byte. 280   /// Move back to the previous byte.
281   buffers_iterator& 281   buffers_iterator&
HITCBC 282   20 operator--() noexcept 282   20 operator--() noexcept
283   { 283   {
HITCBC 284   20 advance(-1); 284   20 advance(-1);
HITCBC 285   20 return *this; 285   20 return *this;
286   } 286   }
287   287  
288   /// Move back to the previous byte, returning the previous position. 288   /// Move back to the previous byte, returning the previous position.
289   buffers_iterator 289   buffers_iterator
290   operator--(int) noexcept 290   operator--(int) noexcept
291   { 291   {
292   auto tmp = *this; 292   auto tmp = *this;
293   --*this; 293   --*this;
294   return tmp; 294   return tmp;
295   } 295   }
296   296  
297   /** Move by `n` bytes. 297   /** Move by `n` bytes.
298   298  
299   @par Complexity 299   @par Complexity
300   Linear in the number of buffers crossed. 300   Linear in the number of buffers crossed.
301   301  
302   @param n The number of bytes to move; negative moves backward. 302   @param n The number of bytes to move; negative moves backward.
303   303  
304   @return A reference to `*this`. 304   @return A reference to `*this`.
305   */ 305   */
306   buffers_iterator& 306   buffers_iterator&
HITCBC 307   166 operator+=(difference_type n) noexcept 307   166 operator+=(difference_type n) noexcept
308   { 308   {
HITCBC 309   166 advance(n); 309   166 advance(n);
HITCBC 310   166 return *this; 310   166 return *this;
311   } 311   }
312   312  
313   /** Move back by `n` bytes. 313   /** Move back by `n` bytes.
314   314  
315   @par Complexity 315   @par Complexity
316   Linear in the number of buffers crossed. 316   Linear in the number of buffers crossed.
317   317  
318   @param n The number of bytes to move back. 318   @param n The number of bytes to move back.
319   319  
320   @return A reference to `*this`. 320   @return A reference to `*this`.
321   */ 321   */
322   buffers_iterator& 322   buffers_iterator&
HITCBC 323   132 operator-=(difference_type n) noexcept 323   132 operator-=(difference_type n) noexcept
324   { 324   {
HITCBC 325   132 advance(-n); 325   132 advance(-n);
HITCBC 326   132 return *this; 326   132 return *this;
327   } 327   }
328   328  
329   /// Return an iterator `n` bytes after `it`. 329   /// Return an iterator `n` bytes after `it`.
330   friend 330   friend
331   buffers_iterator 331   buffers_iterator
HITCBC 332   166 operator+(buffers_iterator it, difference_type n) noexcept 332   166 operator+(buffers_iterator it, difference_type n) noexcept
333   { 333   {
HITCBC 334   166 it += n; 334   166 it += n;
HITCBC 335   166 return it; 335   166 return it;
336   } 336   }
337   337  
338   /// Return an iterator `n` bytes after `it`. 338   /// Return an iterator `n` bytes after `it`.
339   friend 339   friend
340   buffers_iterator 340   buffers_iterator
341   operator+(difference_type n, buffers_iterator it) noexcept 341   operator+(difference_type n, buffers_iterator it) noexcept
342   { 342   {
343   it += n; 343   it += n;
344   return it; 344   return it;
345   } 345   }
346   346  
347   /// Return an iterator `n` bytes before `it`. 347   /// Return an iterator `n` bytes before `it`.
348   friend 348   friend
349   buffers_iterator 349   buffers_iterator
HITCBC 350   11 operator-(buffers_iterator it, difference_type n) noexcept 350   11 operator-(buffers_iterator it, difference_type n) noexcept
351   { 351   {
HITCBC 352   11 it -= n; 352   11 it -= n;
HITCBC 353   11 return it; 353   11 return it;
354   } 354   }
355   355  
356   /// Return the number of bytes from `b` to `a`. 356   /// Return the number of bytes from `b` to `a`.
357   friend 357   friend
358   difference_type 358   difference_type
HITCBC 359   264 operator-( 359   264 operator-(
360   buffers_iterator const& a, 360   buffers_iterator const& a,
361   buffers_iterator const& b) noexcept 361   buffers_iterator const& b) noexcept
362   { 362   {
HITCBC 363   264 return static_cast<difference_type>(a.position_) - 363   264 return static_cast<difference_type>(a.position_) -
HITCBC 364   264 static_cast<difference_type>(b.position_); 364   264 static_cast<difference_type>(b.position_);
365   } 365   }
366   366  
367   /// Test whether two iterators refer to the same byte. 367   /// Test whether two iterators refer to the same byte.
368   friend 368   friend
369   bool 369   bool
HITCBC 370   312 operator==( 370   312 operator==(
371   buffers_iterator const& a, 371   buffers_iterator const& a,
372   buffers_iterator const& b) noexcept 372   buffers_iterator const& b) noexcept
373   { 373   {
HITCBC 374   312 return a.position_ == b.position_; 374   312 return a.position_ == b.position_;
375   } 375   }
376   376  
377   /// Order two iterators by byte position. 377   /// Order two iterators by byte position.
378   friend 378   friend
379   std::strong_ordering 379   std::strong_ordering
HITCBC 380   121 operator<=>( 380   121 operator<=>(
381   buffers_iterator const& a, 381   buffers_iterator const& a,
382   buffers_iterator const& b) noexcept 382   buffers_iterator const& b) noexcept
383   { 383   {
HITCBC 384   121 return a.position_ <=> b.position_; 384   121 return a.position_ <=> b.position_;
385   } 385   }
386   }; 386   };
387   387  
388   /** Return an iterator to the first byte of a buffer sequence. 388   /** Return an iterator to the first byte of a buffer sequence.
389   389  
390   @param bs The buffer sequence. It must outlive the iterator. 390   @param bs The buffer sequence. It must outlive the iterator.
391   391  
392   @return `buffers_iterator<BufferSequence>::begin(bs)`. 392   @return `buffers_iterator<BufferSequence>::begin(bs)`.
393   393  
394   @see @ref buffers_iterator, @ref buffers_end 394   @see @ref buffers_iterator, @ref buffers_end
395   */ 395   */
396   template<detail::common_buffer_sequence BufferSequence> 396   template<detail::common_buffer_sequence BufferSequence>
397   buffers_iterator<BufferSequence> 397   buffers_iterator<BufferSequence>
HITCBC 398   17 buffers_begin(BufferSequence const& bs) noexcept 398   17 buffers_begin(BufferSequence const& bs) noexcept
399   { 399   {
HITCBC 400   17 return buffers_iterator<BufferSequence>::begin(bs); 400   17 return buffers_iterator<BufferSequence>::begin(bs);
401   } 401   }
402   402  
403   /// Rejects a temporary sequence, since the iterator would dangle. 403   /// Rejects a temporary sequence, since the iterator would dangle.
404   template<detail::common_buffer_sequence BufferSequence> 404   template<detail::common_buffer_sequence BufferSequence>
405   buffers_iterator<BufferSequence> 405   buffers_iterator<BufferSequence>
406   buffers_begin(BufferSequence const&& bs) = delete; 406   buffers_begin(BufferSequence const&& bs) = delete;
407   407  
408   /** Return an iterator past the last byte of a buffer sequence. 408   /** Return an iterator past the last byte of a buffer sequence.
409   409  
410   @param bs The buffer sequence. It must outlive the iterator. 410   @param bs The buffer sequence. It must outlive the iterator.
411   411  
412   @return `buffers_iterator<BufferSequence>::end(bs)`. 412   @return `buffers_iterator<BufferSequence>::end(bs)`.
413   413  
414   @see @ref buffers_iterator, @ref buffers_begin 414   @see @ref buffers_iterator, @ref buffers_begin
415   */ 415   */
416   template<detail::common_buffer_sequence BufferSequence> 416   template<detail::common_buffer_sequence BufferSequence>
417   buffers_iterator<BufferSequence> 417   buffers_iterator<BufferSequence>
HITCBC 418   24 buffers_end(BufferSequence const& bs) noexcept 418   24 buffers_end(BufferSequence const& bs) noexcept
419   { 419   {
HITCBC 420   24 return buffers_iterator<BufferSequence>::end(bs); 420   24 return buffers_iterator<BufferSequence>::end(bs);
421   } 421   }
422   422  
423   /// Rejects a temporary sequence, since the iterator would dangle. 423   /// Rejects a temporary sequence, since the iterator would dangle.
424   template<detail::common_buffer_sequence BufferSequence> 424   template<detail::common_buffer_sequence BufferSequence>
425   buffers_iterator<BufferSequence> 425   buffers_iterator<BufferSequence>
426   buffers_end(BufferSequence const&& bs) = delete; 426   buffers_end(BufferSequence const&& bs) = delete;
427   427  
428   } // capy 428   } // capy
429   } // boost 429   } // boost
430   430  
431   #endif 431   #endif