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