• 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 <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     // Returns entry at current position.
212     const Entry& operator*() const;
213     const Entry* operator->() const { return &operator*(); }
214 
215     constexpr bool operator==(const iterator& rhs) const {
216       return entry_count_ == rhs.entry_count_;
217     }
218 
219     constexpr bool operator!=(const iterator& rhs) const {
220       return entry_count_ != rhs.entry_count_;
221     }
222 
223     // Returns the status of the last iteration operation. If the iterator
224     // fails to read an entry, it will move to iterator::end() and indicate
225     // the failure reason here.
status()226     Status status() const { return iteration_status_; }
227 
228    private:
229     static constexpr Entry kEndEntry = {
230         .buffer = span<const std::byte>(),
231         .preamble = 0,
232     };
233 
SkipToEnd(Status status)234     void SkipToEnd(Status status) {
235       iteration_status_ = status;
236       entry_ = kEndEntry;
237       entry_count_ = 0;
238     }
239 
240     PrefixedEntryRingBufferMulti* ring_buffer_;
241     size_t read_idx_;
242     size_t entry_count_;
243 
244     mutable Entry entry_;
245     Status iteration_status_;
246   };
247 
248   using element_type = const Entry;
249   using value_type = std::remove_cv_t<const Entry>;
250   using pointer = const Entry;
251   using reference = const Entry&;
252   using const_iterator = iterator;  // Standard alias for iterable types.
253 
begin()254   iterator begin() { return iterator(GetSlowestReaderWritable()); }
end()255   iterator end() { return iterator(); }
cbegin()256   const_iterator cbegin() { return begin(); }
cend()257   const_iterator cend() { return end(); }
258 
259   // TODO: b/235351861 - Consider changing bool to an enum, to explicitly
260   // enumerate what this variable means in clients.
261   PrefixedEntryRingBufferMulti(bool user_preamble = false)
buffer_(nullptr)262       : buffer_(nullptr),
263         buffer_bytes_(0),
264         write_idx_(0),
265         user_preamble_(user_preamble) {}
266 
267   // Set the raw buffer to be used by the ring buffer.
268   //
269   // Return values:
270   // OK - successfully set the raw buffer.
271   // INVALID_ARGUMENT - Argument was nullptr, size zero, or too large.
272   Status SetBuffer(span<std::byte> buffer);
273 
274   // Determines if the ring buffer has corrupted entries.
275   //
276   // Precondition: At least one reader must be attached to the ring buffer.
277   // Return values:
278   // OK - No corruption was detected.
279   // DATA_LOSS - Corruption was detected.
CheckForCorruption()280   Status CheckForCorruption() {
281     iterator it = begin();
282     for (; it != end(); ++it) {
283     }
284     return it.status();
285   }
286 
287   // Attach reader to the ring buffer. Readers can only be attached to one
288   // ring buffer at a time.
289   //
290   // Return values:
291   // OK - Successfully configured reader for ring buffer.
292   // INVALID_ARGUMENT - Argument was already attached to another ring buffer.
293   Status AttachReader(Reader& reader);
294 
295   // Detach reader from the ring buffer. Readers can only be detached if they
296   // were previously attached.
297   //
298   // Return values:
299   // OK - Successfully removed reader for ring buffer.
300   // INVALID_ARGUMENT - Argument was not previously attached to this ring
301   // buffer.
302   Status DetachReader(Reader& reader);
303 
304   // Removes all data from the ring buffer.
305   void Clear();
306 
307   // Write a chunk of data to the ring buffer. If available space is less than
308   // size of data chunk to be written then silently pop and discard oldest
309   // stored data chunks until space is available.
310   //
311   // Preamble argument is a caller-provided value prepended to the front of the
312   // entry. It is only used if user_preamble was set at class construction
313   // time. It is varint-encoded before insertion into the buffer.
314   //
315   // Return values:
316   // OK - Data successfully written to the ring buffer.
317   // FAILED_PRECONDITION - Buffer not initialized.
318   // OUT_OF_RANGE - Size of data is greater than buffer size.
319   Status PushBack(span<const std::byte> data, uint32_t user_preamble_data = 0) {
320     return InternalPushBack(data, user_preamble_data, true);
321   }
322 
323   // [Deprecated] An implementation of PushBack that accepts a single-byte as
324   // preamble data. Clients should migrate to passing uint32_t preamble data.
PushBack(span<const std::byte> data,std::byte user_preamble_data)325   Status PushBack(span<const std::byte> data, std::byte user_preamble_data) {
326     return PushBack(data, static_cast<uint32_t>(user_preamble_data));
327   }
328 
329   // Write a chunk of data to the ring buffer if there is space available.
330   //
331   // Preamble argument is a caller-provided value prepended to the front of the
332   // entry. It is only used if user_preamble was set at class construction
333   // time. It is varint-encoded before insertion into the buffer.
334   //
335   // Precondition: the buffer data must not be corrupt, otherwise there will
336   // be a crash.
337   //
338   // Return values:
339   // OK - Data successfully written to the ring buffer.
340   // INVALID_ARGUMENT - Size of data to write is zero bytes
341   // FAILED_PRECONDITION - Buffer not initialized.
342   // OUT_OF_RANGE - Size of data is greater than buffer size.
343   // RESOURCE_EXHAUSTED - The ring buffer doesn't have space for the data
344   // without popping off existing elements.
345   Status TryPushBack(span<const std::byte> data,
346                      uint32_t user_preamble_data = 0) {
347     return InternalPushBack(data, user_preamble_data, false);
348   }
349 
350   // [Deprecated] An implementation of TryPushBack that accepts a single-byte as
351   // preamble data. Clients should migrate to passing uint32_t preamble data.
TryPushBack(span<const std::byte> data,std::byte user_preamble_data)352   Status TryPushBack(span<const std::byte> data, std::byte user_preamble_data) {
353     return TryPushBack(data, static_cast<uint32_t>(user_preamble_data));
354   }
355 
356   // Get the size in bytes of all the current entries in the ring buffer,
357   // including preamble and data chunk.
TotalUsedBytes()358   size_t TotalUsedBytes() const { return buffer_bytes_ - RawAvailableBytes(); }
359 
360   // Returns total size of ring buffer in bytes.
TotalSizeBytes()361   size_t TotalSizeBytes() const { return buffer_bytes_; }
362 
363   // Dering the buffer by reordering entries internally in the buffer by
364   // rotating to have the oldest entry is at the lowest address/index with
365   // newest entry at the highest address. If no readers are attached, the buffer
366   // is deringed at the current write index.
367   //
368   // Return values:
369   // OK - Buffer data successfully deringed.
370   // FAILED_PRECONDITION - Buffer not initialized.
371   Status Dering();
372 
373  private:
374   // Read the oldest stored data chunk of data from the ring buffer to
375   // the provided destination span. The number of bytes read is written to
376   // `bytes_read_out`.
377   //
378   // Precondition: the buffer data must not be corrupt, otherwise there will
379   // be a crash.
380   //
381   // Return values:
382   // OK - Data successfully read from the ring buffer.
383   // FAILED_PRECONDITION - Buffer not initialized.
384   // OUT_OF_RANGE - No entries in ring buffer to read.
385   // RESOURCE_EXHAUSTED - Destination data span was smaller number of bytes
386   // than the data size of the data chunk being read.  Available destination
387   // bytes were filled, remaining bytes of the data chunk were ignored.
388   Status InternalPeekFront(const Reader& reader,
389                            span<std::byte> data,
390                            size_t* bytes_read_out) const;
391   Status InternalPeekFront(const Reader& reader, ReadOutput output) const;
392 
393   Status InternalPeekFrontPreamble(const Reader& reader,
394                                    uint32_t& user_preamble_out) const;
395   // Same as Read but includes the entry's preamble of optional user value and
396   // the varint of the data size
397   Status InternalPeekFrontWithPreamble(const Reader& reader,
398                                        span<std::byte> data,
399                                        size_t* bytes_read_out) const;
400   Status InternalPeekFrontWithPreamble(const Reader& reader,
401                                        ReadOutput output) const;
402 
403   // Pop and discard the oldest stored data chunk of data from the ring buffer.
404   //
405   // Precondition: the buffer data must not be corrupt, otherwise there will
406   // be a crash.
407   //
408   // Return values:
409   // OK - Data successfully read from the ring buffer.
410   // FAILED_PRECONDITION - Buffer not initialized.
411   // OUT_OF_RANGE - No entries in ring buffer to pop.
412   Status InternalPopFront(Reader& reader);
413 
414   // Get the size in bytes of the next chunk, not including preamble, to be
415   // read.
416   size_t InternalFrontEntryDataSizeBytes(const Reader& reader) const;
417 
418   // Get the size in bytes of the next chunk, including preamble and data
419   // chunk, to be read.
420   size_t InternalFrontEntryTotalSizeBytes(const Reader& reader) const;
421 
422   // Internal version of Read used by all the public interface versions. T
423   // should be of type ReadOutput.
424   template <typename T>
425   Status InternalRead(const Reader& reader,
426                       T read_output,
427                       bool include_preamble_in_output,
428                       uint32_t* user_preamble_out = nullptr) const;
429 
430   // Dering the buffer by reordering entries internally in the buffer by
431   // rotating to have the oldest entry is at the lowest address/index with
432   // newest entry at the highest address. If no readers are attached, the buffer
433   // is deringed at the current write index.
434   //
435   // Return values:
436   // OK - Buffer data successfully deringed.
437   // FAILED_PRECONDITION - Buffer not initialized.
438   Status InternalDering(Reader& reader);
439 
440   struct EntryInfo {
441     size_t preamble_bytes;
442     uint32_t user_preamble;
443     size_t data_bytes;
444   };
445 
446   // Push back implementation, which optionally discards front elements to fit
447   // the incoming element.
448   Status InternalPushBack(span<const std::byte> data,
449                           uint32_t user_preamble_data,
450                           bool pop_front_if_needed);
451 
452   // Internal function to pop all of the slowest readers. This function may pop
453   // multiple readers if multiple are slow.
454   //
455   // Precondition: This function requires that at least one reader is attached
456   // and has at least one entry to pop. There will be a crash if data is
457   // corrupted.
458   void InternalPopFrontAll();
459 
460   // Returns a the slowest reader in the list.
461   //
462   // Precondition: This function requires that at least one reader is attached.
463   const Reader& GetSlowestReader() const;
GetSlowestReaderWritable()464   Reader& GetSlowestReaderWritable() {
465     return const_cast<Reader&>(GetSlowestReader());
466   }
467 
468   // Get info struct with the size of the preamble and data chunk for the next
469   // entry to be read. Calls RawFrontEntryInfo and asserts on failure.
470   //
471   // Precondition: the buffer data must not be corrupt, otherwise there will
472   // be a crash.
473   EntryInfo FrontEntryInfo(const Reader& reader) const;
474 
475   // Get info struct with the size of the preamble and data chunk for the next
476   // entry to be read.
477   //
478   // Returns:
479   // OK - EntryInfo containing the next entry metadata.
480   // DATA_LOSS - Failed to read the metadata at this location.
481   Result<EntryInfo> RawFrontEntryInfo(size_t source_idx) const;
482 
483   // Get the raw number of available bytes free in the ring buffer. This is
484   // not available bytes for data, since there is a variable size preamble for
485   // each entry.
486   size_t RawAvailableBytes() const;
487 
488   // Do the basic write of the specified number of bytes starting at the last
489   // write index of the ring buffer to the destination, handing any wrap-around
490   // of the ring buffer. This is basic, raw operation with no safety checks.
491   void RawWrite(span<const std::byte> source);
492 
493   // Do the basic read of the specified number of bytes starting at the given
494   // index of the ring buffer to the destination, handing any wrap-around of
495   // the ring buffer. This is basic, raw operation with no safety checks.
496   void RawRead(std::byte* destination,
497                size_t source_idx,
498                size_t length_bytes) const;
499 
500   size_t IncrementIndex(size_t index, size_t count) const;
501 
502   std::byte* buffer_;
503   size_t buffer_bytes_;
504 
505   size_t write_idx_;
506   const bool user_preamble_;
507 
508   // List of attached readers.
509   IntrusiveList<Reader> readers_;
510 
511   // Maximum bufer size allowed. Restricted to this to allow index aliasing to
512   // not overflow.
513   static constexpr size_t kMaxBufferBytes =
514       std::numeric_limits<size_t>::max() / 2;
515 };
516 
517 class PrefixedEntryRingBuffer : public PrefixedEntryRingBufferMulti,
518                                 public PrefixedEntryRingBufferMulti::Reader {
519  public:
520   PrefixedEntryRingBuffer(bool user_preamble = false)
PrefixedEntryRingBufferMulti(user_preamble)521       : PrefixedEntryRingBufferMulti(user_preamble) {
522     AttachReader(*this)
523         .IgnoreError();  // TODO: b/242598609 - Handle Status properly
524   }
525 };
526 
527 }  // namespace ring_buffer
528 }  // namespace pw
529