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