1 // Copyright 2020 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
19 #include "pw_status/status.h"
22 namespace ring_buffer {
24 // A circular ring buffer for arbitrary length data entries. Each PushBack()
25 // produces a buffer entry. Each entry consists of a preamble followed by an
26 // arbitrary length data chunk. The preamble is comprised of an optional user
27 // preamble byte and an always present varint. The varint encodes the number of
28 // bytes in the data chunk.
30 // The ring buffer holds the most recent entries stored in the buffer. Once
31 // filled to capacity, incoming entries bump out the oldest entries to make
32 // room. Entries are internally wrapped around as needed.
33 class PrefixedEntryRingBuffer {
35 typedef Status (*ReadOutput)(std::span<const std::byte>);
37 PrefixedEntryRingBuffer(bool user_preamble = false)
43 user_preamble_(user_preamble) {}
45 // Set the raw buffer to be used by the ring buffer.
48 // OK - successfully set the raw buffer.
49 // INVALID_ARGUMENT - Argument was nullptr, size zero, or too large.
50 Status SetBuffer(std::span<std::byte> buffer);
52 // Removes all data from the ring buffer.
55 // Write a chunk of data to the ring buffer. If available space is less than
56 // size of data chunk to be written then silently pop and discard oldest
57 // stored data chunks until space is available.
59 // Preamble argument is a caller-provided value prepended to the front of the
60 // entry. It is only used if user_preamble was set at class construction
64 // OK - Data successfully written to the ring buffer.
65 // INVALID_ARGUMENT - Size of data to write is zero bytes
66 // FAILED_PRECONDITION - Buffer not initialized.
67 // OUT_OF_RANGE - Size of data is greater than buffer size.
68 Status PushBack(std::span<const std::byte> data,
69 std::byte user_preamble_data = std::byte(0)) {
70 return InternalPushBack(data, user_preamble_data, true);
73 // Write a chunk of data to the ring buffer if there is space available.
75 // Preamble argument is a caller-provided value prepended to the front of the
76 // entry. It is only used if user_preamble was set at class construction
80 // OK - Data successfully written to the ring buffer.
81 // INVALID_ARGUMENT - Size of data to write is zero bytes
82 // FAILED_PRECONDITION - Buffer not initialized.
83 // OUT_OF_RANGE - Size of data is greater than buffer size.
84 // RESOURCE_EXHAUSTED - The ring buffer doesn't have space for the data
85 // without popping off existing elements.
86 Status TryPushBack(std::span<const std::byte> data,
87 std::byte user_preamble_data = std::byte(0)) {
88 return InternalPushBack(data, user_preamble_data, false);
91 // Read the oldest stored data chunk of data from the ring buffer to
92 // the provided destination std::span. The number of bytes read is written to
96 // OK - Data successfully read from the ring buffer.
97 // FAILED_PRECONDITION - Buffer not initialized.
98 // OUT_OF_RANGE - No entries in ring buffer to read.
99 // RESOURCE_EXHAUSTED - Destination data std::span was smaller number of bytes
100 // than the data size of the data chunk being read. Available destination
101 // bytes were filled, remaining bytes of the data chunk were ignored.
102 Status PeekFront(std::span<std::byte> data, size_t* bytes_read);
104 Status PeekFront(ReadOutput output);
106 // Same as Read but includes the entry's preamble of optional user value and
107 // the varint of the data size
108 Status PeekFrontWithPreamble(std::span<std::byte> data, size_t* bytes_read);
110 Status PeekFrontWithPreamble(ReadOutput output);
112 // Pop and discard the oldest stored data chunk of data from the ring buffer.
115 // OK - Data successfully read from the ring buffer.
116 // FAILED_PRECONDITION - Buffer not initialized.
117 // OUT_OF_RANGE - No entries in ring buffer to pop.
120 // Dering the buffer by reordering entries internally in the buffer by
121 // rotating to have the oldest entry is at the lowest address/index with
122 // newest entry at the highest address.
125 // OK - Buffer data successfully deringed.
126 // FAILED_PRECONDITION - Buffer not initialized.
129 // Get the number of variable-length entries currently in the ring buffer.
133 size_t EntryCount() { return entry_count_; }
135 // Get the size in bytes of all the current entries in the ring buffer,
136 // including preamble and data chunk.
137 size_t TotalUsedBytes() { return buffer_bytes_ - RawAvailableBytes(); }
139 // Get the size in bytes of the next chunk, not including preamble, to be
141 size_t FrontEntryDataSizeBytes();
143 // Get the size in bytes of the next chunk, including preamble and data
144 // chunk, to be read.
145 size_t FrontEntryTotalSizeBytes();
149 size_t preamble_bytes;
153 // Internal version of Read used by all the public interface versions. T
154 // should be of type ReadOutput.
155 template <typename T>
156 Status InternalRead(T read_output, bool get_preamble);
158 // Push back implementation, which optionally discards front elements to fit
159 // the incoming element.
160 Status InternalPushBack(std::span<const std::byte> data,
161 std::byte user_preamble_data,
162 bool pop_front_if_needed);
164 // Get info struct with the size of the preamble and data chunk for the next
166 EntryInfo FrontEntryInfo();
168 // Get the raw number of available bytes free in the ring buffer. This is
169 // not available bytes for data, since there is a variable size preamble for
171 size_t RawAvailableBytes();
173 // Do the basic write of the specified number of bytes starting at the last
174 // write index of the ring buffer to the destination, handing any wrap-around
175 // of the ring buffer. This is basic, raw operation with no safety checks.
176 void RawWrite(std::span<const std::byte> source);
178 // Do the basic read of the specified number of bytes starting at the given
179 // index of the ring buffer to the destination, handing any wrap-around of
180 // the ring buffer. This is basic, raw operation with no safety checks.
181 void RawRead(std::byte* destination, size_t source_idx, size_t length_bytes);
183 size_t IncrementIndex(size_t index, size_t count);
186 size_t buffer_bytes_;
191 const bool user_preamble_;
193 // Worst case size for the variable-sized preable that is prepended to
195 static constexpr size_t kMaxEntryPreambleBytes = sizeof(size_t) + 1;
197 // Maximum bufer size allowed. Restricted to this to allow index aliasing to
199 static constexpr size_t kMaxBufferBytes =
200 std::numeric_limits<size_t>::max() / 2;
203 } // namespace ring_buffer