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