libs/capy/include/boost/capy/buffers.hpp

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