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