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/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 460 : ~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 439 : any_buffers(
75 : any_buffers const& other) noexcept
76 439 : {
77 439 : other.sp_->copy(*this, other.sp_);
78 439 : }
79 :
80 : /** Assignment.
81 : */
82 : any_buffers&
83 0 : operator=(
84 : any_buffers const& other) noexcept
85 : {
86 0 : if(this == &other)
87 0 : return *this;
88 0 : other.sp_->copy(*this, other.sp_);
89 0 : 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 14 : any_buffers(
106 : BufferSequence&& buffers)
107 14 : {
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 14 : construct(std::forward<BufferSequence>(buffers),
113 : std::integral_constant<bool, (
114 : sizeof(impl<T>) <= sbo_size)>{});
115 14 : }
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 23 : 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 12 : void construct(T&& t, std::true_type)
162 : {
163 : using U = typename std::decay<T>::type;
164 12 : sp_ = {
165 12 : ::new(&storage_) impl<U>(std::forward<T>(t)),
166 : null_deleter{} };
167 12 : }
168 :
169 : template<class T>
170 2 : void construct(T&& t, std::false_type)
171 : {
172 : using U = typename std::decay<T>::type;
173 2 : sp_ = std::make_shared<impl<U>>(std::forward<T>(t));
174 2 : }
175 :
176 7 : bool is_small_buffers() const noexcept
177 : {
178 7 : return sp_->is_small_buffers();
179 : }
180 :
181 7 : bool is_small_iter() const noexcept
182 : {
183 7 : 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 377 : explicit impl(T_&& t) noexcept
222 377 : : t_(std::forward<T_>(t))
223 : {
224 377 : }
225 :
226 3 : bool is_small_buffers() const noexcept override
227 : {
228 3 : return sizeof(*this) <= sbo_size;
229 : }
230 :
231 3 : bool is_small_iter() const noexcept override
232 : {
233 3 : return true;
234 : }
235 :
236 365 : void copy(any_buffers& dest, std::shared_ptr<
237 : any_buffers<IsConst>::any_impl const> const& sp) const override
238 : {
239 365 : copy(dest, sp, std::integral_constant<bool,
240 : sizeof(*this) <= sbo_size>{});
241 365 : }
242 :
243 365 : 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 365 : dest.sp_ = std::shared_ptr<impl<T>>(
249 365 : ::new(&dest.storage_) impl<T>(t_),
250 : null_deleter{} );
251 365 : }
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 5592 : void it_copy(void* dest, void const* src) const override
262 : {
263 5592 : ::new(dest) iter_t(*static_cast<iter_t const*>(src));
264 5592 : }
265 :
266 10422 : void it_destroy(void* p) const override
267 : {
268 10422 : static_cast<iter_t*>(p)->~iter_t();
269 10422 : }
270 :
271 3779 : void inc(void* p) const override
272 : {
273 3779 : ++(*static_cast<iter_t*>(p));
274 3779 : }
275 :
276 732 : void dec(void* p) const override
277 : {
278 732 : --(*static_cast<iter_t*>(p));
279 732 : }
280 :
281 2254 : value_type deref(void const* p) const override
282 : {
283 2254 : return *(*static_cast<iter_t const*>(p));
284 : }
285 :
286 4840 : bool equal(void const* it0, void const* it1) const override
287 : {
288 4840 : return *static_cast<iter_t const*>(it0) ==
289 4840 : *static_cast<iter_t const*>(it1);
290 : }
291 :
292 4599 : void begin(void* p) const override
293 : {
294 4599 : ::new(p) iter_t(buffers::begin(t_));
295 4599 : }
296 :
297 231 : void end(void* p) const override
298 : {
299 231 : ::new(p) iter_t(buffers::end(t_));
300 231 : }
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 20 : explicit impl(T_&& t) noexcept
318 21 : : t_(std::forward<T_>(t))
319 39 : , len_(length(t_))
320 : {
321 20 : }
322 :
323 1 : bool is_small_buffers() const noexcept override
324 : {
325 1 : return sizeof(*this) <= any_buffers<IsConst>::sbo_size;
326 : }
327 :
328 1 : bool is_small_iter() const noexcept override
329 : {
330 1 : return false;
331 : }
332 :
333 18 : void copy(
334 : any_buffers<IsConst>& dest, std::shared_ptr<
335 : any_buffers<IsConst>::any_impl const> const& sp) const override
336 : {
337 18 : copy(dest, sp, std::integral_constant<bool,
338 : sizeof(*this) <= any_buffers<IsConst>::sbo_size>{});
339 18 : }
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 18 : 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 18 : dest.sp_ = std::make_shared<impl<T>>(t_);
357 18 : }
358 :
359 216 : void it_copy(void* dest, void const* src) const override
360 : {
361 216 : ::new(dest) iter_t(*static_cast<iter_t const*>(src));
362 216 : }
363 :
364 450 : void it_destroy(void* p) const override
365 : {
366 450 : static_cast<iter_t*>(p)->~iter_t();
367 450 : }
368 :
369 2 : void inc(void* p) const override
370 : {
371 2 : ++static_cast<iter_t*>(p)->i;
372 2 : }
373 :
374 0 : void dec(void* p) const override
375 : {
376 0 : --static_cast<iter_t*>(p)->i;
377 0 : }
378 :
379 : typename any_buffers<IsConst>::value_type
380 2 : deref(void const* p) const override
381 : {
382 2 : auto const& it_ = *static_cast<iter_t const*>(p);
383 2 : auto it = buffers::begin(t_);
384 3 : for(auto i = it_.i; i; --i)
385 1 : ++it;
386 2 : return *it;
387 0 : }
388 :
389 126 : bool equal(void const* it0, void const* it1) const override
390 : {
391 126 : return static_cast<iter_t const*>(it0)->i ==
392 126 : static_cast<iter_t const*>(it1)->i;
393 : }
394 :
395 210 : void begin(void* p) const override
396 : {
397 210 : ::new(p) iter_t{ 0 };
398 210 : }
399 :
400 24 : void end(void* p) const override
401 : {
402 24 : ::new(p) iter_t{ len_ };
403 24 : }
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 11838 : ~const_iterator()
459 : {
460 11838 : sp_->it_destroy(&storage_);
461 11838 : }
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 6242 : const_iterator(
475 : const_iterator const& other) noexcept
476 6242 : : sp_(other.sp_)
477 : {
478 6242 : sp_->it_copy(&storage_, &other.storage_);
479 6242 : }
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 5247 : operator==(
505 : const_iterator const& other) const noexcept
506 : {
507 5247 : if(sp_ != other.sp_)
508 0 : return false;
509 5247 : 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 1025 : operator!=(
520 : const_iterator const& other) const noexcept
521 : {
522 1025 : 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 2262 : operator*() const noexcept
534 : {
535 2262 : 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 3783 : operator++() noexcept
548 : {
549 3783 : sp_->inc(&storage_);
550 3783 : 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 6 : operator++(int) noexcept
563 : {
564 6 : auto temp = *this;
565 6 : ++(*this);
566 6 : 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 734 : operator--() noexcept
579 : {
580 734 : sp_->dec(&storage_);
581 734 : 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 78 : operator--(int) noexcept
594 : {
595 78 : auto temp = *this;
596 78 : --(*this);
597 78 : return temp;
598 : }
599 :
600 : private:
601 : friend class any_buffers;
602 :
603 : struct begin_tag {};
604 : struct end_tag {};
605 :
606 5262 : const_iterator(begin_tag, std::shared_ptr<
607 : any_impl const> const& sp) noexcept
608 5262 : : sp_(sp)
609 : {
610 5262 : sp_->begin(&storage_);
611 5262 : }
612 :
613 334 : const_iterator(end_tag, std::shared_ptr<
614 : any_impl const> const& sp) noexcept
615 334 : : sp_(sp)
616 : {
617 334 : sp_->end(&storage_);
618 334 : }
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 5262 : any_buffers<IsConst>::
654 : begin() const noexcept ->
655 : const_iterator
656 : {
657 : return const_iterator(typename
658 5262 : 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
|