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