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`), so it can be passed anywhere a buffer sequence | 31 | `ConstBufferSequence`), so it can be passed anywhere a buffer sequence | |||||
| 32 | is expected. | 32 | 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: O(buffers up | 39 | Construction is a single forward pass to the cut points: O(buffers up | |||||
| 40 | to `offset`) for a to-end slice, O(buffers up to `offset + length`) | 40 | to `offset`) for a to-end slice, O(buffers up to `offset + length`) | |||||
| 41 | for a bounded slice. It never sums the whole sequence. | 41 | for a bounded slice. It never sums the whole sequence. | |||||
| 42 | */ | 42 | */ | |||||
| 43 | template<class BS> | 43 | template<class BS> | |||||
| 44 | requires MutableBufferSequence<BS> || ConstBufferSequence<BS> | 44 | requires MutableBufferSequence<BS> || ConstBufferSequence<BS> | |||||
| 45 | class slice_of | 45 | class slice_of | |||||
| 46 | { | 46 | { | |||||
| 47 | public: | 47 | public: | |||||
| 48 | /// The buffer type yielded by iteration. | 48 | /// The buffer type yielded by iteration. | |||||
| 49 | using buffer_type = capy::buffer_type<BS>; | 49 | using buffer_type = capy::buffer_type<BS>; | |||||
| 50 | 50 | |||||||
| 51 | /// The underlying sequence's iterator type. | 51 | /// The underlying sequence's iterator type. | |||||
| 52 | using iterator_type = | 52 | using iterator_type = | |||||
| 53 | decltype(capy::begin(std::declval<BS const&>())); | 53 | decltype(capy::begin(std::declval<BS const&>())); | |||||
| 54 | 54 | |||||||
| 55 | private: | 55 | private: | |||||
| 56 | iterator_type first_{}; | 56 | iterator_type first_{}; | |||||
| 57 | iterator_type last_{}; | 57 | iterator_type last_{}; | |||||
| 58 | std::size_t front_skip_ = 0; // bytes trimmed from *first_ | 58 | std::size_t front_skip_ = 0; // bytes trimmed from *first_ | |||||
| 59 | std::size_t back_skip_ = 0; // bytes trimmed from the final buffer | 59 | std::size_t back_skip_ = 0; // bytes trimmed from the final buffer | |||||
| 60 | 60 | |||||||
| 61 | static buffer_type | 61 | static buffer_type | |||||
| HITCBC | 62 | 34112 | adjust(buffer_type const& b, | 62 | 34112 | adjust(buffer_type const& b, | ||
| 63 | std::size_t front_n, std::size_t back_n) noexcept | 63 | std::size_t front_n, std::size_t back_n) noexcept | |||||
| 64 | { | 64 | { | |||||
| 65 | if constexpr (std::is_same_v<buffer_type, mutable_buffer>) | 65 | if constexpr (std::is_same_v<buffer_type, mutable_buffer>) | |||||
| HITCBC | 66 | 17006 | return mutable_buffer( | 66 | 17006 | return mutable_buffer( | ||
| HITCBC | 67 | 17006 | static_cast<char*>(b.data()) + front_n, | 67 | 17006 | static_cast<char*>(b.data()) + front_n, | ||
| HITCBC | 68 | 34012 | b.size() - front_n - back_n); | 68 | 34012 | b.size() - front_n - back_n); | ||
| 69 | else | 69 | else | |||||
| HITCBC | 70 | 17106 | return const_buffer( | 70 | 17106 | return const_buffer( | ||
| HITCBC | 71 | 17106 | static_cast<char const*>(b.data()) + front_n, | 71 | 17106 | static_cast<char const*>(b.data()) + front_n, | ||
| HITCBC | 72 | 34212 | b.size() - front_n - back_n); | 72 | 34212 | b.size() - front_n - back_n); | ||
| 73 | } | 73 | } | |||||
| 74 | 74 | |||||||
| 75 | public: | 75 | public: | |||||
| 76 | /// Bidirectional iterator that adjusts the first and last buffers. | 76 | /// Bidirectional iterator that adjusts the first and last buffers. | |||||
| 77 | class const_iterator | 77 | class const_iterator | |||||
| 78 | { | 78 | { | |||||
| 79 | iterator_type cur_{}; | 79 | iterator_type cur_{}; | |||||
| 80 | iterator_type anchor_first_{}; | 80 | iterator_type anchor_first_{}; | |||||
| 81 | iterator_type anchor_last_{}; | 81 | iterator_type anchor_last_{}; | |||||
| 82 | std::size_t front_skip_ = 0; | 82 | std::size_t front_skip_ = 0; | |||||
| 83 | std::size_t back_skip_ = 0; | 83 | std::size_t back_skip_ = 0; | |||||
| 84 | 84 | |||||||
| 85 | public: | 85 | public: | |||||
| 86 | using iterator_category = std::bidirectional_iterator_tag; | 86 | using iterator_category = std::bidirectional_iterator_tag; | |||||
| 87 | using value_type = buffer_type; | 87 | using value_type = buffer_type; | |||||
| 88 | using difference_type = std::ptrdiff_t; | 88 | using difference_type = std::ptrdiff_t; | |||||
| 89 | using pointer = value_type*; | 89 | using pointer = value_type*; | |||||
| 90 | using reference = value_type; | 90 | using reference = value_type; | |||||
| 91 | 91 | |||||||
| 92 | const_iterator() noexcept = default; | 92 | const_iterator() noexcept = default; | |||||
| 93 | 93 | |||||||
| HITCBC | 94 | 51088 | const_iterator( | 94 | 51088 | const_iterator( | ||
| 95 | iterator_type cur, iterator_type anchor_first, | 95 | iterator_type cur, iterator_type anchor_first, | |||||
| 96 | iterator_type anchor_last, std::size_t front_skip, | 96 | iterator_type anchor_last, std::size_t front_skip, | |||||
| 97 | std::size_t back_skip) noexcept | 97 | std::size_t back_skip) noexcept | |||||
| HITCBC | 98 | 51088 | : cur_(cur) | 98 | 51088 | : cur_(cur) | ||
| HITCBC | 99 | 51088 | , anchor_first_(anchor_first) | 99 | 51088 | , anchor_first_(anchor_first) | ||
| HITCBC | 100 | 51088 | , anchor_last_(anchor_last) | 100 | 51088 | , anchor_last_(anchor_last) | ||
| HITCBC | 101 | 51088 | , front_skip_(front_skip) | 101 | 51088 | , front_skip_(front_skip) | ||
| HITCBC | 102 | 51088 | , back_skip_(back_skip) | 102 | 51088 | , back_skip_(back_skip) | ||
| 103 | { | 103 | { | |||||
| HITCBC | 104 | 51088 | } | 104 | 51088 | } | ||
| 105 | 105 | |||||||
| HITCBC | 106 | 59163 | bool operator==(const_iterator const& o) const noexcept | 106 | 59163 | bool operator==(const_iterator const& o) const noexcept | ||
| 107 | { | 107 | { | |||||
| HITCBC | 108 | 59163 | return cur_ == o.cur_; | 108 | 59163 | return cur_ == o.cur_; | ||
| 109 | } | 109 | } | |||||
| 110 | 110 | |||||||
| HITCBC | 111 | 59163 | bool operator!=(const_iterator const& o) const noexcept | 111 | 59163 | bool operator!=(const_iterator const& o) const noexcept | ||
| 112 | { | 112 | { | |||||
| HITCBC | 113 | 59163 | return !(*this == o); | 113 | 59163 | return !(*this == o); | ||
| 114 | } | 114 | } | |||||
| 115 | 115 | |||||||
| HITCBC | 116 | 34112 | value_type operator*() const noexcept | 116 | 34112 | value_type operator*() const noexcept | ||
| 117 | { | 117 | { | |||||
| HITCBC | 118 | 34112 | buffer_type buf = *cur_; | 118 | 34112 | buffer_type buf = *cur_; | ||
| HITCBC | 119 | 34112 | auto const front_n = (cur_ == anchor_first_) ? front_skip_ : 0; | 119 | 34112 | auto const front_n = (cur_ == anchor_first_) ? front_skip_ : 0; | ||
| HITCBC | 120 | 34112 | auto next = cur_; | 120 | 34112 | auto next = cur_; | ||
| HITCBC | 121 | 34112 | ++next; | 121 | 34112 | ++next; | ||
| HITCBC | 122 | 34112 | auto const back_n = (next == anchor_last_) ? back_skip_ : 0; | 122 | 34112 | auto const back_n = (next == anchor_last_) ? back_skip_ : 0; | ||
| HITCBC | 123 | 34112 | return adjust(buf, front_n, back_n); | 123 | 34112 | return adjust(buf, front_n, back_n); | ||
| 124 | } | 124 | } | |||||
| 125 | 125 | |||||||
| HITCBC | 126 | 21911 | const_iterator& operator++() noexcept { ++cur_; return *this; } | 126 | 21911 | const_iterator& operator++() noexcept { ++cur_; return *this; } | ||
| HITCBC | 127 | 6436 | const_iterator operator++(int) noexcept | 127 | 6436 | const_iterator operator++(int) noexcept | ||
| HITCBC | 128 | 6436 | { auto t = *this; ++*this; return t; } | 128 | 6436 | { auto t = *this; ++*this; return t; } | ||
| HITCBC | 129 | 12144 | const_iterator& operator--() noexcept { --cur_; return *this; } | 129 | 12144 | const_iterator& operator--() noexcept { --cur_; return *this; } | ||
| HITCBC | 130 | 6072 | const_iterator operator--(int) noexcept | 130 | 6072 | const_iterator operator--(int) noexcept | ||
| HITCBC | 131 | 6072 | { auto t = *this; --*this; return t; } | 131 | 6072 | { auto t = *this; --*this; return t; } | ||
| 132 | }; | 132 | }; | |||||
| 133 | 133 | |||||||
| 134 | /// Construct an empty slice. | 134 | /// Construct an empty slice. | |||||
| 135 | slice_of() noexcept = default; | 135 | slice_of() noexcept = default; | |||||
| 136 | 136 | |||||||
| 137 | /** Construct a view of `[offset, offset + length)` bytes of `bs`. | 137 | /** Construct a view of `[offset, offset + length)` bytes of `bs`. | |||||
| 138 | 138 | |||||||
| 139 | @param bs The underlying sequence (must outlive the view). | 139 | @param bs The underlying sequence (must outlive the view). | |||||
| 140 | @param offset Bytes skipped from the front. Clamped to the total. | 140 | @param offset Bytes skipped from the front. Clamped to the total. | |||||
| 141 | @param length Bytes exposed; the default exposes to the end. | 141 | @param length Bytes exposed; the default exposes to the end. | |||||
| 142 | */ | 142 | */ | |||||
| HITCBC | 143 | 2291 | slice_of( | 143 | 2291 | slice_of( | ||
| 144 | BS const& bs, | 144 | BS const& bs, | |||||
| 145 | std::size_t offset, | 145 | std::size_t offset, | |||||
| 146 | std::size_t length = | 146 | std::size_t length = | |||||
| 147 | (std::numeric_limits<std::size_t>::max)()) noexcept | 147 | (std::numeric_limits<std::size_t>::max)()) noexcept | |||||
| HITCBC | 148 | 2291 | { | 148 | 2291 | { | ||
| HITCBC | 149 | 2291 | first_ = capy::begin(bs); | 149 | 2291 | first_ = capy::begin(bs); | ||
| HITCBC | 150 | 2291 | last_ = capy::end(bs); | 150 | 2291 | last_ = capy::end(bs); | ||
| 151 | 151 | |||||||
| 152 | // Position first_/front_skip_ at byte `offset` (single forward pass). | 152 | // Position first_/front_skip_ at byte `offset` (single forward pass). | |||||
| HITCBC | 153 | 2291 | std::size_t skip = offset; | 153 | 2291 | std::size_t skip = offset; | ||
| HITCBC | 154 | 2958 | while (first_ != last_) | 154 | 2958 | while (first_ != last_) | ||
| 155 | { | 155 | { | |||||
| HITCBC | 156 | 2853 | std::size_t const sz = buffer_type(*first_).size(); | 156 | 2853 | std::size_t const sz = buffer_type(*first_).size(); | ||
| HITCBC | 157 | 2853 | if (skip < sz) | 157 | 2853 | if (skip < sz) | ||
| 158 | { | 158 | { | |||||
| HITCBC | 159 | 2186 | front_skip_ = skip; | 159 | 2186 | front_skip_ = skip; | ||
| HITCBC | 160 | 2186 | break; | 160 | 2186 | break; | ||
| 161 | } | 161 | } | |||||
| HITCBC | 162 | 667 | skip -= sz; | 162 | 667 | skip -= sz; | ||
| HITCBC | 163 | 667 | ++first_; | 163 | 667 | ++first_; | ||
| 164 | } | 164 | } | |||||
| 165 | 165 | |||||||
| HITCBC | 166 | 2291 | if (first_ == last_) | 166 | 2291 | if (first_ == last_) | ||
| HITCBC | 167 | 181 | return; // offset at or past the end: empty slice | 167 | 181 | return; // offset at or past the end: empty slice | ||
| HITCBC | 168 | 2186 | if (length == (std::numeric_limits<std::size_t>::max)()) | 168 | 2186 | if (length == (std::numeric_limits<std::size_t>::max)()) | ||
| HITCBC | 169 | 1042 | return; // to-end: last_ already end(bs), back_skip_ == 0 | 169 | 1042 | return; // to-end: last_ already end(bs), back_skip_ == 0 | ||
| 170 | 170 | |||||||
| 171 | // Walk `length` live bytes forward to fix last_/back_skip_. | 171 | // Walk `length` live bytes forward to fix last_/back_skip_. | |||||
| HITCBC | 172 | 1144 | std::size_t left = length; | 172 | 1144 | std::size_t left = length; | ||
| HITCBC | 173 | 1144 | auto cursor = first_; | 173 | 1144 | auto cursor = first_; | ||
| HITCBC | 174 | 1144 | std::size_t cursor_front = front_skip_; | 174 | 1144 | std::size_t cursor_front = front_skip_; | ||
| HITCBC | 175 | 1641 | while (cursor != last_ && left > 0) | 175 | 1641 | while (cursor != last_ && left > 0) | ||
| 176 | { | 176 | { | |||||
| HITCBC | 177 | 1536 | std::size_t const sz = buffer_type(*cursor).size(); | 177 | 1536 | std::size_t const sz = buffer_type(*cursor).size(); | ||
| HITCBC | 178 | 1536 | std::size_t const avail = sz - cursor_front; | 178 | 1536 | std::size_t const avail = sz - cursor_front; | ||
| HITCBC | 179 | 1536 | if (left <= avail) | 179 | 1536 | if (left <= avail) | ||
| 180 | { | 180 | { | |||||
| HITCBC | 181 | 1039 | back_skip_ = avail - left; | 181 | 1039 | back_skip_ = avail - left; | ||
| HITCBC | 182 | 1039 | ++cursor; | 182 | 1039 | ++cursor; | ||
| HITCBC | 183 | 1039 | last_ = cursor; | 183 | 1039 | last_ = cursor; | ||
| HITCBC | 184 | 1039 | return; | 184 | 1039 | return; | ||
| 185 | } | 185 | } | |||||
| HITCBC | 186 | 497 | left -= avail; | 186 | 497 | left -= avail; | ||
| HITCBC | 187 | 497 | ++cursor; | 187 | 497 | ++cursor; | ||
| HITCBC | 188 | 497 | cursor_front = 0; | 188 | 497 | cursor_front = 0; | ||
| 189 | } | 189 | } | |||||
| HITCBC | 190 | 105 | last_ = cursor; | 190 | 105 | last_ = cursor; | ||
| 191 | } | 191 | } | |||||
| 192 | 192 | |||||||
| 193 | /** Construct directly from a positioned iterator range. | 193 | /** Construct directly from a positioned iterator range. | |||||
| 194 | 194 | |||||||
| 195 | Used by `consuming_buffers::data()` to expose its current position | 195 | Used by `consuming_buffers::data()` to expose its current position | |||||
| 196 | without re-walking from the start. | 196 | without re-walking from the start. | |||||
| 197 | */ | 197 | */ | |||||
| HITCBC | 198 | 335 | slice_of( | 198 | 335 | slice_of( | ||
| 199 | iterator_type first, iterator_type last, | 199 | iterator_type first, iterator_type last, | |||||
| 200 | std::size_t front_skip = 0, std::size_t back_skip = 0) noexcept | 200 | std::size_t front_skip = 0, std::size_t back_skip = 0) noexcept | |||||
| HITCBC | 201 | 335 | : first_(first) | 201 | 335 | : first_(first) | ||
| HITCBC | 202 | 335 | , last_(last) | 202 | 335 | , last_(last) | ||
| HITCBC | 203 | 335 | , front_skip_(front_skip) | 203 | 335 | , front_skip_(front_skip) | ||
| HITCBC | 204 | 335 | , back_skip_(back_skip) | 204 | 335 | , back_skip_(back_skip) | ||
| 205 | { | 205 | { | |||||
| HITCBC | 206 | 335 | } | 206 | 335 | } | ||
| 207 | 207 | |||||||
| 208 | /// Return an iterator to the first buffer. | 208 | /// Return an iterator to the first buffer. | |||||
| HITCBC | 209 | 25534 | const_iterator begin() const noexcept | 209 | 25534 | const_iterator begin() const noexcept | ||
| 210 | { | 210 | { | |||||
| 211 | return const_iterator( | 211 | return const_iterator( | |||||
| HITCBC | 212 | 25534 | first_, first_, last_, front_skip_, back_skip_); | 212 | 25534 | first_, first_, last_, front_skip_, back_skip_); | ||
| 213 | } | 213 | } | |||||
| 214 | 214 | |||||||
| 215 | /// Return an iterator past the last buffer. | 215 | /// Return an iterator past the last buffer. | |||||
| HITCBC | 216 | 25554 | const_iterator end() const noexcept | 216 | 25554 | const_iterator end() const noexcept | ||
| 217 | { | 217 | { | |||||
| 218 | return const_iterator( | 218 | return const_iterator( | |||||
| HITCBC | 219 | 25554 | last_, first_, last_, front_skip_, back_skip_); | 219 | 25554 | last_, first_, last_, front_skip_, back_skip_); | ||
| 220 | } | 220 | } | |||||
| 221 | }; | 221 | }; | |||||
| 222 | 222 | |||||||
| 223 | } // namespace detail | 223 | } // namespace detail | |||||
| 224 | } // namespace capy | 224 | } // namespace capy | |||||
| 225 | } // namespace boost | 225 | } // namespace boost | |||||
| 226 | 226 | |||||||
| 227 | #endif | 227 | #endif | |||||