• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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