Line data Source code
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 9028 : ~any_buffers()
64 : {
65 9028 : p_->destroy();
66 9028 : }
67 :
68 : /** Constructor.
69 : Default-constructed objects are empty with zero length.
70 : */
71 : any_buffers() noexcept;
72 :
73 : /** Constructor.
74 : */
75 8737 : any_buffers(
76 : any_buffers const& other) noexcept
77 8737 : {
78 8737 : other.p_->copy(*this);
79 8737 : }
80 :
81 : /** Assignment.
82 : */
83 : any_buffers&
84 552 : operator=(
85 : any_buffers const& other) noexcept
86 : {
87 552 : if(this == &other)
88 0 : return *this;
89 552 : p_->destroy();
90 552 : other.p_->copy(*this);
91 552 : 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 11 : any_buffers(
108 : BufferSequence&& buffers)
109 11 : {
110 : using T = typename std::decay<BufferSequence>::type;
111 11 : construct(std::forward<BufferSequence>(buffers),
112 : std::integral_constant<bool, (
113 : sizeof(impl<T>) <= sbo_size)>{});
114 11 : }
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 8400 : 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 9 : void construct(T&& t, std::true_type)
161 : {
162 : using U = typename std::decay<T>::type;
163 9 : p_ = ::new(&storage_) impl<U>(
164 : std::forward<T>(t));
165 9 : }
166 :
167 : template<class T>
168 2 : void construct(T&& t, std::false_type)
169 : {
170 : using U = typename std::decay<T>::type;
171 2 : p_ = new impl<U>(std::forward<T>(t));
172 2 : }
173 :
174 7 : bool is_small_buffers() const noexcept
175 : {
176 7 : return p_->is_small_buffers();
177 : }
178 :
179 7 : bool is_small_iter() const noexcept
180 : {
181 7 : 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 8396 : explicit impl(T_&& t) noexcept
220 8396 : : t_(std::forward<T_>(t))
221 : {
222 8396 : }
223 :
224 3 : bool is_small_buffers() const noexcept override
225 : {
226 3 : return sizeof(*this) <= sbo_size;
227 : }
228 :
229 3 : bool is_small_iter() const noexcept override
230 : {
231 3 : return true;
232 : }
233 :
234 8396 : void destroy() const override
235 : {
236 8396 : destroy(std::integral_constant<bool,
237 : sizeof(*this) <= sbo_size>{});
238 8396 : }
239 :
240 8396 : void destroy(std::true_type) const // small buffers
241 : {
242 8396 : this->~impl();
243 8396 : }
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 8387 : void copy(any_buffers& dest) const override
255 : {
256 8387 : copy(dest, std::integral_constant<bool,
257 : sizeof(*this) <= sbo_size>{});
258 8387 : }
259 :
260 8387 : void copy(any_buffers& dest, std::true_type) const // small buffers
261 : {
262 8387 : dest.p_ = ::new(&dest.storage_) impl<T>(t_);
263 8387 : }
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 104019 : void it_copy(void* dest, void const* src) const override
272 : {
273 104019 : ::new(dest) iter_t(*static_cast<iter_t const*>(src));
274 104019 : }
275 :
276 198027 : void it_destroy(void* p) const override
277 : {
278 198027 : static_cast<iter_t*>(p)->~iter_t();
279 198027 : }
280 :
281 67610 : void inc(void* p) const override
282 : {
283 67610 : ++(*static_cast<iter_t*>(p));
284 67610 : }
285 :
286 14163 : void dec(void* p) const override
287 : {
288 14163 : --(*static_cast<iter_t*>(p));
289 14163 : }
290 :
291 40447 : value_type deref(void const* p) const override
292 : {
293 40447 : return *(*static_cast<iter_t const*>(p));
294 : }
295 :
296 84052 : bool equal(void const* it0, void const* it1) const override
297 : {
298 84052 : return *static_cast<iter_t const*>(it0) ==
299 84052 : *static_cast<iter_t const*>(it1);
300 : }
301 :
302 93726 : void begin(void* p) const override
303 : {
304 93726 : ::new(p) iter_t(buffers::begin(t_));
305 93726 : }
306 :
307 282 : void end(void* p) const override
308 : {
309 282 : ::new(p) iter_t(buffers::end(t_));
310 282 : }
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 2 : explicit impl(T_&& t) noexcept
329 3 : : t_(std::forward<T_>(t))
330 3 : , len_(length(t_))
331 : {
332 2 : }
333 :
334 1 : bool is_small_buffers() const noexcept override
335 : {
336 : return sizeof(*this) <=
337 1 : any_buffers<IsConst>::sbo_size;
338 : }
339 :
340 1 : bool is_small_iter() const noexcept override
341 : {
342 1 : return false;
343 : }
344 :
345 210 : void destroy() const override
346 : {
347 210 : destroy(std::integral_constant<bool,
348 : sizeof(*this) <= any_buffers<IsConst>::sbo_size>{});
349 210 : }
350 :
351 : void destroy(std::true_type) const // small buffers
352 : {
353 : this->~impl();
354 : }
355 :
356 210 : void destroy(std::false_type) const
357 : {
358 210 : if(--refs_ == 0)
359 2 : delete this;
360 210 : }
361 :
362 208 : void copy(any_buffers<IsConst>& dest) const override
363 : {
364 208 : copy(dest, std::integral_constant<bool,
365 : sizeof(*this) <= any_buffers<IsConst>::sbo_size>{});
366 208 : }
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 208 : void copy(any_buffers<IsConst>& dest,
375 : std::false_type) const
376 : {
377 208 : ++refs_;
378 208 : dest.p_ = this;
379 208 : }
380 :
381 1984 : void it_copy(void* dest, void const* src) const override
382 : {
383 1984 : ::new(dest) iter_t(*static_cast<iter_t const*>(src));
384 1984 : }
385 :
386 4003 : void it_destroy(void* p) const override
387 : {
388 4003 : static_cast<iter_t*>(p)->~iter_t();
389 4003 : }
390 :
391 2 : void inc(void* p) const override
392 : {
393 2 : ++static_cast<iter_t*>(p)->i;
394 2 : }
395 :
396 0 : void dec(void* p) const override
397 : {
398 0 : --static_cast<iter_t*>(p)->i;
399 0 : }
400 :
401 : typename any_buffers<IsConst>::value_type
402 2 : deref(void const* p) const override
403 : {
404 2 : auto const& it_ = *static_cast<iter_t const*>(p);
405 2 : auto it = buffers::begin(t_);
406 3 : for(auto i = it_.i; i; --i)
407 1 : ++it;
408 2 : return *it;
409 0 : }
410 :
411 1010 : bool equal(void const* it0, void const* it1) const override
412 : {
413 1010 : return static_cast<iter_t const*>(it0)->i ==
414 1010 : static_cast<iter_t const*>(it1)->i;
415 : }
416 :
417 1989 : void begin(void* p) const override
418 : {
419 1989 : ::new(p) iter_t{ 0 };
420 1989 : }
421 :
422 30 : void end(void* p) const override
423 : {
424 30 : ::new(p) iter_t{ len_ };
425 30 : }
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 210060 : ~const_iterator()
481 : {
482 210060 : p_->it_destroy(&storage_);
483 210060 : }
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 109973 : const_iterator(
497 : const_iterator const& other) noexcept
498 109973 : : p_(other.p_)
499 : {
500 109973 : p_->it_copy(&storage_, &other.storage_);
501 109973 : }
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 87090 : operator==(
527 : const_iterator const& other) const noexcept
528 : {
529 87090 : if(p_ != other.p_)
530 0 : return false;
531 87090 : 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 2573 : operator!=(
542 : const_iterator const& other) const noexcept
543 : {
544 2573 : 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 40455 : operator*() const noexcept
556 : {
557 40455 : 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 67614 : operator++() noexcept
570 : {
571 67614 : p_->inc(&storage_);
572 67614 : 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 6 : operator++(int) noexcept
585 : {
586 6 : auto temp = *this;
587 6 : ++(*this);
588 6 : 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 14165 : operator--() noexcept
601 : {
602 14165 : p_->dec(&storage_);
603 14165 : 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 1401 : operator--(int) noexcept
616 : {
617 1401 : auto temp = *this;
618 1401 : --(*this);
619 1401 : return temp;
620 : }
621 :
622 : private:
623 : friend class any_buffers;
624 :
625 : struct begin_tag {};
626 : struct end_tag {};
627 :
628 99705 : const_iterator(begin_tag,
629 : any_impl const* p) noexcept
630 99705 : : p_(p)
631 : {
632 99705 : p_->begin(&storage_);
633 99705 : }
634 :
635 382 : const_iterator(end_tag,
636 : any_impl const* p) noexcept
637 382 : : p_(p)
638 : {
639 382 : p_->end(&storage_);
640 382 : }
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 99705 : any_buffers<IsConst>::
676 : begin() const noexcept ->
677 : const_iterator
678 : {
679 : return const_iterator(typename
680 99705 : 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
|