GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/buffers.hpp
Date: 2026-01-22 22:47:31
Exec Total Coverage
Lines: 84 84 100.0%
Functions: 87 87 100.0%
Branches: 15 17 88.2%

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 8 constexpr auto data() const noexcept ->
42 std::conditional_t<std::is_const_v<T>, void const*, void*>
43 {
44 8 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 295294 basic_buffer() = default;
57 777141 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 328511 constexpr mutable_buffer(
125 void* data, std::size_t size) noexcept
126 328511 : basic_buffer<unsigned char>(
127 328511 static_cast<unsigned char*>(data), size)
128 {
129 328511 }
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 421901 constexpr void* data() const noexcept
146 {
147 421901 return p_;
148 }
149
150 /** Return the number of valid bytes in the referenced memory region
151 */
152 1256031 constexpr std::size_t size() const noexcept
153 {
154 1256031 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 51782 constexpr const_buffer(
234 void const* data, std::size_t size) noexcept
235 51782 : basic_buffer<unsigned char const>(
236 51782 static_cast<unsigned char const*>(data), size)
237 {
238 51782 }
239
240 /** Constructor
241 */
242 8278 constexpr const_buffer(
243 mutable_buffer const& b) noexcept
244 8278 : basic_buffer<unsigned char const>(
245 8278 static_cast<unsigned char const*>(b.data()), b.size())
246 {
247 8278 }
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 4940903 constexpr void const* data() const noexcept
265 {
266 4940903 return p_;
267 }
268
269 /** Return the number of valid bytes in the referenced memory region
270 */
271 7550877 constexpr std::size_t size() const noexcept
272 {
273 7550877 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 593511 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
368 {
369 593511 return std::addressof(b);
370 }
371
372 template<ConstBufferSequence BS>
373 requires (!std::convertible_to<BS, const_buffer>)
374 4332900 auto operator()(BS const& bs) const noexcept
375 {
376 4332900 return std::ranges::begin(bs);
377 }
378
379 template<ConstBufferSequence BS>
380 requires (!std::convertible_to<BS, const_buffer>)
381 1149944 auto operator()(BS& bs) const noexcept
382 {
383 1149944 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 593349 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
405 {
406 593349 return std::addressof(b) + 1;
407 }
408
409 template<ConstBufferSequence BS>
410 requires (!std::convertible_to<BS, const_buffer>)
411 4304234 auto operator()(BS const& bs) const noexcept
412 {
413 4304234 return std::ranges::end(bs);
414 }
415
416 template<ConstBufferSequence BS>
417 requires (!std::convertible_to<BS, const_buffer>)
418 1149944 auto operator()(BS& bs) const noexcept
419 {
420 1149944 return std::ranges::end(bs);
421 }
422 } end {};
423
424 //------------------------------------------------------------------------------
425
426 template<ConstBufferSequence CB>
427 std::size_t
428 713854 tag_invoke(
429 size_tag const&,
430 CB const& bs) noexcept
431 {
432 713854 std::size_t n = 0;
433 713854 auto const e = end(bs);
434
3/3
✓ Branch 2 taken 708099 times.
✓ Branch 3 taken 1203 times.
✓ Branch 1 taken 1411894 times.
2141144 for(auto it = begin(bs); it != e; ++it)
435 1427290 n += const_buffer(*it).size();
436 713854 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 707135 constexpr std::size_t operator()(
465 CB const& bs) const noexcept
466 {
467 707135 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