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