xref: /aosp_15_r20/external/pigweed/pw_ring_buffer/public/pw_ring_buffer/prefixed_entry_ring_buffer.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <cstddef>
17 #include <cstdint>
18 #include <limits>
19 
20 #include "pw_containers/intrusive_list.h"
21 #include "pw_result/result.h"
22 #include "pw_span/span.h"
23 #include "pw_status/status.h"
24 
25 namespace pw {
26 namespace ring_buffer {
27 
28 // A circular ring buffer for arbitrary length data entries. Each PushBack()
29 // produces a buffer entry. Each entry consists of a preamble followed by an
30 // arbitrary length data chunk. The preamble is comprised of an optional user
31 // preamble byte and an always present varint. The varint encodes the number of
32 // bytes in the data chunk. This is a FIFO queue, with the oldest entries at
33 // the 'front' (to be processed by readers) and the newest entries at the 'back'
34 // (where the writer pushes to).
35 //
36 // The ring buffer supports multiple readers, which can be attached/detached
37 // from the buffer. Each reader has its own read pointer and can peek and pop
38 // the entry at the head. Entries are not bumped out from the buffer until all
39 // readers have moved past that entry, or if the buffer is at capacity and space
40 // is needed to push a new entry. When making space, the buffer will push slow
41 // readers forward to the new oldest entry. Entries are internally wrapped
42 // around as needed.
43 class PrefixedEntryRingBufferMulti {
44  public:
45   typedef Status (*ReadOutput)(span<const std::byte>);
46 
47   // A reader that provides a single-reader interface into the multi-reader ring
48   // buffer it has been attached to via AttachReader(). Readers maintain their
49   // read position in the ring buffer as well as the remaining count of entries
50   // from that position.
51   //
52   // If no readers are currently attached, the reader starts at the current
53   // write head. If readers are currently attached, the reader is set to the
54   // location and entry count of the slowest reader in the set.
55   //
56   // Readers can peek and pop entries similar to the single-reader interface.
57   // When popping entries, although the reader moves forward and drops the
58   // entry, the entry is not removed from the ring buffer until all other
59   // attached readers have moved past that entry.
60   //
61   // When the attached ring buffer needs to make space, it may push the reader
62   // index forward. Users of this class should consider the possibility of data
63   // loss if they read slower than the writer.
64   class Reader : public IntrusiveList<Reader>::Item {
65    public:
Reader()66     constexpr Reader() : buffer_(nullptr), read_idx_(0), entry_count_(0) {}
67 
68     // TODO: b/235351035 - Add locking to the internal functions. Who owns the
69     // lock? This class? Does this class need a lock if it's not a multi-reader?
70     // (One doesn't exist today but presumably nothing prevents push + pop
71     // operations from happening on two different threads).
72 
73     // Read the oldest stored data chunk of data from the ring buffer to
74     // the provided destination span. The number of bytes read is written
75     // to bytes_read
76     //
77     // Precondition: the buffer data must not be corrupt, otherwise there will
78     // be a crash.
79     //
80     // Return values:
81     // OK - Data successfully read from the ring buffer.
82     // FAILED_PRECONDITION - Buffer not initialized.
83     // OUT_OF_RANGE - No entries in ring buffer to read.
84     // RESOURCE_EXHAUSTED - Destination data span was smaller number of
85     // bytes than the data size of the data chunk being read.  Available
86     // destination bytes were filled, remaining bytes of the data chunk were
87     // ignored.
PeekFront(span<std::byte> data,size_t * bytes_read_out)88     Status PeekFront(span<std::byte> data, size_t* bytes_read_out) const {
89       return buffer_->InternalPeekFront(*this, data, bytes_read_out);
90     }
91 
PeekFront(ReadOutput output)92     Status PeekFront(ReadOutput output) const {
93       return buffer_->InternalPeekFront(*this, output);
94     }
95 
96     // Peek the front entry's preamble only to avoid copying data unnecessarily.
97     //
98     // Precondition: the buffer data must not be corrupt, otherwise there will
99     // be a crash.
PeekFrontPreamble(uint32_t & user_preamble_out)100     Status PeekFrontPreamble(uint32_t& user_preamble_out) const {
101       return buffer_->InternalPeekFrontPreamble(*this, user_preamble_out);
102     }
103 
104     // Same as PeekFront but includes the entry's preamble of optional user
105     // value and the varint of the data size.
106     // TODO: b/235351847 - Move all other APIs to passing bytes_read by
107     // reference, as it is required to determine the length populated in the
108     // span.
109     Status PeekFrontWithPreamble(span<std::byte> data,
110                                  uint32_t& user_preamble_out,
111                                  size_t& entry_bytes_read_out) const;
112 
PeekFrontWithPreamble(span<std::byte> data,size_t * bytes_read_out)113     Status PeekFrontWithPreamble(span<std::byte> data,
114                                  size_t* bytes_read_out) const {
115       return buffer_->InternalPeekFrontWithPreamble(
116           *this, data, bytes_read_out);
117     }
118 
PeekFrontWithPreamble(ReadOutput output)119     Status PeekFrontWithPreamble(ReadOutput output) const {
120       return buffer_->InternalPeekFrontWithPreamble(*this, output);
121     }
122 
123     // Pop and discard the oldest stored data chunk of data from the ring
124     // buffer.
125     //
126     // Precondition: the buffer data must not be corrupt, otherwise there will
127     // be a crash.
128     //
129     // Return values:
130     // OK - Data successfully read from the ring buffer.
131     // FAILED_PRECONDITION - Buffer not initialized.
132     // OUT_OF_RANGE - No entries in ring buffer to pop.
PopFront()133     Status PopFront() { return buffer_->InternalPopFront(*this); }
134 
135     // Get the size in bytes of the next chunk, not including preamble, to be
136     // read.
137     //
138     // Precondition: the buffer data must not be corrupt, otherwise there will
139     // be a crash.
FrontEntryDataSizeBytes()140     size_t FrontEntryDataSizeBytes() const {
141       return buffer_->InternalFrontEntryDataSizeBytes(*this);
142     }
143 
144     // Get the size in bytes of the next chunk, including preamble and data
145     // chunk, to be read.
146     //
147     // Precondition: the buffer data must not be corrupt, otherwise there will
148     // be a crash.
FrontEntryTotalSizeBytes()149     size_t FrontEntryTotalSizeBytes() const {
150       return buffer_->InternalFrontEntryTotalSizeBytes(*this);
151     }
152 
153     // Get the number of variable-length entries currently in the ring buffer.
154     //
155     // Return value:
156     // Entry count.
EntryCount()157     size_t EntryCount() const { return entry_count_; }
158 
159     // Get the size of the unread entries currently in the ring buffer.
160     // Return value:
161     // Number of bytes.
162     size_t EntriesSize() const;
163 
164    private:
165     friend PrefixedEntryRingBufferMulti;
166 
167     // Internal constructors for the iterator class to create Reader instances
168     // at specific positions. Readers constructed through this interface cannot
169     // be attached/detached from the multisink.
Reader(Reader & reader)170     constexpr Reader(Reader& reader)
171         : Reader(reader.buffer_, reader.read_idx_, reader.entry_count_) {}
Reader(PrefixedEntryRingBufferMulti * buffer,size_t read_idx,size_t entry_count)172     constexpr Reader(PrefixedEntryRingBufferMulti* buffer,
173                      size_t read_idx,
174                      size_t entry_count)
175         : buffer_(buffer), read_idx_(read_idx), entry_count_(entry_count) {}
176 
177     PrefixedEntryRingBufferMulti* buffer_;
178     size_t read_idx_;
179     size_t entry_count_;
180   };
181 
182   // An entry returned by the iterator containing the byte span of the entry
183   // and preamble data (if the ring buffer was configured with a preamble).
184   struct Entry {
185     span<const std::byte> buffer;
186     uint32_t preamble;
187   };
188 
189   // An iterator that can be used to walk through all entries from a given
190   // Reader position, without mutating the underlying buffer. This is useful in
191   // crash contexts where all available entries in the buffer must be acquired,
192   // even those that have already been consumed by all attached readers.
193   class iterator {
194    public:
iterator()195     iterator() : ring_buffer_(nullptr), read_idx_(0), entry_count_(0) {}
iterator(Reader & reader)196     iterator(Reader& reader)
197         : ring_buffer_(reader.buffer_),
198           read_idx_(0),
199           entry_count_(reader.entry_count_) {
200       Status dering_result = ring_buffer_->InternalDering(reader);
201       PW_DASSERT(dering_result.ok());
202     }
203 
204     iterator& operator++();
205     iterator operator++(int) {
206       iterator original = *this;
207       ++*this;
208       return original;
209     }
210 
211     iterator& operator--();
212     iterator operator--(int) {
213       iterator original = *this;
214       --*this;
215       return original;
216     }
217 
218     // Returns entry at current position.
219     const Entry& operator*() const;
220     const Entry* operator->() const { return &operator*(); }
221 
222     constexpr bool operator==(const iterator& rhs) const {
223       return entry_count_ == rhs.entry_count_;
224     }
225 
226     constexpr bool operator!=(const iterator& rhs) const {
227       return entry_count_ != rhs.entry_count_;
228     }
229 
230     // Returns the status of the last iteration operation. If the iterator
231     // fails to read an entry, it will move to iterator::end() and indicate
232     // the failure reason here.
status()233     Status status() const { return iteration_status_; }
234 
235    private:
236     static constexpr Entry kEndEntry = {
237         .buffer = span<const std::byte>(),
238         .preamble = 0,
239     };
240 
SkipToEnd(Status status)241     void SkipToEnd(Status status) {
242       iteration_status_ = status;
243       entry_ = kEndEntry;
244       entry_count_ = 0;
245     }
246 
247     PrefixedEntryRingBufferMulti* ring_buffer_;
248     size_t read_idx_;
249     size_t entry_count_;
250 
251     mutable Entry entry_;
252     Status iteration_status_;
253   };
254 
255   using element_type = const Entry;
256   using value_type = std::remove_cv_t<const Entry>;
257   using pointer = const Entry;
258   using reference = const Entry&;
259   using const_iterator = iterator;  // Standard alias for iterable types.
260 
begin()261   iterator begin() { return iterator(GetSlowestReaderWritable()); }
end()262   iterator end() { return iterator(); }
cbegin()263   const_iterator cbegin() { return begin(); }
cend()264   const_iterator cend() { return end(); }
265 
266   // TODO: b/235351861 - Consider changing bool to an enum, to explicitly
267   // enumerate what this variable means in clients.
268   PrefixedEntryRingBufferMulti(bool user_preamble = false)
buffer_(nullptr)269       : buffer_(nullptr),
270         buffer_bytes_(0),
271         write_idx_(0),
272         user_preamble_(user_preamble) {}
273 
274   // Set the raw buffer to be used by the ring buffer.
275   //
276   // Return values:
277   // OK - successfully set the raw buffer.
278   // INVALID_ARGUMENT - Argument was nullptr, size zero, or too large.
279   Status SetBuffer(span<std::byte> buffer);
280 
281   // Determines if the ring buffer has corrupted entries.
282   //
283   // Precondition: At least one reader must be attached to the ring buffer.
284   // Return values:
285   // OK - No corruption was detected.
286   // DATA_LOSS - Corruption was detected.
CheckForCorruption()287   Status CheckForCorruption() {
288     iterator it = begin();
289     for (; it != end(); ++it) {
290     }
291     return it.status();
292   }
293 
294   // Attach reader to the ring buffer. Readers can only be attached to one
295   // ring buffer at a time.
296   //
297   // Return values:
298   // OK - Successfully configured reader for ring buffer.
299   // INVALID_ARGUMENT - Argument was already attached to another ring buffer.
300   Status AttachReader(Reader& reader);
301 
302   // Detach reader from the ring buffer. Readers can only be detached if they
303   // were previously attached.
304   //
305   // Return values:
306   // OK - Successfully removed reader for ring buffer.
307   // INVALID_ARGUMENT - Argument was not previously attached to this ring
308   // buffer.
309   Status DetachReader(Reader& reader);
310 
311   // Removes all data from the ring buffer.
312   void Clear();
313 
314   // Write a chunk of data to the ring buffer. If available space is less than
315   // size of data chunk to be written then silently pop and discard oldest
316   // stored data chunks until space is available.
317   //
318   // Preamble argument is a caller-provided value prepended to the front of the
319   // entry. It is only used if user_preamble was set at class construction
320   // time. It is varint-encoded before insertion into the buffer.
321   //
322   // Return values:
323   // OK - Data successfully written to the ring buffer.
324   // FAILED_PRECONDITION - Buffer not initialized.
325   // OUT_OF_RANGE - Size of data is greater than buffer size.
326   Status PushBack(span<const std::byte> data, uint32_t user_preamble_data = 0) {
327     return InternalPushBack(data, user_preamble_data, true);
328   }
329 
330   // [Deprecated] An implementation of PushBack that accepts a single-byte as
331   // preamble data. Clients should migrate to passing uint32_t preamble data.
PushBack(span<const std::byte> data,std::byte user_preamble_data)332   Status PushBack(span<const std::byte> data, std::byte user_preamble_data) {
333     return PushBack(data, static_cast<uint32_t>(user_preamble_data));
334   }
335 
336   // Write a chunk of data to the ring buffer if there is space available.
337   //
338   // Preamble argument is a caller-provided value prepended to the front of the
339   // entry. It is only used if user_preamble was set at class construction
340   // time. It is varint-encoded before insertion into the buffer.
341   //
342   // Precondition: the buffer data must not be corrupt, otherwise there will
343   // be a crash.
344   //
345   // Return values:
346   // OK - Data successfully written to the ring buffer.
347   // INVALID_ARGUMENT - Size of data to write is zero bytes
348   // FAILED_PRECONDITION - Buffer not initialized.
349   // OUT_OF_RANGE - Size of data is greater than buffer size.
350   // RESOURCE_EXHAUSTED - The ring buffer doesn't have space for the data
351   // without popping off existing elements.
352   Status TryPushBack(span<const std::byte> data,
353                      uint32_t user_preamble_data = 0) {
354     return InternalPushBack(data, user_preamble_data, false);
355   }
356 
357   // [Deprecated] An implementation of TryPushBack that accepts a single-byte as
358   // preamble data. Clients should migrate to passing uint32_t preamble data.
TryPushBack(span<const std::byte> data,std::byte user_preamble_data)359   Status TryPushBack(span<const std::byte> data, std::byte user_preamble_data) {
360     return TryPushBack(data, static_cast<uint32_t>(user_preamble_data));
361   }
362 
363   // Get the size in bytes of all the current entries in the ring buffer,
364   // including preamble and data chunk.
TotalUsedBytes()365   size_t TotalUsedBytes() const { return buffer_bytes_ - RawAvailableBytes(); }
366 
367   // Returns total size of ring buffer in bytes.
TotalSizeBytes()368   size_t TotalSizeBytes() const { return buffer_bytes_; }
369 
370   // Dering the buffer by reordering entries internally in the buffer by
371   // rotating to have the oldest entry is at the lowest address/index with
372   // newest entry at the highest address. If no readers are attached, the buffer
373   // is deringed at the current write index.
374   //
375   // Return values:
376   // OK - Buffer data successfully deringed.
377   // FAILED_PRECONDITION - Buffer not initialized.
378   Status Dering();
379 
380  private:
381   // Read the oldest stored data chunk of data from the ring buffer to
382   // the provided destination span. The number of bytes read is written to
383   // `bytes_read_out`.
384   //
385   // Precondition: the buffer data must not be corrupt, otherwise there will
386   // be a crash.
387   //
388   // Return values:
389   // OK - Data successfully read from the ring buffer.
390   // FAILED_PRECONDITION - Buffer not initialized.
391   // OUT_OF_RANGE - No entries in ring buffer to read.
392   // RESOURCE_EXHAUSTED - Destination data span was smaller number of bytes
393   // than the data size of the data chunk being read.  Available destination
394   // bytes were filled, remaining bytes of the data chunk were ignored.
395   Status InternalPeekFront(const Reader& reader,
396                            span<std::byte> data,
397                            size_t* bytes_read_out) const;
398   Status InternalPeekFront(const Reader& reader, ReadOutput output) const;
399 
400   Status InternalPeekFrontPreamble(const Reader& reader,
401                                    uint32_t& user_preamble_out) const;
402   // Same as Read but includes the entry's preamble of optional user value and
403   // the varint of the data size
404   Status InternalPeekFrontWithPreamble(const Reader& reader,
405                                        span<std::byte> data,
406                                        size_t* bytes_read_out) const;
407   Status InternalPeekFrontWithPreamble(const Reader& reader,
408                                        ReadOutput output) const;
409 
410   // Pop and discard the oldest stored data chunk of data from the ring buffer.
411   //
412   // Precondition: the buffer data must not be corrupt, otherwise there will
413   // be a crash.
414   //
415   // Return values:
416   // OK - Data successfully read from the ring buffer.
417   // FAILED_PRECONDITION - Buffer not initialized.
418   // OUT_OF_RANGE - No entries in ring buffer to pop.
419   Status InternalPopFront(Reader& reader);
420 
421   // Get the size in bytes of the next chunk, not including preamble, to be
422   // read.
423   size_t InternalFrontEntryDataSizeBytes(const Reader& reader) const;
424 
425   // Get the size in bytes of the next chunk, including preamble and data
426   // chunk, to be read.
427   size_t InternalFrontEntryTotalSizeBytes(const Reader& reader) const;
428 
429   // Internal version of Read used by all the public interface versions. T
430   // should be of type ReadOutput.
431   template <typename T>
432   Status InternalRead(const Reader& reader,
433                       T read_output,
434                       bool include_preamble_in_output,
435                       uint32_t* user_preamble_out = nullptr) const;
436 
437   // Dering the buffer by reordering entries internally in the buffer by
438   // rotating to have the oldest entry is at the lowest address/index with
439   // newest entry at the highest address. If no readers are attached, the buffer
440   // is deringed at the current write index.
441   //
442   // Return values:
443   // OK - Buffer data successfully deringed.
444   // FAILED_PRECONDITION - Buffer not initialized.
445   Status InternalDering(Reader& reader);
446 
447   struct EntryInfo {
448     size_t preamble_bytes;
449     uint32_t user_preamble;
450     size_t data_bytes;
451   };
452 
453   // Push back implementation, which optionally discards front elements to fit
454   // the incoming element.
455   Status InternalPushBack(span<const std::byte> data,
456                           uint32_t user_preamble_data,
457                           bool pop_front_if_needed);
458 
459   // Internal function to pop all of the slowest readers. This function may pop
460   // multiple readers if multiple are slow.
461   //
462   // Precondition: This function requires that at least one reader is attached
463   // and has at least one entry to pop. There will be a crash if data is
464   // corrupted.
465   void InternalPopFrontAll();
466 
467   // Returns a the slowest reader in the list.
468   //
469   // Precondition: This function requires that at least one reader is attached.
470   const Reader& GetSlowestReader() const;
GetSlowestReaderWritable()471   Reader& GetSlowestReaderWritable() {
472     return const_cast<Reader&>(GetSlowestReader());
473   }
474 
475   // Get info struct with the size of the preamble and data chunk for the next
476   // entry to be read. Calls RawFrontEntryInfo and asserts on failure.
477   //
478   // Precondition: the buffer data must not be corrupt, otherwise there will
479   // be a crash.
480   EntryInfo FrontEntryInfo(const Reader& reader) const;
481 
482   // Get info struct with the size of the preamble and data chunk for the next
483   // entry to be read.
484   //
485   // Returns:
486   // OK - EntryInfo containing the next entry metadata.
487   // DATA_LOSS - Failed to read the metadata at this location.
488   Result<EntryInfo> RawFrontEntryInfo(size_t source_idx) const;
489 
490   // Get the raw number of available bytes free in the ring buffer. This is
491   // not available bytes for data, since there is a variable size preamble for
492   // each entry.
493   size_t RawAvailableBytes() const;
494 
495   // Do the basic write of the specified number of bytes starting at the last
496   // write index of the ring buffer to the destination, handing any wrap-around
497   // of the ring buffer. This is basic, raw operation with no safety checks.
498   void RawWrite(span<const std::byte> source);
499 
500   // Do the basic read of the specified number of bytes starting at the given
501   // index of the ring buffer to the destination, handing any wrap-around of
502   // the ring buffer. This is basic, raw operation with no safety checks.
503   void RawRead(std::byte* destination,
504                size_t source_idx,
505                size_t length_bytes) const;
506 
507   size_t IncrementIndex(size_t index, size_t count) const;
508 
509   std::byte* buffer_;
510   size_t buffer_bytes_;
511 
512   size_t write_idx_;
513   const bool user_preamble_;
514 
515   // List of attached readers.
516   IntrusiveList<Reader> readers_;
517 
518   // Maximum bufer size allowed. Restricted to this to allow index aliasing to
519   // not overflow.
520   static constexpr size_t kMaxBufferBytes =
521       std::numeric_limits<size_t>::max() / 2;
522 };
523 
524 class PrefixedEntryRingBuffer : public PrefixedEntryRingBufferMulti,
525                                 public PrefixedEntryRingBufferMulti::Reader {
526  public:
527   PrefixedEntryRingBuffer(bool user_preamble = false)
PrefixedEntryRingBufferMulti(user_preamble)528       : PrefixedEntryRingBufferMulti(user_preamble) {
529     AttachReader(*this)
530         .IgnoreError();  // TODO: b/242598609 - Handle Status properly
531   }
532 };
533 
534 }  // namespace ring_buffer
535 }  // namespace pw
536