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