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   42582 constexpr mutable_buffer( 62   42586 constexpr mutable_buffer(
63   void* data, std::size_t size) noexcept 63   void* data, std::size_t size) noexcept
HITCBC 64   42582 : p_(static_cast<unsigned char*>(data)) 64   42586 : p_(static_cast<unsigned char*>(data))
HITCBC 65   42582 , n_(size) 65   42586 , n_(size)
66   { 66   {
HITCBC 67   42582 } 67   42586 }
68   68  
69   /// Return a pointer to the memory region. 69   /// Return a pointer to the memory region.
HITCBC 70   62337 constexpr void* data() const noexcept 70   62341 constexpr void* data() const noexcept
71   { 71   {
HITCBC 72   62337 return p_; 72   62341 return p_;
73   } 73   }
74   74  
75   /// Return the size in bytes. 75   /// Return the size in bytes.
HITCBC 76   97996 constexpr std::size_t size() const noexcept 76   98000 constexpr std::size_t size() const noexcept
77   { 77   {
HITCBC 78   97996 return n_; 78   98000 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   43428 constexpr const_buffer( 122   43430 constexpr const_buffer(
123   void const* data, std::size_t size) noexcept 123   void const* data, std::size_t size) noexcept
HITCBC 124   43428 : p_(static_cast<unsigned char const*>(data)) 124   43430 : p_(static_cast<unsigned char const*>(data))
HITCBC 125   43428 , n_(size) 125   43430 , n_(size)
126   { 126   {
HITCBC 127   43428 } 127   43430 }
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   59971 constexpr void const* data() const noexcept 138   59972 constexpr void const* data() const noexcept
139   { 139   {
HITCBC 140   59971 return p_; 140   59972 return p_;
141   } 141   }
142   142  
143   /// Return the size in bytes. 143   /// Return the size in bytes.
HITCBC 144   112905 constexpr std::size_t size() const noexcept 144   112907 constexpr std::size_t size() const noexcept
145   { 145   {
HITCBC 146   112905 return n_; 146   112907 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 + Handles single buffers and ranges uniformly. For a single buffer,
  204 + returns a pointer to it (forming a one-element range).
204   */ 205   */
205 - constexpr struct 206 + constexpr struct begin_mrdocs_workaround_t
206 - /** Return a pointer to a single buffer, forming a one-element range.  
207 -  
208 - @param b A single buffer.  
209 -  
210 - @return A pointer to `b`.  
211 - */  
212   { 207   {
213   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 208   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 214   13219 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 209   13219 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
215   { 210   {
HITCBC 216   13219 return std::addressof(b); 211   13219 return std::addressof(b);
217   } 212   }
218 - /** Return an iterator to the first buffer of a sequence.  
219 -  
220 - @param bs The buffer sequence.  
221 -  
222 - @return An iterator to the first buffer of `bs`.  
223 - */  
224   213  
225   template<ConstBufferSequence BS> 214   template<ConstBufferSequence BS>
226   requires (!std::convertible_to<BS, const_buffer>) 215   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 227   46371 auto operator()(BS const& bs) const noexcept 216   46371 auto operator()(BS const& bs) const noexcept
228   { 217   {
HITCBC 229   46371 return std::ranges::begin(bs); 218   46371 return std::ranges::begin(bs);
230   } 219   }
231 - /** Return an iterator to the first buffer of a sequence.  
232 -  
233 - @param bs The buffer sequence.  
234 -  
235 - @return An iterator to the first buffer of `bs`.  
236 - */  
237   220  
238   template<ConstBufferSequence BS> 221   template<ConstBufferSequence BS>
239   requires (!std::convertible_to<BS, const_buffer>) 222   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 240   9192 auto operator()(BS& bs) const noexcept 223   9192 auto operator()(BS& bs) const noexcept
241   { 224   {
HITCBC 242   9192 return std::ranges::begin(bs); 225   9192 return std::ranges::begin(bs);
243   } 226   }
244   } begin {}; 227   } begin {};
245   228  
246   /** Return an iterator past the last buffer in a sequence. 229   /** Return an iterator past the last buffer in a sequence.
247   230  
248 - @functionobject 231 + Handles single buffers and ranges uniformly. For a single buffer,
  232 + returns a pointer one past it.
249   */ 233   */
250 - constexpr struct 234 + constexpr struct end_mrdocs_workaround_t
251 - /** Return a pointer one past a single buffer, forming a one-element range.  
252 -  
253 - @param b A single buffer.  
254 -  
255 - @return A pointer one past `b`.  
256 - */  
257   { 235   {
258   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 236   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 259   12957 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 237   12957 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
260   { 238   {
HITCBC 261   12957 return std::addressof(b) + 1; 239   12957 return std::addressof(b) + 1;
262   } 240   }
263 - /** Return an iterator past the last buffer of a sequence.  
264 -  
265 - @param bs The buffer sequence.  
266 -  
267 - @return An iterator one past the last buffer of `bs`.  
268 - */  
269   241  
270   template<ConstBufferSequence BS> 242   template<ConstBufferSequence BS>
271   requires (!std::convertible_to<BS, const_buffer>) 243   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 272   46371 auto operator()(BS const& bs) const noexcept 244   46371 auto operator()(BS const& bs) const noexcept
273   { 245   {
HITCBC 274   46371 return std::ranges::end(bs); 246   46371 return std::ranges::end(bs);
275   } 247   }
276 - /** Return an iterator past the last buffer of a sequence.  
277 -  
278 - @param bs The buffer sequence.  
279 -  
280 - @return An iterator one past the last buffer of `bs`.  
281 - */  
282   248  
283   template<ConstBufferSequence BS> 249   template<ConstBufferSequence BS>
284   requires (!std::convertible_to<BS, const_buffer>) 250   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 285   9192 auto operator()(BS& bs) const noexcept 251   9192 auto operator()(BS& bs) const noexcept
286   { 252   {
HITCBC 287   9192 return std::ranges::end(bs); 253   9192 return std::ranges::end(bs);
288   } 254   }
289   } end {}; 255   } end {};
290   256  
291   /** Return the total byte count across all buffers in a sequence. 257   /** Return the total byte count across all buffers in a sequence.
292   258  
293 - @functionobject 259 + Sums the `size()` of each buffer in the sequence. This differs
  260 + from `buffer_length` which counts the number of buffer elements.
  261 +
  262 + @par Example
  263 + @code
  264 + std::array<mutable_buffer, 2> bufs = { ... };
  265 + std::size_t total = buffer_size( bufs ); // sum of both sizes
  266 + @endcode
294   */ 267   */
295 - constexpr struct 268 + constexpr struct buffer_size_mrdocs_workaround_t
296   { 269   {
297   // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array 270   // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
298   // when iterating here. The class uses union storage with placement 271   // when iterating here. The class uses union storage with placement
299   // new for slots 0..n_-1, so reads inside this bounded loop are 272   // 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 273   // well-defined, but the optimizer can't prove the loop bound and
301   // warns. The runtime cost of value-initializing all N slots is 274   // warns. The runtime cost of value-initializing all N slots is
302   // non-trivial for non-trivial value types, so we suppress instead. 275   // non-trivial for non-trivial value types, so we suppress instead.
303   #if defined(__GNUC__) && !defined(__clang__) 276   #if defined(__GNUC__) && !defined(__clang__)
304   #pragma GCC diagnostic push 277   #pragma GCC diagnostic push
305   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 278   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
306 - /** Return the total byte count across all buffers in a sequence.  
307 -  
308 - Sums the `size()` of each buffer in the sequence. This differs  
309 - from `buffer_length` which counts the number of buffer elements.  
310 -  
311 - @param bs The buffer sequence.  
312 -  
313 - @return The sum of the sizes of all buffers in `bs`.  
314 -  
315 - @par Example  
316 - @code  
317 - std::array<mutable_buffer, 2> bufs = { ... };  
318 - std::size_t total = buffer_size( bufs ); // sum of both sizes  
319 - @endcode  
320 - */  
321   #endif 279   #endif
322   template<ConstBufferSequence CB> 280   template<ConstBufferSequence CB>
HITCBC 323   12825 constexpr std::size_t operator()( 281   12825 constexpr std::size_t operator()(
324   CB const& bs) const noexcept 282   CB const& bs) const noexcept
325   { 283   {
HITCBC 326   12825 std::size_t n = 0; 284   12825 std::size_t n = 0;
HITCBC 327   12825 auto const e = capy::end(bs); 285   12825 auto const e = capy::end(bs);
HITCBC 328   27177 for(auto it = capy::begin(bs); it != e; ++it) 286   27177 for(auto it = capy::begin(bs); it != e; ++it)
HITCBC 329   14352 n += const_buffer(*it).size(); 287   14352 n += const_buffer(*it).size();
HITCBC 330   12825 return n; 288   12825 return n;
331   } 289   }
332   #if defined(__GNUC__) && !defined(__clang__) 290   #if defined(__GNUC__) && !defined(__clang__)
333   #pragma GCC diagnostic pop 291   #pragma GCC diagnostic pop
334   #endif 292   #endif
335   } buffer_size {}; 293   } buffer_size {};
336   294  
337   /** Check if a buffer sequence contains no data. 295   /** Check if a buffer sequence contains no data.
338   296  
339 - @functionobject 297 + @return `true` if all buffers have size zero or the sequence
  298 + is empty.
340   */ 299   */
341 - constexpr struct 300 + constexpr struct buffer_empty_mrdocs_workaround_t
342   { 301   {
343   // See note on buffer_size above — same union-storage false positive. 302   // See note on buffer_size above — same union-storage false positive.
344   #if defined(__GNUC__) && !defined(__clang__) 303   #if defined(__GNUC__) && !defined(__clang__)
345   #pragma GCC diagnostic push 304   #pragma GCC diagnostic push
346   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 305   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
347 - /** Check if a buffer sequence contains no data.  
348 -  
349 - @param bs The buffer sequence.  
350 -  
351 - @return `true` if all buffers have size zero or the sequence  
352 - is empty.  
353 - */  
354   #endif 306   #endif
355   template<ConstBufferSequence CB> 307   template<ConstBufferSequence CB>
HITCBC 356   4263 constexpr bool operator()( 308   4263 constexpr bool operator()(
357   CB const& bs) const noexcept 309   CB const& bs) const noexcept
358   { 310   {
HITCBC 359   4263 auto it = begin(bs); 311   4263 auto it = begin(bs);
HITCBC 360   4263 auto const end_ = end(bs); 312   4263 auto const end_ = end(bs);
HITCBC 361   4304 while(it != end_) 313   4304 while(it != end_)
362   { 314   {
HITCBC 363   4264 const_buffer b(*it++); 315   4264 const_buffer b(*it++);
HITCBC 364   4264 if(b.size() != 0) 316   4264 if(b.size() != 0)
HITCBC 365   4223 return false; 317   4223 return false;
366   } 318   }
HITCBC 367   40 return true; 319   40 return true;
368   } 320   }
369   #if defined(__GNUC__) && !defined(__clang__) 321   #if defined(__GNUC__) && !defined(__clang__)
370   #pragma GCC diagnostic pop 322   #pragma GCC diagnostic pop
371   #endif 323   #endif
372   } buffer_empty {}; 324   } buffer_empty {};
373   325  
374   namespace detail { 326   namespace detail {
375   327  
376   template<class It> 328   template<class It>
377   auto 329   auto
HITCBC 378   263 length_impl(It first, It last, int) 330   263 length_impl(It first, It last, int)
379   -> decltype(static_cast<std::size_t>(last - first)) 331   -> decltype(static_cast<std::size_t>(last - first))
380   { 332   {
HITCBC 381   263 return static_cast<std::size_t>(last - first); 333   263 return static_cast<std::size_t>(last - first);
382   } 334   }
383   335  
384   template<class It> 336   template<class It>
385   std::size_t 337   std::size_t
386   length_impl(It first, It last, long) 338   length_impl(It first, It last, long)
387   { 339   {
388   std::size_t n = 0; 340   std::size_t n = 0;
389   while(first != last) 341   while(first != last)
390   { 342   {
391   ++first; 343   ++first;
392   ++n; 344   ++n;
393   } 345   }
394   return n; 346   return n;
395   } 347   }
396   348  
397   } // detail 349   } // detail
398   350  
399   /** Return the number of buffer elements in a sequence. 351   /** Return the number of buffer elements in a sequence.
400   352  
401   Counts the number of individual buffer objects, not bytes. 353   Counts the number of individual buffer objects, not bytes.
402   For a single buffer, returns 1. For a range, returns the 354   For a single buffer, returns 1. For a range, returns the
403   distance from `begin` to `end`. 355   distance from `begin` to `end`.
404   356  
405   @see buffer_size 357   @see buffer_size
406   */ 358   */
407   template<ConstBufferSequence CB> 359   template<ConstBufferSequence CB>
408   std::size_t 360   std::size_t
HITCBC 409   263 buffer_length(CB const& bs) 361   263 buffer_length(CB const& bs)
410   { 362   {
HITCBC 411   263 return detail::length_impl( 363   263 return detail::length_impl(
HITCBC 412   263 begin(bs), end(bs), 0); 364   263 begin(bs), end(bs), 0);
413   } 365   }
414   366  
415   /// Alias for `mutable_buffer` or `const_buffer` based on sequence type. 367   /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
416   template<typename BS> 368   template<typename BS>
417   using buffer_type = std::conditional_t< 369   using buffer_type = std::conditional_t<
418   MutableBufferSequence<BS>, 370   MutableBufferSequence<BS>,
419   mutable_buffer, const_buffer>; 371   mutable_buffer, const_buffer>;
420   372  
421   } // capy 373   } // capy
422   } // boost 374   } // boost
423   375  
424   #endif 376   #endif