xref: /aosp_15_r20/external/pigweed/pw_containers/public/pw_containers/inline_var_len_entry_queue.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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