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 - // tag::const_buffer_sequence_concept[]  
238   */ 237   */
239   template<typename T> 238   template<typename T>
240   concept ConstBufferSequence = 239   concept ConstBufferSequence =
241   std::is_convertible_v<T, const_buffer> || ( 240   std::is_convertible_v<T, const_buffer> || (
242   std::ranges::bidirectional_range<T> && 241   std::ranges::bidirectional_range<T> &&
243 - // end::const_buffer_sequence_concept[]  
244   std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>); 242   std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
245   243  
246   /** Requires a type to convert to `mutable_buffer`, or be a range of such buffers. 244   /** Requires a type to convert to `mutable_buffer`, or be a range of such buffers.
247   245  
248   A type satisfies `MutableBufferSequence` if it represents one or more 246   A type satisfies `MutableBufferSequence` if it represents one or more
249   contiguous memory regions that can be written. This includes single 247   contiguous memory regions that can be written. This includes single
250   buffers (convertible to `mutable_buffer`) and ranges of buffers. 248   buffers (convertible to `mutable_buffer`) and ranges of buffers.
251   249  
252   This does not imply `ConstBufferSequence`. A type reaching 250   This does not imply `ConstBufferSequence`. A type reaching
253   `mutable_buffer` through its own conversion operator would need a 251   `mutable_buffer` through its own conversion operator would need a
254   second conversion, to `const_buffer`. An implicit conversion 252   second conversion, to `const_buffer`. An implicit conversion
255   sequence allows only one user-defined step. 253   sequence allows only one user-defined step.
256   254  
257   @par Syntactic Requirements 255   @par Syntactic Requirements
258   @li Convertible to `mutable_buffer`, OR 256   @li Convertible to `mutable_buffer`, OR
259   @li A bidirectional range with value type convertible to `mutable_buffer` 257   @li A bidirectional range with value type convertible to `mutable_buffer`
260   258  
261   @see mutable_buffer, ConstBufferSequence 259   @see mutable_buffer, ConstBufferSequence
262 - // tag::mutable_buffer_sequence_concept[]  
263   */ 260   */
264   template<typename T> 261   template<typename T>
265   concept MutableBufferSequence = 262   concept MutableBufferSequence =
266   std::is_convertible_v<T, mutable_buffer> || ( 263   std::is_convertible_v<T, mutable_buffer> || (
267   std::ranges::bidirectional_range<T> && 264   std::ranges::bidirectional_range<T> &&
268 - // end::mutable_buffer_sequence_concept[]  
269   std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>); 265   std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
270   266  
271   /** Return an iterator to the first buffer in a sequence. 267   /** Return an iterator to the first buffer in a sequence.
272   268  
273   @functionobject 269   @functionobject
274   */ 270   */
275   constexpr struct 271   constexpr struct
276   { 272   {
277   /** Return a pointer to a single buffer, forming a one-element range. 273   /** Return a pointer to a single buffer, forming a one-element range.
278   274  
279   @param b A single buffer. 275   @param b A single buffer.
280   276  
281   @return A pointer to `b`. 277   @return A pointer to `b`.
282   */ 278   */
283   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 279   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 284   6664 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 280   6664 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
285   { 281   {
HITCBC 286   6664 return std::addressof(b); 282   6664 return std::addressof(b);
287   } 283   }
288   284  
289   /** Return an iterator to the first buffer of a sequence. 285   /** Return an iterator to the first buffer of a sequence.
290   286  
291   @param bs The buffer sequence. 287   @param bs The buffer sequence.
292   288  
293   @return An iterator to the first buffer of `bs`. 289   @return An iterator to the first buffer of `bs`.
294   */ 290   */
295   template<ConstBufferSequence BS> 291   template<ConstBufferSequence BS>
296   requires (!std::convertible_to<BS, const_buffer>) 292   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 297   33709 auto operator()(BS const& bs) const noexcept 293   33709 auto operator()(BS const& bs) const noexcept
298   { 294   {
HITCBC 299   33709 return std::ranges::begin(bs); 295   33709 return std::ranges::begin(bs);
300   } 296   }
301   297  
302   /** Return an iterator to the first buffer of a sequence. 298   /** Return an iterator to the first buffer of a sequence.
303   299  
304   @param bs The buffer sequence. 300   @param bs The buffer sequence.
305   301  
306   @return An iterator to the first buffer of `bs`. 302   @return An iterator to the first buffer of `bs`.
307   */ 303   */
308   template<ConstBufferSequence BS> 304   template<ConstBufferSequence BS>
309   requires (!std::convertible_to<BS, const_buffer>) 305   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 310   9193 auto operator()(BS& bs) const noexcept 306   9193 auto operator()(BS& bs) const noexcept
311   { 307   {
HITCBC 312   9193 return std::ranges::begin(bs); 308   9193 return std::ranges::begin(bs);
313   } 309   }
314   } begin {}; 310   } begin {};
315   311  
316   /** Return an iterator past the last buffer in a sequence. 312   /** Return an iterator past the last buffer in a sequence.
317   313  
318   @functionobject 314   @functionobject
319   */ 315   */
320   constexpr struct 316   constexpr struct
321   { 317   {
322   /** Return a pointer one past a single buffer, forming a one-element range. 318   /** Return a pointer one past a single buffer, forming a one-element range.
323   319  
324   @param b A single buffer. 320   @param b A single buffer.
325   321  
326   @return A pointer one past `b`. 322   @return A pointer one past `b`.
327   */ 323   */
328   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 324   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 329   6666 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 325   6666 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
330   { 326   {
HITCBC 331   6666 return std::addressof(b) + 1; 327   6666 return std::addressof(b) + 1;
332   } 328   }
333   329  
334   /** Return an iterator past the last buffer of a sequence. 330   /** Return an iterator past the last buffer of a sequence.
335   331  
336   @param bs The buffer sequence. 332   @param bs The buffer sequence.
337   333  
338   @return An iterator one past the last buffer of `bs`. 334   @return An iterator one past the last buffer of `bs`.
339   */ 335   */
340   template<ConstBufferSequence BS> 336   template<ConstBufferSequence BS>
341   requires (!std::convertible_to<BS, const_buffer>) 337   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 342   33731 auto operator()(BS const& bs) const noexcept 338   33731 auto operator()(BS const& bs) const noexcept
343   { 339   {
HITCBC 344   33731 return std::ranges::end(bs); 340   33731 return std::ranges::end(bs);
345   } 341   }
346   342  
347   /** Return an iterator past the last buffer of a sequence. 343   /** Return an iterator past the last buffer of a sequence.
348   344  
349   @param bs The buffer sequence. 345   @param bs The buffer sequence.
350   346  
351   @return An iterator one past the last buffer of `bs`. 347   @return An iterator one past the last buffer of `bs`.
352   */ 348   */
353   template<ConstBufferSequence BS> 349   template<ConstBufferSequence BS>
354   requires (!std::convertible_to<BS, const_buffer>) 350   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 355   9193 auto operator()(BS& bs) const noexcept 351   9193 auto operator()(BS& bs) const noexcept
356   { 352   {
HITCBC 357   9193 return std::ranges::end(bs); 353   9193 return std::ranges::end(bs);
358   } 354   }
359   } end {}; 355   } end {};
360   356  
361   /** Return the total byte count across all buffers in a sequence. 357   /** Return the total byte count across all buffers in a sequence.
362   358  
363   @functionobject 359   @functionobject
364   */ 360   */
365   constexpr struct 361   constexpr struct
366   { 362   {
367   // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array 363   // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
368   // when iterating here. The class uses union storage with placement 364   // when iterating here. The class uses union storage with placement
369   // new for slots 0..n_-1, so reads inside this bounded loop are 365   // 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 366   // well-defined, but the optimizer can't prove the loop bound and
371   // warns. The runtime cost of value-initializing all N slots is 367   // warns. The runtime cost of value-initializing all N slots is
372   // non-trivial for non-trivial value types, so we suppress instead. 368   // non-trivial for non-trivial value types, so we suppress instead.
373   #if defined(__GNUC__) && !defined(__clang__) 369   #if defined(__GNUC__) && !defined(__clang__)
374   #pragma GCC diagnostic push 370   #pragma GCC diagnostic push
375   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 371   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
376   #endif 372   #endif
377   /** Return the total byte count across all buffers in a sequence. 373   /** Return the total byte count across all buffers in a sequence.
378   374  
379   Sums the `size()` of each buffer in the sequence. This differs 375   Sums the `size()` of each buffer in the sequence. This differs
380   from `buffer_length` which counts the number of buffer elements. 376   from `buffer_length` which counts the number of buffer elements.
381   377  
382   @param bs The buffer sequence. 378   @param bs The buffer sequence.
383   379  
384   @return The sum of the sizes of all buffers in `bs`. 380   @return The sum of the sizes of all buffers in `bs`.
385   381  
386   @par Example 382   @par Example
387   @par !example example 383   @par !example example
388   384  
389   */ 385   */
390   template<ConstBufferSequence CB> 386   template<ConstBufferSequence CB>
HITCBC 391   6296 constexpr std::size_t operator()( 387   6296 constexpr std::size_t operator()(
392   CB const& bs) const noexcept 388   CB const& bs) const noexcept
393   { 389   {
HITCBC 394   6296 std::size_t n = 0; 390   6296 std::size_t n = 0;
HITCBC 395   6296 auto const e = capy::end(bs); 391   6296 auto const e = capy::end(bs);
HITCBC 396   14520 for(auto it = capy::begin(bs); it != e; ++it) 392   14520 for(auto it = capy::begin(bs); it != e; ++it)
HITCBC 397   8224 n += const_buffer(*it).size(); 393   8224 n += const_buffer(*it).size();
HITCBC 398   6296 return n; 394   6296 return n;
399   } 395   }
400   #if defined(__GNUC__) && !defined(__clang__) 396   #if defined(__GNUC__) && !defined(__clang__)
401   #pragma GCC diagnostic pop 397   #pragma GCC diagnostic pop
402   #endif 398   #endif
403   } buffer_size {}; 399   } buffer_size {};
404   400  
405   /** Check if a buffer sequence contains no data. 401   /** Check if a buffer sequence contains no data.
406   402  
407   @functionobject 403   @functionobject
408   */ 404   */
409   constexpr struct 405   constexpr struct
410   { 406   {
411   // See note on buffer_size above — same union-storage false positive. 407   // See note on buffer_size above — same union-storage false positive.
412   #if defined(__GNUC__) && !defined(__clang__) 408   #if defined(__GNUC__) && !defined(__clang__)
413   #pragma GCC diagnostic push 409   #pragma GCC diagnostic push
414   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 410   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
415   #endif 411   #endif
416   /** Check if a buffer sequence contains no data. 412   /** Check if a buffer sequence contains no data.
417   413  
418   @param bs The buffer sequence. 414   @param bs The buffer sequence.
419   415  
420   @return `true` if all buffers have size zero or the sequence 416   @return `true` if all buffers have size zero or the sequence
421   is empty. 417   is empty.
422   */ 418   */
423   template<ConstBufferSequence CB> 419   template<ConstBufferSequence CB>
HITCBC 424   1584 constexpr bool operator()( 420   1584 constexpr bool operator()(
425   CB const& bs) const noexcept 421   CB const& bs) const noexcept
426   { 422   {
HITCBC 427   1584 auto it = begin(bs); 423   1584 auto it = begin(bs);
HITCBC 428   1584 auto const end_ = end(bs); 424   1584 auto const end_ = end(bs);
HITCBC 429   1632 while(it != end_) 425   1632 while(it != end_)
430   { 426   {
HITCBC 431   1596 const_buffer b(*it++); 427   1596 const_buffer b(*it++);
HITCBC 432   1596 if(b.size() != 0) 428   1596 if(b.size() != 0)
HITCBC 433   1548 return false; 429   1548 return false;
434   } 430   }
HITCBC 435   36 return true; 431   36 return true;
436   } 432   }
437   #if defined(__GNUC__) && !defined(__clang__) 433   #if defined(__GNUC__) && !defined(__clang__)
438   #pragma GCC diagnostic pop 434   #pragma GCC diagnostic pop
439   #endif 435   #endif
440   } buffer_empty {}; 436   } buffer_empty {};
441   437  
442   namespace detail { 438   namespace detail {
443   439  
444   template<class It> 440   template<class It>
445   auto 441   auto
HITCBC 446   11 length_impl(It first, It last, int) 442   11 length_impl(It first, It last, int)
447   -> decltype(static_cast<std::size_t>(last - first)) 443   -> decltype(static_cast<std::size_t>(last - first))
448   { 444   {
HITCBC 449   11 return static_cast<std::size_t>(last - first); 445   11 return static_cast<std::size_t>(last - first);
450   } 446   }
451   447  
452   template<class It> 448   template<class It>
453   std::size_t 449   std::size_t
454   length_impl(It first, It last, long) 450   length_impl(It first, It last, long)
455   { 451   {
456   std::size_t n = 0; 452   std::size_t n = 0;
457   while(first != last) 453   while(first != last)
458   { 454   {
459   ++first; 455   ++first;
460   ++n; 456   ++n;
461   } 457   }
462   return n; 458   return n;
463   } 459   }
464   460  
465   } // detail 461   } // detail
466   462  
467   /** Return the number of buffer elements in a sequence. 463   /** Return the number of buffer elements in a sequence.
468   464  
469   Counts the number of individual buffer objects, not bytes. 465   Counts the number of individual buffer objects, not bytes.
470   For a single buffer, returns 1. For a range, returns the 466   For a single buffer, returns 1. For a range, returns the
471   distance from `begin` to `end`. 467   distance from `begin` to `end`.
472   468  
473   @param bs The buffer sequence. 469   @param bs The buffer sequence.
474   470  
475   @return The number of buffers in `bs`. 471   @return The number of buffers in `bs`.
476   472  
477   @see buffer_size 473   @see buffer_size
478   */ 474   */
479   template<ConstBufferSequence CB> 475   template<ConstBufferSequence CB>
480   std::size_t 476   std::size_t
HITCBC 481   11 buffer_length(CB const& bs) 477   11 buffer_length(CB const& bs)
482   { 478   {
HITCBC 483   11 return detail::length_impl( 479   11 return detail::length_impl(
HITCBC 484   11 begin(bs), end(bs), 0); 480   11 begin(bs), end(bs), 0);
485   } 481   }
486   482  
487   /// Names `mutable_buffer` for a mutable sequence, `const_buffer` otherwise. 483   /// Names `mutable_buffer` for a mutable sequence, `const_buffer` otherwise.
488   template<typename BS> 484   template<typename BS>
489   using buffer_type = std::conditional_t< 485   using buffer_type = std::conditional_t<
490   MutableBufferSequence<BS>, 486   MutableBufferSequence<BS>,
491   mutable_buffer, const_buffer>; 487   mutable_buffer, const_buffer>;
492   488  
493   } // capy 489   } // capy
494   } // boost 490   } // boost
495   491  
496   #endif 492   #endif