| Line | Branch | Exec | Source |
|---|---|---|---|
| 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_BUFFERS_HPP | ||
| 11 | #define BOOST_CAPY_BUFFERS_HPP | ||
| 12 | |||
| 13 | #include <boost/capy/detail/config.hpp> | ||
| 14 | #include <concepts> | ||
| 15 | #include <cstddef> | ||
| 16 | #include <iterator> | ||
| 17 | #include <memory> | ||
| 18 | #include <ranges> | ||
| 19 | #include <type_traits> | ||
| 20 | |||
| 21 | // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html | ||
| 22 | |||
| 23 | namespace boost { | ||
| 24 | |||
| 25 | namespace asio { | ||
| 26 | class const_buffer; | ||
| 27 | class mutable_buffer; | ||
| 28 | } // asio | ||
| 29 | |||
| 30 | namespace capy { | ||
| 31 | |||
| 32 | class const_buffer; | ||
| 33 | class mutable_buffer; | ||
| 34 | |||
| 35 | namespace detail { | ||
| 36 | |||
| 37 | // satisfies Asio's buffer constructors, CANNOT be removed! | ||
| 38 | template<class T, std::size_t Extent = (std::size_t)(-1)> | ||
| 39 | class basic_buffer | ||
| 40 | { | ||
| 41 | 8 | constexpr auto data() const noexcept -> | |
| 42 | std::conditional_t<std::is_const_v<T>, void const*, void*> | ||
| 43 | { | ||
| 44 | 8 | return p_; | |
| 45 | } | ||
| 46 | |||
| 47 | 4 | constexpr std::size_t size() const noexcept | |
| 48 | { | ||
| 49 | 4 | return n_; | |
| 50 | } | ||
| 51 | |||
| 52 | friend class capy::const_buffer; | ||
| 53 | friend class capy::mutable_buffer; | ||
| 54 | friend class asio::const_buffer; | ||
| 55 | friend class asio::mutable_buffer; | ||
| 56 | 295294 | basic_buffer() = default; | |
| 57 | 10641671 | constexpr basic_buffer(T* p, std::size_t n) noexcept : p_(p), n_(n) {} | |
| 58 | constexpr basic_buffer<T, (std::size_t)(-1)> subspan( | ||
| 59 | std::size_t, std::size_t = (std::size_t)(-1)) const noexcept; | ||
| 60 | |||
| 61 | T* p_ = nullptr; | ||
| 62 | std::size_t n_ = 0; | ||
| 63 | }; | ||
| 64 | |||
| 65 | } // detail | ||
| 66 | |||
| 67 | //------------------------------------------------ | ||
| 68 | |||
| 69 | /** size tag for `tag_invoke` | ||
| 70 | |||
| 71 | This type is used in overloads of `tag_invoke` | ||
| 72 | for user-defined types to customize the `size()` | ||
| 73 | algorithm. | ||
| 74 | */ | ||
| 75 | struct size_tag {}; | ||
| 76 | |||
| 77 | /** slice tag for `tag_invoke` | ||
| 78 | |||
| 79 | This type is used in overloads of `tag_invoke` | ||
| 80 | for user-defined types to customize the slicing | ||
| 81 | algorithms. | ||
| 82 | */ | ||
| 83 | struct slice_tag {}; | ||
| 84 | |||
| 85 | /** slice constants for slice customization | ||
| 86 | |||
| 87 | This defines the possible values passed to | ||
| 88 | overloads of `tag_invoke` for user-defined | ||
| 89 | types which customize the slicing algorithms. | ||
| 90 | */ | ||
| 91 | enum class slice_how | ||
| 92 | { | ||
| 93 | /// Indicates that the front of the buffer sequence should be trimmed | ||
| 94 | remove_prefix, | ||
| 95 | |||
| 96 | /// Indicates that the front of the buffer sequence should be preserved | ||
| 97 | keep_prefix | ||
| 98 | }; | ||
| 99 | |||
| 100 | //------------------------------------------------ | ||
| 101 | |||
| 102 | /** Holds a contiguous range of modifiable bytes | ||
| 103 | */ | ||
| 104 | class mutable_buffer | ||
| 105 | : public detail::basic_buffer<unsigned char> | ||
| 106 | { | ||
| 107 | public: | ||
| 108 | /** Constructor. | ||
| 109 | */ | ||
| 110 | 575 | mutable_buffer() = default; | |
| 111 | |||
| 112 | /** Constructor. | ||
| 113 | */ | ||
| 114 | mutable_buffer( | ||
| 115 | mutable_buffer const&) = default; | ||
| 116 | |||
| 117 | /** Assignment. | ||
| 118 | */ | ||
| 119 | mutable_buffer& operator=( | ||
| 120 | mutable_buffer const&) = default; | ||
| 121 | |||
| 122 | /** Constructor. | ||
| 123 | */ | ||
| 124 | 639132 | constexpr mutable_buffer( | |
| 125 | void* data, std::size_t size) noexcept | ||
| 126 | 639132 | : basic_buffer<unsigned char>( | |
| 127 | 639132 | static_cast<unsigned char*>(data), size) | |
| 128 | { | ||
| 129 | 639132 | } | |
| 130 | |||
| 131 | /** Constructor | ||
| 132 | */ | ||
| 133 | template<class MutableBuffer> | ||
| 134 | requires std::same_as<MutableBuffer, asio::mutable_buffer> | ||
| 135 | constexpr mutable_buffer( | ||
| 136 | MutableBuffer const& b) noexcept | ||
| 137 | : basic_buffer<unsigned char>( | ||
| 138 | static_cast<unsigned char*>( | ||
| 139 | b.data()), b.size()) | ||
| 140 | { | ||
| 141 | } | ||
| 142 | |||
| 143 | /** Return a pointer to the beginning of the memory region | ||
| 144 | */ | ||
| 145 | 800649 | constexpr void* data() const noexcept | |
| 146 | { | ||
| 147 | 800649 | return p_; | |
| 148 | } | ||
| 149 | |||
| 150 | /** Return the number of valid bytes in the referenced memory region | ||
| 151 | */ | ||
| 152 | 2106908 | constexpr std::size_t size() const noexcept | |
| 153 | { | ||
| 154 | 2106908 | return n_; | |
| 155 | } | ||
| 156 | |||
| 157 | /** Remove a prefix of the memory region | ||
| 158 | |||
| 159 | If the requested number of bytes is larger than the current size, | ||
| 160 | the resulting buffer will have size 0. | ||
| 161 | |||
| 162 | @param n The number of bytes to remove. | ||
| 163 | */ | ||
| 164 | mutable_buffer& | ||
| 165 | 1026737 | operator+=(std::size_t n) noexcept | |
| 166 | { | ||
| 167 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1026721 times.
|
1026737 | if( n > n_) |
| 168 | 16 | n = n_; | |
| 169 | 1026737 | p_ += n; | |
| 170 | 1026737 | n_ -= n; | |
| 171 | 1026737 | return *this; | |
| 172 | } | ||
| 173 | |||
| 174 | /** Remove a slice from the buffer | ||
| 175 | */ | ||
| 176 | friend | ||
| 177 | void | ||
| 178 | 1088 | tag_invoke( | |
| 179 | slice_tag const&, | ||
| 180 | mutable_buffer& b, | ||
| 181 | slice_how how, | ||
| 182 | std::size_t n) noexcept | ||
| 183 | { | ||
| 184 | 1088 | b.do_slice(how, n); | |
| 185 | 1088 | } | |
| 186 | |||
| 187 | private: | ||
| 188 | 1088 | void do_slice( | |
| 189 | slice_how how, std::size_t n) noexcept | ||
| 190 | { | ||
| 191 |
2/3✓ Branch 0 taken 512 times.
✓ Branch 1 taken 576 times.
✗ Branch 2 not taken.
|
1088 | switch(how) |
| 192 | { | ||
| 193 | 512 | case slice_how::remove_prefix: | |
| 194 | 512 | *this += n; | |
| 195 | 512 | return; | |
| 196 | |||
| 197 | 576 | case slice_how::keep_prefix: | |
| 198 |
2/2✓ Branch 0 taken 476 times.
✓ Branch 1 taken 100 times.
|
576 | if( n < n_) |
| 199 | 476 | n_ = n; | |
| 200 | 576 | return; | |
| 201 | } | ||
| 202 | } | ||
| 203 | }; | ||
| 204 | |||
| 205 | //------------------------------------------------ | ||
| 206 | |||
| 207 | /** Holds a contiguous range of unmodifiable bytes | ||
| 208 | */ | ||
| 209 | class const_buffer | ||
| 210 | : public detail::basic_buffer<unsigned char const> | ||
| 211 | { | ||
| 212 | public: | ||
| 213 | /** Constructor | ||
| 214 | */ | ||
| 215 | 147072 | const_buffer() = default; | |
| 216 | |||
| 217 | /** Constructor | ||
| 218 | */ | ||
| 219 | const_buffer(const_buffer const&) = default; | ||
| 220 | |||
| 221 | /** Assignment | ||
| 222 | |||
| 223 | @par Postconditions | ||
| 224 | @code | ||
| 225 | this->data() == other.data() && this->size() == other.size() | ||
| 226 | @endcode | ||
| 227 | */ | ||
| 228 | const_buffer& operator=( | ||
| 229 | const_buffer const& other) = default; | ||
| 230 | |||
| 231 | /** Constructor | ||
| 232 | */ | ||
| 233 | 4672750 | constexpr const_buffer( | |
| 234 | void const* data, std::size_t size) noexcept | ||
| 235 | 4672750 | : basic_buffer<unsigned char const>( | |
| 236 | 4672750 | static_cast<unsigned char const*>(data), size) | |
| 237 | { | ||
| 238 | 4672750 | } | |
| 239 | |||
| 240 | /** Constructor | ||
| 241 | */ | ||
| 242 | 8954 | constexpr const_buffer( | |
| 243 | mutable_buffer const& b) noexcept | ||
| 244 | 8954 | : basic_buffer<unsigned char const>( | |
| 245 | 8954 | static_cast<unsigned char const*>(b.data()), b.size()) | |
| 246 | { | ||
| 247 | 8954 | } | |
| 248 | |||
| 249 | /** Constructor | ||
| 250 | */ | ||
| 251 | template<class ConstBuffer> | ||
| 252 | requires (std::same_as<ConstBuffer, asio::const_buffer> || | ||
| 253 | std::same_as<ConstBuffer, asio::mutable_buffer>) | ||
| 254 | constexpr const_buffer( | ||
| 255 | ConstBuffer const& b) noexcept | ||
| 256 | : basic_buffer<unsigned char const>( | ||
| 257 | static_cast<unsigned char const*>( | ||
| 258 | b.data()), b.size()) | ||
| 259 | { | ||
| 260 | } | ||
| 261 | |||
| 262 | /** Return a pointer to the beginning of the memory region | ||
| 263 | */ | ||
| 264 | 13069677 | constexpr void const* data() const noexcept | |
| 265 | { | ||
| 266 | 13069677 | return p_; | |
| 267 | } | ||
| 268 | |||
| 269 | /** Return the number of valid bytes in the referenced memory region | ||
| 270 | */ | ||
| 271 | 18416594 | constexpr std::size_t size() const noexcept | |
| 272 | { | ||
| 273 | 18416594 | return n_; | |
| 274 | } | ||
| 275 | |||
| 276 | /** Remove a prefix of the memory region | ||
| 277 | |||
| 278 | If the requested number of bytes is larger than the current size, | ||
| 279 | the resulting buffer will have size 0. | ||
| 280 | |||
| 281 | @param n The number of bytes to remove. | ||
| 282 | */ | ||
| 283 | const_buffer& | ||
| 284 | 1157878 | operator+=(std::size_t n) noexcept | |
| 285 | { | ||
| 286 |
2/2✓ Branch 0 taken 4112 times.
✓ Branch 1 taken 1153766 times.
|
1157878 | if( n > n_) |
| 287 | 4112 | n = n_; | |
| 288 | 1157878 | p_ += n; | |
| 289 | 1157878 | n_ -= n; | |
| 290 | 1157878 | return *this; | |
| 291 | } | ||
| 292 | |||
| 293 | /** Remove a slice from the buffer | ||
| 294 | */ | ||
| 295 | friend | ||
| 296 | void | ||
| 297 | 267349 | tag_invoke( | |
| 298 | slice_tag const&, | ||
| 299 | const_buffer& b, | ||
| 300 | slice_how how, | ||
| 301 | std::size_t n) noexcept | ||
| 302 | { | ||
| 303 | 267349 | b.do_slice(how, n); | |
| 304 | 267349 | } | |
| 305 | |||
| 306 | private: | ||
| 307 | 267349 | void do_slice( | |
| 308 | slice_how how, std::size_t n) noexcept | ||
| 309 | { | ||
| 310 |
2/3✓ Branch 0 taken 131653 times.
✓ Branch 1 taken 135696 times.
✗ Branch 2 not taken.
|
267349 | switch(how) |
| 311 | { | ||
| 312 | 131653 | case slice_how::remove_prefix: | |
| 313 | 131653 | *this += n; | |
| 314 | 131653 | return; | |
| 315 | |||
| 316 | 135696 | case slice_how::keep_prefix: | |
| 317 |
2/2✓ Branch 0 taken 121169 times.
✓ Branch 1 taken 14527 times.
|
135696 | if( n < n_) |
| 318 | 121169 | n_ = n; | |
| 319 | 135696 | return; | |
| 320 | } | ||
| 321 | } | ||
| 322 | }; | ||
| 323 | |||
| 324 | //------------------------------------------------ | ||
| 325 | |||
| 326 | /** Concept for types that model ConstBufferSequence. | ||
| 327 | |||
| 328 | A type satisfies `const_buffer_sequence` if it is convertible | ||
| 329 | to `const_buffer`, or if it is a bidirectional range whose | ||
| 330 | value type is convertible to `const_buffer`. | ||
| 331 | */ | ||
| 332 | template<typename T> | ||
| 333 | concept const_buffer_sequence = | ||
| 334 | std::is_convertible_v<T, const_buffer> || ( | ||
| 335 | std::ranges::bidirectional_range<T> && | ||
| 336 | std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>); | ||
| 337 | |||
| 338 | /** Concept for types that model MutableBufferSequence. | ||
| 339 | |||
| 340 | A type satisfies `mutable_buffer_sequence` if it is convertible | ||
| 341 | to `mutable_buffer`, or if it is a bidirectional range whose | ||
| 342 | value type is convertible to `mutable_buffer`. | ||
| 343 | */ | ||
| 344 | template<typename T> | ||
| 345 | concept mutable_buffer_sequence = | ||
| 346 | std::is_convertible_v<T, mutable_buffer> || ( | ||
| 347 | std::ranges::bidirectional_range<T> && | ||
| 348 | std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>); | ||
| 349 | |||
| 350 | //------------------------------------------------------------------------------ | ||
| 351 | |||
| 352 | /** Return an iterator pointing to the first element of a buffer sequence | ||
| 353 | |||
| 354 | This function returns an iterator to the beginning of the range denoted by | ||
| 355 | `t`. It handles both ranges and single buffers uniformly. | ||
| 356 | |||
| 357 | @par Constraints | ||
| 358 | @code | ||
| 359 | const_buffer_sequence<T> | ||
| 360 | @endcode | ||
| 361 | |||
| 362 | @param t The buffer sequence | ||
| 363 | */ | ||
| 364 | constexpr struct begin_mrdocs_workaround_t | ||
| 365 | { | ||
| 366 | template<std::convertible_to<const_buffer> ConvertibleToBuffer> | ||
| 367 | 1183613 | auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* | |
| 368 | { | ||
| 369 | 1183613 | return std::addressof(b); | |
| 370 | } | ||
| 371 | |||
| 372 | template<const_buffer_sequence BufferSequence> | ||
| 373 | requires (!std::convertible_to<BufferSequence, const_buffer>) | ||
| 374 | 22187888 | auto operator()(BufferSequence const& bs) const noexcept | |
| 375 | { | ||
| 376 | 22187888 | return std::ranges::begin(bs); | |
| 377 | } | ||
| 378 | |||
| 379 | template<const_buffer_sequence BufferSequence> | ||
| 380 | requires (!std::convertible_to<BufferSequence, const_buffer>) | ||
| 381 | 3968008 | auto operator()(BufferSequence& bs) const noexcept | |
| 382 | { | ||
| 383 | 3968008 | return std::ranges::begin(bs); | |
| 384 | } | ||
| 385 | } begin {}; | ||
| 386 | |||
| 387 | //------------------------------------------------------------------------------ | ||
| 388 | |||
| 389 | /** Return an iterator to the end of the buffer sequence | ||
| 390 | |||
| 391 | This function returns an iterator to the end of the range denoted by | ||
| 392 | `t`. It handles both ranges and single buffers uniformly. | ||
| 393 | |||
| 394 | @par Constraints | ||
| 395 | @code | ||
| 396 | const_buffer_sequence<T> | ||
| 397 | @endcode | ||
| 398 | |||
| 399 | @param t The buffer sequence | ||
| 400 | */ | ||
| 401 | constexpr struct end_mrdocs_workaround_t | ||
| 402 | { | ||
| 403 | template<std::convertible_to<const_buffer> ConvertibleToBuffer> | ||
| 404 | 1183451 | auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const* | |
| 405 | { | ||
| 406 | 1183451 | return std::addressof(b) + 1; | |
| 407 | } | ||
| 408 | |||
| 409 | template<const_buffer_sequence BufferSequence> | ||
| 410 | requires (!std::convertible_to<BufferSequence, const_buffer>) | ||
| 411 | 8625974 | auto operator()(BufferSequence const& bs) const noexcept | |
| 412 | { | ||
| 413 | 8625974 | return std::ranges::end(bs); | |
| 414 | } | ||
| 415 | |||
| 416 | template<const_buffer_sequence BufferSequence> | ||
| 417 | requires (!std::convertible_to<BufferSequence, const_buffer>) | ||
| 418 | 3968008 | auto operator()(BufferSequence& bs) const noexcept | |
| 419 | { | ||
| 420 | 3968008 | return std::ranges::end(bs); | |
| 421 | } | ||
| 422 | } end {}; | ||
| 423 | |||
| 424 | //------------------------------------------------------------------------------ | ||
| 425 | |||
| 426 | template<const_buffer_sequence ConstBufferSequence> | ||
| 427 | std::size_t | ||
| 428 | 2114753 | tag_invoke( | |
| 429 | size_tag const&, | ||
| 430 | ConstBufferSequence const& bs) noexcept | ||
| 431 | { | ||
| 432 | 2114753 | std::size_t n = 0; | |
| 433 | 2114753 | auto const e = end(bs); | |
| 434 |
3/3✓ Branch 2 taken 1770438 times.
✓ Branch 3 taken 697523 times.
✓ Branch 1 taken 1420423 times.
|
5675116 | for(auto it = begin(bs); it != e; ++it) |
| 435 | 3560363 | n += const_buffer(*it).size(); | |
| 436 | 2114753 | return n; | |
| 437 | } | ||
| 438 | |||
| 439 | //------------------------------------------------------------------------------ | ||
| 440 | |||
| 441 | /** Return the total number of bytes in a buffer sequence | ||
| 442 | |||
| 443 | This function returns the sum of the number of bytes in each contiguous | ||
| 444 | buffer contained in the range or value. This is different from the length | ||
| 445 | of the sequence returned by `std::ranges::size(t)` | ||
| 446 | |||
| 447 | @par Constraints | ||
| 448 | @code | ||
| 449 | const_buffer_sequence<T> | ||
| 450 | @endcode | ||
| 451 | |||
| 452 | @par Example | ||
| 453 | @code | ||
| 454 | template<const_buffer_sequence ConstBufferSequence> | ||
| 455 | bool is_small( ConstBufferSequence const& bs ) noexcept | ||
| 456 | { | ||
| 457 | return buffer_size(bs) < 100; | ||
| 458 | } | ||
| 459 | @endcode | ||
| 460 | */ | ||
| 461 | constexpr struct buffer_size_mrdocs_workaround_t | ||
| 462 | { | ||
| 463 | template<const_buffer_sequence ConstBufferSequence> | ||
| 464 | 1407618 | constexpr std::size_t operator()( | |
| 465 | ConstBufferSequence const& bs) const noexcept | ||
| 466 | { | ||
| 467 | 1407618 | return tag_invoke(size_tag{}, bs); | |
| 468 | } | ||
| 469 | } buffer_size {}; | ||
| 470 | |||
| 471 | //----------------------------------------------- | ||
| 472 | |||
| 473 | namespace detail { | ||
| 474 | |||
| 475 | template<class It> | ||
| 476 | auto | ||
| 477 | length_impl(It first, It last, int) | ||
| 478 | -> decltype(static_cast<std::size_t>(last - first)) | ||
| 479 | { | ||
| 480 | return static_cast<std::size_t>(last - first); | ||
| 481 | } | ||
| 482 | |||
| 483 | template<class It> | ||
| 484 | std::size_t | ||
| 485 | length_impl(It first, It last, long) | ||
| 486 | { | ||
| 487 | std::size_t n = 0; | ||
| 488 | while(first != last) | ||
| 489 | { | ||
| 490 | ++first; | ||
| 491 | ++n; | ||
| 492 | } | ||
| 493 | return n; | ||
| 494 | } | ||
| 495 | |||
| 496 | } // detail | ||
| 497 | |||
| 498 | /** Return the number of elements in a buffer sequence. | ||
| 499 | */ | ||
| 500 | template<const_buffer_sequence ConstBufferSequence> | ||
| 501 | std::size_t | ||
| 502 | buffer_length(ConstBufferSequence const& bs) | ||
| 503 | { | ||
| 504 | return detail::length_impl( | ||
| 505 | begin(bs), end(bs), 0); | ||
| 506 | } | ||
| 507 | |||
| 508 | /** Alias for const_buffer or mutable_buffer depending on sequence type. | ||
| 509 | */ | ||
| 510 | template<typename BufferSequence> | ||
| 511 | using buffer_type = std::conditional_t< | ||
| 512 | mutable_buffer_sequence<BufferSequence>, | ||
| 513 | mutable_buffer, const_buffer>; | ||
| 514 | |||
| 515 | } // capy | ||
| 516 | } // boost | ||
| 517 | |||
| 518 | #endif | ||
| 519 |