100.00% Lines (72/72)
100.00% Functions (13/13)
| TLA | Baseline | Branch | ||||||
|---|---|---|---|---|---|---|---|---|
| Line | Hits | Code | Line | Hits | Code | |||
| 1 | // | 1 | // | |||||
| 2 | // Copyright (c) 2026 Steve Gerbino | 2 | // Copyright (c) 2026 Steve Gerbino | |||||
| 3 | // | 3 | // | |||||
| 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | 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) | 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |||||
| 6 | // | 6 | // | |||||
| 7 | // Official repository: https://github.com/cppalliance/capy | 7 | // Official repository: https://github.com/cppalliance/capy | |||||
| 8 | // | 8 | // | |||||
| 9 | 9 | |||||||
| 10 | #ifndef BOOST_CAPY_DETAIL_SLICE_OF_HPP | 10 | #ifndef BOOST_CAPY_DETAIL_SLICE_OF_HPP | |||||
| 11 | #define BOOST_CAPY_DETAIL_SLICE_OF_HPP | 11 | #define BOOST_CAPY_DETAIL_SLICE_OF_HPP | |||||
| 12 | 12 | |||||||
| 13 | #include <boost/capy/detail/config.hpp> | 13 | #include <boost/capy/detail/config.hpp> | |||||
| 14 | #include <boost/capy/buffers.hpp> | 14 | #include <boost/capy/buffers.hpp> | |||||
| 15 | 15 | |||||||
| 16 | #include <cstddef> | 16 | #include <cstddef> | |||||
| 17 | #include <iterator> | 17 | #include <iterator> | |||||
| 18 | #include <limits> | 18 | #include <limits> | |||||
| 19 | #include <type_traits> | 19 | #include <type_traits> | |||||
| 20 | 20 | |||||||
| 21 | namespace boost { | 21 | namespace boost { | |||||
| 22 | namespace capy { | 22 | namespace capy { | |||||
| 23 | namespace detail { | 23 | namespace detail { | |||||
| 24 | 24 | |||||||
| 25 | /** A borrowed view over a byte sub-range of a buffer sequence. | 25 | /** A borrowed view over a byte sub-range of a buffer sequence. | |||||
| 26 | 26 | |||||||
| 27 | `slice_of<BS>` is the generic result of `buffer_slice` for a sequence | 27 | `slice_of<BS>` is the generic result of `buffer_slice` for a sequence | |||||
| 28 | that is not closed under sub-ranging (everything except a single | 28 | that is not closed under sub-ranging (everything except a single | |||||
| 29 | buffer). It models the same buffer-sequence concept as `BS`: | 29 | buffer). It models the same buffer-sequence concept as `BS`: | |||||
| 30 | `MutableBufferSequence` if `BS` is mutable, otherwise | 30 | `MutableBufferSequence` if `BS` is mutable, otherwise | |||||
| 31 | `ConstBufferSequence`. It can therefore be passed anywhere a buffer | 31 | `ConstBufferSequence`. It can therefore be passed anywhere a buffer | |||||
| 32 | sequence is expected. | 32 | sequence is expected. | |||||
| 33 | 33 | |||||||
| 34 | It stores iterators into the underlying sequence plus front/back byte | 34 | It stores iterators into the underlying sequence plus front/back byte | |||||
| 35 | offsets; it neither owns nor copies the descriptors. The underlying | 35 | offsets; it neither owns nor copies the descriptors. The underlying | |||||
| 36 | sequence must outlive the view. | 36 | sequence must outlive the view. | |||||
| 37 | 37 | |||||||
| 38 | @par Complexity | 38 | @par Complexity | |||||
| 39 | Construction is a single forward pass to the cut points. It is | 39 | Construction is a single forward pass to the cut points. It is | |||||
| 40 | O(buffers up to `offset`) for a to-end slice, and O(buffers up to | 40 | O(buffers up to `offset`) for a to-end slice, and O(buffers up to | |||||
| 41 | `offset + length`) for a bounded slice. It never sums the whole | 41 | `offset + length`) for a bounded slice. It never sums the whole | |||||
| 42 | sequence. | 42 | sequence. | |||||
| 43 | */ | 43 | */ | |||||
| 44 | template<class BS> | 44 | template<class BS> | |||||
| 45 | requires MutableBufferSequence<BS> || ConstBufferSequence<BS> | 45 | requires MutableBufferSequence<BS> || ConstBufferSequence<BS> | |||||
| 46 | class slice_of | 46 | class slice_of | |||||
| 47 | { | 47 | { | |||||
| 48 | public: | 48 | public: | |||||
| 49 | /// The buffer type yielded by iteration. | 49 | /// The buffer type yielded by iteration. | |||||
| 50 | using buffer_type = capy::buffer_type<BS>; | 50 | using buffer_type = capy::buffer_type<BS>; | |||||
| 51 | 51 | |||||||
| 52 | /// The underlying sequence's iterator type. | 52 | /// The underlying sequence's iterator type. | |||||
| 53 | using iterator_type = | 53 | using iterator_type = | |||||
| 54 | decltype(capy::begin(std::declval<BS const&>())); | 54 | decltype(capy::begin(std::declval<BS const&>())); | |||||
| 55 | 55 | |||||||
| 56 | private: | 56 | private: | |||||
| 57 | iterator_type first_{}; | 57 | iterator_type first_{}; | |||||
| 58 | iterator_type last_{}; | 58 | iterator_type last_{}; | |||||
| 59 | std::size_t front_skip_ = 0; // bytes trimmed from *first_ | 59 | std::size_t front_skip_ = 0; // bytes trimmed from *first_ | |||||
| 60 | std::size_t back_skip_ = 0; // bytes trimmed from the final buffer | 60 | std::size_t back_skip_ = 0; // bytes trimmed from the final buffer | |||||
| 61 | 61 | |||||||
| 62 | static buffer_type | 62 | static buffer_type | |||||
| HITCBC | 63 | 34185 | adjust(buffer_type const& b, | 63 | 34185 | adjust(buffer_type const& b, | ||
| 64 | std::size_t front_n, std::size_t back_n) noexcept | 64 | std::size_t front_n, std::size_t back_n) noexcept | |||||
| 65 | { | 65 | { | |||||
| 66 | if constexpr (std::is_same_v<buffer_type, mutable_buffer>) | 66 | if constexpr (std::is_same_v<buffer_type, mutable_buffer>) | |||||
| HITCBC | 67 | 17023 | return mutable_buffer( | 67 | 17023 | return mutable_buffer( | ||
| HITCBC | 68 | 17023 | static_cast<char*>(b.data()) + front_n, | 68 | 17023 | static_cast<char*>(b.data()) + front_n, | ||
| HITCBC | 69 | 34046 | b.size() - front_n - back_n); | 69 | 34046 | b.size() - front_n - back_n); | ||
| 70 | else | 70 | else | |||||
| HITCBC | 71 | 17162 | return const_buffer( | 71 | 17162 | return const_buffer( | ||
| HITCBC | 72 | 17162 | static_cast<char const*>(b.data()) + front_n, | 72 | 17162 | static_cast<char const*>(b.data()) + front_n, | ||
| HITCBC | 73 | 34324 | b.size() - front_n - back_n); | 73 | 34324 | b.size() - front_n - back_n); | ||
| 74 | } | 74 | } | |||||
| 75 | 75 | |||||||
| 76 | public: | 76 | public: | |||||
| 77 | /// Bidirectional iterator that adjusts the first and last buffers. | 77 | /// Bidirectional iterator that adjusts the first and last buffers. | |||||
| 78 | class const_iterator | 78 | class const_iterator | |||||
| 79 | { | 79 | { | |||||
| 80 | iterator_type cur_{}; | 80 | iterator_type cur_{}; | |||||
| 81 | iterator_type anchor_first_{}; | 81 | iterator_type anchor_first_{}; | |||||
| 82 | iterator_type anchor_last_{}; | 82 | iterator_type anchor_last_{}; | |||||
| 83 | std::size_t front_skip_ = 0; | 83 | std::size_t front_skip_ = 0; | |||||
| 84 | std::size_t back_skip_ = 0; | 84 | std::size_t back_skip_ = 0; | |||||
| 85 | 85 | |||||||
| 86 | public: | 86 | public: | |||||
| 87 | using iterator_category = std::bidirectional_iterator_tag; | 87 | using iterator_category = std::bidirectional_iterator_tag; | |||||
| 88 | using value_type = buffer_type; | 88 | using value_type = buffer_type; | |||||
| 89 | using difference_type = std::ptrdiff_t; | 89 | using difference_type = std::ptrdiff_t; | |||||
| 90 | using pointer = value_type*; | 90 | using pointer = value_type*; | |||||
| 91 | using reference = value_type; | 91 | using reference = value_type; | |||||
| 92 | 92 | |||||||
| 93 | const_iterator() noexcept = default; | 93 | const_iterator() noexcept = default; | |||||
| 94 | 94 | |||||||
| HITCBC | 95 | 51198 | const_iterator( | 95 | 51198 | const_iterator( | ||
| 96 | iterator_type cur, iterator_type anchor_first, | 96 | iterator_type cur, iterator_type anchor_first, | |||||
| 97 | iterator_type anchor_last, std::size_t front_skip, | 97 | iterator_type anchor_last, std::size_t front_skip, | |||||
| 98 | std::size_t back_skip) noexcept | 98 | std::size_t back_skip) noexcept | |||||
| HITCBC | 99 | 51198 | : cur_(cur) | 99 | 51198 | : cur_(cur) | ||
| HITCBC | 100 | 51198 | , anchor_first_(anchor_first) | 100 | 51198 | , anchor_first_(anchor_first) | ||
| HITCBC | 101 | 51198 | , anchor_last_(anchor_last) | 101 | 51198 | , anchor_last_(anchor_last) | ||
| HITCBC | 102 | 51198 | , front_skip_(front_skip) | 102 | 51198 | , front_skip_(front_skip) | ||
| HITCBC | 103 | 51198 | , back_skip_(back_skip) | 103 | 51198 | , back_skip_(back_skip) | ||
| 104 | { | 104 | { | |||||
| HITCBC | 105 | 51198 | } | 105 | 51198 | } | ||
| 106 | 106 | |||||||
| HITCBC | 107 | 59275 | bool operator==(const_iterator const& o) const noexcept | 107 | 59275 | bool operator==(const_iterator const& o) const noexcept | ||
| 108 | { | 108 | { | |||||
| HITCBC | 109 | 59275 | return cur_ == o.cur_; | 109 | 59275 | return cur_ == o.cur_; | ||
| 110 | } | 110 | } | |||||
| 111 | 111 | |||||||
| HITCBC | 112 | 59255 | bool operator!=(const_iterator const& o) const noexcept | 112 | 59255 | bool operator!=(const_iterator const& o) const noexcept | ||
| 113 | { | 113 | { | |||||
| HITCBC | 114 | 59255 | return !(*this == o); | 114 | 59255 | return !(*this == o); | ||
| 115 | } | 115 | } | |||||
| 116 | 116 | |||||||
| HITCBC | 117 | 34185 | value_type operator*() const noexcept | 117 | 34185 | value_type operator*() const noexcept | ||
| 118 | { | 118 | { | |||||
| HITCBC | 119 | 34185 | buffer_type buf = *cur_; | 119 | 34185 | buffer_type buf = *cur_; | ||
| HITCBC | 120 | 34185 | auto const front_n = (cur_ == anchor_first_) ? front_skip_ : 0; | 120 | 34185 | auto const front_n = (cur_ == anchor_first_) ? front_skip_ : 0; | ||
| HITCBC | 121 | 34185 | auto next = cur_; | 121 | 34185 | auto next = cur_; | ||
| HITCBC | 122 | 34185 | ++next; | 122 | 34185 | ++next; | ||
| HITCBC | 123 | 34185 | auto const back_n = (next == anchor_last_) ? back_skip_ : 0; | 123 | 34185 | auto const back_n = (next == anchor_last_) ? back_skip_ : 0; | ||
| HITCBC | 124 | 34185 | return adjust(buf, front_n, back_n); | 124 | 34185 | return adjust(buf, front_n, back_n); | ||
| 125 | } | 125 | } | |||||
| 126 | 126 | |||||||
| HITCBC | 127 | 21985 | const_iterator& operator++() noexcept { ++cur_; return *this; } | 127 | 21985 | const_iterator& operator++() noexcept { ++cur_; return *this; } | ||
| HITCBC | 128 | 6450 | const_iterator operator++(int) noexcept | 128 | 6450 | const_iterator operator++(int) noexcept | ||
| HITCBC | 129 | 6450 | { auto t = *this; ++*this; return t; } | 129 | 6450 | { auto t = *this; ++*this; return t; } | ||
| HITCBC | 130 | 12144 | const_iterator& operator--() noexcept { --cur_; return *this; } | 130 | 12144 | const_iterator& operator--() noexcept { --cur_; return *this; } | ||
| HITCBC | 131 | 6072 | const_iterator operator--(int) noexcept | 131 | 6072 | const_iterator operator--(int) noexcept | ||
| HITCBC | 132 | 6072 | { auto t = *this; --*this; return t; } | 132 | 6072 | { auto t = *this; --*this; return t; } | ||
| 133 | }; | 133 | }; | |||||
| 134 | 134 | |||||||
| 135 | /// Construct an empty slice. | 135 | /// Construct an empty slice. | |||||
| 136 | slice_of() noexcept = default; | 136 | slice_of() noexcept = default; | |||||
| 137 | 137 | |||||||
| 138 | /** Construct a view of `[offset, offset + length)` bytes of `bs`. | 138 | /** Construct a view of `[offset, offset + length)` bytes of `bs`. | |||||
| 139 | 139 | |||||||
| 140 | @param bs The underlying sequence (must outlive the view). | 140 | @param bs The underlying sequence (must outlive the view). | |||||
| 141 | @param offset Bytes skipped from the front. Clamped to the total. | 141 | @param offset Bytes skipped from the front. Clamped to the total. | |||||
| 142 | @param length Bytes exposed; the default exposes to the end. | 142 | @param length Bytes exposed; the default exposes to the end. | |||||
| 143 | */ | 143 | */ | |||||
| HITCBC | 144 | 2293 | slice_of( | 144 | 2293 | slice_of( | ||
| 145 | BS const& bs, | 145 | BS const& bs, | |||||
| 146 | std::size_t offset, | 146 | std::size_t offset, | |||||
| 147 | std::size_t length = | 147 | std::size_t length = | |||||
| 148 | (std::numeric_limits<std::size_t>::max)()) noexcept | 148 | (std::numeric_limits<std::size_t>::max)()) noexcept | |||||
| HITCBC | 149 | 2293 | { | 149 | 2293 | { | ||
| HITCBC | 150 | 2293 | first_ = capy::begin(bs); | 150 | 2293 | first_ = capy::begin(bs); | ||
| HITCBC | 151 | 2293 | last_ = capy::end(bs); | 151 | 2293 | last_ = capy::end(bs); | ||
| 152 | 152 | |||||||
| 153 | // Position first_/front_skip_ at byte `offset` (single forward pass). | 153 | // Position first_/front_skip_ at byte `offset` (single forward pass). | |||||
| HITCBC | 154 | 2293 | std::size_t skip = offset; | 154 | 2293 | std::size_t skip = offset; | ||
| HITCBC | 155 | 2961 | while (first_ != last_) | 155 | 2961 | while (first_ != last_) | ||
| 156 | { | 156 | { | |||||
| HITCBC | 157 | 2856 | std::size_t const sz = buffer_type(*first_).size(); | 157 | 2856 | std::size_t const sz = buffer_type(*first_).size(); | ||
| HITCBC | 158 | 2856 | if (skip < sz) | 158 | 2856 | if (skip < sz) | ||
| 159 | { | 159 | { | |||||
| HITCBC | 160 | 2188 | front_skip_ = skip; | 160 | 2188 | front_skip_ = skip; | ||
| HITCBC | 161 | 2188 | break; | 161 | 2188 | break; | ||
| 162 | } | 162 | } | |||||
| HITCBC | 163 | 668 | skip -= sz; | 163 | 668 | skip -= sz; | ||
| HITCBC | 164 | 668 | ++first_; | 164 | 668 | ++first_; | ||
| 165 | } | 165 | } | |||||
| 166 | 166 | |||||||
| HITCBC | 167 | 2293 | if (first_ == last_) | 167 | 2293 | if (first_ == last_) | ||
| HITCBC | 168 | 181 | return; // offset at or past the end: empty slice | 168 | 181 | return; // offset at or past the end: empty slice | ||
| HITCBC | 169 | 2188 | if (length == (std::numeric_limits<std::size_t>::max)()) | 169 | 2188 | if (length == (std::numeric_limits<std::size_t>::max)()) | ||
| HITCBC | 170 | 1043 | return; // to-end: last_ already end(bs), back_skip_ == 0 | 170 | 1043 | return; // to-end: last_ already end(bs), back_skip_ == 0 | ||
| 171 | 171 | |||||||
| 172 | // Walk `length` live bytes forward to fix last_/back_skip_. | 172 | // Walk `length` live bytes forward to fix last_/back_skip_. | |||||
| HITCBC | 173 | 1145 | std::size_t left = length; | 173 | 1145 | std::size_t left = length; | ||
| HITCBC | 174 | 1145 | auto cursor = first_; | 174 | 1145 | auto cursor = first_; | ||
| HITCBC | 175 | 1145 | std::size_t cursor_front = front_skip_; | 175 | 1145 | std::size_t cursor_front = front_skip_; | ||
| HITCBC | 176 | 1643 | while (cursor != last_ && left > 0) | 176 | 1643 | while (cursor != last_ && left > 0) | ||
| 177 | { | 177 | { | |||||
| HITCBC | 178 | 1538 | std::size_t const sz = buffer_type(*cursor).size(); | 178 | 1538 | std::size_t const sz = buffer_type(*cursor).size(); | ||
| HITCBC | 179 | 1538 | std::size_t const avail = sz - cursor_front; | 179 | 1538 | std::size_t const avail = sz - cursor_front; | ||
| HITCBC | 180 | 1538 | if (left <= avail) | 180 | 1538 | if (left <= avail) | ||
| 181 | { | 181 | { | |||||
| HITCBC | 182 | 1040 | back_skip_ = avail - left; | 182 | 1040 | back_skip_ = avail - left; | ||
| HITCBC | 183 | 1040 | ++cursor; | 183 | 1040 | ++cursor; | ||
| HITCBC | 184 | 1040 | last_ = cursor; | 184 | 1040 | last_ = cursor; | ||
| HITCBC | 185 | 1040 | return; | 185 | 1040 | return; | ||
| 186 | } | 186 | } | |||||
| HITCBC | 187 | 498 | left -= avail; | 187 | 498 | left -= avail; | ||
| HITCBC | 188 | 498 | ++cursor; | 188 | 498 | ++cursor; | ||
| HITCBC | 189 | 498 | cursor_front = 0; | 189 | 498 | cursor_front = 0; | ||
| 190 | } | 190 | } | |||||
| HITCBC | 191 | 105 | last_ = cursor; | 191 | 105 | last_ = cursor; | ||
| 192 | } | 192 | } | |||||
| 193 | 193 | |||||||
| 194 | /** Construct directly from a positioned iterator range. | 194 | /** Construct directly from a positioned iterator range. | |||||
| 195 | 195 | |||||||
| 196 | Used by `consuming_buffers::data()` to expose its current position | 196 | Used by `consuming_buffers::data()` to expose its current position | |||||
| 197 | without re-walking from the start. | 197 | without re-walking from the start. | |||||
| 198 | */ | 198 | */ | |||||
| HITCBC | 199 | 362 | slice_of( | 199 | 362 | slice_of( | ||
| 200 | iterator_type first, iterator_type last, | 200 | iterator_type first, iterator_type last, | |||||
| 201 | std::size_t front_skip = 0, std::size_t back_skip = 0) noexcept | 201 | std::size_t front_skip = 0, std::size_t back_skip = 0) noexcept | |||||
| HITCBC | 202 | 362 | : first_(first) | 202 | 362 | : first_(first) | ||
| HITCBC | 203 | 362 | , last_(last) | 203 | 362 | , last_(last) | ||
| HITCBC | 204 | 362 | , front_skip_(front_skip) | 204 | 362 | , front_skip_(front_skip) | ||
| HITCBC | 205 | 362 | , back_skip_(back_skip) | 205 | 362 | , back_skip_(back_skip) | ||
| 206 | { | 206 | { | |||||
| HITCBC | 207 | 362 | } | 207 | 362 | } | ||
| 208 | 208 | |||||||
| 209 | /// Return an iterator to the first buffer. | 209 | /// Return an iterator to the first buffer. | |||||
| HITCBC | 210 | 25589 | const_iterator begin() const noexcept | 210 | 25589 | const_iterator begin() const noexcept | ||
| 211 | { | 211 | { | |||||
| 212 | return const_iterator( | 212 | return const_iterator( | |||||
| HITCBC | 213 | 25589 | first_, first_, last_, front_skip_, back_skip_); | 213 | 25589 | first_, first_, last_, front_skip_, back_skip_); | ||
| 214 | } | 214 | } | |||||
| 215 | 215 | |||||||
| 216 | /// Return an iterator past the last buffer. | 216 | /// Return an iterator past the last buffer. | |||||
| HITCBC | 217 | 25609 | const_iterator end() const noexcept | 217 | 25609 | const_iterator end() const noexcept | ||
| 218 | { | 218 | { | |||||
| 219 | return const_iterator( | 219 | return const_iterator( | |||||
| HITCBC | 220 | 25609 | last_, first_, last_, front_skip_, back_skip_); | 220 | 25609 | last_, first_, last_, front_skip_, back_skip_); | ||
| 221 | } | 221 | } | |||||
| 222 | }; | 222 | }; | |||||
| 223 | 223 | |||||||
| 224 | } // namespace detail | 224 | } // namespace detail | |||||
| 225 | } // namespace capy | 225 | } // namespace capy | |||||
| 226 | } // namespace boost | 226 | } // namespace boost | |||||
| 227 | 227 | |||||||
| 228 | #endif | 228 | #endif | |||||