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