100.00% Lines (91/91) 100.00% Functions (26/26)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/capy 7   // Official repository: https://github.com/cppalliance/capy
8   // 8   //
9   9  
10   #ifndef BOOST_CAPY_BUFFERS_HPP 10   #ifndef BOOST_CAPY_BUFFERS_HPP
11   #define BOOST_CAPY_BUFFERS_HPP 11   #define BOOST_CAPY_BUFFERS_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <concepts> 14   #include <concepts>
15   #include <cstddef> 15   #include <cstddef>
16   #include <iterator> 16   #include <iterator>
17   #include <memory> 17   #include <memory>
18   #include <ranges> 18   #include <ranges>
19   #include <type_traits> 19   #include <type_traits>
20   20  
21   // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html 21   // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
22   22  
23   namespace boost { 23   namespace boost {
24   24  
25   namespace asio { 25   namespace asio {
26   class const_buffer; 26   class const_buffer;
27   class mutable_buffer; 27   class mutable_buffer;
28   } // asio 28   } // asio
29   29  
30   namespace capy { 30   namespace capy {
31   31  
32   class const_buffer; 32   class const_buffer;
33   class mutable_buffer; 33   class mutable_buffer;
34   34  
35   /// Tag type for customizing `buffer_size` via `tag_invoke`. 35   /// Tag type for customizing `buffer_size` via `tag_invoke`.
36   struct size_tag {}; 36   struct size_tag {};
37   37  
38   /// Tag type for customizing slice operations via `tag_invoke`. 38   /// Tag type for customizing slice operations via `tag_invoke`.
39   struct slice_tag {}; 39   struct slice_tag {};
40   40  
41   /** Constants for slice customization. 41   /** Constants for slice customization.
42   42  
43   Passed to `tag_invoke` overloads to specify which portion 43   Passed to `tag_invoke` overloads to specify which portion
44   of a buffer sequence to retain. 44   of a buffer sequence to retain.
45   */ 45   */
46   enum class slice_how 46   enum class slice_how
47   { 47   {
48   /// Remove bytes from the front of the sequence. 48   /// Remove bytes from the front of the sequence.
49   remove_prefix, 49   remove_prefix,
50   50  
51   /// Keep only the first N bytes. 51   /// Keep only the first N bytes.
52   keep_prefix 52   keep_prefix
53   }; 53   };
54   54  
55   /** A reference to a contiguous region of writable memory. 55   /** A reference to a contiguous region of writable memory.
56   56  
57   Represents a pointer and size pair for a modifiable byte range. 57   Represents a pointer and size pair for a modifiable byte range.
58   Does not own the memory. Satisfies `MutableBufferSequence` (as a 58   Does not own the memory. Satisfies `MutableBufferSequence` (as a
59   single-element sequence) and is implicitly convertible to 59   single-element sequence) and is implicitly convertible to
60   `const_buffer`. 60   `const_buffer`.
61   61  
62   @see const_buffer, MutableBufferSequence 62   @see const_buffer, MutableBufferSequence
63   */ 63   */
64   class mutable_buffer 64   class mutable_buffer
65   { 65   {
66   unsigned char* p_ = nullptr; 66   unsigned char* p_ = nullptr;
67   std::size_t n_ = 0; 67   std::size_t n_ = 0;
68   68  
69   public: 69   public:
70   /// Construct an empty buffer. 70   /// Construct an empty buffer.
HITCBC 71   603 mutable_buffer() = default; 71   603 mutable_buffer() = default;
72   72  
73   /// Construct a copy. 73   /// Construct a copy.
74   mutable_buffer( 74   mutable_buffer(
75   mutable_buffer const&) = default; 75   mutable_buffer const&) = default;
76   76  
77   /// Assign by copying. 77   /// Assign by copying.
78   mutable_buffer& operator=( 78   mutable_buffer& operator=(
79   mutable_buffer const&) = default; 79   mutable_buffer const&) = default;
80   80  
81   /// Construct from pointer and size. 81   /// Construct from pointer and size.
HITCBC 82   29154 constexpr mutable_buffer( 82   29154 constexpr mutable_buffer(
83   void* data, std::size_t size) noexcept 83   void* data, std::size_t size) noexcept
HITCBC 84   29154 : p_(static_cast<unsigned char*>(data)) 84   29154 : p_(static_cast<unsigned char*>(data))
HITCBC 85   29154 , n_(size) 85   29154 , n_(size)
86   { 86   {
HITCBC 87   29154 } 87   29154 }
88   88  
89   /// Return a pointer to the memory region. 89   /// Return a pointer to the memory region.
HITCBC 90   72490 constexpr void* data() const noexcept 90   72490 constexpr void* data() const noexcept
91   { 91   {
HITCBC 92   72490 return p_; 92   72490 return p_;
93   } 93   }
94   94  
95   /// Return the size in bytes. 95   /// Return the size in bytes.
HITCBC 96   112159 constexpr std::size_t size() const noexcept 96   112159 constexpr std::size_t size() const noexcept
97   { 97   {
HITCBC 98   112159 return n_; 98   112159 return n_;
99   } 99   }
100   100  
101   /** Advance the buffer start, shrinking the region. 101   /** Advance the buffer start, shrinking the region.
102   102  
103   @param n Bytes to skip. Clamped to `size()`. 103   @param n Bytes to skip. Clamped to `size()`.
104   */ 104   */
105   mutable_buffer& 105   mutable_buffer&
HITCBC 106   26540 operator+=(std::size_t n) noexcept 106   26540 operator+=(std::size_t n) noexcept
107   { 107   {
HITCBC 108   26540 if( n > n_) 108   26540 if( n > n_)
HITCBC 109   17 n = n_; 109   17 n = n_;
HITCBC 110   26540 p_ += n; 110   26540 p_ += n;
HITCBC 111   26540 n_ -= n; 111   26540 n_ -= n;
HITCBC 112   26540 return *this; 112   26540 return *this;
113   } 113   }
114   114  
115   /// Slice customization point for `tag_invoke`. 115   /// Slice customization point for `tag_invoke`.
116   friend 116   friend
117   void 117   void
HITCBC 118   1335 tag_invoke( 118   1335 tag_invoke(
119   slice_tag const&, 119   slice_tag const&,
120   mutable_buffer& b, 120   mutable_buffer& b,
121   slice_how how, 121   slice_how how,
122   std::size_t n) noexcept 122   std::size_t n) noexcept
123   { 123   {
HITCBC 124   1335 b.do_slice(how, n); 124   1335 b.do_slice(how, n);
HITCBC 125   1335 } 125   1335 }
126   126  
127   private: 127   private:
HITCBC 128   1335 void do_slice( 128   1335 void do_slice(
129   slice_how how, std::size_t n) noexcept 129   slice_how how, std::size_t n) noexcept
130   { 130   {
HITCBC 131   1335 switch(how) 131   1335 switch(how)
132   { 132   {
HITCBC 133   659 case slice_how::remove_prefix: 133   659 case slice_how::remove_prefix:
HITCBC 134   659 *this += n; 134   659 *this += n;
HITCBC 135   659 return; 135   659 return;
136   136  
HITCBC 137   676 case slice_how::keep_prefix: 137   676 case slice_how::keep_prefix:
HITCBC 138   676 if( n < n_) 138   676 if( n < n_)
HITCBC 139   584 n_ = n; 139   584 n_ = n;
HITCBC 140   676 return; 140   676 return;
141   } 141   }
142   } 142   }
143   }; 143   };
144   144  
145   /** A reference to a contiguous region of read-only memory. 145   /** A reference to a contiguous region of read-only memory.
146   146  
147   Represents a pointer and size pair for a non-modifiable byte range. 147   Represents a pointer and size pair for a non-modifiable byte range.
148   Does not own the memory. Satisfies `ConstBufferSequence` (as a 148   Does not own the memory. Satisfies `ConstBufferSequence` (as a
149   single-element sequence). Implicitly constructible from 149   single-element sequence). Implicitly constructible from
150   `mutable_buffer`. 150   `mutable_buffer`.
151   151  
152   @see mutable_buffer, ConstBufferSequence 152   @see mutable_buffer, ConstBufferSequence
153   */ 153   */
154   class const_buffer 154   class const_buffer
155   { 155   {
156   unsigned char const* p_ = nullptr; 156   unsigned char const* p_ = nullptr;
157   std::size_t n_ = 0; 157   std::size_t n_ = 0;
158   158  
159   public: 159   public:
160   /// Construct an empty buffer. 160   /// Construct an empty buffer.
HITCBC 161   631 const_buffer() = default; 161   631 const_buffer() = default;
162   162  
163   /// Construct a copy. 163   /// Construct a copy.
164   const_buffer(const_buffer const&) = default; 164   const_buffer(const_buffer const&) = default;
165   165  
166   /// Assign by copying. 166   /// Assign by copying.
167   const_buffer& operator=( 167   const_buffer& operator=(
168   const_buffer const& other) = default; 168   const_buffer const& other) = default;
169   169  
170   /// Construct from pointer and size. 170   /// Construct from pointer and size.
HITCBC 171   24993 constexpr const_buffer( 171   24993 constexpr const_buffer(
172   void const* data, std::size_t size) noexcept 172   void const* data, std::size_t size) noexcept
HITCBC 173   24993 : p_(static_cast<unsigned char const*>(data)) 173   24993 : p_(static_cast<unsigned char const*>(data))
HITCBC 174   24993 , n_(size) 174   24993 , n_(size)
175   { 175   {
HITCBC 176   24993 } 176   24993 }
177   177  
178   /// Construct from mutable_buffer. 178   /// Construct from mutable_buffer.
HITCBC 179   16810 constexpr const_buffer( 179   16810 constexpr const_buffer(
180   mutable_buffer const& b) noexcept 180   mutable_buffer const& b) noexcept
HITCBC 181   16810 : p_(static_cast<unsigned char const*>(b.data())) 181   16810 : p_(static_cast<unsigned char const*>(b.data()))
HITCBC 182   16810 , n_(b.size()) 182   16810 , n_(b.size())
183   { 183   {
HITCBC 184   16810 } 184   16810 }
185   185  
186   /// Return a pointer to the memory region. 186   /// Return a pointer to the memory region.
HITCBC 187   65180 constexpr void const* data() const noexcept 187   65180 constexpr void const* data() const noexcept
188   { 188   {
HITCBC 189   65180 return p_; 189   65180 return p_;
190   } 190   }
191   191  
192   /// Return the size in bytes. 192   /// Return the size in bytes.
HITCBC 193   126804 constexpr std::size_t size() const noexcept 193   126804 constexpr std::size_t size() const noexcept
194   { 194   {
HITCBC 195   126804 return n_; 195   126804 return n_;
196   } 196   }
197   197  
198   /** Advance the buffer start, shrinking the region. 198   /** Advance the buffer start, shrinking the region.
199   199  
200   @param n Bytes to skip. Clamped to `size()`. 200   @param n Bytes to skip. Clamped to `size()`.
201   */ 201   */
202   const_buffer& 202   const_buffer&
HITCBC 203   27195 operator+=(std::size_t n) noexcept 203   27195 operator+=(std::size_t n) noexcept
204   { 204   {
HITCBC 205   27195 if( n > n_) 205   27195 if( n > n_)
HITCBC 206   16 n = n_; 206   16 n = n_;
HITCBC 207   27195 p_ += n; 207   27195 p_ += n;
HITCBC 208   27195 n_ -= n; 208   27195 n_ -= n;
HITCBC 209   27195 return *this; 209   27195 return *this;
210   } 210   }
211   211  
212   /// Slice customization point for `tag_invoke`. 212   /// Slice customization point for `tag_invoke`.
213   friend 213   friend
214   void 214   void
HITCBC 215   2640 tag_invoke( 215   2640 tag_invoke(
216   slice_tag const&, 216   slice_tag const&,
217   const_buffer& b, 217   const_buffer& b,
218   slice_how how, 218   slice_how how,
219   std::size_t n) noexcept 219   std::size_t n) noexcept
220   { 220   {
HITCBC 221   2640 b.do_slice(how, n); 221   2640 b.do_slice(how, n);
HITCBC 222   2640 } 222   2640 }
223   223  
224   private: 224   private:
HITCBC 225   2640 void do_slice( 225   2640 void do_slice(
226   slice_how how, std::size_t n) noexcept 226   slice_how how, std::size_t n) noexcept
227   { 227   {
HITCBC 228   2640 switch(how) 228   2640 switch(how)
229   { 229   {
HITCBC 230   1313 case slice_how::remove_prefix: 230   1313 case slice_how::remove_prefix:
HITCBC 231   1313 *this += n; 231   1313 *this += n;
HITCBC 232   1313 return; 232   1313 return;
233   233  
HITCBC 234   1327 case slice_how::keep_prefix: 234   1327 case slice_how::keep_prefix:
HITCBC 235   1327 if( n < n_) 235   1327 if( n < n_)
HITCBC 236   1238 n_ = n; 236   1238 n_ = n;
HITCBC 237   1327 return; 237   1327 return;
238   } 238   }
239   } 239   }
240   }; 240   };
241   241  
242   /** Concept for sequences of read-only buffer regions. 242   /** Concept for sequences of read-only buffer regions.
243   243  
244   A type satisfies `ConstBufferSequence` if it represents one or more 244   A type satisfies `ConstBufferSequence` if it represents one or more
245   contiguous memory regions that can be read. This includes single 245   contiguous memory regions that can be read. This includes single
246   buffers (convertible to `const_buffer`) and ranges of buffers. 246   buffers (convertible to `const_buffer`) and ranges of buffers.
247   247  
248   @par Syntactic Requirements 248   @par Syntactic Requirements
249   @li Convertible to `const_buffer`, OR 249   @li Convertible to `const_buffer`, OR
250   @li A bidirectional range with value type convertible to `const_buffer` 250   @li A bidirectional range with value type convertible to `const_buffer`
251   251  
252   @see const_buffer, MutableBufferSequence 252   @see const_buffer, MutableBufferSequence
253   */ 253   */
254   template<typename T> 254   template<typename T>
255   concept ConstBufferSequence = 255   concept ConstBufferSequence =
256   std::is_convertible_v<T, const_buffer> || ( 256   std::is_convertible_v<T, const_buffer> || (
257   std::ranges::bidirectional_range<T> && 257   std::ranges::bidirectional_range<T> &&
258   std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>); 258   std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
259   259  
260   /** Concept for sequences of writable buffer regions. 260   /** Concept for sequences of writable buffer regions.
261   261  
262   A type satisfies `MutableBufferSequence` if it represents one or more 262   A type satisfies `MutableBufferSequence` if it represents one or more
263   contiguous memory regions that can be written. This includes single 263   contiguous memory regions that can be written. This includes single
264   buffers (convertible to `mutable_buffer`) and ranges of buffers. 264   buffers (convertible to `mutable_buffer`) and ranges of buffers.
265   Every `MutableBufferSequence` also satisfies `ConstBufferSequence`. 265   Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
266   266  
267   @par Syntactic Requirements 267   @par Syntactic Requirements
268   @li Convertible to `mutable_buffer`, OR 268   @li Convertible to `mutable_buffer`, OR
269   @li A bidirectional range with value type convertible to `mutable_buffer` 269   @li A bidirectional range with value type convertible to `mutable_buffer`
270   270  
271   @see mutable_buffer, ConstBufferSequence 271   @see mutable_buffer, ConstBufferSequence
272   */ 272   */
273   template<typename T> 273   template<typename T>
274   concept MutableBufferSequence = 274   concept MutableBufferSequence =
275   std::is_convertible_v<T, mutable_buffer> || ( 275   std::is_convertible_v<T, mutable_buffer> || (
276   std::ranges::bidirectional_range<T> && 276   std::ranges::bidirectional_range<T> &&
277   std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>); 277   std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
278   278  
279   /** Return an iterator to the first buffer in a sequence. 279   /** Return an iterator to the first buffer in a sequence.
280   280  
281   Handles single buffers and ranges uniformly. For a single buffer, 281   Handles single buffers and ranges uniformly. For a single buffer,
282   returns a pointer to it (forming a one-element range). 282   returns a pointer to it (forming a one-element range).
283   */ 283   */
284   constexpr struct begin_mrdocs_workaround_t 284   constexpr struct begin_mrdocs_workaround_t
285   { 285   {
286   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 286   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 287   21853 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 287   21853 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
288   { 288   {
HITCBC 289   21853 return std::addressof(b); 289   21853 return std::addressof(b);
290   } 290   }
291   291  
292   template<ConstBufferSequence BS> 292   template<ConstBufferSequence BS>
293   requires (!std::convertible_to<BS, const_buffer>) 293   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 294   56300 auto operator()(BS const& bs) const noexcept 294   56300 auto operator()(BS const& bs) const noexcept
295   { 295   {
HITCBC 296   56300 return std::ranges::begin(bs); 296   56300 return std::ranges::begin(bs);
297   } 297   }
298   298  
299   template<ConstBufferSequence BS> 299   template<ConstBufferSequence BS>
300   requires (!std::convertible_to<BS, const_buffer>) 300   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 301   18574 auto operator()(BS& bs) const noexcept 301   18574 auto operator()(BS& bs) const noexcept
302   { 302   {
HITCBC 303   18574 return std::ranges::begin(bs); 303   18574 return std::ranges::begin(bs);
304   } 304   }
305   } begin {}; 305   } begin {};
306   306  
307   /** Return an iterator past the last buffer in a sequence. 307   /** Return an iterator past the last buffer in a sequence.
308   308  
309   Handles single buffers and ranges uniformly. For a single buffer, 309   Handles single buffers and ranges uniformly. For a single buffer,
310   returns a pointer one past it. 310   returns a pointer one past it.
311   */ 311   */
312   constexpr struct end_mrdocs_workaround_t 312   constexpr struct end_mrdocs_workaround_t
313   { 313   {
314   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 314   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 315   21613 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 315   21613 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
316   { 316   {
HITCBC 317   21613 return std::addressof(b) + 1; 317   21613 return std::addressof(b) + 1;
318   } 318   }
319   319  
320   template<ConstBufferSequence BS> 320   template<ConstBufferSequence BS>
321   requires (!std::convertible_to<BS, const_buffer>) 321   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 322   48771 auto operator()(BS const& bs) const noexcept 322   48771 auto operator()(BS const& bs) const noexcept
323   { 323   {
HITCBC 324   48771 return std::ranges::end(bs); 324   48771 return std::ranges::end(bs);
325   } 325   }
326   326  
327   template<ConstBufferSequence BS> 327   template<ConstBufferSequence BS>
328   requires (!std::convertible_to<BS, const_buffer>) 328   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 329   18574 auto operator()(BS& bs) const noexcept 329   18574 auto operator()(BS& bs) const noexcept
330   { 330   {
HITCBC 331   18574 return std::ranges::end(bs); 331   18574 return std::ranges::end(bs);
332   } 332   }
333   } end {}; 333   } end {};
334   334  
335   template<ConstBufferSequence CB> 335   template<ConstBufferSequence CB>
336   std::size_t 336   std::size_t
HITCBC 337   13743 tag_invoke( 337   13743 tag_invoke(
338   size_tag const&, 338   size_tag const&,
339   CB const& bs) noexcept 339   CB const& bs) noexcept
340   { 340   {
HITCBC 341   13743 std::size_t n = 0; 341   13743 std::size_t n = 0;
HITCBC 342   13743 auto const e = end(bs); 342   13743 auto const e = end(bs);
HITCBC 343   34631 for(auto it = begin(bs); it != e; ++it) 343   34631 for(auto it = begin(bs); it != e; ++it)
HITCBC 344   20888 n += const_buffer(*it).size(); 344   20888 n += const_buffer(*it).size();
HITCBC 345   13743 return n; 345   13743 return n;
346   } 346   }
347   347  
348   /** Return the total byte count across all buffers in a sequence. 348   /** Return the total byte count across all buffers in a sequence.
349   349  
350   Sums the `size()` of each buffer in the sequence. This differs 350   Sums the `size()` of each buffer in the sequence. This differs
351   from `buffer_length` which counts the number of buffer elements. 351   from `buffer_length` which counts the number of buffer elements.
352   352  
353   @par Example 353   @par Example
354   @code 354   @code
355   std::array<mutable_buffer, 2> bufs = { ... }; 355   std::array<mutable_buffer, 2> bufs = { ... };
356   std::size_t total = buffer_size( bufs ); // sum of both sizes 356   std::size_t total = buffer_size( bufs ); // sum of both sizes
357   @endcode 357   @endcode
358   */ 358   */
359   constexpr struct buffer_size_mrdocs_workaround_t 359   constexpr struct buffer_size_mrdocs_workaround_t
360   { 360   {
361   template<ConstBufferSequence CB> 361   template<ConstBufferSequence CB>
HITCBC 362   19242 constexpr std::size_t operator()( 362   19242 constexpr std::size_t operator()(
363   CB const& bs) const noexcept 363   CB const& bs) const noexcept
364   { 364   {
HITCBC 365   19242 return tag_invoke(size_tag{}, bs); 365   19242 return tag_invoke(size_tag{}, bs);
366   } 366   }
367   } buffer_size {}; 367   } buffer_size {};
368   368  
369   /** Check if a buffer sequence contains no data. 369   /** Check if a buffer sequence contains no data.
370   370  
371   @return `true` if all buffers have size zero or the sequence 371   @return `true` if all buffers have size zero or the sequence
372   is empty. 372   is empty.
373   */ 373   */
374   constexpr struct buffer_empty_mrdocs_workaround_t 374   constexpr struct buffer_empty_mrdocs_workaround_t
375   { 375   {
376   template<ConstBufferSequence CB> 376   template<ConstBufferSequence CB>
HITCBC 377   4236 constexpr bool operator()( 377   4236 constexpr bool operator()(
378   CB const& bs) const noexcept 378   CB const& bs) const noexcept
379   { 379   {
HITCBC 380   4236 auto it = begin(bs); 380   4236 auto it = begin(bs);
HITCBC 381   4236 auto const end_ = end(bs); 381   4236 auto const end_ = end(bs);
HITCBC 382   4291 while(it != end_) 382   4291 while(it != end_)
383   { 383   {
HITCBC 384   4249 const_buffer b(*it++); 384   4249 const_buffer b(*it++);
HITCBC 385   4249 if(b.size() != 0) 385   4249 if(b.size() != 0)
HITCBC 386   4194 return false; 386   4194 return false;
387   } 387   }
HITCBC 388   42 return true; 388   42 return true;
389   } 389   }
390   } buffer_empty {}; 390   } buffer_empty {};
391   391  
392   namespace detail { 392   namespace detail {
393   393  
394   template<class It> 394   template<class It>
395   auto 395   auto
HITCBC 396   240 length_impl(It first, It last, int) 396   240 length_impl(It first, It last, int)
397   -> decltype(static_cast<std::size_t>(last - first)) 397   -> decltype(static_cast<std::size_t>(last - first))
398   { 398   {
HITCBC 399   240 return static_cast<std::size_t>(last - first); 399   240 return static_cast<std::size_t>(last - first);
400   } 400   }
401   401  
402   template<class It> 402   template<class It>
403   std::size_t 403   std::size_t
404   length_impl(It first, It last, long) 404   length_impl(It first, It last, long)
405   { 405   {
406   std::size_t n = 0; 406   std::size_t n = 0;
407   while(first != last) 407   while(first != last)
408   { 408   {
409   ++first; 409   ++first;
410   ++n; 410   ++n;
411   } 411   }
412   return n; 412   return n;
413   } 413   }
414   414  
415   } // detail 415   } // detail
416   416  
417   /** Return the number of buffer elements in a sequence. 417   /** Return the number of buffer elements in a sequence.
418   418  
419   Counts the number of individual buffer objects, not bytes. 419   Counts the number of individual buffer objects, not bytes.
420   For a single buffer, returns 1. For a range, returns the 420   For a single buffer, returns 1. For a range, returns the
421   distance from `begin` to `end`. 421   distance from `begin` to `end`.
422   422  
423   @see buffer_size 423   @see buffer_size
424   */ 424   */
425   template<ConstBufferSequence CB> 425   template<ConstBufferSequence CB>
426   std::size_t 426   std::size_t
HITCBC 427   240 buffer_length(CB const& bs) 427   240 buffer_length(CB const& bs)
428   { 428   {
HITCBC 429   240 return detail::length_impl( 429   240 return detail::length_impl(
HITCBC 430   240 begin(bs), end(bs), 0); 430   240 begin(bs), end(bs), 0);
431   } 431   }
432   432  
433   /// Alias for `mutable_buffer` or `const_buffer` based on sequence type. 433   /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
434   template<typename BS> 434   template<typename BS>
435   using buffer_type = std::conditional_t< 435   using buffer_type = std::conditional_t<
436   MutableBufferSequence<BS>, 436   MutableBufferSequence<BS>,
437   mutable_buffer, const_buffer>; 437   mutable_buffer, const_buffer>;
438   438  
439   } // capy 439   } // capy
440   } // boost 440   } // boost
441   441  
442   #endif 442   #endif