96.92% Lines (63/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   // 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   /** A reference to a contiguous region of writable memory. 35   /** A reference to a contiguous region of writable memory.
36   36  
37   Represents a pointer and size pair for a modifiable byte range. 37   Represents a pointer and size pair for a modifiable byte range.
38   Does not own the memory. Satisfies `MutableBufferSequence` (as a 38   Does not own the memory. Satisfies `MutableBufferSequence` (as a
39   single-element sequence) and is implicitly convertible to 39   single-element sequence) and is implicitly convertible to
40   `const_buffer`. 40   `const_buffer`.
41   41  
42   @see const_buffer, MutableBufferSequence 42   @see const_buffer, MutableBufferSequence
43   */ 43   */
44   class mutable_buffer 44   class mutable_buffer
45   { 45   {
46   unsigned char* p_ = nullptr; 46   unsigned char* p_ = nullptr;
47   std::size_t n_ = 0; 47   std::size_t n_ = 0;
48   48  
49   public: 49   public:
50   /// Construct an empty buffer. 50   /// Construct an empty buffer.
HITCBC 51   29 mutable_buffer() = default; 51   29 mutable_buffer() = default;
52   52  
53   /// Construct a copy. 53   /// Construct a copy.
54   mutable_buffer( 54   mutable_buffer(
55   mutable_buffer const&) = default; 55   mutable_buffer const&) = default;
56   56  
57   /// Assign by copying. 57   /// Assign by copying.
58   mutable_buffer& operator=( 58   mutable_buffer& operator=(
59   mutable_buffer const&) = default; 59   mutable_buffer const&) = default;
60   60  
61   /// Construct from pointer and size. 61   /// Construct from pointer and size.
HITCBC 62   42543 constexpr mutable_buffer( 62   42543 constexpr mutable_buffer(
63   void* data, std::size_t size) noexcept 63   void* data, std::size_t size) noexcept
HITCBC 64   42543 : p_(static_cast<unsigned char*>(data)) 64   42543 : p_(static_cast<unsigned char*>(data))
HITCBC 65   42543 , n_(size) 65   42543 , n_(size)
66   { 66   {
HITCBC 67   42543 } 67   42543 }
68   68  
69   /// Return a pointer to the memory region. 69   /// Return a pointer to the memory region.
HITCBC 70   62282 constexpr void* data() const noexcept 70   62282 constexpr void* data() const noexcept
71   { 71   {
HITCBC 72   62282 return p_; 72   62282 return p_;
73   } 73   }
74   74  
75   /// Return the size in bytes. 75   /// Return the size in bytes.
HITCBC 76   97921 constexpr std::size_t size() const noexcept 76   97921 constexpr std::size_t size() const noexcept
77   { 77   {
HITCBC 78   97921 return n_; 78   97921 return n_;
79   } 79   }
80   80  
81   /** Advance the buffer start, shrinking the region. 81   /** Advance the buffer start, shrinking the region.
82   82  
83   @param n Bytes to skip. Clamped to `size()`. 83   @param n Bytes to skip. Clamped to `size()`.
84   */ 84   */
85   mutable_buffer& 85   mutable_buffer&
HITCBC 86   19795 operator+=(std::size_t n) noexcept 86   19795 operator+=(std::size_t n) noexcept
87   { 87   {
HITCBC 88   19795 if( n > n_) 88   19795 if( n > n_)
MISUBC 89   n = n_; 89   n = n_;
HITCBC 90   19795 p_ += n; 90   19795 p_ += n;
HITCBC 91   19795 n_ -= n; 91   19795 n_ -= n;
HITCBC 92   19795 return *this; 92   19795 return *this;
93   } 93   }
94   }; 94   };
95   95  
96   /** A reference to a contiguous region of read-only memory. 96   /** A reference to a contiguous region of read-only memory.
97   97  
98   Represents a pointer and size pair for a non-modifiable byte range. 98   Represents a pointer and size pair for a non-modifiable byte range.
99   Does not own the memory. Satisfies `ConstBufferSequence` (as a 99   Does not own the memory. Satisfies `ConstBufferSequence` (as a
100   single-element sequence). Implicitly constructible from 100   single-element sequence). Implicitly constructible from
101   `mutable_buffer`. 101   `mutable_buffer`.
102   102  
103   @see mutable_buffer, ConstBufferSequence 103   @see mutable_buffer, ConstBufferSequence
104   */ 104   */
105   class const_buffer 105   class const_buffer
106   { 106   {
107   unsigned char const* p_ = nullptr; 107   unsigned char const* p_ = nullptr;
108   std::size_t n_ = 0; 108   std::size_t n_ = 0;
109   109  
110   public: 110   public:
111   /// Construct an empty buffer. 111   /// Construct an empty buffer.
HITCBC 112   57 const_buffer() = default; 112   57 const_buffer() = default;
113   113  
114   /// Construct a copy. 114   /// Construct a copy.
115   const_buffer(const_buffer const&) = default; 115   const_buffer(const_buffer const&) = default;
116   116  
117   /// Assign by copying. 117   /// Assign by copying.
118   const_buffer& operator=( 118   const_buffer& operator=(
119   const_buffer const& other) = default; 119   const_buffer const& other) = default;
120   120  
121   /// Construct from pointer and size. 121   /// Construct from pointer and size.
HITCBC 122   43395 constexpr const_buffer( 122   43395 constexpr const_buffer(
123   void const* data, std::size_t size) noexcept 123   void const* data, std::size_t size) noexcept
HITCBC 124   43395 : p_(static_cast<unsigned char const*>(data)) 124   43395 : p_(static_cast<unsigned char const*>(data))
HITCBC 125   43395 , n_(size) 125   43395 , n_(size)
126   { 126   {
HITCBC 127   43395 } 127   43395 }
128   128  
129   /// Construct from mutable_buffer. 129   /// Construct from mutable_buffer.
HITCBC 130   11963 constexpr const_buffer( 130   11963 constexpr const_buffer(
131   mutable_buffer const& b) noexcept 131   mutable_buffer const& b) noexcept
HITCBC 132   11963 : p_(static_cast<unsigned char const*>(b.data())) 132   11963 : p_(static_cast<unsigned char const*>(b.data()))
HITCBC 133   11963 , n_(b.size()) 133   11963 , n_(b.size())
134   { 134   {
HITCBC 135   11963 } 135   11963 }
136   136  
137   /// Return a pointer to the memory region. 137   /// Return a pointer to the memory region.
HITCBC 138   59932 constexpr void const* data() const noexcept 138   59932 constexpr void const* data() const noexcept
139   { 139   {
HITCBC 140   59932 return p_; 140   59932 return p_;
141   } 141   }
142   142  
143   /// Return the size in bytes. 143   /// Return the size in bytes.
HITCBC 144   112803 constexpr std::size_t size() const noexcept 144   112803 constexpr std::size_t size() const noexcept
145   { 145   {
HITCBC 146   112803 return n_; 146   112803 return n_;
147   } 147   }
148   148  
149   /** Advance the buffer start, shrinking the region. 149   /** Advance the buffer start, shrinking the region.
150   150  
151   @param n Bytes to skip. Clamped to `size()`. 151   @param n Bytes to skip. Clamped to `size()`.
152   */ 152   */
153   const_buffer& 153   const_buffer&
HITCBC 154   19796 operator+=(std::size_t n) noexcept 154   19796 operator+=(std::size_t n) noexcept
155   { 155   {
HITCBC 156   19796 if( n > n_) 156   19796 if( n > n_)
MISUBC 157   n = n_; 157   n = n_;
HITCBC 158   19796 p_ += n; 158   19796 p_ += n;
HITCBC 159   19796 n_ -= n; 159   19796 n_ -= n;
HITCBC 160   19796 return *this; 160   19796 return *this;
161   } 161   }
162   }; 162   };
163   163  
164   /** Concept for sequences of read-only buffer regions. 164   /** Concept for sequences of read-only buffer regions.
165   165  
166   A type satisfies `ConstBufferSequence` if it represents one or more 166   A type satisfies `ConstBufferSequence` if it represents one or more
167   contiguous memory regions that can be read. This includes single 167   contiguous memory regions that can be read. This includes single
168   buffers (convertible to `const_buffer`) and ranges of buffers. 168   buffers (convertible to `const_buffer`) and ranges of buffers.
169   169  
170   @par Syntactic Requirements 170   @par Syntactic Requirements
171   @li Convertible to `const_buffer`, OR 171   @li Convertible to `const_buffer`, OR
172   @li A bidirectional range with value type convertible to `const_buffer` 172   @li A bidirectional range with value type convertible to `const_buffer`
173   173  
174   @see const_buffer, MutableBufferSequence 174   @see const_buffer, MutableBufferSequence
175   */ 175   */
176   template<typename T> 176   template<typename T>
177   concept ConstBufferSequence = 177   concept ConstBufferSequence =
178   std::is_convertible_v<T, const_buffer> || ( 178   std::is_convertible_v<T, const_buffer> || (
179   std::ranges::bidirectional_range<T> && 179   std::ranges::bidirectional_range<T> &&
180   std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>); 180   std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
181   181  
182   /** Concept for sequences of writable buffer regions. 182   /** Concept for sequences of writable buffer regions.
183   183  
184   A type satisfies `MutableBufferSequence` if it represents one or more 184   A type satisfies `MutableBufferSequence` if it represents one or more
185   contiguous memory regions that can be written. This includes single 185   contiguous memory regions that can be written. This includes single
186   buffers (convertible to `mutable_buffer`) and ranges of buffers. 186   buffers (convertible to `mutable_buffer`) and ranges of buffers.
187   Every `MutableBufferSequence` also satisfies `ConstBufferSequence`. 187   Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
188   188  
189   @par Syntactic Requirements 189   @par Syntactic Requirements
190   @li Convertible to `mutable_buffer`, OR 190   @li Convertible to `mutable_buffer`, OR
191   @li A bidirectional range with value type convertible to `mutable_buffer` 191   @li A bidirectional range with value type convertible to `mutable_buffer`
192   192  
193   @see mutable_buffer, ConstBufferSequence 193   @see mutable_buffer, ConstBufferSequence
194   */ 194   */
195   template<typename T> 195   template<typename T>
196   concept MutableBufferSequence = 196   concept MutableBufferSequence =
197   std::is_convertible_v<T, mutable_buffer> || ( 197   std::is_convertible_v<T, mutable_buffer> || (
198   std::ranges::bidirectional_range<T> && 198   std::ranges::bidirectional_range<T> &&
199   std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>); 199   std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
200   200  
201   /** Return an iterator to the first buffer in a sequence. 201   /** Return an iterator to the first buffer in a sequence.
202   202  
203   Handles single buffers and ranges uniformly. For a single buffer, 203   Handles single buffers and ranges uniformly. For a single buffer,
204   returns a pointer to it (forming a one-element range). 204   returns a pointer to it (forming a one-element range).
205   */ 205   */
206   constexpr struct begin_mrdocs_workaround_t 206   constexpr struct begin_mrdocs_workaround_t
207   { 207   {
208   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 208   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 209   13111 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 209   13111 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
210   { 210   {
HITCBC 211   13111 return std::addressof(b); 211   13111 return std::addressof(b);
212   } 212   }
213   213  
214   template<ConstBufferSequence BS> 214   template<ConstBufferSequence BS>
215   requires (!std::convertible_to<BS, const_buffer>) 215   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 216   46366 auto operator()(BS const& bs) const noexcept 216   46366 auto operator()(BS const& bs) const noexcept
217   { 217   {
HITCBC 218   46366 return std::ranges::begin(bs); 218   46366 return std::ranges::begin(bs);
219   } 219   }
220   220  
221   template<ConstBufferSequence BS> 221   template<ConstBufferSequence BS>
222   requires (!std::convertible_to<BS, const_buffer>) 222   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 223   9192 auto operator()(BS& bs) const noexcept 223   9192 auto operator()(BS& bs) const noexcept
224   { 224   {
HITCBC 225   9192 return std::ranges::begin(bs); 225   9192 return std::ranges::begin(bs);
226   } 226   }
227   } begin {}; 227   } begin {};
228   228  
229   /** Return an iterator past the last buffer in a sequence. 229   /** Return an iterator past the last buffer in a sequence.
230   230  
231   Handles single buffers and ranges uniformly. For a single buffer, 231   Handles single buffers and ranges uniformly. For a single buffer,
232   returns a pointer one past it. 232   returns a pointer one past it.
233   */ 233   */
234   constexpr struct end_mrdocs_workaround_t 234   constexpr struct end_mrdocs_workaround_t
235   { 235   {
236   template<std::convertible_to<const_buffer> ConvertibleToBuffer> 236   template<std::convertible_to<const_buffer> ConvertibleToBuffer>
HITCBC 237   12871 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* 237   12871 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
238   { 238   {
HITCBC 239   12871 return std::addressof(b) + 1; 239   12871 return std::addressof(b) + 1;
240   } 240   }
241   241  
242   template<ConstBufferSequence BS> 242   template<ConstBufferSequence BS>
243   requires (!std::convertible_to<BS, const_buffer>) 243   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 244   46366 auto operator()(BS const& bs) const noexcept 244   46366 auto operator()(BS const& bs) const noexcept
245   { 245   {
HITCBC 246   46366 return std::ranges::end(bs); 246   46366 return std::ranges::end(bs);
247   } 247   }
248   248  
249   template<ConstBufferSequence BS> 249   template<ConstBufferSequence BS>
250   requires (!std::convertible_to<BS, const_buffer>) 250   requires (!std::convertible_to<BS, const_buffer>)
HITCBC 251   9192 auto operator()(BS& bs) const noexcept 251   9192 auto operator()(BS& bs) const noexcept
252   { 252   {
HITCBC 253   9192 return std::ranges::end(bs); 253   9192 return std::ranges::end(bs);
254   } 254   }
255   } end {}; 255   } end {};
256   256  
257   /** Return the total byte count across all buffers in a sequence. 257   /** Return the total byte count across all buffers in a sequence.
258   258  
259   Sums the `size()` of each buffer in the sequence. This differs 259   Sums the `size()` of each buffer in the sequence. This differs
260   from `buffer_length` which counts the number of buffer elements. 260   from `buffer_length` which counts the number of buffer elements.
261   261  
262   @par Example 262   @par Example
263   @code 263   @code
264   std::array<mutable_buffer, 2> bufs = { ... }; 264   std::array<mutable_buffer, 2> bufs = { ... };
265   std::size_t total = buffer_size( bufs ); // sum of both sizes 265   std::size_t total = buffer_size( bufs ); // sum of both sizes
266   @endcode 266   @endcode
267   */ 267   */
268   constexpr struct buffer_size_mrdocs_workaround_t 268   constexpr struct buffer_size_mrdocs_workaround_t
269   { 269   {
270   // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array 270   // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
271   // when iterating here. The class uses union storage with placement 271   // when iterating here. The class uses union storage with placement
272   // new for slots 0..n_-1, so reads inside this bounded loop are 272   // new for slots 0..n_-1, so reads inside this bounded loop are
273   // well-defined, but the optimizer can't prove the loop bound and 273   // well-defined, but the optimizer can't prove the loop bound and
274   // warns. The runtime cost of value-initializing all N slots is 274   // warns. The runtime cost of value-initializing all N slots is
275   // non-trivial for non-trivial value types, so we suppress instead. 275   // non-trivial for non-trivial value types, so we suppress instead.
276   #if defined(__GNUC__) && !defined(__clang__) 276   #if defined(__GNUC__) && !defined(__clang__)
277   #pragma GCC diagnostic push 277   #pragma GCC diagnostic push
278   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 278   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
279   #endif 279   #endif
280   template<ConstBufferSequence CB> 280   template<ConstBufferSequence CB>
HITCBC 281   12812 constexpr std::size_t operator()( 281   12812 constexpr std::size_t operator()(
282   CB const& bs) const noexcept 282   CB const& bs) const noexcept
283   { 283   {
HITCBC 284   12812 std::size_t n = 0; 284   12812 std::size_t n = 0;
HITCBC 285   12812 auto const e = capy::end(bs); 285   12812 auto const e = capy::end(bs);
HITCBC 286   27149 for(auto it = capy::begin(bs); it != e; ++it) 286   27149 for(auto it = capy::begin(bs); it != e; ++it)
HITCBC 287   14337 n += const_buffer(*it).size(); 287   14337 n += const_buffer(*it).size();
HITCBC 288   12812 return n; 288   12812 return n;
289   } 289   }
290   #if defined(__GNUC__) && !defined(__clang__) 290   #if defined(__GNUC__) && !defined(__clang__)
291   #pragma GCC diagnostic pop 291   #pragma GCC diagnostic pop
292   #endif 292   #endif
293   } buffer_size {}; 293   } buffer_size {};
294   294  
295   /** Check if a buffer sequence contains no data. 295   /** Check if a buffer sequence contains no data.
296   296  
297   @return `true` if all buffers have size zero or the sequence 297   @return `true` if all buffers have size zero or the sequence
298   is empty. 298   is empty.
299   */ 299   */
300   constexpr struct buffer_empty_mrdocs_workaround_t 300   constexpr struct buffer_empty_mrdocs_workaround_t
301   { 301   {
302   // See note on buffer_size above — same union-storage false positive. 302   // See note on buffer_size above — same union-storage false positive.
303   #if defined(__GNUC__) && !defined(__clang__) 303   #if defined(__GNUC__) && !defined(__clang__)
304   #pragma GCC diagnostic push 304   #pragma GCC diagnostic push
305   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 305   #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
306   #endif 306   #endif
307   template<ConstBufferSequence CB> 307   template<ConstBufferSequence CB>
HITCBC 308   4243 constexpr bool operator()( 308   4243 constexpr bool operator()(
309   CB const& bs) const noexcept 309   CB const& bs) const noexcept
310   { 310   {
HITCBC 311   4243 auto it = begin(bs); 311   4243 auto it = begin(bs);
HITCBC 312   4243 auto const end_ = end(bs); 312   4243 auto const end_ = end(bs);
HITCBC 313   4284 while(it != end_) 313   4284 while(it != end_)
314   { 314   {
HITCBC 315   4244 const_buffer b(*it++); 315   4244 const_buffer b(*it++);
HITCBC 316   4244 if(b.size() != 0) 316   4244 if(b.size() != 0)
HITCBC 317   4203 return false; 317   4203 return false;
318   } 318   }
HITCBC 319   40 return true; 319   40 return true;
320   } 320   }
321   #if defined(__GNUC__) && !defined(__clang__) 321   #if defined(__GNUC__) && !defined(__clang__)
322   #pragma GCC diagnostic pop 322   #pragma GCC diagnostic pop
323   #endif 323   #endif
324   } buffer_empty {}; 324   } buffer_empty {};
325   325  
326   namespace detail { 326   namespace detail {
327   327  
328   template<class It> 328   template<class It>
329   auto 329   auto
HITCBC 330   240 length_impl(It first, It last, int) 330   240 length_impl(It first, It last, int)
331   -> decltype(static_cast<std::size_t>(last - first)) 331   -> decltype(static_cast<std::size_t>(last - first))
332   { 332   {
HITCBC 333   240 return static_cast<std::size_t>(last - first); 333   240 return static_cast<std::size_t>(last - first);
334   } 334   }
335   335  
336   template<class It> 336   template<class It>
337   std::size_t 337   std::size_t
338   length_impl(It first, It last, long) 338   length_impl(It first, It last, long)
339   { 339   {
340   std::size_t n = 0; 340   std::size_t n = 0;
341   while(first != last) 341   while(first != last)
342   { 342   {
343   ++first; 343   ++first;
344   ++n; 344   ++n;
345   } 345   }
346   return n; 346   return n;
347   } 347   }
348   348  
349   } // detail 349   } // detail
350   350  
351   /** Return the number of buffer elements in a sequence. 351   /** Return the number of buffer elements in a sequence.
352   352  
353   Counts the number of individual buffer objects, not bytes. 353   Counts the number of individual buffer objects, not bytes.
354   For a single buffer, returns 1. For a range, returns the 354   For a single buffer, returns 1. For a range, returns the
355   distance from `begin` to `end`. 355   distance from `begin` to `end`.
356   356  
357   @see buffer_size 357   @see buffer_size
358   */ 358   */
359   template<ConstBufferSequence CB> 359   template<ConstBufferSequence CB>
360   std::size_t 360   std::size_t
HITCBC 361   240 buffer_length(CB const& bs) 361   240 buffer_length(CB const& bs)
362   { 362   {
HITCBC 363   240 return detail::length_impl( 363   240 return detail::length_impl(
HITCBC 364   240 begin(bs), end(bs), 0); 364   240 begin(bs), end(bs), 0);
365   } 365   }
366   366  
367   /// Alias for `mutable_buffer` or `const_buffer` based on sequence type. 367   /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
368   template<typename BS> 368   template<typename BS>
369   using buffer_type = std::conditional_t< 369   using buffer_type = std::conditional_t<
370   MutableBufferSequence<BS>, 370   MutableBufferSequence<BS>,
371   mutable_buffer, const_buffer>; 371   mutable_buffer, const_buffer>;
372   372  
373   } // capy 373   } // capy
374   } // boost 374   } // boost
375   375  
376   #endif 376   #endif