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   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/capy 8   // Official repository: https://github.com/cppalliance/capy
9   // 9   //
10   10  
11   #ifndef BOOST_CAPY_BUFFERS_HPP 11   #ifndef BOOST_CAPY_BUFFERS_HPP
12   #define BOOST_CAPY_BUFFERS_HPP 12   #define BOOST_CAPY_BUFFERS_HPP
13   13  
14   #include <boost/capy/detail/config.hpp> 14   #include <boost/capy/detail/config.hpp>
15   #include <concepts> 15   #include <concepts>
16   #include <cstddef> 16   #include <cstddef>
17   #include <iterator> 17   #include <iterator>
18   #include <memory> 18   #include <memory>
19   #include <ranges> 19   #include <ranges>
20   #include <type_traits> 20   #include <type_traits>
21   21  
22   // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html 22   // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
23   23  
24   namespace boost { 24   namespace boost {
25   25  
26   namespace asio { 26   namespace asio {
27   class const_buffer; 27   class const_buffer;
28   class mutable_buffer; 28   class mutable_buffer;
29   } // asio 29   } // asio
30   30  
31   namespace capy { 31   namespace capy {
32   32  
33   class const_buffer; 33   class const_buffer;
34   class mutable_buffer; 34   class mutable_buffer;
35   35  
36   /** A reference to a contiguous region of writable memory. 36   /** A reference to a contiguous region of writable memory.
37   37  
38   Represents a pointer and size pair for a modifiable byte range. 38   Represents a pointer and size pair for a modifiable byte range.
39   Does not own the memory. Satisfies `MutableBufferSequence` (as a 39   Does not own the memory. Satisfies `MutableBufferSequence` (as a
40   single-element sequence) and is implicitly convertible to 40   single-element sequence) and is implicitly convertible to
41   `const_buffer`. 41   `const_buffer`.
42   42  
43   @see const_buffer, MutableBufferSequence 43   @see const_buffer, MutableBufferSequence
44   */ 44   */
45   class mutable_buffer 45   class mutable_buffer
46   { 46   {
47   unsigned char* p_ = nullptr; 47   unsigned char* p_ = nullptr;
48   std::size_t n_ = 0; 48   std::size_t n_ = 0;
49   49  
50   public: 50   public:
51   /// Construct an empty buffer. 51   /// Construct an empty buffer.
HITCBC 52   19 mutable_buffer() = default; 52   19 mutable_buffer() = default;
53   53  
54   /** Construct a copy. 54   /** Construct a copy.
55   55  
56   @param other The buffer to copy. 56   @param other The buffer to copy.
57   */ 57   */
58   mutable_buffer( 58   mutable_buffer(
59   mutable_buffer const& other) = default; 59   mutable_buffer const& other) = default;
60   60  
61   /** Assign by copying. 61   /** Assign by copying.
62   62  
63   @param other The buffer to copy. 63   @param other The buffer to copy.
64   64  
65   @return A reference to `*this`. 65   @return A reference to `*this`.
66   */ 66   */
67   mutable_buffer& operator=( 67   mutable_buffer& operator=(
68   mutable_buffer const& other) = default; 68   mutable_buffer const& other) = default;
69   69  
70   /** Construct from a pointer and size. 70   /** Construct from a pointer and size.
71   71  
72   Takes `void*` so a pointer to any object type binds without a 72   Takes `void*` so a pointer to any object type binds without a
73   cast, since the buffer represents a raw, untyped writable 73   cast, since the buffer represents a raw, untyped writable
74   region. Stored internally as `unsigned char*` for byte-wise 74   region. Stored internally as `unsigned char*` for byte-wise
75   pointer arithmetic (see `operator+=`). 75   pointer arithmetic (see `operator+=`).
76   76  
77   @param data A pointer to the first byte of the region. 77   @param data A pointer to the first byte of the region.
78   78  
79   @param size The size of the region, in bytes. 79   @param size The size of the region, in bytes.
80   */ 80   */
HITCBC 81   35281 constexpr mutable_buffer( 81   35281 constexpr mutable_buffer(
82   void* data, std::size_t size) noexcept 82   void* data, std::size_t size) noexcept
HITCBC 83   35281 : p_(static_cast<unsigned char*>(data)) 83   35281 : p_(static_cast<unsigned char*>(data))
HITCBC 84   35281 , n_(size) 84   35281 , n_(size)
85   { 85   {
HITCBC 86   35281 } 86   35281 }
87   87  
88   /** Return a pointer to the memory region. 88   /** Return a pointer to the memory region.
89   89  
90   Returns `void*`, symmetric with the constructor, so the 90   Returns `void*`, symmetric with the constructor, so the
91   caller can reinterpret the raw region as whatever type it needs. 91   caller can reinterpret the raw region as whatever type it needs.
92   92  
93   @return A pointer to the first byte of the region. 93   @return A pointer to the first byte of the region.
94   */ 94   */
HITCBC 95   54037 constexpr void* data() const noexcept 95   54037 constexpr void* data() const noexcept
96   { 96   {
HITCBC 97   54037 return p_; 97   54037 return p_;
98   } 98   }
99   99  
100   /** Return the size in bytes. 100   /** Return the size in bytes.
101   101  
102   @return The size of the region, in bytes. 102   @return The size of the region, in bytes.
103   */ 103   */
HITCBC 104   80565 constexpr std::size_t size() const noexcept 104   80565 constexpr std::size_t size() const noexcept
105   { 105   {
HITCBC 106   80565 return n_; 106   80565 return n_;
107   } 107   }
108   108  
109   /** Advance the buffer start, shrinking the region. 109   /** Advance the buffer start, shrinking the region.
110   110  
111   @param n Bytes to skip. Clamped to `size()`. 111   @param n Bytes to skip. Clamped to `size()`.
112   112  
113   @return A reference to `*this`. 113   @return A reference to `*this`.
114   */ 114   */
115   mutable_buffer& 115   mutable_buffer&
HITCBC 116   17732 operator+=(std::size_t n) noexcept 116   17732 operator+=(std::size_t n) noexcept
117   { 117   {
HITCBC 118   17732 if( n > n_) 118   17732 if( n > n_)
HITCBC 119   1 n = n_; 119   1 n = n_;
HITCBC 120   17732 p_ += n; 120   17732 p_ += n;
HITCBC 121   17732 n_ -= n; 121   17732 n_ -= n;
HITCBC 122   17732 return *this; 122   17732 return *this;
123   } 123   }
124   }; 124   };
125   125  
126   /** A reference to a contiguous region of read-only memory. 126   /** A reference to a contiguous region of read-only memory.
127   127  
128   Represents a pointer and size pair for a non-modifiable byte range. 128   Represents a pointer and size pair for a non-modifiable byte range.
129   Does not own the memory. Satisfies `ConstBufferSequence` (as a 129   Does not own the memory. Satisfies `ConstBufferSequence` (as a
130   single-element sequence). Implicitly constructible from 130   single-element sequence). Implicitly constructible from
131   `mutable_buffer`. 131   `mutable_buffer`.
132   132  
133   @see mutable_buffer, ConstBufferSequence 133   @see mutable_buffer, ConstBufferSequence
134   */ 134   */
135   class const_buffer 135   class const_buffer
136   { 136   {
137   unsigned char const* p_ = nullptr; 137   unsigned char const* p_ = nullptr;
138   std::size_t n_ = 0; 138   std::size_t n_ = 0;
139   139  
140   public: 140   public:
141   /// Construct an empty buffer. 141   /// Construct an empty buffer.
HITCBC 142   13 const_buffer() = default; 142   13 const_buffer() = default;
143   143  
144   /** Construct a copy. 144   /** Construct a copy.
145   145  
146   @param other The buffer to copy. 146   @param other The buffer to copy.
147   */ 147   */
148   const_buffer(const_buffer const& other) = default; 148   const_buffer(const_buffer const& other) = default;
149   149  
150   /** Assign by copying. 150   /** Assign by copying.
151   151  
152   @param other The buffer to copy. 152   @param other The buffer to copy.
153   153  
154   @return A reference to `*this`. 154   @return A reference to `*this`.
155   */ 155   */
156   const_buffer& operator=( 156   const_buffer& operator=(
157   const_buffer const& other) = default; 157   const_buffer const& other) = default;
158   158  
159   /** Construct from a pointer and size. 159   /** Construct from a pointer and size.
160   160  
161   Takes `void const*` so a pointer to any object type binds 161   Takes `void const*` so a pointer to any object type binds
162   without a cast, since the buffer represents a raw, untyped 162   without a cast, since the buffer represents a raw, untyped
163   read-only region. Stored internally as `unsigned char const*` 163   read-only region. Stored internally as `unsigned char const*`
164   for byte-wise pointer arithmetic (see `operator+=`). 164   for byte-wise pointer arithmetic (see `operator+=`).
165   165  
166   @param data A pointer to the first byte of the region. 166   @param data A pointer to the first byte of the region.
167   167  
168   @param size The size of the region, in bytes. 168   @param size The size of the region, in bytes.
169   */ 169   */
HITCBC 170   32088 constexpr const_buffer( 170   32088 constexpr const_buffer(
171   void const* data, std::size_t size) noexcept 171   void const* data, std::size_t size) noexcept
HITCBC 172   32088 : p_(static_cast<unsigned char const*>(data)) 172   32088 : p_(static_cast<unsigned char const*>(data))
HITCBC 173   32088 , n_(size) 173   32088 , n_(size)
174   { 174   {
HITCBC 175   32088 } 175   32088 }
176   176  
177   /** Construct from mutable_buffer. 177   /** Construct from mutable_buffer.
178   178  
179   @param b The writable buffer whose region is referenced. 179   @param b The writable buffer whose region is referenced.
180   */ 180   */
HITCBC 181   7887 constexpr const_buffer( 181   7887 constexpr const_buffer(
182   mutable_buffer const& b) noexcept 182   mutable_buffer const& b) noexcept
HITCBC 183   7887 : p_(static_cast<unsigned char const*>(b.data())) 183   7887 : p_(static_cast<unsigned char const*>(b.data()))
HITCBC 184   7887 , n_(b.size()) 184   7887 , n_(b.size())
185   { 185   {
HITCBC 186   7887 } 186   7887 }
187   187  
188   /** Return a pointer to the memory region. 188   /** Return a pointer to the memory region.
189   189  
190   Returns `void const*`, symmetric with the constructor, so the 190   Returns `void const*`, symmetric with the constructor, so the
191   caller can reinterpret the raw region as whatever type it needs. 191   caller can reinterpret the raw region as whatever type it needs.
192   192  
193   @return A pointer to the first byte of the region. 193   @return A pointer to the first byte of the region.
194   */ 194   */
HITCBC 195   46527 constexpr void const* data() const noexcept 195   46527 constexpr void const* data() const noexcept
196   { 196   {
HITCBC 197   46527 return p_; 197   46527 return p_;
198   } 198   }
199   199  
200   /** Return the size in bytes. 200   /** Return the size in bytes.
201   201  
202   @return The size of the region, in bytes. 202   @return The size of the region, in bytes.
203   */ 203   */
HITCBC 204   77663 constexpr std::size_t size() const noexcept 204   77663 constexpr std::size_t size() const noexcept
205   { 205   {
HITCBC 206   77663 return n_; 206   77663 return n_;
207   } 207   }
208   208  
209   /** Advance the buffer start, shrinking the region. 209   /** Advance the buffer start, shrinking the region.
210   210  
211   @param n Bytes to skip. Clamped to `size()`. 211   @param n Bytes to skip. Clamped to `size()`.
212   212  
213   @return A reference to `*this`. 213   @return A reference to `*this`.
214   */ 214   */
215   const_buffer& 215   const_buffer&
HITCBC 216   17380 operator+=(std::size_t n) noexcept 216   17380 operator+=(std::size_t n) noexcept
217   { 217   {
HITCBC 218   17380 if( n > n_) 218   17380 if( n > n_)
HITCBC 219   1 n = n_; 219   1 n = n_;
HITCBC 220   17380 p_ += n; 220   17380 p_ += n;
HITCBC 221   17380 n_ -= n; 221   17380 n_ -= n;
HITCBC 222   17380 return *this; 222   17380 return *this;
223   } 223   }
224   }; 224   };
225   225  
226   /** Requires a type to convert to `const_buffer`, or be a range of such buffers. 226   /** Requires a type to convert to `const_buffer`, or be a range of such buffers.
227   227  
228   A type satisfies `ConstBufferSequence` if it represents one or more 228   A type satisfies `ConstBufferSequence` if it represents one or more
229   contiguous memory regions that can be read. This includes single 229   contiguous memory regions that can be read. This includes single
230   buffers (convertible to `const_buffer`) and ranges of buffers. 230   buffers (convertible to `const_buffer`) and ranges of buffers.
231   231  
232   @par Syntactic Requirements 232   @par Syntactic Requirements
233   @li Convertible to `const_buffer`, OR 233   @li Convertible to `const_buffer`, OR
234   @li A bidirectional range with value type convertible to `const_buffer` 234   @li A bidirectional range with value type convertible to `const_buffer`
235   235  
236   @see const_buffer, MutableBufferSequence 236   @see const_buffer, MutableBufferSequence
237   */ 237   */
238   // tag::const_buffer_sequence_concept[] 238   // tag::const_buffer_sequence_concept[]
239   template<typename T> 239   template<typename T>
240   concept ConstBufferSequence = 240   concept ConstBufferSequence =
241   std::is_convertible_v<T, const_buffer> || ( 241   std::is_convertible_v<T, const_buffer> || (
242   std::ranges::bidirectional_range<T> && 242   std::ranges::bidirectional_range<T> &&
243   std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>); 243   std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
244   // end::const_buffer_sequence_concept[] 244   // end::const_buffer_sequence_concept[]
245   245  
246   /** Requires a type to convert to `mutable_buffer`, or be a range of such buffers. 246   /** Requires a type to convert to `mutable_buffer`, or be a range of such buffers.
247   247  
248   A type satisfies `MutableBufferSequence` if it represents one or more 248   A type satisfies `MutableBufferSequence` if it represents one or more
249   contiguous memory regions that can be written. This includes single 249   contiguous memory regions that can be written. This includes single
250   buffers (convertible to `mutable_buffer`) and ranges of buffers. 250   buffers (convertible to `mutable_buffer`) and ranges of buffers.
251   251  
252   This does not imply `ConstBufferSequence`. A type reaching 252   This does not imply `ConstBufferSequence`. A type reaching
253   `mutable_buffer` through its own conversion operator would need a 253   `mutable_buffer` through its own conversion operator would need a
254   second conversion, to `const_buffer`. An implicit conversion 254   second conversion, to `const_buffer`. An implicit conversion
255   sequence allows only one user-defined step. 255   sequence allows only one user-defined step.
256   256  
257   @par Syntactic Requirements 257   @par Syntactic Requirements
258   @li Convertible to `mutable_buffer`, OR 258   @li Convertible to `mutable_buffer`, OR
259   @li A bidirectional range with value type convertible to `mutable_buffer` 259   @li A bidirectional range with value type convertible to `mutable_buffer`
260   260  
261   @see mutable_buffer, ConstBufferSequence 261   @see mutable_buffer, ConstBufferSequence
262   */ 262   */
263   // tag::mutable_buffer_sequence_concept[] 263   // tag::mutable_buffer_sequence_concept[]
264   template<typename T> 264   template<typename T>
265   concept MutableBufferSequence = 265   concept MutableBufferSequence =
266   std::is_convertible_v<T, mutable_buffer> || ( 266   std::is_convertible_v<T, mutable_buffer> || (
267   std::ranges::bidirectional_range<T> && 267   std::ranges::bidirectional_range<T> &&
268   std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>); 268   std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
269   // end::mutable_buffer_sequence_concept[] 269   // end::mutable_buffer_sequence_concept[]
270   270  
271   /** Return an iterator to the first buffer in a sequence. 271   /** Return an iterator to the first buffer in a sequence.
272   272  
273   @functionobject 273   @functionobject
274   */ 274   */
275   constexpr struct 275   constexpr struct
276   { 276   {
277   /** Return a pointer to a single buffer, forming a one-element range. 277   /** Return a pointer to a single buffer, forming a one-element range.
278   278  
279   @param b A single buffer. 279   @param b A single buffer.
280   280  
281   @return A pointer to `b`. 281   @return A pointer to `b`.
282   */ 282   */
283   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 283   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 284   6664 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 284   6664 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
285   { 285   {
HITCBC 286   6664 return std::addressof(b); 286   6664 return std::addressof(b);
287   } 287   }
288   288  
289   /** Return an iterator to the first buffer of a sequence. 289   /** Return an iterator to the first buffer of a sequence.
290   290  
291   @param bs The buffer sequence. 291   @param bs The buffer sequence.
292   292  
293   @return An iterator to the first buffer of `bs`. 293   @return An iterator to the first buffer of `bs`.
294   */ 294   */
295   template<ConstBufferSequence BS> 295   template<ConstBufferSequence BS>
296   requires (!std::convertible_to<BS, const_buffer>) 296   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 297   33709 auto operator()(BS const& bs) const noexcept 297   33709 auto operator()(BS const& bs) const noexcept
298   { 298   {
HITCBC 299   33709 return std::ranges::begin(bs); 299   33709 return std::ranges::begin(bs);
300   } 300   }
301   301  
302   /** Return an iterator to the first buffer of a sequence. 302   /** Return an iterator to the first buffer of a sequence.
303   303  
304   @param bs The buffer sequence. 304   @param bs The buffer sequence.
305   305  
306   @return An iterator to the first buffer of `bs`. 306   @return An iterator to the first buffer of `bs`.
307   */ 307   */
308   template<ConstBufferSequence BS> 308   template<ConstBufferSequence BS>
309   requires (!std::convertible_to<BS, const_buffer>) 309   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 310   9193 auto operator()(BS& bs) const noexcept 310   9193 auto operator()(BS& bs) const noexcept
311   { 311   {
HITCBC 312   9193 return std::ranges::begin(bs); 312   9193 return std::ranges::begin(bs);
313   } 313   }
314   } begin {}; 314   } begin {};
315   315  
316   /** Return an iterator past the last buffer in a sequence. 316   /** Return an iterator past the last buffer in a sequence.
317   317  
318   @functionobject 318   @functionobject
319   */ 319   */
320   constexpr struct 320   constexpr struct
321   { 321   {
322   /** Return a pointer one past a single buffer, forming a one-element range. 322   /** Return a pointer one past a single buffer, forming a one-element range.
323   323  
324   @param b A single buffer. 324   @param b A single buffer.
325   325  
326   @return A pointer one past `b`. 326   @return A pointer one past `b`.
327   */ 327   */
328   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 328   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 329   6666 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 329   6666 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
330   { 330   {
HITCBC 331   6666 return std::addressof(b) + 1; 331   6666 return std::addressof(b) + 1;
332   } 332   }
333   333  
334   /** Return an iterator past the last buffer of a sequence. 334   /** Return an iterator past the last buffer of a sequence.
335   335  
336   @param bs The buffer sequence. 336   @param bs The buffer sequence.
337   337  
338   @return An iterator one past the last buffer of `bs`. 338   @return An iterator one past the last buffer of `bs`.
339   */ 339   */
340   template<ConstBufferSequence BS> 340   template<ConstBufferSequence BS>
341   requires (!std::convertible_to<BS, const_buffer>) 341   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 342   33731 auto operator()(BS const& bs) const noexcept 342   33731 auto operator()(BS const& bs) const noexcept
343   { 343   {
HITCBC 344   33731 return std::ranges::end(bs); 344   33731 return std::ranges::end(bs);
345   } 345   }
346   346  
347   /** Return an iterator past the last buffer of a sequence. 347   /** Return an iterator past the last buffer of a sequence.
348   348  
349   @param bs The buffer sequence. 349   @param bs The buffer sequence.
350   350  
351   @return An iterator one past the last buffer of `bs`. 351   @return An iterator one past the last buffer of `bs`.
352   */ 352   */
353   template<ConstBufferSequence BS> 353   template<ConstBufferSequence BS>
354   requires (!std::convertible_to<BS, const_buffer>) 354   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 355   9193 auto operator()(BS& bs) const noexcept 355   9193 auto operator()(BS& bs) const noexcept
356   { 356   {
HITCBC 357   9193 return std::ranges::end(bs); 357   9193 return std::ranges::end(bs);
358   } 358   }
359   } end {}; 359   } end {};
360   360  
361   /** Return the total byte count across all buffers in a sequence. 361   /** Return the total byte count across all buffers in a sequence.
362   362  
363   @functionobject 363   @functionobject
364   */ 364   */
365   constexpr struct 365   constexpr struct
366   { 366   {
367   // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array 367   // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
368   // when iterating here. The class uses union storage with placement 368   // when iterating here. The class uses union storage with placement
369   // new for slots 0..n_-1, so reads inside this bounded loop are 369   // new for slots 0..n_-1, so reads inside this bounded loop are
370   // well-defined, but the optimizer can't prove the loop bound and 370   // well-defined, but the optimizer can't prove the loop bound and
371   // warns. The runtime cost of value-initializing all N slots is 371   // warns. The runtime cost of value-initializing all N slots is
372   // non-trivial for non-trivial value types, so we suppress instead. 372   // non-trivial for non-trivial value types, so we suppress instead.
373   #if defined(__GNUC__) && !defined(__clang__) 373   #if defined(__GNUC__) && !defined(__clang__)
374   #pragma GCC diagnostic push 374   #pragma GCC diagnostic push
375   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 375   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
376   #endif 376   #endif
377   /** Return the total byte count across all buffers in a sequence. 377   /** Return the total byte count across all buffers in a sequence.
378   378  
379   Sums the `size()` of each buffer in the sequence. This differs 379   Sums the `size()` of each buffer in the sequence. This differs
380   from `buffer_length` which counts the number of buffer elements. 380   from `buffer_length` which counts the number of buffer elements.
381   381  
382   @param bs The buffer sequence. 382   @param bs The buffer sequence.
383   383  
384   @return The sum of the sizes of all buffers in `bs`. 384   @return The sum of the sizes of all buffers in `bs`.
385   385  
386   @par Example 386   @par Example
387   @par !example example 387   @par !example example
388   388  
389   */ 389   */
390   template<ConstBufferSequence CB> 390   template<ConstBufferSequence CB>
HITCBC 391   6296 constexpr std::size_t operator()( 391   6296 constexpr std::size_t operator()(
392   CB const& bs) const noexcept 392   CB const& bs) const noexcept
393   { 393   {
HITCBC 394   6296 std::size_t n = 0; 394   6296 std::size_t n = 0;
HITCBC 395   6296 auto const e = capy::end(bs); 395   6296 auto const e = capy::end(bs);
HITCBC 396   14520 for(auto it = capy::begin(bs); it != e; ++it) 396   14520 for(auto it = capy::begin(bs); it != e; ++it)
HITCBC 397   8224 n += const_buffer(*it).size(); 397   8224 n += const_buffer(*it).size();
HITCBC 398   6296 return n; 398   6296 return n;
399   } 399   }
400   #if defined(__GNUC__) && !defined(__clang__) 400   #if defined(__GNUC__) && !defined(__clang__)
401   #pragma GCC diagnostic pop 401   #pragma GCC diagnostic pop
402   #endif 402   #endif
403   } buffer_size {}; 403   } buffer_size {};
404   404  
405   /** Check if a buffer sequence contains no data. 405   /** Check if a buffer sequence contains no data.
406   406  
407   @functionobject 407   @functionobject
408   */ 408   */
409   constexpr struct 409   constexpr struct
410   { 410   {
411   // See note on buffer_size above — same union-storage false positive. 411   // See note on buffer_size above — same union-storage false positive.
412   #if defined(__GNUC__) && !defined(__clang__) 412   #if defined(__GNUC__) && !defined(__clang__)
413   #pragma GCC diagnostic push 413   #pragma GCC diagnostic push
414   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 414   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
415   #endif 415   #endif
416   /** Check if a buffer sequence contains no data. 416   /** Check if a buffer sequence contains no data.
417   417  
418   @param bs The buffer sequence. 418   @param bs The buffer sequence.
419   419  
420   @return `true` if all buffers have size zero or the sequence 420   @return `true` if all buffers have size zero or the sequence
421   is empty. 421   is empty.
422   */ 422   */
423   template<ConstBufferSequence CB> 423   template<ConstBufferSequence CB>
HITCBC 424   1584 constexpr bool operator()( 424   1584 constexpr bool operator()(
425   CB const& bs) const noexcept 425   CB const& bs) const noexcept
426   { 426   {
HITCBC 427   1584 auto it = begin(bs); 427   1584 auto it = begin(bs);
HITCBC 428   1584 auto const end_ = end(bs); 428   1584 auto const end_ = end(bs);
HITCBC 429   1632 while(it != end_) 429   1632 while(it != end_)
430   { 430   {
HITCBC 431   1596 const_buffer b(*it++); 431   1596 const_buffer b(*it++);
HITCBC 432   1596 if(b.size() != 0) 432   1596 if(b.size() != 0)
HITCBC 433   1548 return false; 433   1548 return false;
434   } 434   }
HITCBC 435   36 return true; 435   36 return true;
436   } 436   }
437   #if defined(__GNUC__) && !defined(__clang__) 437   #if defined(__GNUC__) && !defined(__clang__)
438   #pragma GCC diagnostic pop 438   #pragma GCC diagnostic pop
439   #endif 439   #endif
440   } buffer_empty {}; 440   } buffer_empty {};
441   441  
442   namespace detail { 442   namespace detail {
443   443  
444   template<class It> 444   template<class It>
445   auto 445   auto
HITCBC 446   11 length_impl(It first, It last, int) 446   11 length_impl(It first, It last, int)
447   -> decltype(static_cast<std::size_t>(last - first)) 447   -> decltype(static_cast<std::size_t>(last - first))
448   { 448   {
HITCBC 449   11 return static_cast<std::size_t>(last - first); 449   11 return static_cast<std::size_t>(last - first);
450   } 450   }
451   451  
452   template<class It> 452   template<class It>
453   std::size_t 453   std::size_t
454   length_impl(It first, It last, long) 454   length_impl(It first, It last, long)
455   { 455   {
456   std::size_t n = 0; 456   std::size_t n = 0;
457   while(first != last) 457   while(first != last)
458   { 458   {
459   ++first; 459   ++first;
460   ++n; 460   ++n;
461   } 461   }
462   return n; 462   return n;
463   } 463   }
464   464  
465   } // detail 465   } // detail
466   466  
467   /** Return the number of buffer elements in a sequence. 467   /** Return the number of buffer elements in a sequence.
468   468  
469   Counts the number of individual buffer objects, not bytes. 469   Counts the number of individual buffer objects, not bytes.
470   For a single buffer, returns 1. For a range, returns the 470   For a single buffer, returns 1. For a range, returns the
471   distance from `begin` to `end`. 471   distance from `begin` to `end`.
472   472  
473   @param bs The buffer sequence. 473   @param bs The buffer sequence.
474   474  
475   @return The number of buffers in `bs`. 475   @return The number of buffers in `bs`.
476   476  
477   @see buffer_size 477   @see buffer_size
478   */ 478   */
479   template<ConstBufferSequence CB> 479   template<ConstBufferSequence CB>
480   std::size_t 480   std::size_t
HITCBC 481   11 buffer_length(CB const& bs) 481   11 buffer_length(CB const& bs)
482   { 482   {
HITCBC 483   11 return detail::length_impl( 483   11 return detail::length_impl(
HITCBC 484   11 begin(bs), end(bs), 0); 484   11 begin(bs), end(bs), 0);
485   } 485   }
486   486  
487   /// Names `mutable_buffer` for a mutable sequence, `const_buffer` otherwise. 487   /// Names `mutable_buffer` for a mutable sequence, `const_buffer` otherwise.
488   template<typename BS> 488   template<typename BS>
489   using buffer_type = std::conditional_t< 489   using buffer_type = std::conditional_t<
490   MutableBufferSequence<BS>, 490   MutableBufferSequence<BS>,
491   mutable_buffer, const_buffer>; 491   mutable_buffer, const_buffer>;
492   492  
493   } // capy 493   } // capy
494   } // boost 494   } // boost
495   495  
496   #endif 496   #endif