100.00% Lines (65/65) 100.00% Functions (21/21)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
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_HPP 10   #ifndef BOOST_CAPY_BUFFERS_HPP
11   #define BOOST_CAPY_BUFFERS_HPP 11   #define BOOST_CAPY_BUFFERS_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <concepts> 14   #include <concepts>
15   #include <cstddef> 15   #include <cstddef>
16   #include <iterator> 16   #include <iterator>
17   #include <memory> 17   #include <memory>
18   #include <ranges> 18   #include <ranges>
19   #include <type_traits> 19   #include <type_traits>
20   20  
21   // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html 21   // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
22   22  
23   namespace boost { 23   namespace boost {
24   24  
25   namespace asio { 25   namespace asio {
26   class const_buffer; 26   class const_buffer;
27   class mutable_buffer; 27   class mutable_buffer;
28   } // asio 28   } // asio
29   29  
30   namespace capy { 30   namespace capy {
31   31  
32   class const_buffer; 32   class const_buffer;
33   class mutable_buffer; 33   class mutable_buffer;
34   34  
35   /** A reference to a contiguous region of writable memory. 35   /** A reference to a contiguous region of writable memory.
36   36  
37   Represents a pointer and size pair for a modifiable byte range. 37   Represents a pointer and size pair for a modifiable byte range.
38   Does not own the memory. Satisfies `MutableBufferSequence` (as a 38   Does not own the memory. Satisfies `MutableBufferSequence` (as a
39   single-element sequence) and is implicitly convertible to 39   single-element sequence) and is implicitly convertible to
40   `const_buffer`. 40   `const_buffer`.
41   41  
42   @see const_buffer, MutableBufferSequence 42   @see const_buffer, MutableBufferSequence
43   */ 43   */
44   class mutable_buffer 44   class mutable_buffer
45   { 45   {
46   unsigned char* p_ = nullptr; 46   unsigned char* p_ = nullptr;
47   std::size_t n_ = 0; 47   std::size_t n_ = 0;
48   48  
49   public: 49   public:
50   /// Construct an empty buffer. 50   /// Construct an empty buffer.
HITCBC 51   29 mutable_buffer() = default; 51   29 mutable_buffer() = default;
52   52  
53   /// Construct a copy. 53   /// Construct a copy.
54   mutable_buffer( 54   mutable_buffer(
55   mutable_buffer const&) = default; 55   mutable_buffer const&) = default;
56   56  
57   /// Assign by copying. 57   /// Assign by copying.
58   mutable_buffer& operator=( 58   mutable_buffer& operator=(
59   mutable_buffer const&) = default; 59   mutable_buffer const&) = default;
60   60  
61   /// Construct from pointer and size. 61   /// Construct from pointer and size.
HITCBC 62   42586 constexpr mutable_buffer( 62   42582 constexpr mutable_buffer(
63   void* data, std::size_t size) noexcept 63   void* data, std::size_t size) noexcept
HITCBC 64   42586 : p_(static_cast<unsigned char*>(data)) 64   42582 : p_(static_cast<unsigned char*>(data))
HITCBC 65   42586 , n_(size) 65   42582 , n_(size)
66   { 66   {
HITCBC 67   42586 } 67   42582 }
68   68  
69   /// Return a pointer to the memory region. 69   /// Return a pointer to the memory region.
HITCBC 70   62341 constexpr void* data() const noexcept 70   62337 constexpr void* data() const noexcept
71   { 71   {
HITCBC 72   62341 return p_; 72   62337 return p_;
73   } 73   }
74   74  
75   /// Return the size in bytes. 75   /// Return the size in bytes.
HITCBC 76   98000 constexpr std::size_t size() const noexcept 76   97996 constexpr std::size_t size() const noexcept
77   { 77   {
HITCBC 78   98000 return n_; 78   97996 return n_;
79   } 79   }
80   80  
81   /** Advance the buffer start, shrinking the region. 81   /** Advance the buffer start, shrinking the region.
82   82  
83   @param n Bytes to skip. Clamped to `size()`. 83   @param n Bytes to skip. Clamped to `size()`.
84   */ 84   */
85   mutable_buffer& 85   mutable_buffer&
HITCBC 86   19809 operator+=(std::size_t n) noexcept 86   19809 operator+=(std::size_t n) noexcept
87   { 87   {
HITCBC 88   19809 if( n > n_) 88   19809 if( n > n_)
HITCBC 89   1 n = n_; 89   1 n = n_;
HITCBC 90   19809 p_ += n; 90   19809 p_ += n;
HITCBC 91   19809 n_ -= n; 91   19809 n_ -= n;
HITCBC 92   19809 return *this; 92   19809 return *this;
93   } 93   }
94   }; 94   };
95   95  
96   /** A reference to a contiguous region of read-only memory. 96   /** A reference to a contiguous region of read-only memory.
97   97  
98   Represents a pointer and size pair for a non-modifiable byte range. 98   Represents a pointer and size pair for a non-modifiable byte range.
99   Does not own the memory. Satisfies `ConstBufferSequence` (as a 99   Does not own the memory. Satisfies `ConstBufferSequence` (as a
100   single-element sequence). Implicitly constructible from 100   single-element sequence). Implicitly constructible from
101   `mutable_buffer`. 101   `mutable_buffer`.
102   102  
103   @see mutable_buffer, ConstBufferSequence 103   @see mutable_buffer, ConstBufferSequence
104   */ 104   */
105   class const_buffer 105   class const_buffer
106   { 106   {
107   unsigned char const* p_ = nullptr; 107   unsigned char const* p_ = nullptr;
108   std::size_t n_ = 0; 108   std::size_t n_ = 0;
109   109  
110   public: 110   public:
111   /// Construct an empty buffer. 111   /// Construct an empty buffer.
HITCBC 112   57 const_buffer() = default; 112   57 const_buffer() = default;
113   113  
114   /// Construct a copy. 114   /// Construct a copy.
115   const_buffer(const_buffer const&) = default; 115   const_buffer(const_buffer const&) = default;
116   116  
117   /// Assign by copying. 117   /// Assign by copying.
118   const_buffer& operator=( 118   const_buffer& operator=(
119   const_buffer const& other) = default; 119   const_buffer const& other) = default;
120   120  
121   /// Construct from pointer and size. 121   /// Construct from pointer and size.
HITCBC 122   43430 constexpr const_buffer( 122   43428 constexpr const_buffer(
123   void const* data, std::size_t size) noexcept 123   void const* data, std::size_t size) noexcept
HITCBC 124   43430 : p_(static_cast<unsigned char const*>(data)) 124   43428 : p_(static_cast<unsigned char const*>(data))
HITCBC 125   43430 , n_(size) 125   43428 , n_(size)
126   { 126   {
HITCBC 127   43430 } 127   43428 }
128   128  
129   /// Construct from mutable_buffer. 129   /// Construct from mutable_buffer.
HITCBC 130   12005 constexpr const_buffer( 130   12005 constexpr const_buffer(
131   mutable_buffer const& b) noexcept 131   mutable_buffer const& b) noexcept
HITCBC 132   12005 : p_(static_cast<unsigned char const*>(b.data())) 132   12005 : p_(static_cast<unsigned char const*>(b.data()))
HITCBC 133   12005 , n_(b.size()) 133   12005 , n_(b.size())
134   { 134   {
HITCBC 135   12005 } 135   12005 }
136   136  
137   /// Return a pointer to the memory region. 137   /// Return a pointer to the memory region.
HITCBC 138   59972 constexpr void const* data() const noexcept 138   59971 constexpr void const* data() const noexcept
139   { 139   {
HITCBC 140   59972 return p_; 140   59971 return p_;
141   } 141   }
142   142  
143   /// Return the size in bytes. 143   /// Return the size in bytes.
HITCBC 144   112907 constexpr std::size_t size() const noexcept 144   112905 constexpr std::size_t size() const noexcept
145   { 145   {
HITCBC 146   112907 return n_; 146   112905 return n_;
147   } 147   }
148   148  
149   /** Advance the buffer start, shrinking the region. 149   /** Advance the buffer start, shrinking the region.
150   150  
151   @param n Bytes to skip. Clamped to `size()`. 151   @param n Bytes to skip. Clamped to `size()`.
152   */ 152   */
153   const_buffer& 153   const_buffer&
HITCBC 154   19810 operator+=(std::size_t n) noexcept 154   19810 operator+=(std::size_t n) noexcept
155   { 155   {
HITCBC 156   19810 if( n > n_) 156   19810 if( n > n_)
HITCBC 157   1 n = n_; 157   1 n = n_;
HITCBC 158   19810 p_ += n; 158   19810 p_ += n;
HITCBC 159   19810 n_ -= n; 159   19810 n_ -= n;
HITCBC 160   19810 return *this; 160   19810 return *this;
161   } 161   }
162   }; 162   };
163   163  
164   /** Concept for sequences of read-only buffer regions. 164   /** Concept for sequences of read-only buffer regions.
165   165  
166   A type satisfies `ConstBufferSequence` if it represents one or more 166   A type satisfies `ConstBufferSequence` if it represents one or more
167   contiguous memory regions that can be read. This includes single 167   contiguous memory regions that can be read. This includes single
168   buffers (convertible to `const_buffer`) and ranges of buffers. 168   buffers (convertible to `const_buffer`) and ranges of buffers.
169   169  
170   @par Syntactic Requirements 170   @par Syntactic Requirements
171   @li Convertible to `const_buffer`, OR 171   @li Convertible to `const_buffer`, OR
172   @li A bidirectional range with value type convertible to `const_buffer` 172   @li A bidirectional range with value type convertible to `const_buffer`
173   173  
174   @see const_buffer, MutableBufferSequence 174   @see const_buffer, MutableBufferSequence
175   */ 175   */
176   template<typename T> 176   template<typename T>
177   concept ConstBufferSequence = 177   concept ConstBufferSequence =
178   std::is_convertible_v<T, const_buffer> || ( 178   std::is_convertible_v<T, const_buffer> || (
179   std::ranges::bidirectional_range<T> && 179   std::ranges::bidirectional_range<T> &&
180   std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>); 180   std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
181   181  
182   /** Concept for sequences of writable buffer regions. 182   /** Concept for sequences of writable buffer regions.
183   183  
184   A type satisfies `MutableBufferSequence` if it represents one or more 184   A type satisfies `MutableBufferSequence` if it represents one or more
185   contiguous memory regions that can be written. This includes single 185   contiguous memory regions that can be written. This includes single
186   buffers (convertible to `mutable_buffer`) and ranges of buffers. 186   buffers (convertible to `mutable_buffer`) and ranges of buffers.
187   Every `MutableBufferSequence` also satisfies `ConstBufferSequence`. 187   Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
188   188  
189   @par Syntactic Requirements 189   @par Syntactic Requirements
190   @li Convertible to `mutable_buffer`, OR 190   @li Convertible to `mutable_buffer`, OR
191   @li A bidirectional range with value type convertible to `mutable_buffer` 191   @li A bidirectional range with value type convertible to `mutable_buffer`
192   192  
193   @see mutable_buffer, ConstBufferSequence 193   @see mutable_buffer, ConstBufferSequence
194   */ 194   */
195   template<typename T> 195   template<typename T>
196   concept MutableBufferSequence = 196   concept MutableBufferSequence =
197   std::is_convertible_v<T, mutable_buffer> || ( 197   std::is_convertible_v<T, mutable_buffer> || (
198   std::ranges::bidirectional_range<T> && 198   std::ranges::bidirectional_range<T> &&
199   std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>); 199   std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
200   200  
201   /** Return an iterator to the first buffer in a sequence. 201   /** Return an iterator to the first buffer in a sequence.
202   202  
203   @functionobject 203   @functionobject
204   */ 204   */
205   constexpr struct 205   constexpr struct
206   { 206   {
207   /** Return a pointer to a single buffer, forming a one-element range. 207   /** Return a pointer to a single buffer, forming a one-element range.
208   208  
209   @param b A single buffer. 209   @param b A single buffer.
210   210  
211   @return A pointer to `b`. 211   @return A pointer to `b`.
212   */ 212   */
213   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 213   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 214   13219 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 214   13219 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
215   { 215   {
HITCBC 216   13219 return std::addressof(b); 216   13219 return std::addressof(b);
217   } 217   }
218   218  
219   /** Return an iterator to the first buffer of a sequence. 219   /** Return an iterator to the first buffer of a sequence.
220   220  
221   @param bs The buffer sequence. 221   @param bs The buffer sequence.
222   222  
223   @return An iterator to the first buffer of `bs`. 223   @return An iterator to the first buffer of `bs`.
224   */ 224   */
225   template<ConstBufferSequence BS> 225   template<ConstBufferSequence BS>
226   requires (!std::convertible_to<BS, const_buffer>) 226   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 227   46371 auto operator()(BS const& bs) const noexcept 227   46371 auto operator()(BS const& bs) const noexcept
228   { 228   {
HITCBC 229   46371 return std::ranges::begin(bs); 229   46371 return std::ranges::begin(bs);
230   } 230   }
231   231  
232   /** Return an iterator to the first buffer of a sequence. 232   /** Return an iterator to the first buffer of a sequence.
233   233  
234   @param bs The buffer sequence. 234   @param bs The buffer sequence.
235   235  
236   @return An iterator to the first buffer of `bs`. 236   @return An iterator to the first buffer of `bs`.
237   */ 237   */
238   template<ConstBufferSequence BS> 238   template<ConstBufferSequence BS>
239   requires (!std::convertible_to<BS, const_buffer>) 239   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 240   9192 auto operator()(BS& bs) const noexcept 240   9192 auto operator()(BS& bs) const noexcept
241   { 241   {
HITCBC 242   9192 return std::ranges::begin(bs); 242   9192 return std::ranges::begin(bs);
243   } 243   }
244   } begin {}; 244   } begin {};
245   245  
246   /** Return an iterator past the last buffer in a sequence. 246   /** Return an iterator past the last buffer in a sequence.
247   247  
248   @functionobject 248   @functionobject
249   */ 249   */
250   constexpr struct 250   constexpr struct
251   { 251   {
252   /** Return a pointer one past a single buffer, forming a one-element range. 252   /** Return a pointer one past a single buffer, forming a one-element range.
253   253  
254   @param b A single buffer. 254   @param b A single buffer.
255   255  
256   @return A pointer one past `b`. 256   @return A pointer one past `b`.
257   */ 257   */
258   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 258   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 259   12957 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 259   12957 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
260   { 260   {
HITCBC 261   12957 return std::addressof(b) + 1; 261   12957 return std::addressof(b) + 1;
262   } 262   }
263   263  
264   /** Return an iterator past the last buffer of a sequence. 264   /** Return an iterator past the last buffer of a sequence.
265   265  
266   @param bs The buffer sequence. 266   @param bs The buffer sequence.
267   267  
268   @return An iterator one past the last buffer of `bs`. 268   @return An iterator one past the last buffer of `bs`.
269   */ 269   */
270   template<ConstBufferSequence BS> 270   template<ConstBufferSequence BS>
271   requires (!std::convertible_to<BS, const_buffer>) 271   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 272   46371 auto operator()(BS const& bs) const noexcept 272   46371 auto operator()(BS const& bs) const noexcept
273   { 273   {
HITCBC 274   46371 return std::ranges::end(bs); 274   46371 return std::ranges::end(bs);
275   } 275   }
276   276  
277   /** Return an iterator past the last buffer of a sequence. 277   /** Return an iterator past the last buffer of a sequence.
278   278  
279   @param bs The buffer sequence. 279   @param bs The buffer sequence.
280   280  
281   @return An iterator one past the last buffer of `bs`. 281   @return An iterator one past the last buffer of `bs`.
282   */ 282   */
283   template<ConstBufferSequence BS> 283   template<ConstBufferSequence BS>
284   requires (!std::convertible_to<BS, const_buffer>) 284   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 285   9192 auto operator()(BS& bs) const noexcept 285   9192 auto operator()(BS& bs) const noexcept
286   { 286   {
HITCBC 287   9192 return std::ranges::end(bs); 287   9192 return std::ranges::end(bs);
288   } 288   }
289   } end {}; 289   } end {};
290   290  
291   /** Return the total byte count across all buffers in a sequence. 291   /** Return the total byte count across all buffers in a sequence.
292   292  
293   @functionobject 293   @functionobject
294   */ 294   */
295   constexpr struct 295   constexpr struct
296   { 296   {
297   // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array 297   // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
298   // when iterating here. The class uses union storage with placement 298   // when iterating here. The class uses union storage with placement
299   // new for slots 0..n_-1, so reads inside this bounded loop are 299   // new for slots 0..n_-1, so reads inside this bounded loop are
300   // well-defined, but the optimizer can't prove the loop bound and 300   // well-defined, but the optimizer can't prove the loop bound and
301   // warns. The runtime cost of value-initializing all N slots is 301   // warns. The runtime cost of value-initializing all N slots is
302   // non-trivial for non-trivial value types, so we suppress instead. 302   // non-trivial for non-trivial value types, so we suppress instead.
303   #if defined(__GNUC__) && !defined(__clang__) 303   #if defined(__GNUC__) && !defined(__clang__)
304   #pragma GCC diagnostic push 304   #pragma GCC diagnostic push
305   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 305   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
306   #endif 306   #endif
307   /** Return the total byte count across all buffers in a sequence. 307   /** Return the total byte count across all buffers in a sequence.
308   308  
309   Sums the `size()` of each buffer in the sequence. This differs 309   Sums the `size()` of each buffer in the sequence. This differs
310   from `buffer_length` which counts the number of buffer elements. 310   from `buffer_length` which counts the number of buffer elements.
311   311  
312   @param bs The buffer sequence. 312   @param bs The buffer sequence.
313   313  
314   @return The sum of the sizes of all buffers in `bs`. 314   @return The sum of the sizes of all buffers in `bs`.
315   315  
316   @par Example 316   @par Example
317   @code 317   @code
318   std::array<mutable_buffer, 2> bufs = { ... }; 318   std::array<mutable_buffer, 2> bufs = { ... };
319   std::size_t total = buffer_size( bufs ); // sum of both sizes 319   std::size_t total = buffer_size( bufs ); // sum of both sizes
320   @endcode 320   @endcode
321   */ 321   */
322   template<ConstBufferSequence CB> 322   template<ConstBufferSequence CB>
HITCBC 323   12825 constexpr std::size_t operator()( 323   12825 constexpr std::size_t operator()(
324   CB const& bs) const noexcept 324   CB const& bs) const noexcept
325   { 325   {
HITCBC 326   12825 std::size_t n = 0; 326   12825 std::size_t n = 0;
HITCBC 327   12825 auto const e = capy::end(bs); 327   12825 auto const e = capy::end(bs);
HITCBC 328   27177 for(auto it = capy::begin(bs); it != e; ++it) 328   27177 for(auto it = capy::begin(bs); it != e; ++it)
HITCBC 329   14352 n += const_buffer(*it).size(); 329   14352 n += const_buffer(*it).size();
HITCBC 330   12825 return n; 330   12825 return n;
331   } 331   }
332   #if defined(__GNUC__) && !defined(__clang__) 332   #if defined(__GNUC__) && !defined(__clang__)
333   #pragma GCC diagnostic pop 333   #pragma GCC diagnostic pop
334   #endif 334   #endif
335   } buffer_size {}; 335   } buffer_size {};
336   336  
337   /** Check if a buffer sequence contains no data. 337   /** Check if a buffer sequence contains no data.
338   338  
339   @functionobject 339   @functionobject
340   */ 340   */
341   constexpr struct 341   constexpr struct
342   { 342   {
343   // See note on buffer_size above — same union-storage false positive. 343   // See note on buffer_size above — same union-storage false positive.
344   #if defined(__GNUC__) && !defined(__clang__) 344   #if defined(__GNUC__) && !defined(__clang__)
345   #pragma GCC diagnostic push 345   #pragma GCC diagnostic push
346   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 346   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
347   #endif 347   #endif
348   /** Check if a buffer sequence contains no data. 348   /** Check if a buffer sequence contains no data.
349   349  
350   @param bs The buffer sequence. 350   @param bs The buffer sequence.
351   351  
352   @return `true` if all buffers have size zero or the sequence 352   @return `true` if all buffers have size zero or the sequence
353   is empty. 353   is empty.
354   */ 354   */
355   template<ConstBufferSequence CB> 355   template<ConstBufferSequence CB>
HITCBC 356   4263 constexpr bool operator()( 356   4263 constexpr bool operator()(
357   CB const& bs) const noexcept 357   CB const& bs) const noexcept
358   { 358   {
HITCBC 359   4263 auto it = begin(bs); 359   4263 auto it = begin(bs);
HITCBC 360   4263 auto const end_ = end(bs); 360   4263 auto const end_ = end(bs);
HITCBC 361   4304 while(it != end_) 361   4304 while(it != end_)
362   { 362   {
HITCBC 363   4264 const_buffer b(*it++); 363   4264 const_buffer b(*it++);
HITCBC 364   4264 if(b.size() != 0) 364   4264 if(b.size() != 0)
HITCBC 365   4223 return false; 365   4223 return false;
366   } 366   }
HITCBC 367   40 return true; 367   40 return true;
368   } 368   }
369   #if defined(__GNUC__) && !defined(__clang__) 369   #if defined(__GNUC__) && !defined(__clang__)
370   #pragma GCC diagnostic pop 370   #pragma GCC diagnostic pop
371   #endif 371   #endif
372   } buffer_empty {}; 372   } buffer_empty {};
373   373  
374   namespace detail { 374   namespace detail {
375   375  
376   template<class It> 376   template<class It>
377   auto 377   auto
HITCBC 378   263 length_impl(It first, It last, int) 378   263 length_impl(It first, It last, int)
379   -> decltype(static_cast<std::size_t>(last - first)) 379   -> decltype(static_cast<std::size_t>(last - first))
380   { 380   {
HITCBC 381   263 return static_cast<std::size_t>(last - first); 381   263 return static_cast<std::size_t>(last - first);
382   } 382   }
383   383  
384   template<class It> 384   template<class It>
385   std::size_t 385   std::size_t
386   length_impl(It first, It last, long) 386   length_impl(It first, It last, long)
387   { 387   {
388   std::size_t n = 0; 388   std::size_t n = 0;
389   while(first != last) 389   while(first != last)
390   { 390   {
391   ++first; 391   ++first;
392   ++n; 392   ++n;
393   } 393   }
394   return n; 394   return n;
395   } 395   }
396   396  
397   } // detail 397   } // detail
398   398  
399   /** Return the number of buffer elements in a sequence. 399   /** Return the number of buffer elements in a sequence.
400   400  
401   Counts the number of individual buffer objects, not bytes. 401   Counts the number of individual buffer objects, not bytes.
402   For a single buffer, returns 1. For a range, returns the 402   For a single buffer, returns 1. For a range, returns the
403   distance from `begin` to `end`. 403   distance from `begin` to `end`.
404   404  
405   @see buffer_size 405   @see buffer_size
406   */ 406   */
407   template<ConstBufferSequence CB> 407   template<ConstBufferSequence CB>
408   std::size_t 408   std::size_t
HITCBC 409   263 buffer_length(CB const& bs) 409   263 buffer_length(CB const& bs)
410   { 410   {
HITCBC 411   263 return detail::length_impl( 411   263 return detail::length_impl(
HITCBC 412   263 begin(bs), end(bs), 0); 412   263 begin(bs), end(bs), 0);
413   } 413   }
414   414  
415   /// Alias for `mutable_buffer` or `const_buffer` based on sequence type. 415   /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
416   template<typename BS> 416   template<typename BS>
417   using buffer_type = std::conditional_t< 417   using buffer_type = std::conditional_t<
418   MutableBufferSequence<BS>, 418   MutableBufferSequence<BS>,
419   mutable_buffer, const_buffer>; 419   mutable_buffer, const_buffer>;
420   420  
421   } // capy 421   } // capy
422   } // boost 422   } // boost
423   423  
424   #endif 424   #endif