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