C/C++ API Reference
Loading...
Searching...
No Matches
generic_var_len_entry_queue.h
1// Copyright 2025 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 <array>
19#include <cstdint>
20#include <cstring>
21
22#include "pw_assert/assert.h"
23#include "pw_bytes/span.h"
24#include "pw_containers/internal/var_len_entry.h"
25#include "pw_containers/internal/var_len_entry_queue_iterator.h"
26#include "pw_containers/internal/wrap.h"
27#include "pw_preprocessor/compiler.h"
28#include "pw_span/cast.h"
29#include "pw_span/span.h"
30#include "pw_varint/varint.h"
31
32#endif // __cplusplus
33
36#define PW_VAR_QUEUE_DATA_SIZE_BYTES(max_size_bytes) \
37 (PW_VARINT_ENCODED_SIZE_BYTES(max_size_bytes) + max_size_bytes + \
38 1 /*end byte*/)
39
40#ifdef __cplusplus
41namespace pw::containers::internal {
42
43// Forward declarations for friending.
44class GenericVarLenEntryQueueBase;
45
46template <typename Derived, typename T>
47class GenericVarLenEntryQueue;
48
49template <typename D1, typename T1, typename D2, typename T2>
50constexpr void CopyVarLenEntriesImpl(const GenericVarLenEntryQueue<D1, T1>& src,
51 GenericVarLenEntryQueue<D2, T2>& dst,
52 bool overwrite);
53
54template <typename D1, typename T1, typename D2, typename T2>
55constexpr void MoveVarLenEntriesImpl(GenericVarLenEntryQueue<D1, T1>& src,
56 GenericVarLenEntryQueue<D2, T2>& dst,
57 bool overwrite);
58
62 protected:
63 struct Info {
64 size_t num_entries;
65 size_t total_data_size;
66 };
67
71 static constexpr Info GetInfo(ConstByteSpan bytes, size_t head, size_t tail);
72
74 static constexpr size_t AvailableBytes(ConstByteSpan bytes,
75 size_t head,
76 size_t tail);
77
79 static constexpr bool Push(ConstByteSpan data,
80 bool overwrite,
81 ByteSpan bytes,
82 size_t& head,
83 size_t& tail);
84
86 static constexpr size_t PopNonEmpty(ConstByteSpan bytes, size_t& head);
87
92 static constexpr void CopyEntries(ConstByteSpan src_bytes,
93 size_t& src_head,
94 size_t src_tail,
95 ByteSpan dst_bytes,
96 size_t& dst_head,
97 size_t& dst_tail,
98 bool overwrite);
99
101 static constexpr void CopyAndWrap(ConstByteSpan data,
102 ByteSpan bytes,
103 size_t& tail);
104
109 static constexpr std::pair<ConstByteSpan, ConstByteSpan> ContiguousRawStorage(
110 ConstByteSpan bytes, size_t head, size_t tail);
111
113 static void Dering(ByteSpan bytes, size_t head) {
114 std::rotate(
115 bytes.begin(),
116 bytes.begin() + static_cast<span<std::byte>::difference_type>(head),
117 bytes.end());
118 }
119
120 private:
121 template <typename D1, typename T1, typename D2, typename T2>
122 friend constexpr void CopyVarLenEntriesImpl(
125 bool overwrite);
126
127 template <typename D1, typename T1, typename D2, typename T2>
128 friend constexpr void MoveVarLenEntriesImpl(
131 bool overwrite);
132};
133
138template <typename Derived, typename T>
140 private:
142
143 public:
148
150 static constexpr size_t DataSizeBytes(size_t max_size_bytes) {
151 return PW_VAR_QUEUE_DATA_SIZE_BYTES(max_size_bytes);
152 }
153
155 constexpr iterator begin() { return iterator(GetBuffer(), GetHead()); }
156 constexpr const_iterator begin() const {
157 return const_iterator(GetBuffer(), GetHead());
158 }
159 constexpr const_iterator cbegin() const { return begin(); }
160
162 constexpr iterator end() { return iterator(GetBuffer(), GetTail()); }
163 constexpr const_iterator end() const {
164 return const_iterator(GetBuffer(), GetTail());
165 }
166 constexpr const_iterator cend() const { return end(); }
167
169 [[nodiscard]] constexpr bool empty() const { return GetHead() == GetTail(); }
170
173 constexpr size_t size() const {
174 return Base::GetInfo(GetBytes(), GetHead(), GetTail()).num_entries;
175 }
176
179 constexpr size_t max_size() const { return GetBuffer().size() - 1; }
180
183 constexpr size_t size_bytes() const {
184 return Base::GetInfo(GetBytes(), GetHead(), GetTail()).total_data_size;
185 }
186
189 constexpr size_t encoded_size_bytes() const {
190 return max_size() - Base::AvailableBytes(GetBytes(), GetHead(), GetTail());
191 }
192
197 constexpr size_t max_size_bytes() const {
199 }
200
203 constexpr value_type front();
204 constexpr const_value_type front() const;
205
210 constexpr void push(span<const T> data) { PW_ASSERT(try_push(data)); }
211
217 [[nodiscard]] constexpr bool try_push(span<const T> data) {
218 return Push(data, /* overwrite: */ false);
219 }
220
226 constexpr void push_overwrite(span<const T> data) {
227 Push(data, /* overwrite: */ true);
228 }
229
233 constexpr void pop();
234
236 constexpr void clear();
237
239 constexpr std::pair<span<const T>, span<const T>> contiguous_raw_storage()
240 const;
241
246
247 protected:
248 constexpr GenericVarLenEntryQueue() = default;
249
250 // The queue metadata and data is part of the derived type, and can be
251 // accessed and modified by downcasting to that type.
252 constexpr size_t GetHead() const { return derived().head(); }
253 constexpr void SetHead(size_t head) { return derived().set_head(head); }
254 constexpr size_t GetTail() const { return derived().tail(); }
255 constexpr void SetTail(size_t tail) { return derived().set_tail(tail); }
256 constexpr span<T> GetBuffer() { return derived().buffer(); }
257 constexpr span<const T> GetBuffer() const { return derived().buffer(); }
258
262 constexpr ByteSpan GetBytes() { return AsWritableBytes(GetBuffer()); }
263 constexpr ConstByteSpan GetBytes() const { return AsBytes(GetBuffer()); }
265
266 private:
267 template <typename D1, typename T1, typename D2, typename T2>
268 friend constexpr void CopyVarLenEntriesImpl(
269 const GenericVarLenEntryQueue<D1, T1>& src,
270 GenericVarLenEntryQueue<D2, T2>& dst,
271 bool overwrite);
272
273 template <typename D1, typename T1, typename D2, typename T2>
274 friend constexpr void MoveVarLenEntriesImpl(
275 GenericVarLenEntryQueue<D1, T1>& src,
276 GenericVarLenEntryQueue<D2, T2>& dst,
277 bool overwrite);
278
279 static_assert(std::is_integral_v<T> || std::is_same_v<T, std::byte>);
280 static_assert(sizeof(T) == sizeof(std::byte));
281
282 constexpr Derived& derived() { return *static_cast<Derived*>(this); }
283 constexpr const Derived& derived() const {
284 return *static_cast<const Derived*>(this);
285 }
286
289 static constexpr ByteSpan AsWritableBytes(span<T> data);
290
293 static constexpr ConstByteSpan AsBytes(span<const T> data);
294
298 constexpr bool Push(span<const T> data, bool overwrite);
299};
300
303template <typename D1, typename T1, typename D2, typename T2>
304constexpr void CopyVarLenEntries(const GenericVarLenEntryQueue<D1, T1>& src,
305 GenericVarLenEntryQueue<D2, T2>& dst) {
306 CopyVarLenEntriesImpl(src, dst, /*overwrite=*/false);
307}
308
313template <typename D1, typename T1, typename D2, typename T2>
314constexpr void CopyVarLenEntriesOverwrite(
315 const GenericVarLenEntryQueue<D1, T1>& src,
316 GenericVarLenEntryQueue<D2, T2>& dst) {
317 CopyVarLenEntriesImpl(src, dst, /*overwrite=*/true);
318}
319
322template <typename D1, typename T1, typename D2, typename T2>
323constexpr void MoveVarLenEntries(GenericVarLenEntryQueue<D1, T1>& src,
324 GenericVarLenEntryQueue<D2, T2>& dst) {
325 MoveVarLenEntriesImpl(src, dst, /*overwrite=*/false);
326}
327
332template <typename D1, typename T1, typename D2, typename T2>
333constexpr void MoveVarLenEntriesOverwrite(
334 GenericVarLenEntryQueue<D1, T1>& src,
335 GenericVarLenEntryQueue<D2, T2>& dst) {
336 MoveVarLenEntriesImpl(src, dst, /*overwrite=*/true);
337}
338
340// Constexpr method implementations.
341
342constexpr GenericVarLenEntryQueueBase::Info
344 size_t head,
345 size_t tail) {
346 Info info = {0, 0};
347 while (head != tail) {
348 auto [prefix_size, data_size] = ReadVarLenEntrySize(bytes, head);
349 size_t entry_size = ReadVarLenEntryEncodedSize(bytes, head);
350 IncrementWithWrap(head, entry_size, bytes.size());
351 ++info.num_entries;
352 info.total_data_size += data_size;
353 }
354 return info;
355}
356
358 ConstByteSpan bytes, size_t head, size_t tail) {
359 PW_ASSERT(head < bytes.size());
360 PW_ASSERT(tail < bytes.size());
361 if (head <= tail) {
362 head += bytes.size();
363 }
364 return head - tail - 1;
365}
366
368 bool overwrite,
369 ByteSpan bytes,
370 size_t& head,
371 size_t& tail) {
372 // Encode the data size in a temporary buffer.
373 std::byte tmp[varint::kMaxVarint32SizeBytes] = {};
374 auto size_u32 = static_cast<uint32_t>(data.size());
375 size_t encoded = varint::Encode(size_u32, ByteSpan(tmp, sizeof(tmp)));
376 ConstByteSpan prefix(tmp, encoded);
377
378 // Calculate how much space is neededvs how much is available.
379 size_t needed_bytes = prefix.size() + data.size();
380 PW_ASSERT(needed_bytes < bytes.size());
381 size_t available_bytes = AvailableBytes(bytes, head, tail);
382
383 // Check if sufficient space is available, or can be made available.
384 if (overwrite) {
385 while (needed_bytes > available_bytes) {
386 available_bytes += PopNonEmpty(bytes, head);
387 }
388 } else {
389 if (needed_bytes > available_bytes) {
390 return false;
391 }
392 }
393
394 // Write out the prefix and data.
395 CopyAndWrap(prefix, bytes, tail);
396 CopyAndWrap(data, bytes, tail);
397 return true;
398}
399
401 size_t& head) {
402 size_t entry_size = ReadVarLenEntryEncodedSize(bytes, head);
403 IncrementWithWrap(head, entry_size, bytes.size());
404 return entry_size;
405}
406
408 size_t& src_head,
409 size_t src_tail,
410 ByteSpan dst_bytes,
411 size_t& dst_head,
412 size_t& dst_tail,
413 bool overwrite) {
414 size_t available = AvailableBytes(dst_bytes, dst_head, dst_tail);
415 size_t to_copy = 0;
416 size_t offset = src_head;
417 while (src_head != src_tail) {
418 size_t src_entry_size = ReadVarLenEntryEncodedSize(src_bytes, src_head);
419 if (src_entry_size <= available) {
420 // Sufficient room; no-op.
421
422 } else if (!overwrite) {
423 // Not enough room, and cannot make more.
424 break;
425
426 } else if ((dst_bytes.size() - 1) < src_entry_size ||
427 (dst_bytes.size() - 1 - src_entry_size) < to_copy) {
428 // The next entry is too big to fit even if the destination were cleared.
429 break;
430
431 } else {
432 // Pop just enough elements to make room.
433 while (dst_head != dst_tail && available < src_entry_size) {
434 size_t dst_entry_size = ReadVarLenEntryEncodedSize(dst_bytes, dst_head);
435 IncrementWithWrap(dst_head, dst_entry_size, dst_bytes.size());
436 available += dst_entry_size;
437 }
438 // If dst_head == dst_tail, then available is dst_bytes.size() - 1, which
439 // is guaranteed to be greater than or equal to src_entry_size.
440 }
441 to_copy += src_entry_size;
442 available -= src_entry_size;
443 IncrementWithWrap(src_head, src_entry_size, src_bytes.size());
444 }
445
446 if (to_copy == 0) {
447 return;
448 }
449 size_t src_chunk = src_bytes.size() - offset;
450 if (src_chunk >= to_copy) {
451 CopyAndWrap(src_bytes.subspan(offset, to_copy), dst_bytes, dst_tail);
452 } else {
453 CopyAndWrap(src_bytes.subspan(offset), dst_bytes, dst_tail);
454 CopyAndWrap(src_bytes.subspan(0, to_copy - src_chunk), dst_bytes, dst_tail);
455 }
456}
457
459 ByteSpan bytes,
460 size_t& tail) {
461 if (data.empty()) {
462 return;
463 }
464 // Copy the new data in one or two chunks. The first chunk is copied to the
465 // byte after the tail, the second from the beginning of the buffer. Both may
466 // be zero bytes.
467 size_t first_chunk = bytes.size() - tail;
468 if (first_chunk >= data.size()) {
469 first_chunk = data.size();
470 } else { // Copy 2nd chunk from the beginning of the buffer (may be 0 bytes).
471 pw::copy(data.begin() +
472 static_cast<span<std::byte>::difference_type>(first_chunk),
473 data.end(),
474 bytes.begin());
475 }
476 pw::copy(
477 data.begin(),
478 data.begin() + static_cast<span<std::byte>::difference_type>(first_chunk),
479 bytes.begin() + static_cast<span<std::byte>::difference_type>(tail));
480 IncrementWithWrap(tail, data.size(), bytes.size());
481}
482
483constexpr std::pair<ConstByteSpan, ConstByteSpan>
485 size_t head,
486 size_t tail) {
487 if (head == tail) {
488 return std::make_pair(ConstByteSpan(), ConstByteSpan());
489 }
490 if (head < tail) {
491 return std::make_pair(bytes.subspan(head, tail - head), ConstByteSpan());
492 }
493 return std::make_pair(bytes.subspan(head), bytes.subspan(0, tail));
494}
495
497// Template method implementations.
498
499template <typename Derived, typename T>
502 PW_ASSERT(!empty());
503 return *begin();
504}
505
506template <typename Derived, typename T>
509 PW_ASSERT(!empty());
510 return *cbegin();
511}
512
513template <typename Derived, typename T>
515 PW_ASSERT(!empty());
516 size_t head = GetHead();
517 PopNonEmpty(GetBytes(), head);
518 SetHead(head);
519}
520
521template <typename Derived, typename T>
523 SetHead(0);
524 SetTail(0);
525}
526
527template <typename Derived, typename T>
528constexpr std::pair<span<const T>, span<const T>>
530 auto [first, second] =
531 Base::ContiguousRawStorage(GetBytes(), GetHead(), GetTail());
532 return std::make_pair(span_cast<const T>(first), span_cast<const T>(second));
533}
534
535template <typename Derived, typename T>
537 ByteSpan bytes = GetBytes();
538 size_t tail = encoded_size_bytes();
539 Base::Dering(bytes, GetHead());
540 SetHead(0);
541 SetTail(tail);
542 return span_cast<const T>(bytes.subspan(0, tail));
543}
544
545template <typename Derived, typename T>
547 span<T> data) {
548 if constexpr (std::is_same_v<T, std::byte>) {
549 return data;
550 } else {
551 return as_writable_bytes(data);
552 }
553}
554
555template <typename Derived, typename T>
556constexpr ConstByteSpan GenericVarLenEntryQueue<Derived, T>::AsBytes(
557 span<const T> data) {
558 if constexpr (std::is_same_v<T, std::byte>) {
559 return data;
560 } else {
561 return as_bytes(data);
562 }
563}
564
565template <typename Derived, typename T>
566constexpr bool GenericVarLenEntryQueue<Derived, T>::Push(span<const T> data,
567 bool overwrite) {
568 size_t head = GetHead();
569 size_t tail = GetTail();
570 bool result = Base::Push(AsBytes(data), overwrite, GetBytes(), head, tail);
571 SetHead(head);
572 SetTail(tail);
573 return result;
574}
575
576template <typename D1, typename T1, typename D2, typename T2>
577constexpr void CopyVarLenEntriesImpl(const GenericVarLenEntryQueue<D1, T1>& src,
578 GenericVarLenEntryQueue<D2, T2>& dst,
579 bool overwrite) {
580 size_t src_head = src.GetHead();
581 size_t dst_head = dst.GetHead();
582 size_t dst_tail = dst.GetTail();
584 src_head,
585 src.GetTail(),
586 dst.GetBytes(),
587 dst_head,
588 dst_tail,
589 overwrite);
590 dst.SetHead(dst_head);
591 dst.SetTail(dst_tail);
592}
593
594template <typename D1, typename T1, typename D2, typename T2>
595constexpr void MoveVarLenEntriesImpl(GenericVarLenEntryQueue<D1, T1>& src,
596 GenericVarLenEntryQueue<D2, T2>& dst,
597 bool overwrite) {
598 size_t src_head = src.GetHead();
599 size_t dst_head = dst.GetHead();
600 size_t dst_tail = dst.GetTail();
602 src_head,
603 src.GetTail(),
604 dst.GetBytes(),
605 dst_head,
606 dst_tail,
607 overwrite);
608 src.SetHead(src_head);
609 dst.SetHead(dst_head);
610 dst.SetTail(dst_tail);
611}
612
613} // namespace pw::containers::internal
614#endif // __cplusplus
Definition: generic_var_len_entry_queue.h:61
static constexpr std::pair< ConstByteSpan, ConstByteSpan > ContiguousRawStorage(ConstByteSpan bytes, size_t head, size_t tail)
Definition: generic_var_len_entry_queue.h:484
static constexpr Info GetInfo(ConstByteSpan bytes, size_t head, size_t tail)
Definition: generic_var_len_entry_queue.h:343
static void Dering(ByteSpan bytes, size_t head)
Moves entries to be contiguous and start from the beginning of the buffer.
Definition: generic_var_len_entry_queue.h:113
static constexpr size_t PopNonEmpty(ConstByteSpan bytes, size_t &head)
Removes an entry from the head of the queue. Must not be empty.
Definition: generic_var_len_entry_queue.h:400
static constexpr void CopyAndWrap(ConstByteSpan data, ByteSpan bytes, size_t &tail)
Copies data to the buffer, wrapping around the end if needed.
Definition: generic_var_len_entry_queue.h:458
static constexpr bool Push(ConstByteSpan data, bool overwrite, ByteSpan bytes, size_t &head, size_t &tail)
Add an entry to the tail of the queue.
Definition: generic_var_len_entry_queue.h:367
static constexpr void CopyEntries(ConstByteSpan src_bytes, size_t &src_head, size_t src_tail, ByteSpan dst_bytes, size_t &dst_head, size_t &dst_tail, bool overwrite)
Definition: generic_var_len_entry_queue.h:407
static constexpr size_t AvailableBytes(ConstByteSpan bytes, size_t head, size_t tail)
Returns the number of bytes not being used to store entries.
Definition: generic_var_len_entry_queue.h:357
Definition: generic_var_len_entry_queue.h:139
constexpr size_t encoded_size_bytes() const
Definition: generic_var_len_entry_queue.h:189
span< const T > dering()
Moves entries to be contiguous and start from the beginning of the buffer.
Definition: generic_var_len_entry_queue.h:536
constexpr bool try_push(span< const T > data)
Definition: generic_var_len_entry_queue.h:217
constexpr iterator begin()
Returns an iterator to the start of the queue.
Definition: generic_var_len_entry_queue.h:155
constexpr void push_overwrite(span< const T > data)
Definition: generic_var_len_entry_queue.h:226
constexpr std::pair< span< const T >, span< const T > > contiguous_raw_storage() const
Definition: generic_var_len_entry_queue.h:529
constexpr value_type front()
Definition: generic_var_len_entry_queue.h:501
constexpr iterator end()
Returns an iterator to the end of the queue.
Definition: generic_var_len_entry_queue.h:162
constexpr size_t size_bytes() const
Definition: generic_var_len_entry_queue.h:183
constexpr size_t size() const
Definition: generic_var_len_entry_queue.h:173
constexpr void push(span< const T > data)
Definition: generic_var_len_entry_queue.h:210
static constexpr size_t DataSizeBytes(size_t max_size_bytes)
Definition: generic_var_len_entry_queue.h:150
constexpr size_t max_size() const
Definition: generic_var_len_entry_queue.h:179
constexpr void clear()
Empties the queue.
Definition: generic_var_len_entry_queue.h:522
constexpr size_t max_size_bytes() const
Definition: generic_var_len_entry_queue.h:197
constexpr bool empty() const
Returns true if the queue is empty, false if it has at least one entry.
Definition: generic_var_len_entry_queue.h:169
constexpr void pop()
Definition: generic_var_len_entry_queue.h:514
Refers to an entry in-place in the queue. Entries may be discontiguous.
Definition: var_len_entry.h:65
Definition: var_len_entry_queue_iterator.h:46
constexpr OutputIt copy(InputIt first, InputIt last, OutputIt d_first)
constexpr backport of <algorithm>'s std::copy for C++17.
Definition: algorithm.h:355
constexpr size_t kMaxVarint32SizeBytes
Maximum size of a varint (LEB128) encoded uint32_t.
Definition: varint.h:74
constexpr size_t EncodedSize(T value)
Computes the size of an integer when encoded as a varint.
Definition: varint.h:123
Definition: generic_var_len_entry_queue.h:63