GCC Code Coverage Report


Directory: ./
File: libs/buffers/include/boost/buffers/any_buffers.hpp
Date: 2025-12-09 13:02:49
Exec Total Coverage
Lines: 136 146 93.2%
Functions: 125 151 82.8%
Branches: 8 15 53.3%

Line Branch Exec Source
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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/buffers
8 //
9
10 #ifndef BOOST_BUFFERS_ANY_BUFFERS_HPP
11 #define BOOST_BUFFERS_ANY_BUFFERS_HPP
12
13 #include <boost/buffers/detail/config.hpp>
14 #include <boost/buffers/buffer.hpp>
15 #include <boost/core/null_deleter.hpp>
16 #include <boost/core/detail/static_assert.hpp>
17 #include <boost/assert.hpp>
18 #include <cstddef>
19 #include <memory>
20 #include <new>
21 #include <type_traits>
22
23 namespace boost {
24 namespace buffers {
25
26 /** A type-erased buffer sequence.
27
28 This class template wraps any buffer sequence and
29 exposes it through a uniform interface, hiding the
30 concrete type. Iteration is performed via a type-erased
31 bidirectional iterator.
32
33 The implementation uses small buffer optimization (SBO)
34 for iterators that are small, trivially aligned, and
35 nothrow copy constructible. Larger iterators fall back
36 to an index-based traversal strategy.
37
38 @tparam IsConst If `true`, the sequence yields
39 @ref const_buffer elements. If `false`, it yields
40 @ref mutable_buffer elements.
41
42 @see any_const_buffers, any_mutable_buffers
43 */
44 template<bool IsConst>
45 class any_buffers
46 {
47 public:
48 /** The buffer type returned when dereferencing iterators.
49
50 This is @ref const_buffer when `IsConst` is `true`,
51 otherwise @ref mutable_buffer.
52 */
53 using value_type = typename std::conditional<
54 IsConst, const_buffer, mutable_buffer>::type;
55
56 /** A bidirectional iterator over the buffer sequence.
57
58 @see begin, end
59 */
60 class const_iterator;
61
62 /** Destructor.
63 */
64 914 ~any_buffers() = default;
65
66 /** Constructor.
67 Default-constructed objects are empty with zero length.
68 */
69 BOOST_BUFFERS_DECL
70 any_buffers() noexcept;
71
72 /** Constructor.
73 */
74 878 any_buffers(
75 any_buffers const& other) noexcept
76 878 {
77 878 other.sp_->copy(*this, other.sp_);
78 878 }
79
80 /** Assignment.
81 */
82 any_buffers&
83 operator=(
84 any_buffers const& other) noexcept
85 {
86 if(this == &other)
87 return *this;
88 other.sp_->copy(*this, other.sp_);
89 return *this;
90 }
91
92 /** Constructor.
93
94 The type-erased buffer sequence is constructed
95 from the specified buffer sequence, which must satisfy
96 `ConstBufferSequence`. If `IsConst` is `false`, must
97 also satisfy `MutableBufferSequence`.
98
99 @param buffers The buffer sequence to type-erase.
100 */
101 template<class BufferSequence
102 , class = typename std::enable_if<! std::is_same<
103 any_buffers, typename std::decay<BufferSequence
104 >::type>::value>::type>
105 25 any_buffers(
106 BufferSequence&& buffers)
107 25 {
108 BOOST_CORE_STATIC_ASSERT(
109 is_const_buffer_sequence<BufferSequence>::value && (IsConst ||
110 is_mutable_buffer_sequence<BufferSequence>::value));
111 using T = typename std::decay<BufferSequence>::type;
112
1/1
✓ Branch 2 taken 14 times.
25 construct(std::forward<BufferSequence>(buffers),
113 std::integral_constant<bool, (
114 sizeof(impl<T>) <= sbo_size)>{});
115 25 }
116
117 /** Return an iterator to the beginning.
118
119 @return An iterator pointing to the first buffer,
120 or `end()` if the sequence is empty.
121 */
122 const_iterator begin() const noexcept;
123
124 /** Return an iterator to the end.
125
126 @return An iterator pointing one past the last buffer.
127 */
128 const_iterator end() const noexcept;
129
130 private:
131 friend struct any_buffers_test;
132
133 static constexpr std::size_t sbo_size = 6 * sizeof(void*);
134
135 static constexpr std::size_t iter_sbo_size = 4 * sizeof(void*);
136
137 struct BOOST_SYMBOL_VISIBLE
138 any_impl
139 {
140 46 virtual ~any_impl() = default;
141 virtual bool is_small_buffers() const noexcept = 0;
142 virtual bool is_small_iter() const noexcept = 0;
143 virtual void copy(any_buffers& dest,
144 std::shared_ptr<any_impl const> const&) const = 0;
145 virtual void it_copy(void*, void const*) const = 0;
146 virtual void it_destroy(void*) const = 0;
147 virtual void inc(void*) const = 0;
148 virtual void dec(void*) const = 0;
149 virtual auto deref(void const*) const -> value_type = 0;
150 virtual bool equal(void const*, void const*) const = 0;
151 virtual void begin(void*) const = 0;
152 virtual void end(void*) const = 0;
153 };
154
155 template<class T, bool IsIterSmall = (sizeof(decltype(
156 buffers::begin(std::declval<T const>()))) <= iter_sbo_size)>
157 struct impl;
158
159 // small buffer sequence
160 template<class T>
161 21 void construct(T&& t, std::true_type)
162 {
163 using U = typename std::decay<T>::type;
164
1/3
✓ Branch 1 taken 12 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
21 sp_ = {
165 21 ::new(&storage_) impl<U>(std::forward<T>(t)),
166 null_deleter{} };
167 21 }
168
169 template<class T>
170 4 void construct(T&& t, std::false_type)
171 {
172 using U = typename std::decay<T>::type;
173
1/1
✓ Branch 2 taken 2 times.
4 sp_ = std::make_shared<impl<U>>(std::forward<T>(t));
174 4 }
175
176 14 bool is_small_buffers() const noexcept
177 {
178 14 return sp_->is_small_buffers();
179 }
180
181 14 bool is_small_iter() const noexcept
182 {
183 14 return sp_->is_small_iter();
184 }
185
186 alignas(std::max_align_t)
187 unsigned char mutable storage_[sbo_size] = {};
188 std::shared_ptr<any_impl const> sp_;
189 };
190
191 //-----------------------------------------------
192
193 /** Alias for a type-erased const buffer sequence.
194
195 Equivalent to `any_buffers<true>`.
196
197 @see any_buffers, any_mutable_buffers
198 */
199 using any_const_buffers = any_buffers<true>;
200
201 /** Alias for a type-erased mutable buffer sequence.
202
203 Equivalent to `any_buffers<false>`.
204
205 @see any_buffers, any_const_buffers
206 */
207 using any_mutable_buffers = any_buffers<false>;
208
209 //-----------------------------------------------
210
211 // small iterator
212 template<bool IsConst>
213 template<class T, bool>
214 struct any_buffers<IsConst>::
215 impl : any_impl
216 {
217 using iter_t = decltype(buffers::begin(
218 std::declval<T const&>()));
219
220 template<class T_>
221 754 explicit impl(T_&& t) noexcept
222 754 : t_(std::forward<T_>(t))
223 {
224 754 }
225
226 6 bool is_small_buffers() const noexcept override
227 {
228 6 return sizeof(*this) <= sbo_size;
229 }
230
231 6 bool is_small_iter() const noexcept override
232 {
233 6 return true;
234 }
235
236 730 void copy(any_buffers& dest, std::shared_ptr<
237 any_buffers<IsConst>::any_impl const> const& sp) const override
238 {
239 730 copy(dest, sp, std::integral_constant<bool,
240 sizeof(*this) <= sbo_size>{});
241 730 }
242
243 730 void copy(
244 any_buffers& dest, std::shared_ptr<
245 any_buffers<IsConst>::any_impl const> const&,
246 std::true_type) const // small buffers
247 {
248
1/3
✓ Branch 1 taken 365 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
730 dest.sp_ = std::shared_ptr<impl<T>>(
249 730 ::new(&dest.storage_) impl<T>(t_),
250 null_deleter{} );
251 730 }
252
253 void copy(
254 any_buffers& dest, std::shared_ptr<
255 any_buffers<IsConst>::any_impl const> const&,
256 std::false_type) const
257 {
258 dest.sp_ = std::make_shared<impl<T>>(t_);
259 }
260
261 11184 void it_copy(void* dest, void const* src) const override
262 {
263 11184 ::new(dest) iter_t(*static_cast<iter_t const*>(src));
264 11184 }
265
266 20838 void it_destroy(void* p) const override
267 {
268 20838 static_cast<iter_t*>(p)->~iter_t();
269 20838 }
270
271 7555 void inc(void* p) const override
272 {
273 7555 ++(*static_cast<iter_t*>(p));
274 7555 }
275
276 1464 void dec(void* p) const override
277 {
278 1464 --(*static_cast<iter_t*>(p));
279 1464 }
280
281 4505 value_type deref(void const* p) const override
282 {
283 4505 return *(*static_cast<iter_t const*>(p));
284 }
285
286 9674 bool equal(void const* it0, void const* it1) const override
287 {
288 9674 return *static_cast<iter_t const*>(it0) ==
289 9674 *static_cast<iter_t const*>(it1);
290 }
291
292 9195 void begin(void* p) const override
293 {
294 9195 ::new(p) iter_t(buffers::begin(t_));
295 9195 }
296
297 459 void end(void* p) const override
298 {
299 459 ::new(p) iter_t(buffers::end(t_));
300 459 }
301
302 private:
303 T t_;
304 };
305
306 template<bool IsConst>
307 template<class T>
308 struct any_buffers<IsConst>::
309 impl<T, false> : any_impl
310 {
311 struct iter_t
312 {
313 std::size_t i;
314 };
315
316 template<class T_>
317 40 explicit impl(T_&& t) noexcept
318 42 : t_(std::forward<T_>(t))
319 78 , len_(length(t_))
320 {
321 40 }
322
323 2 bool is_small_buffers() const noexcept override
324 {
325 2 return sizeof(*this) <= any_buffers<IsConst>::sbo_size;
326 }
327
328 2 bool is_small_iter() const noexcept override
329 {
330 2 return false;
331 }
332
333 36 void copy(
334 any_buffers<IsConst>& dest, std::shared_ptr<
335 any_buffers<IsConst>::any_impl const> const& sp) const override
336 {
337 36 copy(dest, sp, std::integral_constant<bool,
338 sizeof(*this) <= any_buffers<IsConst>::sbo_size>{});
339 36 }
340
341 void copy(
342 any_buffers<IsConst>& dest, std::shared_ptr<
343 any_buffers<IsConst>::any_impl const> const&,
344 std::true_type) const // small buffers
345 {
346 dest.sp_ = std::shared_ptr<impl<T>>(
347 ::new(&dest.storage_) impl<T>(t_),
348 null_deleter{});
349 }
350
351 36 void copy(
352 any_buffers<IsConst>& dest, std::shared_ptr<
353 any_buffers<IsConst>::any_impl const> const&,
354 std::false_type) const
355 {
356
1/1
✓ Branch 1 taken 18 times.
36 dest.sp_ = std::make_shared<impl<T>>(t_);
357 36 }
358
359 432 void it_copy(void* dest, void const* src) const override
360 {
361 432 ::new(dest) iter_t(*static_cast<iter_t const*>(src));
362 432 }
363
364 900 void it_destroy(void* p) const override
365 {
366 900 static_cast<iter_t*>(p)->~iter_t();
367 900 }
368
369 4 void inc(void* p) const override
370 {
371 4 ++static_cast<iter_t*>(p)->i;
372 4 }
373
374 void dec(void* p) const override
375 {
376 --static_cast<iter_t*>(p)->i;
377 }
378
379 typename any_buffers<IsConst>::value_type
380 4 deref(void const* p) const override
381 {
382 4 auto const& it_ = *static_cast<iter_t const*>(p);
383 4 auto it = buffers::begin(t_);
384
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
6 for(auto i = it_.i; i; --i)
385 2 ++it;
386 4 return *it;
387 }
388
389 252 bool equal(void const* it0, void const* it1) const override
390 {
391 252 return static_cast<iter_t const*>(it0)->i ==
392 252 static_cast<iter_t const*>(it1)->i;
393 }
394
395 420 void begin(void* p) const override
396 {
397 420 ::new(p) iter_t{ 0 };
398 420 }
399
400 48 void end(void* p) const override
401 {
402 48 ::new(p) iter_t{ len_ };
403 48 }
404
405 private:
406 T t_;
407 std::size_t len_;
408 };
409
410 //-----------------------------------------------
411
412 /** A bidirectional iterator for @ref any_buffers.
413
414 This iterator provides type-erased access to the
415 underlying buffer sequence elements. It models
416 `BidirectionalIterator` and returns buffer objects
417 by value.
418 */
419 template<bool IsConst>
420 class any_buffers<IsConst>::
421 const_iterator
422 {
423 public:
424 /** The buffer type returned by dereferencing.
425 */
426 using value_type = typename
427 any_buffers<IsConst>::value_type;
428
429 /** The type returned by `operator*`.
430
431 Buffers are returned by value.
432 */
433 using reference = value_type;
434
435 /** Pointer type (void, not used).
436 */
437 using pointer = void;
438
439 /** Signed integer type for iterator differences.
440 */
441 using difference_type = std::ptrdiff_t;
442
443 /** Iterator category tag.
444 */
445 using iterator_category =
446 std::bidirectional_iterator_tag;
447
448 #if defined(__cpp_concepts) || defined(__cpp_lib_concepts)
449 /** Iterator concept tag (C++20).
450 */
451 using iterator_concept = std::bidirectional_iterator_tag;
452 #endif
453
454 /** Destructor.
455
456 Destroys the type-erased iterator state.
457 */
458 23664 ~const_iterator()
459 {
460 23664 sp_->it_destroy(&storage_);
461 23664 }
462
463 /** Default constructor.
464
465 Constructs a singular iterator. A default-constructed
466 iterator may only be assigned to or destroyed.
467 */
468 const_iterator() noexcept;
469
470 /** Copy constructor.
471
472 @param other The iterator to copy.
473 */
474 12484 const_iterator(
475 const_iterator const& other) noexcept
476 12484 : sp_(other.sp_)
477 {
478 12484 sp_->it_copy(&storage_, &other.storage_);
479 12484 }
480
481 /** Copy assignment.
482
483 @param other The iterator to copy.
484 @return `*this`
485 */
486 const_iterator& operator=(
487 const_iterator const& other) noexcept
488 {
489 if(this == &other)
490 return *this;
491 sp_->it_destroy(&storage_);
492 sp_ = other.sp_;
493 sp_->it_copy(&storage_, &other.storage_);
494 return *this;
495 }
496
497 /** Test for equality.
498
499 @param other The iterator to compare.
500 @return `true` if both iterators point to the
501 same element of the same sequence.
502 */
503 bool
504 10485 operator==(
505 const_iterator const& other) const noexcept
506 {
507
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5247 times.
10485 if(sp_ != other.sp_)
508 return false;
509 10485 return sp_->equal(&storage_, &other.storage_);
510 }
511
512 /** Test for inequality.
513
514 @param other The iterator to compare.
515 @return `true` if the iterators point to
516 different elements or different sequences.
517 */
518 bool
519 2041 operator!=(
520 const_iterator const& other) const noexcept
521 {
522 2041 return !(*this == other);
523 }
524
525 /** Dereference the iterator.
526
527 @return The buffer at the current position.
528
529 @pre The iterator is dereferenceable
530 (not default-constructed or past-the-end).
531 */
532 reference
533 4521 operator*() const noexcept
534 {
535 4521 return sp_->deref(&storage_);
536 }
537
538 /** Pre-increment.
539
540 Advances the iterator to the next buffer.
541
542 @return `*this`
543
544 @pre The iterator is incrementable.
545 */
546 const_iterator&
547 7563 operator++() noexcept
548 {
549 7563 sp_->inc(&storage_);
550 7563 return *this;
551 }
552
553 /** Post-increment.
554
555 Advances the iterator to the next buffer.
556
557 @return A copy of the iterator before incrementing.
558
559 @pre The iterator is incrementable.
560 */
561 const_iterator
562 12 operator++(int) noexcept
563 {
564 12 auto temp = *this;
565 12 ++(*this);
566 12 return temp;
567 }
568
569 /** Pre-decrement.
570
571 Moves the iterator to the previous buffer.
572
573 @return `*this`
574
575 @pre The iterator is decrementable.
576 */
577 const_iterator&
578 1468 operator--() noexcept
579 {
580 1468 sp_->dec(&storage_);
581 1468 return *this;
582 }
583
584 /** Post-decrement.
585
586 Moves the iterator to the previous buffer.
587
588 @return A copy of the iterator before decrementing.
589
590 @pre The iterator is decrementable.
591 */
592 const_iterator
593 156 operator--(int) noexcept
594 {
595 156 auto temp = *this;
596 156 --(*this);
597 156 return temp;
598 }
599
600 private:
601 friend class any_buffers;
602
603 struct begin_tag {};
604 struct end_tag {};
605
606 10518 const_iterator(begin_tag, std::shared_ptr<
607 any_impl const> const& sp) noexcept
608 10518 : sp_(sp)
609 {
610 10518 sp_->begin(&storage_);
611 10518 }
612
613 662 const_iterator(end_tag, std::shared_ptr<
614 any_impl const> const& sp) noexcept
615 662 : sp_(sp)
616 {
617 662 sp_->end(&storage_);
618 662 }
619
620 alignas(std::max_align_t)
621 unsigned char mutable storage_[iter_sbo_size] = {};
622 std::shared_ptr<any_buffers<IsConst>::any_impl const> sp_;
623 };
624
625 //-----------------------------------------------
626
627 template<>
628 BOOST_BUFFERS_DECL
629 any_buffers<true>::
630 any_buffers() noexcept;
631
632 template<>
633 BOOST_BUFFERS_DECL
634 any_buffers<false>::
635 any_buffers() noexcept;
636
637 template<>
638 BOOST_BUFFERS_DECL
639 any_buffers<true>::
640 const_iterator::
641 const_iterator() noexcept;
642
643 template<>
644 BOOST_BUFFERS_DECL
645 any_buffers<false>::
646 const_iterator::
647 const_iterator() noexcept;
648
649 //-----------------------------------------------
650
651 template<bool IsConst>
652 auto
653 10518 any_buffers<IsConst>::
654 begin() const noexcept ->
655 const_iterator
656 {
657 return const_iterator(typename
658 10518 const_iterator::begin_tag{}, sp_);
659 }
660
661 template<bool IsConst>
662 auto
663 334 any_buffers<IsConst>::
664 end() const noexcept ->
665 const_iterator
666 {
667 return const_iterator(typename
668 334 const_iterator::end_tag{}, sp_);
669 }
670
671 } // buffers
672 } // boost
673
674 #endif
675