C/C++ API Reference
Loading...
Searching...
No Matches
chunk_iterator.h
1// Copyright 2025 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <cstddef>
17#include <cstdint>
18#include <iterator>
19
20#include "pw_assert/assert.h"
21#include "pw_bytes/span.h"
22#include "pw_containers/dynamic_deque.h"
23#include "pw_multibuf/internal/entry.h"
24#include "pw_preprocessor/compiler.h"
25
26namespace pw::multibuf_impl {
27
28// Forward declarations.
29template <typename, typename>
30class ChunksImpl;
31
40template <typename SizeType, bool kIsConst>
42 private:
43 using SpanType = std::conditional_t<kIsConst, ConstByteSpan, ByteSpan>;
44 using ByteType = typename SpanType::element_type;
45 using Deque = std::conditional_t<kIsConst,
48
49 public:
50 using size_type = SizeType;
51 using difference_type = std::ptrdiff_t;
52 using value_type = SpanType;
53 using pointer = value_type*;
54 using const_pointer = const value_type*;
55 using reference = value_type&;
56 using const_reference = const value_type&;
57 using iterator_category = std::bidirectional_iterator_tag;
58
59 constexpr ChunkIterator() = default;
60 constexpr ChunkIterator(const ChunkIterator& other) { *this = other; }
61 constexpr ChunkIterator& operator=(const ChunkIterator& other);
62
63 // Support converting non-const iterators to const_iterators.
64 constexpr operator ChunkIterator<SizeType, /*kIsConst=*/true>() const {
65 return {deque_, depth_, index_};
66 }
67
68 constexpr reference operator*() {
69 PW_ASSERT(is_valid());
70 return current_;
71 }
72
73 constexpr const_reference operator*() const {
74 PW_ASSERT(is_valid());
75 return current_;
76 }
77
78 constexpr pointer operator->() {
79 PW_ASSERT(is_valid());
80 return &current_;
81 }
82
83 constexpr const_pointer operator->() const {
84 PW_ASSERT(is_valid());
85 return &current_;
86 }
87
88 constexpr ChunkIterator& operator++();
89
90 constexpr ChunkIterator operator++(int) {
91 ChunkIterator previous(*this);
92 operator++();
93 return previous;
94 }
95
96 constexpr ChunkIterator& operator--();
97
98 constexpr ChunkIterator operator--(int) {
99 ChunkIterator previous(*this);
100 operator--();
101 return previous;
102 }
103
104 constexpr friend bool operator==(const ChunkIterator& lhs,
105 const ChunkIterator& rhs) {
106 return lhs.deque_ == rhs.deque_ && lhs.depth_ == rhs.depth_ &&
107 lhs.index_ == rhs.index_;
108 }
109
110 constexpr friend bool operator!=(const ChunkIterator& lhs,
111 const ChunkIterator& rhs) {
112 return !(lhs == rhs);
113 }
114
115 private:
116 // Iterators that point to something are created `Chunks` or `ConstChunks`.
117 template <typename, typename>
118 friend class ChunksImpl;
119
120 // Allow non-const iterators to construct const_iterators in conversions.
121 template <typename, bool>
122 friend class ChunkIterator;
123
124 // Byte iterators use chunk iterators to get contiguous spans.
125 template <typename, bool>
126 friend class ByteIterator;
127
128 constexpr ChunkIterator(Deque* deque, size_type depth, size_type index)
129 : deque_(deque), depth_(depth), index_(index) {
130 ResetCurrent();
131 }
132
133 constexpr bool is_valid() const {
134 return deque_ != nullptr && index_ < deque_->size();
135 }
136
137 constexpr ByteType* data(size_type index) const {
138 return (*deque_)[index].data + (*deque_)[index + depth_ - 1].view.offset;
139 }
140
141 constexpr size_t size(size_type index) const {
142 return (*deque_)[index + depth_ - 1].view.length;
143 }
144
145 constexpr void ResetCurrent();
146
147 Deque* deque_ = nullptr;
148 size_type depth_ = 0;
149 size_type index_ = 0;
150 SpanType current_;
151};
152
154template <typename Derived, typename Deque>
156 public:
157 using size_type = typename Deque::size_type;
158 using value_type = typename Deque::value_type;
159 using difference_type = typename Deque::difference_type;
160 using iterator = ChunkIterator<size_type, /*kIsConst=*/false>;
161 using const_iterator = ChunkIterator<size_type, /*kIsConst=*/true>;
162
163 constexpr ChunksImpl() = default;
164
165 constexpr size_type size() const { return deque().size() / depth(); }
166 constexpr size_type capacity() const { return deque().capacity() / depth(); }
167
168 constexpr const_iterator cbegin() const { return derived().begin(); }
169 constexpr const_iterator cend() const { return derived().end(); }
170
171 protected:
172 constexpr void Init(Deque& deque, size_type depth) {
173 derived().begin_.deque_ = &deque;
174 derived().begin_.depth_ = depth;
175 derived().end_.deque_ = &deque;
176 derived().end_.depth_ = depth;
177 derived().end_.index_ = deque.size();
178 }
179
180 constexpr Derived& derived() { return static_cast<Derived&>(*this); }
181 constexpr const Derived& derived() const {
182 return static_cast<const Derived&>(*this);
183 }
184
185 constexpr const Deque& deque() const { return *(derived().begin_.deque_); }
186 constexpr size_type depth() const { return derived().begin_.depth_; }
187};
188
201template <typename SizeType = uint16_t>
203 : public ChunksImpl<Chunks<SizeType>, DynamicDeque<Entry, SizeType>> {
204 private:
207
208 public:
209 using typename Base::const_iterator;
210 using typename Base::difference_type;
211 using typename Base::iterator;
212 using typename Base::size_type;
213 using typename Base::value_type;
214
215 constexpr Chunks() = default;
216
217 constexpr iterator begin() const { return begin_; }
218 constexpr iterator end() const { return end_; }
219
220 private:
221 template <typename, typename>
222 friend class ChunksImpl;
223 friend class GenericMultiBuf;
224
225 // For unit testing.
226 friend class IteratorTest;
227
228 constexpr Chunks(Deque& deque, size_type depth) { Base::Init(deque, depth); }
229
230 iterator begin_;
231 iterator end_;
232};
233
246template <typename SizeType = uint16_t>
247class ConstChunks : public ChunksImpl<ConstChunks<SizeType>,
248 const DynamicDeque<Entry, SizeType>> {
249 private:
252
253 public:
254 using typename Base::const_iterator;
255 using typename Base::difference_type;
256 using typename Base::size_type;
257 using typename Base::value_type;
258
259 constexpr ConstChunks() = default;
260
261 constexpr const_iterator begin() const { return begin_; }
262 constexpr const_iterator end() const { return end_; }
263
264 private:
265 template <typename, typename>
266 friend class ChunksImpl;
267 friend class GenericMultiBuf;
268
269 constexpr ConstChunks(Deque& deque, size_type depth) {
270 Base::Init(deque, depth);
271 }
272
273 const_iterator begin_;
274 const_iterator end_;
275};
276
277// Template method implementations.
278
279template <typename SizeType, bool kIsConst>
282 deque_ = other.deque_;
283 depth_ = other.depth_;
284 index_ = other.index_;
285 ResetCurrent();
286 return *this;
287}
288
289template <typename SizeType, bool kIsConst>
290constexpr ChunkIterator<SizeType, kIsConst>&
291ChunkIterator<SizeType, kIsConst>::operator++() {
292 PW_ASSERT(is_valid());
293 size_t left = current_.size();
294 while (left != 0) {
295 left -= size(index_);
296 index_ += depth_;
297 }
298 while (index_ < deque_->size() && size(index_) == 0) {
299 index_ += depth_;
300 }
301 ResetCurrent();
302 return *this;
303}
304
305template <typename SizeType, bool kIsConst>
306constexpr ChunkIterator<SizeType, kIsConst>&
307ChunkIterator<SizeType, kIsConst>::operator--() {
308 PW_ASSERT(deque_ != nullptr);
309 PW_ASSERT(index_ != 0);
310 current_ = SpanType();
311 while (index_ != 0) {
312 SpanType prev(data(index_ - depth_), size(index_ - depth_));
313 if (!current_.empty() && prev.data() + prev.size() != current_.data()) {
314 break;
315 }
316 current_ = SpanType(prev.data(), prev.size() + current_.size());
317 index_ -= depth_;
318 }
319 return *this;
320}
321
322template <typename SizeType, bool kIsConst>
323constexpr void ChunkIterator<SizeType, kIsConst>::ResetCurrent() {
324 if (!is_valid()) {
325 current_ = SpanType();
326 return;
327 }
328 current_ = SpanType(data(index_), size(index_));
329 for (size_type i = index_; i < deque_->size() - depth_; i += depth_) {
330 SpanType next(data(i + depth_), size(i + depth_));
331 if (current_.empty()) {
332 current_ = next;
333 index_ += depth_;
334 continue;
335 }
336 if (current_.data() + current_.size() != next.data()) {
337 break;
338 }
339 current_ = SpanType(current_.data(), current_.size() + next.size());
340 }
341}
342
343} // namespace pw::multibuf_impl
Definition: dynamic_deque.h:60
Definition: byte_iterator.h:38
Definition: chunk_iterator.h:41
Definition: chunk_iterator.h:203
Base class for ranges of chunks.
Definition: chunk_iterator.h:155
Definition: chunk_iterator.h:248
Definition: multibuf_v2.h:1172