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