GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 302 / 0 / 302
Functions: 100.0% 95 / 0 / 95
Branches: 96.0% 48 / 0 / 50

libs/capy/include/boost/capy/buffers.hpp
Line Branch Exec Source
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 4 constexpr auto data() const noexcept ->
42 std::conditional_t<std::is_const_v<T>, void const*, void*>
43 {
44 4 return p_;
45 }
46
47 4 constexpr std::size_t size() const noexcept
48 {
49 4 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 147647 basic_buffer() = default;
57 388643 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 /** size tag for `tag_invoke`
70
71 This type is used in overloads of `tag_invoke`
72 for user-defined types to customize the `size()`
73 algorithm.
74 */
75 struct size_tag {};
76
77 /** slice tag for `tag_invoke`
78
79 This type is used in overloads of `tag_invoke`
80 for user-defined types to customize the slicing
81 algorithms.
82 */
83 struct slice_tag {};
84
85 /** slice constants for slice customization
86
87 This defines the possible values passed to
88 overloads of `tag_invoke` for user-defined
89 types which customize the slicing algorithms.
90 */
91 enum class slice_how
92 {
93 /// Indicates that the front of the buffer sequence should be trimmed
94 remove_prefix,
95
96 /// Indicates that the front of the buffer sequence should be preserved
97 keep_prefix
98 };
99
100 //------------------------------------------------
101
102 /** Holds a contiguous range of modifiable bytes
103 */
104 class mutable_buffer
105 : public detail::basic_buffer<unsigned char>
106 {
107 public:
108 /** Constructor.
109 */
110 575 mutable_buffer() = default;
111
112 /** Constructor.
113 */
114 mutable_buffer(
115 mutable_buffer const&) = default;
116
117 /** Assignment.
118 */
119 mutable_buffer& operator=(
120 mutable_buffer const&) = default;
121
122 /** Constructor.
123 */
124 328541 constexpr mutable_buffer(
125 void* data, std::size_t size) noexcept
126 328541 : basic_buffer<unsigned char>(
127 328541 static_cast<unsigned char*>(data), size)
128 {
129 328541 }
130
131 /** Constructor
132 */
133 template<class MutableBuffer>
134 requires std::same_as<MutableBuffer, asio::mutable_buffer>
135 constexpr mutable_buffer(
136 MutableBuffer const& b) noexcept
137 : basic_buffer<unsigned char>(
138 static_cast<unsigned char*>(
139 b.data()), b.size())
140 {
141 }
142
143 /** Return a pointer to the beginning of the memory region
144 */
145 421939 constexpr void* data() const noexcept
146 {
147 421939 return p_;
148 }
149
150 /** Return the number of valid bytes in the referenced memory region
151 */
152 1256079 constexpr std::size_t size() const noexcept
153 {
154 1256079 return n_;
155 }
156
157 /** Remove a prefix of the memory region
158
159 If the requested number of bytes is larger than the current size,
160 the resulting buffer will have size 0.
161
162 @param n The number of bytes to remove.
163 */
164 mutable_buffer&
165 606702 operator+=(std::size_t n) noexcept
166 {
167
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 606686 times.
606702 if( n > n_)
168 16 n = n_;
169 606702 p_ += n;
170 606702 n_ -= n;
171 606702 return *this;
172 }
173
174 /** Remove a slice from the buffer
175 */
176 friend
177 void
178 1050 tag_invoke(
179 slice_tag const&,
180 mutable_buffer& b,
181 slice_how how,
182 std::size_t n) noexcept
183 {
184 1050 b.do_slice(how, n);
185 1050 }
186
187 private:
188 1050 void do_slice(
189 slice_how how, std::size_t n) noexcept
190 {
191
2/3
✓ Branch 0 taken 512 times.
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
1050 switch(how)
192 {
193 512 case slice_how::remove_prefix:
194 512 *this += n;
195 512 return;
196
197 538 case slice_how::keep_prefix:
198
2/2
✓ Branch 0 taken 475 times.
✓ Branch 1 taken 63 times.
538 if( n < n_)
199 475 n_ = n;
200 538 return;
201 }
202 }
203 };
204
205 //------------------------------------------------
206
207 /** Holds a contiguous range of unmodifiable bytes
208 */
209 class const_buffer
210 : public detail::basic_buffer<unsigned char const>
211 {
212 public:
213 /** Constructor
214 */
215 147072 const_buffer() = default;
216
217 /** Constructor
218 */
219 const_buffer(const_buffer const&) = default;
220
221 /** Assignment
222
223 @par Postconditions
224 @code
225 this->data() == other.data() && this->size() == other.size()
226 @endcode
227 */
228 const_buffer& operator=(
229 const_buffer const& other) = default;
230
231 /** Constructor
232 */
233 51812 constexpr const_buffer(
234 void const* data, std::size_t size) noexcept
235 51812 : basic_buffer<unsigned char const>(
236 51812 static_cast<unsigned char const*>(data), size)
237 {
238 51812 }
239
240 /** Constructor
241 */
242 8290 constexpr const_buffer(
243 mutable_buffer const& b) noexcept
244 8290 : basic_buffer<unsigned char const>(
245 8290 static_cast<unsigned char const*>(b.data()), b.size())
246 {
247 8290 }
248
249 /** Constructor
250 */
251 template<class ConstBuffer>
252 requires (std::same_as<ConstBuffer, asio::const_buffer> ||
253 std::same_as<ConstBuffer, asio::mutable_buffer>)
254 constexpr const_buffer(
255 ConstBuffer const& b) noexcept
256 : basic_buffer<unsigned char const>(
257 static_cast<unsigned char const*>(
258 b.data()), b.size())
259 {
260 }
261
262 /** Return a pointer to the beginning of the memory region
263 */
264 4940943 constexpr void const* data() const noexcept
265 {
266 4940943 return p_;
267 }
268
269 /** Return the number of valid bytes in the referenced memory region
270 */
271 7550957 constexpr std::size_t size() const noexcept
272 {
273 7550957 return n_;
274 }
275
276 /** Remove a prefix of the memory region
277
278 If the requested number of bytes is larger than the current size,
279 the resulting buffer will have size 0.
280
281 @param n The number of bytes to remove.
282 */
283 const_buffer&
284 737774 operator+=(std::size_t n) noexcept
285 {
286
2/2
✓ Branch 0 taken 4112 times.
✓ Branch 1 taken 733662 times.
737774 if( n > n_)
287 4112 n = n_;
288 737774 p_ += n;
289 737774 n_ -= n;
290 737774 return *this;
291 }
292
293 /** Remove a slice from the buffer
294 */
295 friend
296 void
297 267280 tag_invoke(
298 slice_tag const&,
299 const_buffer& b,
300 slice_how how,
301 std::size_t n) noexcept
302 {
303 267280 b.do_slice(how, n);
304 267280 }
305
306 private:
307 267280 void do_slice(
308 slice_how how, std::size_t n) noexcept
309 {
310
2/3
✓ Branch 0 taken 131584 times.
✓ Branch 1 taken 135696 times.
✗ Branch 2 not taken.
267280 switch(how)
311 {
312 131584 case slice_how::remove_prefix:
313 131584 *this += n;
314 131584 return;
315
316 135696 case slice_how::keep_prefix:
317
2/2
✓ Branch 0 taken 121169 times.
✓ Branch 1 taken 14527 times.
135696 if( n < n_)
318 121169 n_ = n;
319 135696 return;
320 }
321 }
322 };
323
324 //------------------------------------------------
325
326 /** Concept for types that model ConstBufferSequence.
327
328 A type satisfies `ConstBufferSequence` if it is convertible
329 to `const_buffer`, or if it is a bidirectional range whose
330 value type is convertible to `const_buffer`.
331 */
332 template<typename T>
333 concept ConstBufferSequence =
334 std::is_convertible_v<T, const_buffer> || (
335 std::ranges::bidirectional_range<T> &&
336 std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
337
338 /** Concept for types that model MutableBufferSequence.
339
340 A type satisfies `MutableBufferSequence` if it is convertible
341 to `mutable_buffer`, or if it is a bidirectional range whose
342 value type is convertible to `mutable_buffer`.
343 */
344 template<typename T>
345 concept MutableBufferSequence =
346 std::is_convertible_v<T, mutable_buffer> || (
347 std::ranges::bidirectional_range<T> &&
348 std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
349
350 //------------------------------------------------------------------------------
351
352 /** Return an iterator pointing to the first element of a buffer sequence
353
354 This function returns an iterator to the beginning of the range denoted by
355 `t`. It handles both ranges and single buffers uniformly.
356
357 @par Constraints
358 @code
359 const_buffer_sequence<T>
360 @endcode
361
362 @param t The buffer sequence
363 */
364 constexpr struct begin_mrdocs_workaround_t
365 {
366 template<std::convertible_to<const_buffer> ConvertibleToBuffer>
367 298291 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
368 {
369 298291 return std::addressof(b);
370 }
371
372 template<ConstBufferSequence BS>
373 requires (!std::convertible_to<BS, const_buffer>)
374 2166492 auto operator()(BS const& bs) const noexcept
375 {
376 2166492 return std::ranges::begin(bs);
377 }
378
379 template<ConstBufferSequence BS>
380 requires (!std::convertible_to<BS, const_buffer>)
381 1140220 auto operator()(BS& bs) const noexcept
382 {
383 1140220 return std::ranges::begin(bs);
384 }
385 } begin {};
386
387 //------------------------------------------------------------------------------
388
389 /** Return an iterator to the end of the buffer sequence
390
391 This function returns an iterator to the end of the range denoted by
392 `t`. It handles both ranges and single buffers uniformly.
393
394 @par Constraints
395 @code
396 const_buffer_sequence<T>
397 @endcode
398
399 @param t The buffer sequence
400 */
401 constexpr struct end_mrdocs_workaround_t
402 {
403 template<std::convertible_to<const_buffer> ConvertibleToBuffer>
404 298291 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
405 {
406 298291 return std::addressof(b) + 1;
407 }
408
409 template<ConstBufferSequence BS>
410 requires (!std::convertible_to<BS, const_buffer>)
411 2159014 auto operator()(BS const& bs) const noexcept
412 {
413 2159014 return std::ranges::end(bs);
414 }
415
416 template<ConstBufferSequence BS>
417 requires (!std::convertible_to<BS, const_buffer>)
418 1140220 auto operator()(BS& bs) const noexcept
419 {
420 1140220 return std::ranges::end(bs);
421 }
422 } end {};
423
424 //------------------------------------------------------------------------------
425
426 template<ConstBufferSequence CB>
427 std::size_t
428 707153 tag_invoke(
429 size_tag const&,
430 CB const& bs) noexcept
431 {
432 707153 std::size_t n = 0;
433 707153 auto const e = end(bs);
434
36/36
unsigned long boost::capy::tag_invoke<boost::capy::const_buffer [3]>(boost::capy::size_tag const&, boost::capy::const_buffer const (&) [3]):
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
unsigned long boost::capy::tag_invoke<boost::capy::const_buffer>(boost::capy::size_tag const&, boost::capy::const_buffer const&):
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 9 times.
unsigned long boost::capy::tag_invoke<boost::capy::consuming_buffers<boost::capy::mutable_buffer> >(boost::capy::size_tag const&, boost::capy::consuming_buffers<boost::capy::mutable_buffer> const&):
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
unsigned long boost::capy::tag_invoke<boost::capy::consuming_buffers<std::array<boost::capy::mutable_buffer, 2ul> > >(boost::capy::size_tag const&, boost::capy::consuming_buffers<std::array<boost::capy::mutable_buffer, 2ul> > const&):
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 time.
unsigned long boost::capy::tag_invoke<boost::capy::mutable_buffer [3]>(boost::capy::size_tag const&, boost::capy::mutable_buffer const (&) [3]):
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
unsigned long boost::capy::tag_invoke<boost::capy::mutable_buffer>(boost::capy::size_tag const&, boost::capy::mutable_buffer const&):
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 10 times.
unsigned long boost::capy::tag_invoke<boost::capy::slice_of<boost::span<boost::capy::const_buffer const, 18446744073709551615ul> > >(boost::capy::size_tag const&, boost::capy::slice_of<boost::span<boost::capy::const_buffer const, 18446744073709551615ul> > const&):
✓ Branch 2 taken 222 times.
✓ Branch 3 taken 110 times.
unsigned long boost::capy::tag_invoke<boost::capy::slice_of<boost::span<boost::capy::mutable_buffer const, 18446744073709551615ul> > >(boost::capy::size_tag const&, boost::capy::slice_of<boost::span<boost::capy::mutable_buffer const, 18446744073709551615ul> > const&):
✓ Branch 2 taken 222 times.
✓ Branch 3 taken 110 times.
unsigned long boost::capy::tag_invoke<boost::capy::slice_of<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> > > >(boost::capy::size_tag const&, boost::capy::slice_of<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> > > const&):
✓ Branch 2 taken 1720 times.
✓ Branch 3 taken 982 times.
unsigned long boost::capy::tag_invoke<boost::span<boost::capy::const_buffer const, 18446744073709551615ul> >(boost::capy::size_tag const&, boost::span<boost::capy::const_buffer const, 18446744073709551615ul> const&):
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 28 times.
unsigned long boost::capy::tag_invoke<boost::span<boost::capy::const_buffer, 3ul> >(boost::capy::size_tag const&, boost::span<boost::capy::const_buffer, 3ul> const&):
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
unsigned long boost::capy::tag_invoke<boost::span<boost::capy::mutable_buffer const, 18446744073709551615ul> >(boost::capy::size_tag const&, boost::span<boost::capy::mutable_buffer const, 18446744073709551615ul> const&):
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
unsigned long boost::capy::tag_invoke<boost::span<boost::capy::mutable_buffer, 3ul> >(boost::capy::size_tag const&, boost::span<boost::capy::mutable_buffer, 3ul> const&):
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
unsigned long boost::capy::tag_invoke<std::array<boost::capy::const_buffer, 2ul> >(boost::capy::size_tag const&, std::array<boost::capy::const_buffer, 2ul> const&):
✓ Branch 1 taken 1406310 times.
✓ Branch 2 taken 703155 times.
unsigned long boost::capy::tag_invoke<std::array<boost::capy::const_buffer, 3ul> >(boost::capy::size_tag const&, std::array<boost::capy::const_buffer, 3ul> const&):
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
unsigned long boost::capy::tag_invoke<std::array<boost::capy::mutable_buffer, 2ul> >(boost::capy::size_tag const&, std::array<boost::capy::mutable_buffer, 2ul> const&):
✓ Branch 1 taken 5478 times.
✓ Branch 2 taken 2739 times.
unsigned long boost::capy::tag_invoke<std::array<boost::capy::mutable_buffer, 3ul> >(boost::capy::size_tag const&, std::array<boost::capy::mutable_buffer, 3ul> const&):
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
unsigned long boost::capy::tag_invoke<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> > >(boost::capy::size_tag const&, std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> > const&):
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 time.
2121235 for(auto it = begin(bs); it != e; ++it)
435 1414082 n += const_buffer(*it).size();
436 707153 return n;
437 }
438
439 //------------------------------------------------------------------------------
440
441 /** Return the total number of bytes in a buffer sequence
442
443 This function returns the sum of the number of bytes in each contiguous
444 buffer contained in the range or value. This is different from the length
445 of the sequence returned by `std::ranges::size(t)`
446
447 @par Constraints
448 @code
449 ConstBufferSequence<T>
450 @endcode
451
452 @par Example
453 @code
454 template<ConstBufferSequence CB>
455 bool is_small( CB const& bs ) noexcept
456 {
457 return buffer_size(bs) < 100;
458 }
459 @endcode
460 */
461 constexpr struct buffer_size_mrdocs_workaround_t
462 {
463 template<ConstBufferSequence CB>
464 707153 constexpr std::size_t operator()(
465 CB const& bs) const noexcept
466 {
467 707153 return tag_invoke(size_tag{}, bs);
468 }
469 } buffer_size {};
470
471 //-----------------------------------------------
472
473 namespace detail {
474
475 template<class It>
476 auto
477 length_impl(It first, It last, int)
478 -> decltype(static_cast<std::size_t>(last - first))
479 {
480 return static_cast<std::size_t>(last - first);
481 }
482
483 template<class It>
484 std::size_t
485 length_impl(It first, It last, long)
486 {
487 std::size_t n = 0;
488 while(first != last)
489 {
490 ++first;
491 ++n;
492 }
493 return n;
494 }
495
496 } // detail
497
498 /** Return the number of elements in a buffer sequence.
499 */
500 template<ConstBufferSequence CB>
501 std::size_t
502 buffer_length(CB const& bs)
503 {
504 return detail::length_impl(
505 begin(bs), end(bs), 0);
506 }
507
508 /** Alias for const_buffer or mutable_buffer depending on sequence type.
509 */
510 template<typename BS>
511 using buffer_type = std::conditional_t<
512 MutableBufferSequence<BS>,
513 mutable_buffer, const_buffer>;
514
515 } // capy
516 } // boost
517
518 #endif
519