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_status/status.h" 21 22 namespace pw { 23 namespace ring_buffer { 24 25 // A circular ring buffer for arbitrary length data entries. Each PushBack() 26 // produces a buffer entry. Each entry consists of a preamble followed by an 27 // arbitrary length data chunk. The preamble is comprised of an optional user 28 // preamble byte and an always present varint. The varint encodes the number of 29 // bytes in the data chunk. This is a FIFO queue, with the oldest entries at 30 // the 'front' (to be processed by readers) and the newest entries at the 'back' 31 // (where the writer pushes to). 32 // 33 // The ring buffer supports multiple readers, which can be attached/detached 34 // from the buffer. Each reader has its own read pointer and can peek and pop 35 // the entry at the head. Entries are not bumped out from the buffer until all 36 // readers have moved past that entry, or if the buffer is at capacity and space 37 // is needed to push a new entry. When making space, the buffer will push slow 38 // readers forward to the new oldest entry. Entries are internally wrapped 39 // around as needed. 40 class PrefixedEntryRingBufferMulti { 41 public: 42 typedef Status (*ReadOutput)(std::span<const std::byte>); 43 44 // A reader that provides a single-reader interface into the multi-reader ring 45 // buffer it has been attached to via AttachReader(). Readers maintain their 46 // read position in the ring buffer as well as the remaining count of entries 47 // from that position. Readers are only able to consume entries that were 48 // pushed after the attach operation. 49 // 50 // Readers can peek and pop entries similar to the single-reader interface. 51 // When popping entries, although the reader moves forward and drops the 52 // entry, the entry is not removed from the ring buffer until all other 53 // attached readers have moved past that entry. 54 // 55 // When the attached ring buffer needs to make space, it may push the reader 56 // index forward. Users of this class should consider the possibility of data 57 // loss if they read slower than the writer. 58 class Reader : public IntrusiveList<Reader>::Item { 59 public: Reader()60 constexpr Reader() : buffer(nullptr), read_idx(0), entry_count(0) {} 61 62 // TODO(pwbug/344): Add locking to the internal functions. Who owns the 63 // lock? This class? Does this class need a lock if it's not a multi-reader? 64 // (One doesn't exist today but presumably nothing prevents push + pop 65 // operations from happening on two different threads). 66 67 // Read the oldest stored data chunk of data from the ring buffer to 68 // the provided destination std::span. The number of bytes read is written 69 // to bytes_read 70 // 71 // Return values: 72 // OK - Data successfully read from the ring buffer. 73 // FAILED_PRECONDITION - Buffer not initialized. 74 // OUT_OF_RANGE - No entries in ring buffer to read. 75 // RESOURCE_EXHAUSTED - Destination data std::span was smaller number of 76 // bytes than the data size of the data chunk being read. Available 77 // destination bytes were filled, remaining bytes of the data chunk were 78 // ignored. PeekFront(std::span<std::byte> data,size_t * bytes_read_out)79 Status PeekFront(std::span<std::byte> data, size_t* bytes_read_out) { 80 return buffer->InternalPeekFront(*this, data, bytes_read_out); 81 } 82 PeekFront(ReadOutput output)83 Status PeekFront(ReadOutput output) { 84 return buffer->InternalPeekFront(*this, output); 85 } 86 87 // Same as PeekFront but includes the entry's preamble of optional user 88 // value and the varint of the data size. 89 // TODO(pwbug/341): Move all other APIs to passing bytes_read by reference, 90 // as it is required to determine the length populated in the span. 91 Status PeekFrontWithPreamble(std::span<std::byte> data, 92 uint32_t& user_preamble_out, 93 size_t& entry_bytes_read_out); 94 PeekFrontWithPreamble(std::span<std::byte> data,size_t * bytes_read_out)95 Status PeekFrontWithPreamble(std::span<std::byte> data, 96 size_t* bytes_read_out) { 97 return buffer->InternalPeekFrontWithPreamble(*this, data, bytes_read_out); 98 } 99 PeekFrontWithPreamble(ReadOutput output)100 Status PeekFrontWithPreamble(ReadOutput output) { 101 return buffer->InternalPeekFrontWithPreamble(*this, output); 102 } 103 104 // Pop and discard the oldest stored data chunk of data from the ring 105 // buffer. 106 // 107 // Return values: 108 // OK - Data successfully read from the ring buffer. 109 // FAILED_PRECONDITION - Buffer not initialized. 110 // OUT_OF_RANGE - No entries in ring buffer to pop. PopFront()111 Status PopFront() { return buffer->InternalPopFront(*this); } 112 113 // Get the size in bytes of the next chunk, not including preamble, to be 114 // read. FrontEntryDataSizeBytes()115 size_t FrontEntryDataSizeBytes() { 116 return buffer->InternalFrontEntryDataSizeBytes(*this); 117 } 118 119 // Get the size in bytes of the next chunk, including preamble and data 120 // chunk, to be read. FrontEntryTotalSizeBytes()121 size_t FrontEntryTotalSizeBytes() { 122 return buffer->InternalFrontEntryTotalSizeBytes(*this); 123 } 124 125 // Get the number of variable-length entries currently in the ring buffer. 126 // 127 // Return value: 128 // Entry count. EntryCount()129 size_t EntryCount() { return entry_count; } 130 131 protected: 132 friend PrefixedEntryRingBufferMulti; 133 134 PrefixedEntryRingBufferMulti* buffer; 135 size_t read_idx; 136 size_t entry_count; 137 }; 138 139 // TODO(pwbug/340): Consider changing bool to an enum, to explicitly enumerate 140 // what this variable means in clients. 141 PrefixedEntryRingBufferMulti(bool user_preamble = false) buffer_(nullptr)142 : buffer_(nullptr), 143 buffer_bytes_(0), 144 write_idx_(0), 145 user_preamble_(user_preamble) {} 146 147 // Set the raw buffer to be used by the ring buffer. 148 // 149 // Return values: 150 // OK - successfully set the raw buffer. 151 // INVALID_ARGUMENT - Argument was nullptr, size zero, or too large. 152 Status SetBuffer(std::span<std::byte> buffer); 153 154 // Attach reader to the ring buffer. Readers can only be attached to one 155 // ring buffer at a time. 156 // 157 // Return values: 158 // OK - Successfully configured reader for ring buffer. 159 // INVALID_ARGUMENT - Argument was already attached to another ring buffer. 160 Status AttachReader(Reader& reader); 161 162 // Detach reader from the ring buffer. Readers can only be detached if they 163 // were previously attached. 164 // 165 // Return values: 166 // OK - Successfully removed reader for ring buffer. 167 // INVALID_ARGUMENT - Argument was not previously attached to this ring 168 // buffer. 169 Status DetachReader(Reader& reader); 170 171 // Removes all data from the ring buffer. 172 void Clear(); 173 174 // Write a chunk of data to the ring buffer. If available space is less than 175 // size of data chunk to be written then silently pop and discard oldest 176 // stored data chunks until space is available. 177 // 178 // Preamble argument is a caller-provided value prepended to the front of the 179 // entry. It is only used if user_preamble was set at class construction 180 // time. It is varint-encoded before insertion into the buffer. 181 // 182 // Return values: 183 // OK - Data successfully written to the ring buffer. 184 // INVALID_ARGUMENT - Size of data to write is zero bytes 185 // FAILED_PRECONDITION - Buffer not initialized. 186 // OUT_OF_RANGE - Size of data is greater than buffer size. 187 Status PushBack(std::span<const std::byte> data, 188 uint32_t user_preamble_data = 0) { 189 return InternalPushBack(data, user_preamble_data, true); 190 } 191 192 // [Deprecated] An implementation of PushBack that accepts a single-byte as 193 // preamble data. Clients should migrate to passing uint32_t preamble data. PushBack(std::span<const std::byte> data,std::byte user_preamble_data)194 Status PushBack(std::span<const std::byte> data, 195 std::byte user_preamble_data) { 196 return PushBack(data, static_cast<uint32_t>(user_preamble_data)); 197 } 198 199 // Write a chunk of data to the ring buffer if there is space available. 200 // 201 // Preamble argument is a caller-provided value prepended to the front of the 202 // entry. It is only used if user_preamble was set at class construction 203 // time. It is varint-encoded before insertion into the buffer. 204 // 205 // Return values: 206 // OK - Data successfully written to the ring buffer. 207 // INVALID_ARGUMENT - Size of data to write is zero bytes 208 // FAILED_PRECONDITION - Buffer not initialized. 209 // OUT_OF_RANGE - Size of data is greater than buffer size. 210 // RESOURCE_EXHAUSTED - The ring buffer doesn't have space for the data 211 // without popping off existing elements. 212 Status TryPushBack(std::span<const std::byte> data, 213 uint32_t user_preamble_data = 0) { 214 return InternalPushBack(data, user_preamble_data, false); 215 } 216 217 // [Deprecated] An implementation of TryPushBack that accepts a single-byte as 218 // preamble data. Clients should migrate to passing uint32_t preamble data. TryPushBack(std::span<const std::byte> data,std::byte user_preamble_data)219 Status TryPushBack(std::span<const std::byte> data, 220 std::byte user_preamble_data) { 221 return TryPushBack(data, static_cast<uint32_t>(user_preamble_data)); 222 } 223 224 // Get the size in bytes of all the current entries in the ring buffer, 225 // including preamble and data chunk. TotalUsedBytes()226 size_t TotalUsedBytes() { return buffer_bytes_ - RawAvailableBytes(); } 227 228 // Dering the buffer by reordering entries internally in the buffer by 229 // rotating to have the oldest entry is at the lowest address/index with 230 // newest entry at the highest address. 231 // 232 // Return values: 233 // OK - Buffer data successfully deringed. 234 // FAILED_PRECONDITION - Buffer not initialized, or no readers attached. 235 Status Dering(); 236 237 protected: 238 // Read the oldest stored data chunk of data from the ring buffer to 239 // the provided destination std::span. The number of bytes read is written to 240 // `bytes_read_out`. 241 // 242 // Return values: 243 // OK - Data successfully read from the ring buffer. 244 // FAILED_PRECONDITION - Buffer not initialized. 245 // OUT_OF_RANGE - No entries in ring buffer to read. 246 // RESOURCE_EXHAUSTED - Destination data std::span was smaller number of bytes 247 // than the data size of the data chunk being read. Available destination 248 // bytes were filled, remaining bytes of the data chunk were ignored. 249 Status InternalPeekFront(Reader& reader, 250 std::span<std::byte> data, 251 size_t* bytes_read_out); 252 Status InternalPeekFront(Reader& reader, ReadOutput output); 253 254 // Same as Read but includes the entry's preamble of optional user value and 255 // the varint of the data size 256 Status InternalPeekFrontWithPreamble(Reader& reader, 257 std::span<std::byte> data, 258 size_t* bytes_read_out); 259 Status InternalPeekFrontWithPreamble(Reader& reader, ReadOutput output); 260 261 // Pop and discard the oldest stored data chunk of data from the ring buffer. 262 // 263 // Return values: 264 // OK - Data successfully read from the ring buffer. 265 // FAILED_PRECONDITION - Buffer not initialized. 266 // OUT_OF_RANGE - No entries in ring buffer to pop. 267 Status InternalPopFront(Reader& reader); 268 269 // Get the size in bytes of the next chunk, not including preamble, to be 270 // read. 271 size_t InternalFrontEntryDataSizeBytes(Reader& reader); 272 273 // Get the size in bytes of the next chunk, including preamble and data 274 // chunk, to be read. 275 size_t InternalFrontEntryTotalSizeBytes(Reader& reader); 276 277 // Internal version of Read used by all the public interface versions. T 278 // should be of type ReadOutput. 279 template <typename T> 280 Status InternalRead(Reader& reader, 281 T read_output, 282 bool include_preamble_in_output, 283 uint32_t* user_preamble_out = nullptr); 284 285 private: 286 struct EntryInfo { 287 size_t preamble_bytes; 288 uint32_t user_preamble; 289 size_t data_bytes; 290 }; 291 292 // Push back implementation, which optionally discards front elements to fit 293 // the incoming element. 294 Status InternalPushBack(std::span<const std::byte> data, 295 uint32_t user_preamble_data, 296 bool pop_front_if_needed); 297 298 // Internal function to pop all of the slowest readers. This function may pop 299 // multiple readers if multiple are slow. 300 // 301 // Precondition: This function requires that at least one reader is attached 302 // and has at least one entry to pop. 303 void InternalPopFrontAll(); 304 305 // Returns the slowest reader in the list. 306 // 307 // Precondition: This function requires that at least one reader is attached. 308 Reader& GetSlowestReader(); 309 310 // Get info struct with the size of the preamble and data chunk for the next 311 // entry to be read. 312 EntryInfo FrontEntryInfo(Reader& reader); 313 314 // Get the raw number of available bytes free in the ring buffer. This is 315 // not available bytes for data, since there is a variable size preamble for 316 // each entry. 317 size_t RawAvailableBytes(); 318 319 // Do the basic write of the specified number of bytes starting at the last 320 // write index of the ring buffer to the destination, handing any wrap-around 321 // of the ring buffer. This is basic, raw operation with no safety checks. 322 void RawWrite(std::span<const std::byte> source); 323 324 // Do the basic read of the specified number of bytes starting at the given 325 // index of the ring buffer to the destination, handing any wrap-around of 326 // the ring buffer. This is basic, raw operation with no safety checks. 327 void RawRead(std::byte* destination, size_t source_idx, size_t length_bytes); 328 329 size_t IncrementIndex(size_t index, size_t count); 330 331 std::byte* buffer_; 332 size_t buffer_bytes_; 333 334 size_t write_idx_; 335 const bool user_preamble_; 336 337 // List of attached readers. 338 IntrusiveList<Reader> readers_; 339 340 // Maximum bufer size allowed. Restricted to this to allow index aliasing to 341 // not overflow. 342 static constexpr size_t kMaxBufferBytes = 343 std::numeric_limits<size_t>::max() / 2; 344 }; 345 346 class PrefixedEntryRingBuffer : public PrefixedEntryRingBufferMulti, 347 public PrefixedEntryRingBufferMulti::Reader { 348 public: 349 PrefixedEntryRingBuffer(bool user_preamble = false) PrefixedEntryRingBufferMulti(user_preamble)350 : PrefixedEntryRingBufferMulti(user_preamble) { 351 AttachReader(*this); 352 } 353 }; 354 355 } // namespace ring_buffer 356 } // namespace pw 357