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 | ||||||