96.92% Lines (63/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 - /// Tag type for customizing `buffer_size` via `tag_invoke`.  
35 - struct size_tag {};  
36 -  
37 - /// Tag type for customizing slice operations via `tag_invoke`.  
38 - struct slice_tag {};  
39 -  
40 - /** Constants for slice customization.  
41 -  
42 - Passed to `tag_invoke` overloads to specify which portion  
43 - of a buffer sequence to retain.  
44 - */  
45 - enum class slice_how  
46 - {  
47 - /// Remove bytes from the front of the sequence.  
48 - remove_prefix,  
49 -  
50 - /// Keep only the first N bytes.  
51 - keep_prefix  
52 - };  
53 -  
54   34  
55   /** A reference to a contiguous region of writable memory. 35   /** A reference to a contiguous region of writable memory.
56   36  
57   Represents a pointer and size pair for a modifiable byte range. 37   Represents a pointer and size pair for a modifiable byte range.
58   Does not own the memory. Satisfies `MutableBufferSequence` (as a 38   Does not own the memory. Satisfies `MutableBufferSequence` (as a
59   single-element sequence) and is implicitly convertible to 39   single-element sequence) and is implicitly convertible to
60   `const_buffer`. 40   `const_buffer`.
61   41  
62   @see const_buffer, MutableBufferSequence 42   @see const_buffer, MutableBufferSequence
63   */ 43   */
64   class mutable_buffer 44   class mutable_buffer
65   { 45   {
66   unsigned char* p_ = nullptr; 46   unsigned char* p_ = nullptr;
67   std::size_t n_ = 0; 47   std::size_t n_ = 0;
68   48  
69   public: 49   public:
70   /// Construct an empty buffer. 50   /// Construct an empty buffer.
HITCBC 71   603 mutable_buffer() = default; 51   29 mutable_buffer() = default;
72   52  
73   /// Construct a copy. 53   /// Construct a copy.
74   mutable_buffer( 54   mutable_buffer(
75   mutable_buffer const&) = default; 55   mutable_buffer const&) = default;
76   56  
77   /// Assign by copying. 57   /// Assign by copying.
78   mutable_buffer& operator=( 58   mutable_buffer& operator=(
79   mutable_buffer const&) = default; 59   mutable_buffer const&) = default;
80   60  
81   /// Construct from pointer and size. 61   /// Construct from pointer and size.
HITCBC 82   29246 constexpr mutable_buffer( 62   42543 constexpr mutable_buffer(
83   void* data, std::size_t size) noexcept 63   void* data, std::size_t size) noexcept
HITCBC 84   29246 : p_(static_cast<unsigned char*>(data)) 64   42543 : p_(static_cast<unsigned char*>(data))
HITCBC 85   29246 , n_(size) 65   42543 , n_(size)
86   { 66   {
HITCBC 87   29246 } 67   42543 }
88   68  
89   /// Return a pointer to the memory region. 69   /// Return a pointer to the memory region.
HITCBC 90   72638 constexpr void* data() const noexcept 70   62282 constexpr void* data() const noexcept
91   { 71   {
HITCBC 92   72638 return p_; 72   62282 return p_;
93   } 73   }
94   74  
95   /// Return the size in bytes. 75   /// Return the size in bytes.
HITCBC 96   112749 constexpr std::size_t size() const noexcept 76   97921 constexpr std::size_t size() const noexcept
97   { 77   {
HITCBC 98   112749 return n_; 78   97921 return n_;
99   } 79   }
100   80  
101   /** Advance the buffer start, shrinking the region. 81   /** Advance the buffer start, shrinking the region.
102   82  
103   @param n Bytes to skip. Clamped to `size()`. 83   @param n Bytes to skip. Clamped to `size()`.
104   */ 84   */
105   mutable_buffer& 85   mutable_buffer&
HITCBC 106   26544 operator+=(std::size_t n) noexcept 86   19795 operator+=(std::size_t n) noexcept
107   { 87   {
HITCBC 108   26544 if( n > n_) 88   19795 if( n > n_)
MISLBC 109   17 n = n_; 89   n = n_;
HITCBC 110   26544 p_ += n; 90   19795 p_ += n;
HITCBC 111   26544 n_ -= n; 91   19795 n_ -= n;
HITCBC 112   26544 return *this; 92   19795 return *this;
113 -  
114 - /// Slice customization point for `tag_invoke`.  
115 - friend  
116 - void  
117 - tag_invoke(  
DCB 118 - 1335 slice_tag const&,  
119 - mutable_buffer& b,  
120 - slice_how how,  
121 - std::size_t n) noexcept  
122 - {  
123 - b.do_slice(how, n);  
DCB 124 - 1335 }  
DCB 125 - 1335  
126 - private:  
127 - void do_slice(  
DCB 128 - 1335 slice_how how, std::size_t n) noexcept  
129 - {  
130 - switch(how)  
DCB 131 - 1335 {  
132 - case slice_how::remove_prefix:  
DCB 133 - 659 *this += n;  
DCB 134 - 659 return;  
DCB 135 - 659  
136 - case slice_how::keep_prefix:  
DCB 137 - 676 if( n < n_)  
DCB 138 - 676 n_ = n;  
DCB 139 - 584 return;  
DCB 140 - 676 }  
141 - }  
142   } 93   }
143   }; 94   };
144   95  
145   /** A reference to a contiguous region of read-only memory. 96   /** A reference to a contiguous region of read-only memory.
146   97  
147   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.
148   Does not own the memory. Satisfies `ConstBufferSequence` (as a 99   Does not own the memory. Satisfies `ConstBufferSequence` (as a
149   single-element sequence). Implicitly constructible from 100   single-element sequence). Implicitly constructible from
150   `mutable_buffer`. 101   `mutable_buffer`.
151   102  
152   @see mutable_buffer, ConstBufferSequence 103   @see mutable_buffer, ConstBufferSequence
153   */ 104   */
154   class const_buffer 105   class const_buffer
155   { 106   {
156   unsigned char const* p_ = nullptr; 107   unsigned char const* p_ = nullptr;
157   std::size_t n_ = 0; 108   std::size_t n_ = 0;
158   109  
159   public: 110   public:
160   /// Construct an empty buffer. 111   /// Construct an empty buffer.
HITCBC 161   631 const_buffer() = default; 112   57 const_buffer() = default;
162   113  
163   /// Construct a copy. 114   /// Construct a copy.
164   const_buffer(const_buffer const&) = default; 115   const_buffer(const_buffer const&) = default;
165   116  
166   /// Assign by copying. 117   /// Assign by copying.
167   const_buffer& operator=( 118   const_buffer& operator=(
168   const_buffer const& other) = default; 119   const_buffer const& other) = default;
169   120  
170   /// Construct from pointer and size. 121   /// Construct from pointer and size.
HITCBC 171   25004 constexpr const_buffer( 122   43395 constexpr const_buffer(
172   void const* data, std::size_t size) noexcept 123   void const* data, std::size_t size) noexcept
HITCBC 173   25004 : p_(static_cast<unsigned char const*>(data)) 124   43395 : p_(static_cast<unsigned char const*>(data))
HITCBC 174   25004 , n_(size) 125   43395 , n_(size)
175   { 126   {
HITCBC 176   25004 } 127   43395 }
177   128  
178   /// Construct from mutable_buffer. 129   /// Construct from mutable_buffer.
HITCBC 179   16858 constexpr const_buffer( 130   11963 constexpr const_buffer(
180   mutable_buffer const& b) noexcept 131   mutable_buffer const& b) noexcept
HITCBC 181   16858 : p_(static_cast<unsigned char const*>(b.data())) 132   11963 : p_(static_cast<unsigned char const*>(b.data()))
HITCBC 182   16858 , n_(b.size()) 133   11963 , n_(b.size())
183   { 134   {
HITCBC 184   16858 } 135   11963 }
185   136  
186   /// Return a pointer to the memory region. 137   /// Return a pointer to the memory region.
HITCBC 187   65184 constexpr void const* data() const noexcept 138   59932 constexpr void const* data() const noexcept
188   { 139   {
HITCBC 189   65184 return p_; 140   59932 return p_;
190   } 141   }
191   142  
192   /// Return the size in bytes. 143   /// Return the size in bytes.
HITCBC 193   127291 constexpr std::size_t size() const noexcept 144   112803 constexpr std::size_t size() const noexcept
194   { 145   {
HITCBC 195   127291 return n_; 146   112803 return n_;
196   } 147   }
197   148  
198   /** Advance the buffer start, shrinking the region. 149   /** Advance the buffer start, shrinking the region.
199   150  
200   @param n Bytes to skip. Clamped to `size()`. 151   @param n Bytes to skip. Clamped to `size()`.
201   */ 152   */
202   const_buffer& 153   const_buffer&
HITCBC 203   27199 operator+=(std::size_t n) noexcept 154   19796 operator+=(std::size_t n) noexcept
204   { 155   {
HITCBC 205   27199 if( n > n_) 156   19796 if( n > n_)
MISLBC 206   16 n = n_; 157   n = n_;
HITCBC 207   27199 p_ += n; 158   19796 p_ += n;
HITCBC 208   27199 n_ -= n; 159   19796 n_ -= n;
HITCBC 209   27199 return *this; 160   19796 return *this;
210 -  
211 - /// Slice customization point for `tag_invoke`.  
212 - friend  
213 - void  
214 - tag_invoke(  
DCB 215 - 2640 slice_tag const&,  
216 - const_buffer& b,  
217 - slice_how how,  
218 - std::size_t n) noexcept  
219 - {  
220 - b.do_slice(how, n);  
DCB 221 - 2640 }  
DCB 222 - 2640  
223 - private:  
224 - void do_slice(  
DCB 225 - 2640 slice_how how, std::size_t n) noexcept  
226 - {  
227 - switch(how)  
DCB 228 - 2640 {  
229 - case slice_how::remove_prefix:  
DCB 230 - 1313 *this += n;  
DCB 231 - 1313 return;  
DCB 232 - 1313  
233 - case slice_how::keep_prefix:  
DCB 234 - 1327 if( n < n_)  
DCB 235 - 1327 n_ = n;  
DCB 236 - 1238 return;  
DCB 237 - 1327 }  
238 - }  
239   } 161   }
240   }; 162   };
241   163  
242   /** Concept for sequences of read-only buffer regions. 164   /** Concept for sequences of read-only buffer regions.
243   165  
244   A type satisfies `ConstBufferSequence` if it represents one or more 166   A type satisfies `ConstBufferSequence` if it represents one or more
245   contiguous memory regions that can be read. This includes single 167   contiguous memory regions that can be read. This includes single
246   buffers (convertible to `const_buffer`) and ranges of buffers. 168   buffers (convertible to `const_buffer`) and ranges of buffers.
247   169  
248   @par Syntactic Requirements 170   @par Syntactic Requirements
249   @li Convertible to `const_buffer`, OR 171   @li Convertible to `const_buffer`, OR
250   @li A bidirectional range with value type convertible to `const_buffer` 172   @li A bidirectional range with value type convertible to `const_buffer`
251   173  
252   @see const_buffer, MutableBufferSequence 174   @see const_buffer, MutableBufferSequence
253   */ 175   */
254   template<typename T> 176   template<typename T>
255   concept ConstBufferSequence = 177   concept ConstBufferSequence =
256   std::is_convertible_v<T, const_buffer> || ( 178   std::is_convertible_v<T, const_buffer> || (
257   std::ranges::bidirectional_range<T> && 179   std::ranges::bidirectional_range<T> &&
258   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>);
259   181  
260   /** Concept for sequences of writable buffer regions. 182   /** Concept for sequences of writable buffer regions.
261   183  
262   A type satisfies `MutableBufferSequence` if it represents one or more 184   A type satisfies `MutableBufferSequence` if it represents one or more
263   contiguous memory regions that can be written. This includes single 185   contiguous memory regions that can be written. This includes single
264   buffers (convertible to `mutable_buffer`) and ranges of buffers. 186   buffers (convertible to `mutable_buffer`) and ranges of buffers.
265   Every `MutableBufferSequence` also satisfies `ConstBufferSequence`. 187   Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
266   188  
267   @par Syntactic Requirements 189   @par Syntactic Requirements
268   @li Convertible to `mutable_buffer`, OR 190   @li Convertible to `mutable_buffer`, OR
269   @li A bidirectional range with value type convertible to `mutable_buffer` 191   @li A bidirectional range with value type convertible to `mutable_buffer`
270   192  
271   @see mutable_buffer, ConstBufferSequence 193   @see mutable_buffer, ConstBufferSequence
272   */ 194   */
273   template<typename T> 195   template<typename T>
274   concept MutableBufferSequence = 196   concept MutableBufferSequence =
275   std::is_convertible_v<T, mutable_buffer> || ( 197   std::is_convertible_v<T, mutable_buffer> || (
276   std::ranges::bidirectional_range<T> && 198   std::ranges::bidirectional_range<T> &&
277   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>);
278   200  
279   /** Return an iterator to the first buffer in a sequence. 201   /** Return an iterator to the first buffer in a sequence.
280   202  
281   Handles single buffers and ranges uniformly. For a single buffer, 203   Handles single buffers and ranges uniformly. For a single buffer,
282   returns a pointer to it (forming a one-element range). 204   returns a pointer to it (forming a one-element range).
283   */ 205   */
284   constexpr struct begin_mrdocs_workaround_t 206   constexpr struct begin_mrdocs_workaround_t
285   { 207   {
286   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 208   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 287   21858 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 209   13111 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
288   { 210   {
HITCBC 289   21858 return std::addressof(b); 211   13111 return std::addressof(b);
290   } 212   }
291   213  
292   template<ConstBufferSequence BS> 214   template<ConstBufferSequence BS>
293   requires (!std::convertible_to<BS, const_buffer>) 215   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 294   56387 auto operator()(BS const& bs) const noexcept 216   46366 auto operator()(BS const& bs) const noexcept
295   { 217   {
HITCBC 296   56387 return std::ranges::begin(bs); 218   46366 return std::ranges::begin(bs);
297   } 219   }
298   220  
299   template<ConstBufferSequence BS> 221   template<ConstBufferSequence BS>
300   requires (!std::convertible_to<BS, const_buffer>) 222   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 301   18574 auto operator()(BS& bs) const noexcept 223   9192 auto operator()(BS& bs) const noexcept
302   { 224   {
HITCBC 303   18574 return std::ranges::begin(bs); 225   9192 return std::ranges::begin(bs);
304   } 226   }
305   } begin {}; 227   } begin {};
306   228  
307   /** Return an iterator past the last buffer in a sequence. 229   /** Return an iterator past the last buffer in a sequence.
308   230  
309   Handles single buffers and ranges uniformly. For a single buffer, 231   Handles single buffers and ranges uniformly. For a single buffer,
310   returns a pointer one past it. 232   returns a pointer one past it.
311   */ 233   */
312   constexpr struct end_mrdocs_workaround_t 234   constexpr struct end_mrdocs_workaround_t
313   { 235   {
314   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 236   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 315   21618 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 237   12871 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
316   { 238   {
HITCBC 317   21618 return std::addressof(b) + 1; 239   12871 return std::addressof(b) + 1;
318   } 240   }
319   241  
320   template<ConstBufferSequence BS> 242   template<ConstBufferSequence BS>
321   requires (!std::convertible_to<BS, const_buffer>) 243   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 322   48858 auto operator()(BS const& bs) const noexcept 244   46366 auto operator()(BS const& bs) const noexcept
323   { 245   {
HITCBC 324   48858 return std::ranges::end(bs); 246   46366 return std::ranges::end(bs);
325   } 247   }
326   248  
327   template<ConstBufferSequence BS> 249   template<ConstBufferSequence BS>
328   requires (!std::convertible_to<BS, const_buffer>) 250   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 329   18574 auto operator()(BS& bs) const noexcept 251   9192 auto operator()(BS& bs) const noexcept
330   { 252   {
HITCBC 331   18574 return std::ranges::end(bs); 253   9192 return std::ranges::end(bs);
332   } 254   }
333   } end {}; 255   } end {};
334 - template<ConstBufferSequence CB>  
335 - std::size_t  
336 - tag_invoke(  
DCB 337 - 13783 size_tag const&,  
338 - CB const& bs) noexcept  
339 - {  
340 - std::size_t n = 0;  
DCB 341 - 13783 auto const e = end(bs);  
DCB 342 - 13783 for(auto it = begin(bs); it != e; ++it)  
DCB 343 - 34723 n += const_buffer(*it).size();  
DCB 344 - 20940 return n;  
DCB 345 - 13783 }  
346 -  
347   256  
348   /** Return the total byte count across all buffers in a sequence. 257   /** Return the total byte count across all buffers in a sequence.
349   258  
350   Sums the `size()` of each buffer in the sequence. This differs 259   Sums the `size()` of each buffer in the sequence. This differs
351   from `buffer_length` which counts the number of buffer elements. 260   from `buffer_length` which counts the number of buffer elements.
352   261  
353   @par Example 262   @par Example
354   @code 263   @code
355   std::array<mutable_buffer, 2> bufs = { ... }; 264   std::array<mutable_buffer, 2> bufs = { ... };
356   std::size_t total = buffer_size( bufs ); // sum of both sizes 265   std::size_t total = buffer_size( bufs ); // sum of both sizes
357   @endcode 266   @endcode
358   */ 267   */
359   constexpr struct buffer_size_mrdocs_workaround_t 268   constexpr struct buffer_size_mrdocs_workaround_t
360   { 269   {
  270 + // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
  271 + // when iterating here. The class uses union storage with placement
  272 + // 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
  274 + // warns. The runtime cost of value-initializing all N slots is
  275 + // non-trivial for non-trivial value types, so we suppress instead.
  276 + #if defined(__GNUC__) && !defined(__clang__)
  277 + #pragma GCC diagnostic push
  278 + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
  279 + #endif
361   template<ConstBufferSequence CB> 280   template<ConstBufferSequence CB>
HITCBC 362   19282 constexpr std::size_t operator()( 281   12812 constexpr std::size_t operator()(
363   CB const& bs) const noexcept 282   CB const& bs) const noexcept
364   { 283   {
HITCBC 365 - 19282 return tag_invoke(size_tag{}, bs); 284 + 12812 std::size_t n = 0;
HITGNC   285 + 12812 auto const e = capy::end(bs);
HITGNC   286 + 27149 for(auto it = capy::begin(bs); it != e; ++it)
HITGNC   287 + 14337 n += const_buffer(*it).size();
HITGNC   288 + 12812 return n;
366   } 289   }
  290 + #if defined(__GNUC__) && !defined(__clang__)
  291 + #pragma GCC diagnostic pop
  292 + #endif
367   } buffer_size {}; 293   } buffer_size {};
368   294  
369   /** Check if a buffer sequence contains no data. 295   /** Check if a buffer sequence contains no data.
370   296  
371   @return `true` if all buffers have size zero or the sequence 297   @return `true` if all buffers have size zero or the sequence
372   is empty. 298   is empty.
373   */ 299   */
374   constexpr struct buffer_empty_mrdocs_workaround_t 300   constexpr struct buffer_empty_mrdocs_workaround_t
375   { 301   {
  302 + // See note on buffer_size above — same union-storage false positive.
  303 + #if defined(__GNUC__) && !defined(__clang__)
  304 + #pragma GCC diagnostic push
  305 + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
  306 + #endif
376   template<ConstBufferSequence CB> 307   template<ConstBufferSequence CB>
HITCBC 377   4248 constexpr bool operator()( 308   4243 constexpr bool operator()(
378   CB const& bs) const noexcept 309   CB const& bs) const noexcept
379   { 310   {
HITCBC 380   4248 auto it = begin(bs); 311   4243 auto it = begin(bs);
HITCBC 381   4248 auto const end_ = end(bs); 312   4243 auto const end_ = end(bs);
HITCBC 382   4291 while(it != end_) 313   4284 while(it != end_)
383   { 314   {
HITCBC 384   4249 const_buffer b(*it++); 315   4244 const_buffer b(*it++);
HITCBC 385   4249 if(b.size() != 0) 316   4244 if(b.size() != 0)
HITCBC 386   4206 return false; 317   4203 return false;
387   } 318   }
HITCBC 388   42 return true; 319   40 return true;
389   } 320   }
  321 + #if defined(__GNUC__) && !defined(__clang__)
  322 + #pragma GCC diagnostic pop
  323 + #endif
390   } buffer_empty {}; 324   } buffer_empty {};
391   325  
392   namespace detail { 326   namespace detail {
393   327  
394   template<class It> 328   template<class It>
395   auto 329   auto
HITCBC 396   240 length_impl(It first, It last, int) 330   240 length_impl(It first, It last, int)
397   -> decltype(static_cast<std::size_t>(last - first)) 331   -> decltype(static_cast<std::size_t>(last - first))
398   { 332   {
HITCBC 399   240 return static_cast<std::size_t>(last - first); 333   240 return static_cast<std::size_t>(last - first);
400   } 334   }
401   335  
402   template<class It> 336   template<class It>
403   std::size_t 337   std::size_t
404   length_impl(It first, It last, long) 338   length_impl(It first, It last, long)
405   { 339   {
406   std::size_t n = 0; 340   std::size_t n = 0;
407   while(first != last) 341   while(first != last)
408   { 342   {
409   ++first; 343   ++first;
410   ++n; 344   ++n;
411   } 345   }
412   return n; 346   return n;
413   } 347   }
414   348  
415   } // detail 349   } // detail
416   350  
417   /** Return the number of buffer elements in a sequence. 351   /** Return the number of buffer elements in a sequence.
418   352  
419   Counts the number of individual buffer objects, not bytes. 353   Counts the number of individual buffer objects, not bytes.
420   For a single buffer, returns 1. For a range, returns the 354   For a single buffer, returns 1. For a range, returns the
421   distance from `begin` to `end`. 355   distance from `begin` to `end`.
422   356  
423   @see buffer_size 357   @see buffer_size
424   */ 358   */
425   template<ConstBufferSequence CB> 359   template<ConstBufferSequence CB>
426   std::size_t 360   std::size_t
HITCBC 427   240 buffer_length(CB const& bs) 361   240 buffer_length(CB const& bs)
428   { 362   {
HITCBC 429   240 return detail::length_impl( 363   240 return detail::length_impl(
HITCBC 430   240 begin(bs), end(bs), 0); 364   240 begin(bs), end(bs), 0);
431   } 365   }
432   366  
433   /// Alias for `mutable_buffer` or `const_buffer` based on sequence type. 367   /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
434   template<typename BS> 368   template<typename BS>
435   using buffer_type = std::conditional_t< 369   using buffer_type = std::conditional_t<
436   MutableBufferSequence<BS>, 370   MutableBufferSequence<BS>,
437   mutable_buffer, const_buffer>; 371   mutable_buffer, const_buffer>;
438   372  
439   } // capy 373   } // capy
440   } // boost 374   } // boost
441   375  
442   #endif 376   #endif