GCC Code Coverage Report


Directory: ./
File: libs/buffers/include/boost/buffers/any_buffers.hpp
Date: 2025-12-08 17:18:03
Exec Total Coverage
Lines: 155 161 96.3%
Functions: 137 160 85.6%
Branches: 7 10 70.0%

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