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