1 // Copyright 2023 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 #ifdef __cplusplus
17
18 #include <cstddef>
19 #include <cstdint>
20
21 #else
22
23 #include <stdbool.h>
24 #include <stddef.h>
25 #include <stdint.h>
26
27 #endif // __cplusplus
28
29 #include "pw_preprocessor/util.h"
30 #include "pw_varint/varint.h"
31
32 /// @file pw_containers/inline_var_len_entry_queue.h
33 ///
34 /// A `InlineVarLenEntryQueue` is a queue of inline variable-length binary
35 /// entries. It is implemented as a ring (circular) buffer and supports
36 /// operations to append entries and overwrite if necessary. Entries may be zero
37 /// bytes up to the maximum size supported by the queue.
38 ///
39 /// The `InlineVarLenEntryQueue` has a few interesting properties.
40 ///
41 /// - Data and metadata are stored inline in a contiguous block of
42 /// `uint32_t`-aligned memory.
43 /// - The data structure is trivially copyable.
44 /// - All state changes are accomplished with a single update to a `uint32_t`.
45 /// The memory is always in a valid state and may be parsed offline.
46 ///
47 /// This data structure is a much simpler version of
48 /// @cpp_class{pw::ring_buffer::PrefixedEntryRingBuffer}. Prefer this
49 /// sized-entry ring buffer to `PrefixedEntryRingBuffer` when:
50 /// - A simple ring buffer of variable-length entries is needed. Advanced
51 /// features like multiple readers and a user-defined preamble are not
52 /// required.
53 /// - A consistent, parsable, in-memory representation is required (e.g. to
54 /// decode the buffer from a block of memory).
55 /// - C support is required.
56 ///
57 /// `InlineVarLenEntryQueue` is implemented in C and provides complete C and C++
58 /// APIs. The `InlineVarLenEntryQueue` C++ class is structured similarly to
59 /// `pw::InlineQueue` and `pw::Vector`.
60
61 #ifdef __cplusplus
62 extern "C" {
63 #endif // __cplusplus
64
65 /// @defgroup inline_var_len_entry_queue_c_api InlineVarLenEntryQueue C API
66 /// @{
67
68 /// Handle that refers to a `InlineVarLenEntryQueue`. In memory, the queue is a
69 /// `uint32_t` array.
70 typedef uint32_t* pw_InlineVarLenEntryQueue_Handle;
71 typedef const uint32_t* pw_InlineVarLenEntryQueue_ConstHandle;
72
73 /// Declares and initializes a `InlineVarLenEntryQueue` that can hold up to
74 /// `max_size_bytes` bytes. `max_size_bytes` is the largest supported size for a
75 /// single entry; attempting to store larger entries is invalid and will fail an
76 /// assertion.
77 ///
78 /// @param variable variable name for the queue
79 /// @param max_size_bytes the capacity of the queue
80 #define PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(variable, max_size_bytes) \
81 uint32_t variable[PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32 + \
82 _PW_VAR_QUEUE_DATA_SIZE_UINT32(max_size_bytes)] = { \
83 _PW_VAR_QUEUE_DATA_SIZE_BYTES(max_size_bytes), /*head=*/0u, /*tail=*/0u}
84
85 /// The size of the `InlineVarLenEntryQueue` header, in `uint32_t` elements.
86 /// This header stores the buffer length and head and tail offsets.
87 ///
88 /// The underlying `uint32_t` array of a `InlineVarLenEntryQueue` must be larger
89 /// than this size.
90 #define PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32 (3)
91
92 /// Initializes a `InlineVarLenEntryQueue` in place in a `uint32_t` array. The
93 /// array MUST be larger than
94 /// @c_macro{PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32} (3) elements.
95 static inline void pw_InlineVarLenEntryQueue_Init(uint32_t array[],
96 size_t array_size_uint32);
97
98 /// Empties the queue.
99 static inline void pw_InlineVarLenEntryQueue_Clear(
100 pw_InlineVarLenEntryQueue_Handle queue);
101
102 /// Appends an entry to the end of the queue.
103 ///
104 /// @pre The entry MUST NOT be larger than `max_size_bytes()`.
105 /// @pre There must be sufficient space in the queue for this entry.
106 void pw_InlineVarLenEntryQueue_Push(pw_InlineVarLenEntryQueue_Handle queue,
107 const void* data,
108 uint32_t data_size_bytes);
109
110 /// Appends an entry to the end of the queue, but only if there is sufficient
111 /// space for it.
112 ///
113 /// @returns true if the data was added to the queue; false if it did not fit
114 /// @pre The entry MUST NOT be larger than `max_size_bytes()`.
115 bool pw_InlineVarLenEntryQueue_TryPush(pw_InlineVarLenEntryQueue_Handle queue,
116 const void* data,
117 const uint32_t data_size_bytes);
118
119 /// Appends an entry to the end of the queue, removing entries with `Pop`
120 /// as necessary to make room. Calling this function drops old entries to make
121 /// room for new; call `try_push()` to drop new entries instead.
122 ///
123 /// @pre The entry MUST NOT be larger than `max_size_bytes()`.
124 void pw_InlineVarLenEntryQueue_PushOverwrite(
125 pw_InlineVarLenEntryQueue_Handle queue,
126 const void* data,
127 uint32_t data_size_bytes);
128
129 /// Removes the first entry from queue.
130 ///
131 /// @pre The queue MUST have at least one entry.
132 void pw_InlineVarLenEntryQueue_Pop(pw_InlineVarLenEntryQueue_Handle queue);
133
134 /// Iterator object for a `InlineVarLenEntryQueue`. Iterators are checked for
135 /// equality with @cpp_func{pw_InlineVarLenEntryQueue_Iterator_Equal}.
136 ///
137 /// Iterators are invalidated by any operations that change the container or
138 /// its underlying data (push/pop/init).
139 typedef struct {
140 // Private: do not access these fields directly!
141 pw_InlineVarLenEntryQueue_ConstHandle _pw_queue;
142 uint32_t _pw_offset;
143 } pw_InlineVarLenEntryQueue_Iterator;
144
145 /// An entry in the queue. Entries may be stored in up to two segments, so this
146 /// struct includes pointers to both portions of the entry.
147 typedef struct {
148 const uint8_t* data_1;
149 uint32_t size_1;
150 const uint8_t* data_2;
151 uint32_t size_2;
152 } pw_InlineVarLenEntryQueue_Entry;
153
154 /// Returns an iterator to the start of the `InlineVarLenEntryQueue`.
155 static inline pw_InlineVarLenEntryQueue_Iterator
156 pw_InlineVarLenEntryQueue_Begin(pw_InlineVarLenEntryQueue_ConstHandle queue);
157
158 /// Returns an iterator that points past the end of the queue.
159 static inline pw_InlineVarLenEntryQueue_Iterator pw_InlineVarLenEntryQueue_End(
160 pw_InlineVarLenEntryQueue_ConstHandle queue);
161
162 /// Advances an iterator to point to the next entry in the queue. It is
163 /// invalid to call `Advance` on an iterator equal to the `End` iterator.
164 void pw_InlineVarLenEntryQueue_Iterator_Advance(
165 pw_InlineVarLenEntryQueue_Iterator* iterator);
166
167 /// Compares two iterators for equality.
168 static inline bool pw_InlineVarLenEntryQueue_Iterator_Equal(
169 const pw_InlineVarLenEntryQueue_Iterator* lhs,
170 const pw_InlineVarLenEntryQueue_Iterator* rhs);
171
172 /// Dereferences an iterator, loading the entry it points to.
173 pw_InlineVarLenEntryQueue_Entry pw_InlineVarLenEntryQueue_GetEntry(
174 const pw_InlineVarLenEntryQueue_Iterator* iterator);
175
176 /// Copies the contents of the entry to the provided buffer. The entry may be
177 /// split into two regions; this serializes it into one buffer.
178 ///
179 /// @param entry The entry whose contents to copy
180 /// @param dest The buffer into which to copy the serialized entry
181 /// @param count Copy up to this many bytes; must not be larger than the `dest`
182 /// buffer, but may be larger than the entry
183 uint32_t pw_InlineVarLenEntryQueue_Entry_Copy(
184 const pw_InlineVarLenEntryQueue_Entry* entry, void* dest, uint32_t count);
185
186 /// Returns the byte at the specified index in the entry. Asserts if index is
187 /// out-of-bounds.
188 static inline uint8_t pw_InlineVarLenEntryQueue_Entry_At(
189 const pw_InlineVarLenEntryQueue_Entry* entry, size_t index);
190
191 /// Returns the number of variable-length entries in the queue. This is O(n) in
192 /// the number of entries in the queue.
193 uint32_t pw_InlineVarLenEntryQueue_Size(
194 pw_InlineVarLenEntryQueue_ConstHandle queue);
195
196 /// Returns the maximum number of entries in the queue. This is only attainable
197 /// if all entries are empty.
198 static inline uint32_t pw_InlineVarLenEntryQueue_MaxSize(
199 pw_InlineVarLenEntryQueue_ConstHandle queue);
200
201 /// Returns the combined size in bytes of all entries in the queue, excluding
202 /// metadata. This is O(n) in the number of entries in the queue.
203 uint32_t pw_InlineVarLenEntryQueue_SizeBytes(
204 pw_InlineVarLenEntryQueue_ConstHandle queue);
205
206 /// Returns the the maximum number of bytes that can be stored in the queue.
207 /// This is largest possible value of `size_bytes()`, and the size of the
208 /// largest single entry that can be stored in this queue. Attempting to store a
209 /// larger entry is invalid and results in a crash.
210 static inline uint32_t pw_InlineVarLenEntryQueue_MaxSizeBytes(
211 pw_InlineVarLenEntryQueue_ConstHandle queue);
212
213 /// Returns the size of the raw underlying `InlineVarLenEntryQueue` storage.
214 /// This size may be used to copy a `InlineVarLenEntryQueue` into another
215 /// 32-bit aligned memory location.
216 static inline uint32_t pw_InlineVarLenEntryQueue_RawStorageSizeBytes(
217 pw_InlineVarLenEntryQueue_ConstHandle queue);
218
219 /// Returns true if the `InlineVarLenEntryQueue` is empty, false if it has at
220 /// least one entry.
221 static inline bool pw_InlineVarLenEntryQueue_Empty(
222 pw_InlineVarLenEntryQueue_ConstHandle queue);
223
224 /// @}
225
226 // Implementation details.
227
228 #define _PW_VAR_QUEUE_DATA_SIZE_UINT32(max_size_bytes) \
229 ((_PW_VAR_QUEUE_DATA_SIZE_BYTES(max_size_bytes) + 3 /* round up */) / 4)
230
231 #define _PW_VAR_QUEUE_DATA_SIZE_BYTES(max_size_bytes) \
232 (PW_VARINT_ENCODED_SIZE_BYTES(max_size_bytes) + max_size_bytes + \
233 1 /*end byte*/)
234
235 #define _PW_VAR_QUEUE_ARRAY_SIZE_BYTES queue[0]
236 #define _PW_VAR_QUEUE_HEAD queue[1]
237 #define _PW_VAR_QUEUE_TAIL queue[2] // points after the last byte
238 #define _PW_VAR_QUEUE_DATA ((const uint8_t*)&queue[3])
239
pw_InlineVarLenEntryQueue_Init(uint32_t array[],size_t array_size_uint32)240 static inline void pw_InlineVarLenEntryQueue_Init(uint32_t array[],
241 size_t array_size_uint32) {
242 array[0] = (uint32_t)(array_size_uint32 -
243 PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32) *
244 sizeof(uint32_t);
245 array[1] = 0; // head
246 array[2] = 0; // tail
247 }
248
pw_InlineVarLenEntryQueue_Clear(pw_InlineVarLenEntryQueue_Handle queue)249 static inline void pw_InlineVarLenEntryQueue_Clear(
250 pw_InlineVarLenEntryQueue_Handle queue) {
251 _PW_VAR_QUEUE_HEAD = 0; // head
252 _PW_VAR_QUEUE_TAIL = 0; // tail
253 }
254
255 static inline pw_InlineVarLenEntryQueue_Iterator
pw_InlineVarLenEntryQueue_Begin(pw_InlineVarLenEntryQueue_ConstHandle queue)256 pw_InlineVarLenEntryQueue_Begin(pw_InlineVarLenEntryQueue_ConstHandle queue) {
257 pw_InlineVarLenEntryQueue_Iterator begin = {queue, _PW_VAR_QUEUE_HEAD};
258 return begin;
259 }
260
pw_InlineVarLenEntryQueue_End(pw_InlineVarLenEntryQueue_ConstHandle queue)261 static inline pw_InlineVarLenEntryQueue_Iterator pw_InlineVarLenEntryQueue_End(
262 pw_InlineVarLenEntryQueue_ConstHandle queue) {
263 pw_InlineVarLenEntryQueue_Iterator end = {queue, _PW_VAR_QUEUE_TAIL};
264 return end;
265 }
266
pw_InlineVarLenEntryQueue_Iterator_Equal(const pw_InlineVarLenEntryQueue_Iterator * lhs,const pw_InlineVarLenEntryQueue_Iterator * rhs)267 static inline bool pw_InlineVarLenEntryQueue_Iterator_Equal(
268 const pw_InlineVarLenEntryQueue_Iterator* lhs,
269 const pw_InlineVarLenEntryQueue_Iterator* rhs) {
270 return lhs->_pw_offset == rhs->_pw_offset && lhs->_pw_queue == rhs->_pw_queue;
271 }
272
273 // Private function that returns a pointer to the specified index in the Entry.
_pw_InlineVarLenEntryQueue_Entry_GetPointer(const pw_InlineVarLenEntryQueue_Entry * entry,size_t index)274 static inline const uint8_t* _pw_InlineVarLenEntryQueue_Entry_GetPointer(
275 const pw_InlineVarLenEntryQueue_Entry* entry, size_t index) {
276 if (index < entry->size_1) {
277 return &entry->data_1[index];
278 }
279 return &entry->data_2[index - entry->size_1];
280 }
281
282 const uint8_t* _pw_InlineVarLenEntryQueue_Entry_GetPointerChecked(
283 const pw_InlineVarLenEntryQueue_Entry* entry, size_t index);
284
pw_InlineVarLenEntryQueue_Entry_At(const pw_InlineVarLenEntryQueue_Entry * entry,size_t index)285 static inline uint8_t pw_InlineVarLenEntryQueue_Entry_At(
286 const pw_InlineVarLenEntryQueue_Entry* entry, size_t index) {
287 return *_pw_InlineVarLenEntryQueue_Entry_GetPointerChecked(entry, index);
288 }
289
pw_InlineVarLenEntryQueue_RawStorageSizeBytes(pw_InlineVarLenEntryQueue_ConstHandle queue)290 static inline uint32_t pw_InlineVarLenEntryQueue_RawStorageSizeBytes(
291 pw_InlineVarLenEntryQueue_ConstHandle queue) {
292 return PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32 * sizeof(uint32_t) +
293 _PW_VAR_QUEUE_ARRAY_SIZE_BYTES;
294 }
295
pw_InlineVarLenEntryQueue_MaxSize(pw_InlineVarLenEntryQueue_ConstHandle queue)296 static inline uint32_t pw_InlineVarLenEntryQueue_MaxSize(
297 pw_InlineVarLenEntryQueue_ConstHandle queue) {
298 return _PW_VAR_QUEUE_ARRAY_SIZE_BYTES - 1;
299 }
300
pw_InlineVarLenEntryQueue_MaxSizeBytes(pw_InlineVarLenEntryQueue_ConstHandle queue)301 static inline uint32_t pw_InlineVarLenEntryQueue_MaxSizeBytes(
302 pw_InlineVarLenEntryQueue_ConstHandle queue) {
303 return _PW_VAR_QUEUE_ARRAY_SIZE_BYTES - 1 -
304 (uint32_t)pw_varint_EncodedSizeBytes(_PW_VAR_QUEUE_ARRAY_SIZE_BYTES -
305 1);
306 }
307
pw_InlineVarLenEntryQueue_Empty(pw_InlineVarLenEntryQueue_ConstHandle queue)308 static inline bool pw_InlineVarLenEntryQueue_Empty(
309 pw_InlineVarLenEntryQueue_ConstHandle queue) {
310 return _PW_VAR_QUEUE_HEAD == _PW_VAR_QUEUE_TAIL;
311 }
312
313 // These macros are not part of the public API, so undefine them.
314 #undef _PW_VAR_QUEUE_ARRAY_SIZE_BYTES
315 #undef _PW_VAR_QUEUE_HEAD
316 #undef _PW_VAR_QUEUE_TAIL
317 #undef _PW_VAR_QUEUE_DATA
318
319 #ifdef __cplusplus
320 } // extern "C"
321
322 #include <cstddef>
323 #include <limits>
324 #include <type_traits>
325 #include <utility>
326
327 #include "pw_containers/internal/raw_storage.h"
328 #include "pw_span/span.h"
329
330 namespace pw {
331
332 // A`BasicInlineVarLenEntryQueue` with a known maximum size of a single entry.
333 // The member functions are immplemented in the generic-capacity base.
334 // TODO: b/303056683 - Add helper for calculating kMaxSizeBytes for N entries of
335 // a particular size.
336 template <typename T,
337 size_t kMaxSizeBytes = containers::internal::kGenericSized>
338 class BasicInlineVarLenEntryQueue
339 : public BasicInlineVarLenEntryQueue<T,
340 containers::internal::kGenericSized> {
341 private:
342 using Base =
343 BasicInlineVarLenEntryQueue<T, containers::internal::kGenericSized>;
344
345 public:
BasicInlineVarLenEntryQueue()346 constexpr BasicInlineVarLenEntryQueue() : Base(kMaxSizeBytes) {}
347
348 // `BasicInlineVarLenEntryQueue` is trivially copyable.
349 BasicInlineVarLenEntryQueue(const BasicInlineVarLenEntryQueue&) = default;
350 BasicInlineVarLenEntryQueue& operator=(const BasicInlineVarLenEntryQueue&) =
351 default;
352
353 private:
354 static_assert(kMaxSizeBytes <=
355 std::numeric_limits<typename Base::size_type>::max());
356
357 using Base::Init; // Disallow Init since the size template param is not used.
358
359 uint32_t data_[_PW_VAR_QUEUE_DATA_SIZE_UINT32(kMaxSizeBytes)];
360 };
361
362 /// @defgroup inline_var_len_entry_queue_cpp_api
363 /// @{
364
365 /// Variable-length entry queue class template for any byte type (e.g.
366 /// ``std::byte`` or ``uint8_t``).
367 ///
368 /// ``BasicInlineVarLenEntryQueue`` instances are declared with their capacity
369 /// / max single entry size (``BasicInlineVarLenEntryQueue<char, 64>``), but
370 /// may be referred to without the size
371 /// (``BasicInlineVarLenEntryQueue<char>&``).
372 template <typename T>
373 class BasicInlineVarLenEntryQueue<T, containers::internal::kGenericSized> {
374 public:
375 class Entry;
376
377 using value_type = Entry;
378 using size_type = std::uint32_t;
379 using pointer = const value_type*;
380 using const_pointer = pointer;
381 using reference = const value_type&;
382 using const_reference = reference;
383
384 // Refers to an entry in-place in the queue. Entries may not be contiguous.
385 class iterator;
386
387 // Currently, iterators provide read-only access.
388 // TODO: b/303046109 - Provide a non-const iterator.
389 using const_iterator = iterator;
390
391 /// @copydoc pw_InlineVarLenEntryQueue_Init
392 template <size_t kArraySize>
Init(uint32_t (& array)[kArraySize])393 static BasicInlineVarLenEntryQueue& Init(uint32_t (&array)[kArraySize]) {
394 static_assert(
395 kArraySize > PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32,
396 "InlineVarLenEntryQueue must be backed by an array with more than "
397 "PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32 (3) elements");
398 return Init(array, kArraySize);
399 }
400
401 /// @copydoc pw_InlineVarLenEntryQueue_Init
Init(uint32_t array[],size_t array_size_uint32)402 static BasicInlineVarLenEntryQueue& Init(uint32_t array[],
403 size_t array_size_uint32) {
404 pw_InlineVarLenEntryQueue_Init(array, array_size_uint32);
405 return *std::launder(reinterpret_cast<BasicInlineVarLenEntryQueue*>(array));
406 }
407
408 /// Returns the first entry in the queue.
409 /// @pre The queue must NOT empty (`empty()` is false).
front()410 Entry front() const { return *begin(); }
411
412 /// @copydoc pw_InlineVarLenEntryQueue_Begin
begin()413 const_iterator begin() const {
414 return const_iterator(pw_InlineVarLenEntryQueue_Begin(array_));
415 }
cbegin()416 const_iterator cbegin() const { return begin(); }
417
418 /// @copydoc pw_InlineVarLenEntryQueue_End
end()419 const_iterator end() const {
420 return const_iterator(pw_InlineVarLenEntryQueue_End(array_));
421 }
cend()422 const_iterator cend() const { return end(); }
423
424 /// @copydoc pw_InlineVarLenEntryQueue_Empty
empty()425 [[nodiscard]] bool empty() const {
426 return pw_InlineVarLenEntryQueue_Empty(array_);
427 }
428
429 /// @copydoc pw_InlineVarLenEntryQueue_Size
size()430 size_type size() const { return pw_InlineVarLenEntryQueue_Size(array_); }
431
432 /// @copydoc pw_InlineVarLenEntryQueue_MaxSize
max_size()433 size_type max_size() const {
434 return pw_InlineVarLenEntryQueue_MaxSize(array_);
435 }
436
437 /// @copydoc pw_InlineVarLenEntryQueue_SizeBytes
size_bytes()438 size_type size_bytes() const {
439 return pw_InlineVarLenEntryQueue_SizeBytes(array_);
440 }
441
442 /// @copydoc pw_InlineVarLenEntryQueue_MaxSizeBytes
max_size_bytes()443 size_type max_size_bytes() const {
444 return pw_InlineVarLenEntryQueue_MaxSizeBytes(array_);
445 }
446
447 /// Underlying storage of the variable-length entry queue. May be used to
448 /// memcpy the queue.
raw_storage()449 span<const T> raw_storage() const {
450 return span<const T>(reinterpret_cast<const T*>(array_),
451 pw_InlineVarLenEntryQueue_RawStorageSizeBytes(array_));
452 }
453
454 /// @copydoc pw_InlineVarLenEntryQueue_Clear
clear()455 void clear() { pw_InlineVarLenEntryQueue_Clear(array_); }
456
457 /// @copydoc pw_InlineVarLenEntryQueue_Push
push(span<const T> value)458 void push(span<const T> value) {
459 pw_InlineVarLenEntryQueue_Push(
460 array_, value.data(), static_cast<size_type>(value.size()));
461 }
462
463 /// @copydoc pw_InlineVarLenEntryQueue_TryPush
try_push(span<const T> value)464 [[nodiscard]] bool try_push(span<const T> value) {
465 return pw_InlineVarLenEntryQueue_TryPush(
466 array_, value.data(), static_cast<size_type>(value.size()));
467 }
468
469 /// @copydoc pw_InlineVarLenEntryQueue_PushOverwrite
push_overwrite(span<const T> value)470 void push_overwrite(span<const T> value) {
471 pw_InlineVarLenEntryQueue_PushOverwrite(
472 array_, value.data(), static_cast<size_type>(value.size()));
473 }
474
475 /// @copydoc pw_InlineVarLenEntryQueue_Pop
pop()476 void pop() { pw_InlineVarLenEntryQueue_Pop(array_); }
477
478 protected:
BasicInlineVarLenEntryQueue(uint32_t max_size_bytes)479 constexpr BasicInlineVarLenEntryQueue(uint32_t max_size_bytes)
480 : array_{_PW_VAR_QUEUE_DATA_SIZE_BYTES(max_size_bytes), 0, 0} {}
481
482 // Polymorphic-sized queues cannot be destroyed directly due to the lack of a
483 // virtual destructor.
484 ~BasicInlineVarLenEntryQueue() = default;
485
486 BasicInlineVarLenEntryQueue(const BasicInlineVarLenEntryQueue&) = default;
487 BasicInlineVarLenEntryQueue& operator=(const BasicInlineVarLenEntryQueue&) =
488 default;
489
490 private:
491 static_assert(std::is_integral_v<T> || std::is_same_v<T, std::byte>);
492 static_assert(sizeof(T) == sizeof(std::byte));
493
494 uint32_t array_[PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32];
495 };
496
497 /// Refers to an entry in-place in the queue. Entries may be discontiguous.
498 template <typename T>
499 class BasicInlineVarLenEntryQueue<T>::Entry {
500 public:
501 using value_type = T;
502 using size_type = std::uint32_t;
503 using pointer = const T*;
504 using const_pointer = pointer;
505 using reference = const T&;
506 using const_reference = reference;
507
508 /// Iterator for the bytes in an Entry. Entries may be discontiguous, so a
509 /// pointer cannot serve as an iterator.
510 class iterator {
511 public:
512 using difference_type = std::ptrdiff_t;
513 using value_type = T;
514 using pointer = const T*;
515 using reference = const T&;
516 using iterator_category = std::forward_iterator_tag;
517
iterator()518 constexpr iterator() : entry_(nullptr), index_(0) {}
519
520 constexpr iterator(const iterator&) = default;
521 constexpr iterator& operator=(const iterator&) = default;
522
523 constexpr iterator& operator++() {
524 index_ += 1;
525 return *this;
526 }
527 constexpr iterator operator++(int) {
528 iterator previous_value(*this);
529 operator++();
530 return previous_value;
531 }
532
533 reference operator*() const { return *GetIndex(*entry_, index_); }
534 pointer operator->() const { return GetIndex(*entry_, index_); }
535
536 bool operator==(const iterator& rhs) const {
537 return entry_->data_1 == rhs.entry_->data_1 && index_ == rhs.index_;
538 }
539 bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
540
541 private:
542 friend class Entry;
543
iterator(const pw_InlineVarLenEntryQueue_Entry & entry,size_t index)544 constexpr iterator(const pw_InlineVarLenEntryQueue_Entry& entry,
545 size_t index)
546 : entry_(&entry), index_(index) {}
547
548 const pw_InlineVarLenEntryQueue_Entry* entry_;
549 size_t index_;
550 };
551
552 // TODO: b/303046109 - Provide mutable access to Entry contents.
553 using const_iterator = iterator;
554
555 constexpr Entry(const Entry&) = default;
556 constexpr Entry& operator=(const Entry&) = default;
557
at(size_t index)558 const_reference at(size_t index) const {
559 return *reinterpret_cast<const T*>(
560 _pw_InlineVarLenEntryQueue_Entry_GetPointerChecked(&entry_, index));
561 }
562
563 const_reference operator[](size_t index) const {
564 return *GetIndex(entry_, index);
565 }
566
front()567 const_reference front() const { return *entry_.data_1; }
back()568 const_reference back() const { *GetIndex(entry_, size() - 1); }
569
570 /// Entries may be stored in up to two segments, so this returns spans
571 /// refering to both portions of the entry. If the entry is contiguous, the
572 /// second span is empty.
contiguous_data()573 std::pair<span<const value_type>, span<const value_type>> contiguous_data()
574 const {
575 return std::make_pair(
576 span(reinterpret_cast<const_pointer>(entry_.data_1), entry_.size_1),
577 span(reinterpret_cast<const_pointer>(entry_.data_2), entry_.size_2));
578 }
579
580 /// @copydoc pw_InlineVarLenEntryQueue_Entry_Copy
581 ///
582 /// Copying with `copy()` is likely more efficient than an iterator-based copy
583 /// with `std::copy()`, since `copy()` uses one or two `memcpy` calls instead
584 /// of copying byte-by-byte.
copy(T * dest,size_type count)585 size_type copy(T* dest, size_type count) const {
586 return pw_InlineVarLenEntryQueue_Entry_Copy(&entry_, dest, count);
587 }
588
begin()589 const_iterator begin() const { return const_iterator(entry_, 0); }
cbegin()590 const_iterator cbegin() const { return begin(); }
591
end()592 const_iterator end() const { return const_iterator(entry_, size()); }
cend()593 const_iterator cend() const { return cend(); }
594
empty()595 [[nodiscard]] bool empty() const { return size() == 0; }
596
size()597 size_type size() const { return entry_.size_1 + entry_.size_2; }
598
599 private:
600 friend class BasicInlineVarLenEntryQueue;
601
GetIndex(const pw_InlineVarLenEntryQueue_Entry & entry,size_t index)602 static const T* GetIndex(const pw_InlineVarLenEntryQueue_Entry& entry,
603 size_t index) {
604 return reinterpret_cast<const T*>(
605 _pw_InlineVarLenEntryQueue_Entry_GetPointer(&entry, index));
606 }
607
Entry(const pw_InlineVarLenEntryQueue_Entry & entry)608 explicit constexpr Entry(const pw_InlineVarLenEntryQueue_Entry& entry)
609 : entry_(entry) {}
610
Entry()611 constexpr Entry() : entry_{} {}
612
613 pw_InlineVarLenEntryQueue_Entry entry_;
614 };
615
616 /// Iterator object for a `InlineVarLenEntryQueue`.
617 ///
618 /// Iterators are invalidated by any operations that change the container or
619 /// its underlying data (push/pop/init).
620 template <typename T>
621 class BasicInlineVarLenEntryQueue<T>::iterator {
622 public:
623 using difference_type = std::ptrdiff_t;
624 using value_type = Entry;
625 using pointer = const Entry*;
626 using reference = const Entry&;
627 using iterator_category = std::forward_iterator_tag;
628
iterator()629 constexpr iterator() : iterator_{}, entry_{} {}
630
631 constexpr iterator(const iterator&) = default;
632 constexpr iterator& operator=(const iterator&) = default;
633
634 iterator& operator++() {
635 pw_InlineVarLenEntryQueue_Iterator_Advance(&iterator_);
636 entry_.entry_.data_1 = nullptr; // mark the entry as unloaded
637 return *this;
638 }
639 iterator operator++(int) {
640 iterator previous_value(*this);
641 operator++();
642 return previous_value;
643 }
644
645 reference operator*() const {
646 LoadEntry();
647 return entry_;
648 }
649 pointer operator->() const {
650 LoadEntry();
651 return &entry_;
652 }
653
654 bool operator==(const iterator& rhs) const {
655 return pw_InlineVarLenEntryQueue_Iterator_Equal(&iterator_, &rhs.iterator_);
656 }
657 bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
658
659 private:
660 friend class BasicInlineVarLenEntryQueue;
661
iterator(const pw_InlineVarLenEntryQueue_Iterator & it)662 explicit constexpr iterator(const pw_InlineVarLenEntryQueue_Iterator& it)
663 : iterator_(it) {}
664
LoadEntry()665 void LoadEntry() const {
666 if (entry_.entry_.data_1 == nullptr) {
667 entry_.entry_ = pw_InlineVarLenEntryQueue_GetEntry(&iterator_);
668 }
669 }
670
671 pw_InlineVarLenEntryQueue_Iterator iterator_;
672 mutable Entry entry_;
673 };
674
675 /// Variable-length entry queue that uses ``std::byte`` for the byte type.
676 template <size_t kMaxSizeBytes = containers::internal::kGenericSized>
677 using InlineVarLenEntryQueue =
678 BasicInlineVarLenEntryQueue<std::byte, kMaxSizeBytes>;
679
680 /// @}
681
682 } // namespace pw
683
684 #endif // __cplusplus
685