GCC Code Coverage Report


Directory: ./
File: libs/buffers/include/boost/buffers/any_buffers.hpp
Date: 2025-12-06 02:12:43
Exec Total Coverage
Lines: 138 154 89.6%
Functions: 122 154 79.2%
Branches: 10 16 62.5%

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 44 ~any_buffers()
64 {
65 44 p_->destroy();
66 44 }
67
68 /** Constructor.
69 Default-constructed objects are empty with zero length.
70 */
71 any_buffers() noexcept;
72
73 /** Constructor.
74 */
75 14 any_buffers(
76 any_buffers const& other) noexcept
77 14 {
78 14 other.p_->copy(*this);
79 14 }
80
81 /** Assignment.
82 */
83 any_buffers&
84 operator=(
85 any_buffers const& other) noexcept
86 {
87 if(this == &other)
88 return *this;
89 p_->destroy();
90 other.p_->copy(*this);
91 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 30 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_ = nullptr;
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 22 explicit impl(T_&& t) noexcept
220 22 : t_(std::forward<T_>(t))
221 {
222 22 }
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 22 void destroy() const override
235 {
236 22 destroy(std::integral_constant<bool,
237 sizeof(*this) <= sbo_size>{});
238 22 }
239
240 22 void destroy(std::true_type) const // small buffers
241 {
242 22 this->~impl();
243 22 }
244
245 void destroy(std::false_type) const
246 {
247 if(--refs_ == 0)
248 delete this;
249 }
250
251 4 void copy(any_buffers& dest) const override
252 {
253 4 copy(dest, std::integral_constant<bool,
254 sizeof(*this) <= sbo_size>{});
255 4 }
256
257 4 void copy(any_buffers& dest, std::true_type) const // small buffers
258 {
259 4 dest.p_ = ::new(&dest.storage_) impl<T>(t_);
260 4 }
261
262 void copy(any_buffers& dest, std::false_type) const
263 {
264 ++refs_;
265 dest.p_ = this;
266 }
267
268 void it_copy(void* dest, void const* src) const override
269 {
270 ::new(dest) iter_t(*static_cast<iter_t const*>(src));
271 }
272
273 84 void it_destroy(void* p) const override
274 {
275 84 static_cast<iter_t*>(p)->~iter_t();
276 84 }
277
278 52 void inc(void* p) const override
279 {
280 52 ++(*static_cast<iter_t*>(p));
281 52 }
282
283 void dec(void* p) const override
284 {
285 --(*static_cast<iter_t*>(p));
286 }
287
288 38 value_type deref(void const* p) const override
289 {
290 38 return *(*static_cast<iter_t const*>(p));
291 }
292
293 92 bool equal(void const* it0, void const* it1) const override
294 {
295 92 return *static_cast<iter_t const*>(it0) ==
296 92 *static_cast<iter_t const*>(it1);
297 }
298
299 42 void begin(void* p) const override
300 {
301 42 ::new(p) iter_t(buffers::begin(t_));
302 42 }
303
304 42 void end(void* p) const override
305 {
306 42 ::new(p) iter_t(buffers::end(t_));
307 42 }
308
309 private:
310 T t_;
311 std::atomic<std::size_t> mutable refs_{1};
312 };
313
314 template<bool IsConst>
315 template<class T>
316 struct any_buffers<IsConst>::
317 impl<T, false> : any_impl
318 {
319 struct iter_t
320 {
321 std::size_t i;
322 };
323
324 template<class T_>
325 4 explicit impl(T_&& t) noexcept
326 6 : t_(std::forward<T_>(t))
327 6 , len_(length(t_))
328 {
329 4 }
330
331 2 bool is_small_buffers() const noexcept override
332 {
333 return sizeof(*this) <=
334 2 any_buffers<IsConst>::sbo_size;
335 }
336
337 2 bool is_small_iter() const noexcept override
338 {
339 2 return false;
340 }
341
342 6 void destroy() const override
343 {
344 6 destroy(std::integral_constant<bool,
345 sizeof(*this) <= any_buffers<IsConst>::sbo_size>{});
346 6 }
347
348 void destroy(std::true_type) const // small buffers
349 {
350 this->~impl();
351 }
352
353 6 void destroy(std::false_type) const
354 {
355
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
6 if(--refs_ == 0)
356
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 delete this;
357 6 }
358
359 2 void copy(any_buffers<IsConst>& dest) const override
360 {
361 2 copy(dest, std::integral_constant<bool,
362 sizeof(*this) <= sbo_size>{});
363 2 }
364
365 void copy(any_buffers<IsConst>& dest,
366 std::true_type) const // small buffers
367 {
368 dest.p_ = ::new(&dest.storage_) impl<T>(t_);
369 }
370
371 2 void copy(any_buffers<IsConst>& dest,
372 std::false_type) const
373 {
374 2 ++refs_;
375 2 dest.p_ = this;
376 2 }
377
378 void it_copy(void* dest, void const* src) const override
379 {
380 ::new(dest) iter_t(*static_cast<iter_t const*>(src));
381 }
382
383 28 void it_destroy(void* p) const override
384 {
385 28 static_cast<iter_t*>(p)->~iter_t();
386 28 }
387
388 4 void inc(void* p) const override
389 {
390 4 ++static_cast<iter_t*>(p)->i;
391 4 }
392
393 void dec(void* p) const override
394 {
395 --static_cast<iter_t*>(p)->i;
396 }
397
398 typename any_buffers<IsConst>::value_type
399 4 deref(void const* p) const override
400 {
401 4 auto const& it_ = *static_cast<iter_t const*>(p);
402 4 auto it = buffers::begin(t_);
403
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
6 for(auto i = it_.i; i; --i)
404 2 ++it;
405 4 return *it;
406 }
407
408 18 bool equal(void const* it0, void const* it1) const override
409 {
410 18 return static_cast<iter_t const*>(it0)->i ==
411 18 static_cast<iter_t const*>(it1)->i;
412 }
413
414 14 void begin(void* p) const override
415 {
416 14 ::new(p) iter_t{ 0 };
417 14 }
418
419 14 void end(void* p) const override
420 {
421 14 ::new(p) iter_t{ len_ };
422 14 }
423
424 private:
425 T t_;
426 std::atomic<std::size_t> mutable refs_{1};
427 std::size_t len_;
428 };
429
430 //-----------------------------------------------
431
432 /** A bidirectional iterator for @ref any_buffers.
433
434 This iterator provides type-erased access to the
435 underlying buffer sequence elements. It models
436 `BidirectionalIterator` and returns buffer objects
437 by value.
438 */
439 template<bool IsConst>
440 class any_buffers<IsConst>::
441 const_iterator
442 {
443 public:
444 /** The buffer type returned by dereferencing.
445 */
446 using value_type = typename any_buffers::value_type;
447
448 /** The type returned by `operator*`.
449
450 Buffers are returned by value.
451 */
452 using reference = value_type;
453
454 /** Pointer type (void, not used).
455 */
456 using pointer = void;
457
458 /** Signed integer type for iterator differences.
459 */
460 using difference_type = std::ptrdiff_t;
461
462 /** Iterator category tag.
463 */
464 using iterator_category =
465 std::bidirectional_iterator_tag;
466
467 #if defined(__cpp_concepts) || defined(__cpp_lib_concepts)
468 /** Iterator concept tag (C++20).
469 */
470 using iterator_concept = std::bidirectional_iterator_tag;
471 #endif
472
473 /** Destructor.
474
475 Destroys the type-erased iterator state.
476 */
477 216 ~const_iterator()
478 {
479 216 p_->it_destroy(&storage_);
480 216 }
481
482 /** Default constructor.
483
484 Constructs a singular iterator. A default-constructed
485 iterator may only be assigned to or destroyed.
486 */
487 const_iterator() noexcept;
488
489 /** Copy constructor.
490
491 @param other The iterator to copy.
492 */
493 4 const_iterator(
494 const_iterator const& other) noexcept
495 4 : p_(other.p_)
496 {
497 4 p_->it_copy(&storage_, &other.storage_);
498 4 }
499
500 /** Copy assignment.
501
502 @param other The iterator to copy.
503 @return `*this`
504 */
505 const_iterator& operator=(
506 const_iterator const& other) noexcept
507 {
508 if(this == &other)
509 return *this;
510 p_->it_destroy(&storage_);
511 p_ = other.p_;
512 p_->it_copy(&storage_, &other.storage_);
513 return *this;
514 }
515
516 /** Test for equality.
517
518 @param other The iterator to compare.
519 @return `true` if both iterators point to the
520 same element of the same sequence.
521 */
522 bool
523 162 operator==(
524 const_iterator const& other) const noexcept
525 {
526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 81 times.
162 if(p_ != other.p_)
527 return false;
528 162 return p_->equal(&storage_, &other.storage_);
529 }
530
531 /** Test for inequality.
532
533 @param other The iterator to compare.
534 @return `true` if the iterators point to
535 different elements or different sequences.
536 */
537 bool
538 154 operator!=(
539 const_iterator const& other) const noexcept
540 {
541 154 return !(*this == other);
542 }
543
544 /** Dereference the iterator.
545
546 @return The buffer at the current position.
547
548 @pre The iterator is dereferenceable
549 (not default-constructed or past-the-end).
550 */
551 reference
552 54 operator*() const noexcept
553 {
554 54 return p_->deref(&storage_);
555 }
556
557 /** Pre-increment.
558
559 Advances the iterator to the next buffer.
560
561 @return `*this`
562
563 @pre The iterator is incrementable.
564 */
565 const_iterator&
566 60 operator++() noexcept
567 {
568 60 p_->inc(&storage_);
569 60 return *this;
570 }
571
572 /** Post-increment.
573
574 Advances the iterator to the next buffer.
575
576 @return A copy of the iterator before incrementing.
577
578 @pre The iterator is incrementable.
579 */
580 const_iterator
581 operator++(int) noexcept
582 {
583 auto temp = *this;
584 ++(*this);
585 return temp;
586 }
587
588 /** Pre-decrement.
589
590 Moves the iterator to the previous buffer.
591
592 @return `*this`
593
594 @pre The iterator is decrementable.
595 */
596 const_iterator&
597 4 operator--() noexcept
598 {
599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 BOOST_ASSERT(p_ != nullptr);
600 4 p_->dec(&storage_);
601 4 return *this;
602 }
603
604 /** Post-decrement.
605
606 Moves the iterator to the previous buffer.
607
608 @return A copy of the iterator before decrementing.
609
610 @pre The iterator is decrementable.
611 */
612 const_iterator
613 operator--(int) noexcept
614 {
615 auto temp = *this;
616 --(*this);
617 return temp;
618 }
619
620 private:
621 friend class any_buffers;
622
623 struct begin_tag {};
624 struct end_tag {};
625
626 108 const_iterator(begin_tag,
627 any_impl const* p) noexcept
628 108 : p_(p)
629 {
630 108 p_->begin(&storage_);
631
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
108 BOOST_ASSERT(p_ != nullptr);
632 108 }
633
634 104 const_iterator(end_tag,
635 any_impl const* p) noexcept
636 104 : p_(p)
637 {
638 104 p_->end(&storage_);
639
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
104 BOOST_ASSERT(p_ != nullptr);
640 104 }
641
642 alignas(std::max_align_t)
643 unsigned char mutable storage_[iter_sbo_size] = {};
644 any_buffers::any_impl const* p_ = nullptr;
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 108 any_buffers<IsConst>::
676 begin() const noexcept ->
677 const_iterator
678 {
679
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
108 if(! p_)
680 return const_iterator();
681 return const_iterator(typename
682 108 const_iterator::begin_tag{}, p_);
683 }
684
685 template<bool IsConst>
686 auto
687 52 any_buffers<IsConst>::
688 end() const noexcept ->
689 const_iterator
690 {
691 52 if(! p_)
692 return const_iterator();
693 return const_iterator(typename
694 52 const_iterator::end_tag{}, p_);
695 }
696
697 } // buffers
698 } // boost
699
700 #endif
701