98.85% Lines (86/87) 100.00% Functions (16/16)
TLA Baseline Branch
Line Hits Code Line Hits Code
  1 + //
  2 + // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.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/capy
  8 + //
  9 +
  10 + #ifndef BOOST_CAPY_DETAIL_BUFFER_ARRAY_HPP
  11 + #define BOOST_CAPY_DETAIL_BUFFER_ARRAY_HPP
  12 +
  13 + #include <boost/capy/detail/config.hpp>
  14 + #include <boost/capy/detail/except.hpp>
  15 + #include <boost/capy/buffers.hpp>
  16 +
  17 + #include <cstddef>
  18 + #include <new>
  19 + #include <span>
  20 + #include <utility>
  21 +
  22 + namespace boost {
  23 + namespace capy {
  24 + namespace detail {
  25 +
  26 + /** A buffer sequence holding up to N buffers.
  27 +
  28 + This class template stores a fixed-capacity array of buffer
  29 + descriptors, where the actual count can vary from 0 to N.
  30 + It provides efficient storage for small buffer sequences
  31 + without dynamic allocation.
  32 +
  33 + @par Example
  34 + @code
  35 + void process(ConstBufferSequence auto const& buffers)
  36 + {
  37 + detail::const_buffer_array<4> bufs(buffers);
  38 + // use bufs.begin(), bufs.end(), bufs.to_span()
  39 + }
  40 + @endcode
  41 +
  42 + @tparam N Maximum number of buffers the array can hold.
  43 + @tparam IsConst If true, holds const_buffer; otherwise mutable_buffer.
  44 + */
  45 + template<std::size_t N, bool IsConst>
  46 + class buffer_array
  47 + {
  48 + public:
  49 + /** The type of buffer stored in the array.
  50 + */
  51 + using value_type = std::conditional_t<IsConst, const_buffer, mutable_buffer>;
  52 +
  53 + private:
  54 + std::size_t n_ = 0;
  55 + std::size_t size_ = 0;
  56 + union {
  57 + int dummy_;
  58 + value_type arr_[N];
  59 + };
  60 +
  61 + public:
  62 + /** Construct a default instance.
  63 +
  64 + Constructs an empty buffer array.
  65 + */
HITGNC   66 + 6 buffer_array() noexcept
HITGNC   67 + 6 : dummy_(0)
  68 + {
HITGNC   69 + 6 }
  70 +
  71 + /** Construct a copy.
  72 + */
HITGNC   73 + 292 buffer_array(buffer_array const& other) noexcept
HITGNC   74 + 292 : n_(other.n_)
HITGNC   75 + 292 , size_(other.size_)
  76 + {
HITGNC   77 + 811 for(std::size_t i = 0; i < n_; ++i)
HITGNC   78 + 519 ::new(&arr_[i]) value_type(other.arr_[i]);
HITGNC   79 + 292 }
  80 +
  81 + /** Construct from a single buffer.
  82 +
  83 + @param b The buffer to store.
  84 + */
HITGNC   85 + 130 buffer_array(value_type const& b) noexcept
HITGNC   86 + 130 : dummy_(0)
  87 + {
HITGNC   88 + 130 if(b.size() != 0)
  89 + {
HITGNC   90 + 122 ::new(&arr_[0]) value_type(b);
HITGNC   91 + 122 n_ = 1;
HITGNC   92 + 122 size_ = b.size();
  93 + }
HITGNC   94 + 130 }
  95 +
  96 + /** Construct from a buffer sequence.
  97 +
  98 + Copies up to N buffer descriptors from the source
  99 + sequence into the internal array. If the sequence
  100 + contains more than N non-empty buffers, excess
  101 + buffers are silently ignored.
  102 +
  103 + @param bs The buffer sequence to copy from.
  104 + */
  105 + template<class BS>
  106 + requires (IsConst ? ConstBufferSequence<BS> : MutableBufferSequence<BS>)
  107 + && (!std::same_as<std::remove_cvref_t<BS>, buffer_array>)
  108 + && (!std::same_as<std::remove_cvref_t<BS>, value_type>)
HITGNC   109 + 185 buffer_array(BS const& bs) noexcept
HITGNC   110 + 185 : dummy_(0)
  111 + {
HITGNC   112 + 185 auto it = capy::begin(bs);
HITGNC   113 + 185 auto const last = capy::end(bs);
HITGNC   114 + 618 while(it != last && n_ < N)
  115 + {
HITGNC   116 + 433 value_type b(*it);
HITGNC   117 + 433 if(b.size() != 0)
  118 + {
HITGNC   119 + 427 ::new(&arr_[n_++]) value_type(b);
HITGNC   120 + 427 size_ += b.size();
  121 + }
HITGNC   122 + 433 ++it;
  123 + }
HITGNC   124 + 185 }
  125 +
  126 + /** Construct from a buffer sequence with overflow checking.
  127 +
  128 + Copies buffer descriptors from the source sequence
  129 + into the internal array.
  130 +
  131 + @param bs The buffer sequence to copy from.
  132 +
  133 + @throws std::length_error if the sequence contains
  134 + more than N non-empty buffers.
  135 + */
  136 + template<class BS>
  137 + requires (IsConst ? ConstBufferSequence<BS> : MutableBufferSequence<BS>)
HITGNC   138 + 4 buffer_array(std::in_place_t, BS const& bs)
HITGNC   139 + 4 : dummy_(0)
  140 + {
HITGNC   141 + 4 auto it = capy::begin(bs);
HITGNC   142 + 4 auto const last = capy::end(bs);
HITGNC   143 + 14 while(it != last)
  144 + {
HITGNC   145 + 12 value_type b(*it);
HITGNC   146 + 12 if(b.size() != 0)
  147 + {
HITGNC   148 + 12 if(n_ >= N)
HITGNC   149 + 2 detail::throw_length_error();
HITGNC   150 + 10 ::new(&arr_[n_++]) value_type(b);
HITGNC   151 + 10 size_ += b.size();
  152 + }
HITGNC   153 + 10 ++it;
  154 + }
HITGNC   155 + 2 }
  156 +
  157 + /** Construct from an iterator range.
  158 +
  159 + Copies up to N non-empty buffer descriptors from the
  160 + range `[first, last)`. If the range contains more than
  161 + N non-empty buffers, excess buffers are silently ignored.
  162 +
  163 + @param first Iterator to the first buffer descriptor.
  164 + @param last Iterator past the last buffer descriptor.
  165 + */
  166 + template<class Iterator>
HITGNC   167 + 8 buffer_array(Iterator first, Iterator last) noexcept
HITGNC   168 + 8 : dummy_(0)
  169 + {
HITGNC   170 + 26 while(first != last && n_ < N)
  171 + {
HITGNC   172 + 18 value_type b(*first);
HITGNC   173 + 18 if(b.size() != 0)
  174 + {
HITGNC   175 + 14 ::new(&arr_[n_++]) value_type(b);
HITGNC   176 + 14 size_ += b.size();
  177 + }
HITGNC   178 + 18 ++first;
  179 + }
HITGNC   180 + 8 }
  181 +
  182 + /** Construct from an iterator range with overflow checking.
  183 +
  184 + Copies all non-empty buffer descriptors from the range
  185 + `[first, last)` into the internal array.
  186 +
  187 + @param first Iterator to the first buffer descriptor.
  188 + @param last Iterator past the last buffer descriptor.
  189 +
  190 + @throws std::length_error if the range contains more
  191 + than N non-empty buffers.
  192 + */
  193 + template<class Iterator>
HITGNC   194 + 4 buffer_array(std::in_place_t, Iterator first, Iterator last)
HITGNC   195 + 4 : dummy_(0)
  196 + {
HITGNC   197 + 14 while(first != last)
  198 + {
HITGNC   199 + 12 value_type b(*first);
HITGNC   200 + 12 if(b.size() != 0)
  201 + {
HITGNC   202 + 12 if(n_ >= N)
HITGNC   203 + 2 detail::throw_length_error();
HITGNC   204 + 10 ::new(&arr_[n_++]) value_type(b);
HITGNC   205 + 10 size_ += b.size();
  206 + }
HITGNC   207 + 10 ++first;
  208 + }
HITGNC   209 + 2 }
  210 +
  211 + /** Destructor.
  212 + */
HITGNC   213 + 625 ~buffer_array()
  214 + {
HITGNC   215 + 1725 while(n_--)
HITGNC   216 + 1100 arr_[n_].~value_type();
HITGNC   217 + 625 }
  218 +
  219 + /** Assign by copying.
  220 + */
  221 + buffer_array&
HITGNC   222 + 4 operator=(buffer_array const& other) noexcept
  223 + {
HITGNC   224 + 4 if(this != &other)
  225 + {
HITGNC   226 + 4 while(n_--)
MISUNC   227 + arr_[n_].~value_type();
HITGNC   228 + 4 n_ = other.n_;
HITGNC   229 + 4 size_ = other.size_;
HITGNC   230 + 10 for(std::size_t i = 0; i < n_; ++i)
HITGNC   231 + 6 ::new(&arr_[i]) value_type(other.arr_[i]);
  232 + }
HITGNC   233 + 4 return *this;
  234 + }
  235 +
  236 + /** Return an iterator to the beginning.
  237 + */
  238 + value_type*
HITGNC   239 + 130 begin() noexcept
  240 + {
HITGNC   241 + 130 return arr_;
  242 + }
  243 +
  244 + /** Return an iterator to the beginning.
  245 + */
  246 + value_type const*
HITGNC   247 + 2441 begin() const noexcept
  248 + {
HITGNC   249 + 2441 return arr_;
  250 + }
  251 +
  252 + /** Return an iterator to the end.
  253 + */
  254 + value_type*
HITGNC   255 + 129 end() noexcept
  256 + {
HITGNC   257 + 129 return arr_ + n_;
  258 + }
  259 +
  260 + /** Return an iterator to the end.
  261 + */
  262 + value_type const*
HITGNC   263 + 2441 end() const noexcept
  264 + {
HITGNC   265 + 2441 return arr_ + n_;
  266 + }
  267 +
  268 + /** Return a span of the buffers.
  269 + */
  270 + std::span<value_type>
HITGNC   271 + 379 to_span() noexcept
  272 + {
HITGNC   273 + 379 return { arr_, n_ };
  274 + }
  275 +
  276 + /** Return a span of the buffers.
  277 + */
  278 + std::span<value_type const>
HITGNC   279 + 175 to_span() const noexcept
  280 + {
HITGNC   281 + 175 return { arr_, n_ };
  282 + }
  283 +
  284 + /** Conversion to mutable span.
  285 + */
HITGNC   286 + 1 operator std::span<value_type>() noexcept
  287 + {
HITGNC   288 + 1 return { arr_, n_ };
  289 + }
  290 +
  291 + /** Conversion to const span.
  292 + */
  293 + operator std::span<value_type const>() const noexcept
  294 + {
  295 + return { arr_, n_ };
  296 + }
  297 +
  298 + /** Return the total byte count in O(1).
  299 + */
  300 + std::size_t
  301 + byte_size() const noexcept
  302 + {
  303 + return size_;
  304 + }
  305 + };
  306 +
  307 + /** Alias for buffer_array holding const_buffer.
  308 +
  309 + @tparam N Maximum number of buffers.
  310 + */
  311 + template<std::size_t N>
  312 + using const_buffer_array = buffer_array<N, true>;
  313 +
  314 + /** Alias for buffer_array holding mutable_buffer.
  315 +
  316 + @tparam N Maximum number of buffers.
  317 + */
  318 + template<std::size_t N>
  319 + using mutable_buffer_array = buffer_array<N, false>;
  320 +
  321 + } // namespace detail
  322 + } // namespace capy
  323 + } // namespace boost
  324 +
  325 + #endif