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