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 <iterator>
18#include <type_traits>
19#include <utility>
20
21#include "pw_assert/assert.h"
22#include "pw_bytes/span.h"
23#include "pw_containers/dynamic_deque.h"
24#include "pw_multibuf/internal/entry.h"
25
26namespace pw::multibuf {
27
28// Forward declarations.
29template <typename, typename>
31
32class MultiBufV1Adapter;
33
34namespace internal {
35
36enum class ChunkContiguity {
37 kKeepAll,
38 kCoalesce,
39};
40
41enum class ChunkMutability {
42 kMutable,
43 kConst,
44};
45
58template <typename SizeType,
59 ChunkContiguity kContiguity,
60 ChunkMutability kMutability>
62 private:
63 using SpanType = std::conditional_t<kMutability == ChunkMutability::kConst,
65 ByteSpan>;
66 using ByteType = typename SpanType::element_type;
67 using Deque = std::conditional_t<kMutability == ChunkMutability::kConst,
70
71 public:
72 using size_type = SizeType;
73 using difference_type = std::ptrdiff_t;
74 using value_type = SpanType;
75 using pointer = value_type*;
76 using const_pointer = const value_type*;
77 using reference = value_type&;
78 using const_reference = const value_type&;
79 using iterator_category = std::bidirectional_iterator_tag;
80
81 constexpr ChunkIterator() = default;
82 ~ChunkIterator() = default;
83 constexpr ChunkIterator(const ChunkIterator& other) { *this = other; }
84 constexpr ChunkIterator& operator=(const ChunkIterator& other);
85 constexpr ChunkIterator(ChunkIterator&& other) = default;
86 constexpr ChunkIterator& operator=(ChunkIterator&& other) = default;
87
88 // Support converting non-const iterators to const_iterators.
89 constexpr
91 const {
92 return {deque_, chunk_, entries_per_chunk_};
93 }
94
95 constexpr reference operator*() {
96 PW_ASSERT(is_valid());
97 return current_;
98 }
99
100 constexpr const_reference operator*() const {
101 PW_ASSERT(is_valid());
102 return current_;
103 }
104
105 constexpr pointer operator->() {
106 PW_ASSERT(is_valid());
107 return &current_;
108 }
109
110 constexpr const_pointer operator->() const {
111 PW_ASSERT(is_valid());
112 return &current_;
113 }
114
115 constexpr ChunkIterator& operator++();
116
117 constexpr ChunkIterator operator++(int) {
118 ChunkIterator previous(*this);
119 operator++();
120 return previous;
121 }
122
123 constexpr ChunkIterator& operator--();
124
125 constexpr ChunkIterator operator--(int) {
126 ChunkIterator previous(*this);
127 operator--();
128 return previous;
129 }
130
131 constexpr friend bool operator==(const ChunkIterator& lhs,
132 const ChunkIterator& rhs) {
133 return lhs.deque_ == rhs.deque_ &&
134 lhs.entries_per_chunk_ == rhs.entries_per_chunk_ &&
135 lhs.chunk_ == rhs.chunk_;
136 }
137
138 constexpr friend bool operator!=(const ChunkIterator& lhs,
139 const ChunkIterator& rhs) {
140 return !(lhs == rhs);
141 }
142
143 private:
144 // Iterators that point to something are created `Chunks` or `ConstChunks`.
145 template <typename, ChunkContiguity, ChunkMutability>
146 friend class ChunksImpl;
147
148 // Allow internal conversions between iterator subtypes
149 template <typename, ChunkContiguity, ChunkMutability>
150 friend class ChunkIterator;
151
152 // Byte iterators use chunk iterators to get contiguous spans.
153 template <typename, ChunkMutability>
154 friend class ByteIterator;
155
156 friend MultiBufV1Adapter;
157
158 constexpr ChunkIterator(Deque* deque,
159 size_type chunk,
160 size_type entries_per_chunk)
161 : deque_(deque), chunk_(chunk), entries_per_chunk_(entries_per_chunk) {
162 ResetCurrent();
163 }
164
165 [[nodiscard]] constexpr bool is_valid() const {
166 return deque_ != nullptr && chunk_ < num_chunks();
167 }
168
169 constexpr size_type num_chunks() const {
170 return deque_ == nullptr ? 0 : deque_->size() / entries_per_chunk_;
171 }
172
173 constexpr ByteType* data(size_type chunk) const {
174 size_type data_index = Entry::data_index(chunk, entries_per_chunk_);
175 size_type view_index = Entry::top_view_index(chunk, entries_per_chunk_);
176 size_type offset = entries_per_chunk_ == Entry::kMinEntriesPerChunk
177 ? (*deque_)[view_index].base_view.offset
178 : (*deque_)[view_index].view.offset;
179 return (*deque_)[data_index].data + offset;
180 }
181
182 constexpr size_t size(size_type chunk) const {
183 size_type view_index = Entry::top_view_index(chunk, entries_per_chunk_);
184 return entries_per_chunk_ == Entry::kMinEntriesPerChunk
185 ? (*deque_)[view_index].base_view.length
186 : (*deque_)[view_index].view.length;
187 }
188
189 constexpr void ResetCurrent();
190
191 Deque* deque_ = nullptr;
192 size_type chunk_ = 0;
193 size_type entries_per_chunk_ = Entry::kMinEntriesPerChunk;
194 SpanType current_;
195};
196
197// Template method implementations.
198
199template <typename SizeType,
200 ChunkContiguity kContiguity,
201 ChunkMutability kMutability>
204 const ChunkIterator& other) {
205 deque_ = other.deque_;
206 chunk_ = other.chunk_;
207 entries_per_chunk_ = other.entries_per_chunk_;
208 ResetCurrent();
209 return *this;
210}
211
212template <typename SizeType,
213 ChunkContiguity kContiguity,
214 ChunkMutability kMutability>
215constexpr ChunkIterator<SizeType, kContiguity, kMutability>&
216ChunkIterator<SizeType, kContiguity, kMutability>::operator++() {
217 PW_ASSERT(is_valid());
218
219 if constexpr (kContiguity == ChunkContiguity::kKeepAll) {
220 ++chunk_;
221 ResetCurrent();
222 return *this;
223 }
224
225 size_t left = current_.size();
226 while (left != 0) {
227 left -= size(chunk_);
228 ++chunk_;
229 }
230 while (chunk_ < num_chunks() && size(chunk_) == 0) {
231 ++chunk_;
232 }
233 ResetCurrent();
234 return *this;
235}
236
237template <typename SizeType,
238 ChunkContiguity kContiguity,
239 ChunkMutability kMutability>
240constexpr ChunkIterator<SizeType, kContiguity, kMutability>&
241ChunkIterator<SizeType, kContiguity, kMutability>::operator--() {
242 PW_ASSERT(deque_ != nullptr);
243 PW_ASSERT(chunk_ != 0);
244
245 if constexpr (kContiguity == ChunkContiguity::kKeepAll) {
246 --chunk_;
247 current_ = SpanType(data(chunk_), size(chunk_));
248 return *this;
249 }
250
251 current_ = SpanType();
252 while (chunk_ != 0) {
253 SpanType prev(data(chunk_ - 1), size(chunk_ - 1));
254 if (!current_.empty() && prev.data() + prev.size() != current_.data()) {
255 break;
256 }
257 current_ = SpanType(prev.data(), prev.size() + current_.size());
258 --chunk_;
259 }
260 return *this;
261}
262
263template <typename SizeType,
264 ChunkContiguity kContiguity,
265 ChunkMutability kMutability>
266constexpr void
267ChunkIterator<SizeType, kContiguity, kMutability>::ResetCurrent() {
268 if (!is_valid()) {
269 current_ = SpanType();
270 chunk_ = num_chunks();
271 return;
272 }
273
274 current_ = SpanType(data(chunk_), size(chunk_));
275 if constexpr (kContiguity == ChunkContiguity::kKeepAll) {
276 return;
277 }
278
279 for (size_type i = chunk_ + 1; i < num_chunks(); ++i) {
280 size_t next_size = size(i);
281 if (next_size == 0) {
282 continue;
283 }
284 auto* next_data = data(i);
285 if (current_.empty()) {
286 current_ = SpanType(next_data, next_size);
287 chunk_ = i;
288 continue;
289 }
290 if (current_.data() + current_.size() != next_data) {
291 break;
292 }
293 current_ = SpanType(current_.data(), current_.size() + next_size);
294 }
295 if (current_.empty()) {
296 chunk_ = num_chunks();
297 }
298}
299
300} // namespace internal
301} // namespace pw::multibuf
Definition: dynamic_deque.h:60
Definition: chunk_iterator.h:30
Definition: byte_iterator.h:43
Definition: chunk_iterator.h:61
Base class for ranges of chunks.
Definition: chunks.h:33
static constexpr size_type kMinEntriesPerChunk
Minimum number of entries per chunk.
Definition: entry.h:55
static constexpr size_type data_index(size_type chunk, size_type entries_per_chunk)
Returns the index to the data entry of a given chunk.
Definition: entry.h:64
static constexpr size_type top_view_index(size_type chunk, size_type entries_per_chunk)
Returns the index to the top view entry of a given chunk.
Definition: entry.h:83