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