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