GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/buffers.hpp
Date: 2026-01-15 20:40:20
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 10641671 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 639132 constexpr mutable_buffer(
125 void* data, std::size_t size) noexcept
126 639132 : basic_buffer<unsigned char>(
127 639132 static_cast<unsigned char*>(data), size)
128 {
129 639132 }
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 800649 constexpr void* data() const noexcept
146 {
147 800649 return p_;
148 }
149
150 /** Return the number of valid bytes in the referenced memory region
151 */
152 2106908 constexpr std::size_t size() const noexcept
153 {
154 2106908 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 1026737 operator+=(std::size_t n) noexcept
166 {
167
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1026721 times.
1026737 if( n > n_)
168 16 n = n_;
169 1026737 p_ += n;
170 1026737 n_ -= n;
171 1026737 return *this;
172 }
173
174 /** Remove a slice from the buffer
175 */
176 friend
177 void
178 1088 tag_invoke(
179 slice_tag const&,
180 mutable_buffer& b,
181 slice_how how,
182 std::size_t n) noexcept
183 {
184 1088 b.do_slice(how, n);
185 1088 }
186
187 private:
188 1088 void do_slice(
189 slice_how how, std::size_t n) noexcept
190 {
191
2/3
✓ Branch 0 taken 512 times.
✓ Branch 1 taken 576 times.
✗ Branch 2 not taken.
1088 switch(how)
192 {
193 512 case slice_how::remove_prefix:
194 512 *this += n;
195 512 return;
196
197 576 case slice_how::keep_prefix:
198
2/2
✓ Branch 0 taken 476 times.
✓ Branch 1 taken 100 times.
576 if( n < n_)
199 476 n_ = n;
200 576 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 4672750 constexpr const_buffer(
234 void const* data, std::size_t size) noexcept
235 4672750 : basic_buffer<unsigned char const>(
236 4672750 static_cast<unsigned char const*>(data), size)
237 {
238 4672750 }
239
240 /** Constructor
241 */
242 8954 constexpr const_buffer(
243 mutable_buffer const& b) noexcept
244 8954 : basic_buffer<unsigned char const>(
245 8954 static_cast<unsigned char const*>(b.data()), b.size())
246 {
247 8954 }
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 13069677 constexpr void const* data() const noexcept
265 {
266 13069677 return p_;
267 }
268
269 /** Return the number of valid bytes in the referenced memory region
270 */
271 18416594 constexpr std::size_t size() const noexcept
272 {
273 18416594 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 1157878 operator+=(std::size_t n) noexcept
285 {
286
2/2
✓ Branch 0 taken 4112 times.
✓ Branch 1 taken 1153766 times.
1157878 if( n > n_)
287 4112 n = n_;
288 1157878 p_ += n;
289 1157878 n_ -= n;
290 1157878 return *this;
291 }
292
293 /** Remove a slice from the buffer
294 */
295 friend
296 void
297 267349 tag_invoke(
298 slice_tag const&,
299 const_buffer& b,
300 slice_how how,
301 std::size_t n) noexcept
302 {
303 267349 b.do_slice(how, n);
304 267349 }
305
306 private:
307 267349 void do_slice(
308 slice_how how, std::size_t n) noexcept
309 {
310
2/3
✓ Branch 0 taken 131653 times.
✓ Branch 1 taken 135696 times.
✗ Branch 2 not taken.
267349 switch(how)
311 {
312 131653 case slice_how::remove_prefix:
313 131653 *this += n;
314 131653 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 `const_buffer_sequence` 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 const_buffer_sequence =
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 `mutable_buffer_sequence` 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 mutable_buffer_sequence =
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 1183613 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
368 {
369 1183613 return std::addressof(b);
370 }
371
372 template<const_buffer_sequence BufferSequence>
373 requires (!std::convertible_to<BufferSequence, const_buffer>)
374 22187888 auto operator()(BufferSequence const& bs) const noexcept
375 {
376 22187888 return std::ranges::begin(bs);
377 }
378
379 template<const_buffer_sequence BufferSequence>
380 requires (!std::convertible_to<BufferSequence, const_buffer>)
381 3968008 auto operator()(BufferSequence& bs) const noexcept
382 {
383 3968008 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 1183451 auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
405 {
406 1183451 return std::addressof(b) + 1;
407 }
408
409 template<const_buffer_sequence BufferSequence>
410 requires (!std::convertible_to<BufferSequence, const_buffer>)
411 8625974 auto operator()(BufferSequence const& bs) const noexcept
412 {
413 8625974 return std::ranges::end(bs);
414 }
415
416 template<const_buffer_sequence BufferSequence>
417 requires (!std::convertible_to<BufferSequence, const_buffer>)
418 3968008 auto operator()(BufferSequence& bs) const noexcept
419 {
420 3968008 return std::ranges::end(bs);
421 }
422 } end {};
423
424 //------------------------------------------------------------------------------
425
426 template<const_buffer_sequence ConstBufferSequence>
427 std::size_t
428 2114753 tag_invoke(
429 size_tag const&,
430 ConstBufferSequence const& bs) noexcept
431 {
432 2114753 std::size_t n = 0;
433 2114753 auto const e = end(bs);
434
3/3
✓ Branch 2 taken 1770438 times.
✓ Branch 3 taken 697523 times.
✓ Branch 1 taken 1420423 times.
5675116 for(auto it = begin(bs); it != e; ++it)
435 3560363 n += const_buffer(*it).size();
436 2114753 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 const_buffer_sequence<T>
450 @endcode
451
452 @par Example
453 @code
454 template<const_buffer_sequence ConstBufferSequence>
455 bool is_small( ConstBufferSequence 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<const_buffer_sequence ConstBufferSequence>
464 1407618 constexpr std::size_t operator()(
465 ConstBufferSequence const& bs) const noexcept
466 {
467 1407618 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<const_buffer_sequence ConstBufferSequence>
501 std::size_t
502 buffer_length(ConstBufferSequence 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 BufferSequence>
511 using buffer_type = std::conditional_t<
512 mutable_buffer_sequence<BufferSequence>,
513 mutable_buffer, const_buffer>;
514
515 } // capy
516 } // boost
517
518 #endif
519