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 22 : ~any_buffers()
64 : {
65 22 : p_->destroy();
66 22 : }
67 :
68 : /** Constructor.
69 : Default-constructed objects are empty with zero length.
70 : */
71 : any_buffers() noexcept;
72 :
73 : /** Constructor.
74 : */
75 7 : any_buffers(
76 : any_buffers const& other) noexcept
77 7 : {
78 7 : other.p_->copy(*this);
79 7 : }
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 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 15 : 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_ = 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 11 : explicit impl(T_&& t) noexcept
220 11 : : t_(std::forward<T_>(t))
221 : {
222 11 : }
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 11 : void destroy() const override
235 : {
236 11 : destroy(std::integral_constant<bool,
237 : sizeof(*this) <= sbo_size>{});
238 11 : }
239 :
240 11 : void destroy(std::true_type) const // small buffers
241 : {
242 11 : this->~impl();
243 11 : }
244 :
245 : void destroy(std::false_type) const
246 : {
247 : if(--refs_ == 0)
248 : delete this;
249 : }
250 :
251 2 : void copy(any_buffers& dest) const override
252 : {
253 2 : copy(dest, std::integral_constant<bool,
254 : sizeof(*this) <= sbo_size>{});
255 2 : }
256 :
257 2 : void copy(any_buffers& dest, std::true_type) const // small buffers
258 : {
259 2 : dest.p_ = ::new(&dest.storage_) impl<T>(t_);
260 2 : }
261 :
262 : void copy(any_buffers& dest, std::false_type) const
263 : {
264 : ++refs_;
265 : dest.p_ = this;
266 : }
267 :
268 0 : void it_copy(void* dest, void const* src) const override
269 : {
270 0 : ::new(dest) iter_t(*static_cast<iter_t const*>(src));
271 0 : }
272 :
273 42 : void it_destroy(void* p) const override
274 : {
275 42 : static_cast<iter_t*>(p)->~iter_t();
276 42 : }
277 :
278 26 : void inc(void* p) const override
279 : {
280 26 : ++(*static_cast<iter_t*>(p));
281 26 : }
282 :
283 0 : void dec(void* p) const override
284 : {
285 0 : --(*static_cast<iter_t*>(p));
286 0 : }
287 :
288 19 : value_type deref(void const* p) const override
289 : {
290 19 : return *(*static_cast<iter_t const*>(p));
291 : }
292 :
293 46 : bool equal(void const* it0, void const* it1) const override
294 : {
295 46 : return *static_cast<iter_t const*>(it0) ==
296 46 : *static_cast<iter_t const*>(it1);
297 : }
298 :
299 21 : void begin(void* p) const override
300 : {
301 21 : ::new(p) iter_t(buffers::begin(t_));
302 21 : }
303 :
304 21 : void end(void* p) const override
305 : {
306 21 : ::new(p) iter_t(buffers::end(t_));
307 21 : }
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 2 : explicit impl(T_&& t) noexcept
326 3 : : t_(std::forward<T_>(t))
327 3 : , len_(length(t_))
328 : {
329 2 : }
330 :
331 1 : bool is_small_buffers() const noexcept override
332 : {
333 : return sizeof(*this) <=
334 1 : any_buffers<IsConst>::sbo_size;
335 : }
336 :
337 1 : bool is_small_iter() const noexcept override
338 : {
339 1 : return false;
340 : }
341 :
342 3 : void destroy() const override
343 : {
344 3 : destroy(std::integral_constant<bool,
345 : sizeof(*this) <= any_buffers<IsConst>::sbo_size>{});
346 3 : }
347 :
348 : void destroy(std::true_type) const // small buffers
349 : {
350 : this->~impl();
351 : }
352 :
353 3 : void destroy(std::false_type) const
354 : {
355 3 : if(--refs_ == 0)
356 2 : delete this;
357 3 : }
358 :
359 1 : void copy(any_buffers<IsConst>& dest) const override
360 : {
361 1 : copy(dest, std::integral_constant<bool,
362 : sizeof(*this) <= sbo_size>{});
363 1 : }
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 1 : void copy(any_buffers<IsConst>& dest,
372 : std::false_type) const
373 : {
374 1 : ++refs_;
375 1 : dest.p_ = this;
376 1 : }
377 :
378 0 : void it_copy(void* dest, void const* src) const override
379 : {
380 0 : ::new(dest) iter_t(*static_cast<iter_t const*>(src));
381 0 : }
382 :
383 14 : void it_destroy(void* p) const override
384 : {
385 14 : static_cast<iter_t*>(p)->~iter_t();
386 14 : }
387 :
388 2 : void inc(void* p) const override
389 : {
390 2 : ++static_cast<iter_t*>(p)->i;
391 2 : }
392 :
393 0 : void dec(void* p) const override
394 : {
395 0 : --static_cast<iter_t*>(p)->i;
396 0 : }
397 :
398 : typename any_buffers<IsConst>::value_type
399 2 : deref(void const* p) const override
400 : {
401 2 : auto const& it_ = *static_cast<iter_t const*>(p);
402 2 : auto it = buffers::begin(t_);
403 3 : for(auto i = it_.i; i; --i)
404 1 : ++it;
405 2 : return *it;
406 0 : }
407 :
408 9 : bool equal(void const* it0, void const* it1) const override
409 : {
410 9 : return static_cast<iter_t const*>(it0)->i ==
411 9 : static_cast<iter_t const*>(it1)->i;
412 : }
413 :
414 7 : void begin(void* p) const override
415 : {
416 7 : ::new(p) iter_t{ 0 };
417 7 : }
418 :
419 7 : void end(void* p) const override
420 : {
421 7 : ::new(p) iter_t{ len_ };
422 7 : }
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 108 : ~const_iterator()
478 : {
479 108 : p_->it_destroy(&storage_);
480 108 : }
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 2 : const_iterator(
494 : const_iterator const& other) noexcept
495 2 : : p_(other.p_)
496 : {
497 2 : p_->it_copy(&storage_, &other.storage_);
498 2 : }
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 81 : operator==(
524 : const_iterator const& other) const noexcept
525 : {
526 81 : if(p_ != other.p_)
527 0 : return false;
528 81 : 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 77 : operator!=(
539 : const_iterator const& other) const noexcept
540 : {
541 77 : 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 27 : operator*() const noexcept
553 : {
554 27 : 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 30 : operator++() noexcept
567 : {
568 30 : p_->inc(&storage_);
569 30 : 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 2 : operator--() noexcept
598 : {
599 2 : BOOST_ASSERT(p_ != nullptr);
600 2 : p_->dec(&storage_);
601 2 : 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 54 : const_iterator(begin_tag,
627 : any_impl const* p) noexcept
628 54 : : p_(p)
629 : {
630 54 : p_->begin(&storage_);
631 54 : BOOST_ASSERT(p_ != nullptr);
632 54 : }
633 :
634 52 : const_iterator(end_tag,
635 : any_impl const* p) noexcept
636 52 : : p_(p)
637 : {
638 52 : p_->end(&storage_);
639 52 : BOOST_ASSERT(p_ != nullptr);
640 52 : }
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 54 : any_buffers<IsConst>::
676 : begin() const noexcept ->
677 : const_iterator
678 : {
679 54 : if(! p_)
680 0 : return const_iterator();
681 : return const_iterator(typename
682 54 : 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 0 : return const_iterator();
693 : return const_iterator(typename
694 52 : const_iterator::end_tag{}, p_);
695 : }
696 :
697 : } // buffers
698 : } // boost
699 :
700 : #endif
|