blob: e164834af5abff43abad51d89ef6515fb620a17e [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <cstddef>
#include <span>
#include "pw_status/status.h"
namespace pw {
namespace ring_buffer {
// A circular ring buffer for arbitrary length data entries. Each PushBack()
// produces a buffer entry. Each entry consists of a preamble followed by an
// arbitrary length data chunk. The preamble is comprised of an optional user
// preamble byte and an always present varint. The varint encodes the number of
// bytes in the data chunk.
//
// The ring buffer holds the most recent entries stored in the buffer. Once
// filled to capacity, incoming entries bump out the oldest entries to make
// room. Entries are internally wrapped around as needed.
class PrefixedEntryRingBuffer {
public:
typedef Status (*ReadOutput)(std::span<const std::byte>);
PrefixedEntryRingBuffer(bool user_preamble = false)
: buffer_(nullptr),
buffer_bytes_(0),
write_idx_(0),
read_idx_(0),
entry_count_(0),
user_preamble_(user_preamble) {}
// Set the raw buffer to be used by the ring buffer.
//
// Return values:
// OK - successfully set the raw buffer.
// INVALID_ARGUMENT - Argument was nullptr, size zero, or too large.
Status SetBuffer(std::span<std::byte> buffer);
// Removes all data from the ring buffer.
void Clear();
// Write a chunk of data to the ring buffer. If available space is less than
// size of data chunk to be written then silently pop and discard oldest
// stored data chunks until space is available.
//
// Preamble argument is a caller-provided value prepended to the front of the
// entry. It is only used if user_preamble was set at class construction
// time.
//
// Return values:
// OK - Data successfully written to the ring buffer.
// INVALID_ARGUMENT - Size of data to write is zero bytes
// FAILED_PRECONDITION - Buffer not initialized.
// OUT_OF_RANGE - Size of data is greater than buffer size.
Status PushBack(std::span<const std::byte> data,
std::byte user_preamble_data = std::byte(0)) {
return InternalPushBack(data, user_preamble_data, true);
}
// Write a chunk of data to the ring buffer if there is space available.
//
// Preamble argument is a caller-provided value prepended to the front of the
// entry. It is only used if user_preamble was set at class construction
// time.
//
// Return values:
// OK - Data successfully written to the ring buffer.
// INVALID_ARGUMENT - Size of data to write is zero bytes
// FAILED_PRECONDITION - Buffer not initialized.
// OUT_OF_RANGE - Size of data is greater than buffer size.
// RESOURCE_EXHAUSTED - The ring buffer doesn't have space for the data
// without popping off existing elements.
Status TryPushBack(std::span<const std::byte> data,
std::byte user_preamble_data = std::byte(0)) {
return InternalPushBack(data, user_preamble_data, false);
}
// Read the oldest stored data chunk of data from the ring buffer to
// the provided destination std::span. The number of bytes read is written to
// bytes_read
//
// Return values:
// OK - Data successfully read from the ring buffer.
// FAILED_PRECONDITION - Buffer not initialized.
// OUT_OF_RANGE - No entries in ring buffer to read.
// RESOURCE_EXHAUSTED - Destination data std::span was smaller number of bytes
// than the data size of the data chunk being read. Available destination
// bytes were filled, remaining bytes of the data chunk were ignored.
Status PeekFront(std::span<std::byte> data, size_t* bytes_read);
Status PeekFront(ReadOutput output);
// Same as Read but includes the entry's preamble of optional user value and
// the varint of the data size
Status PeekFrontWithPreamble(std::span<std::byte> data, size_t* bytes_read);
Status PeekFrontWithPreamble(ReadOutput output);
// Pop and discard the oldest stored data chunk of data from the ring buffer.
//
// Return values:
// OK - Data successfully read from the ring buffer.
// FAILED_PRECONDITION - Buffer not initialized.
// OUT_OF_RANGE - No entries in ring buffer to pop.
Status PopFront();
// Dering the buffer by reordering entries internally in the buffer by
// rotating to have the oldest entry is at the lowest address/index with
// newest entry at the highest address.
//
// Return values:
// OK - Buffer data successfully deringed.
// FAILED_PRECONDITION - Buffer not initialized.
Status Dering();
// Get the number of variable-length entries currently in the ring buffer.
//
// Return value:
// Entry count.
size_t EntryCount() { return entry_count_; }
// Get the size in bytes of all the current entries in the ring buffer,
// including preamble and data chunk.
size_t TotalUsedBytes() { return buffer_bytes_ - RawAvailableBytes(); }
// Get the size in bytes of the next chunk, not including preamble, to be
// read.
size_t FrontEntryDataSizeBytes();
// Get the size in bytes of the next chunk, including preamble and data
// chunk, to be read.
size_t FrontEntryTotalSizeBytes();
private:
struct EntryInfo {
size_t preamble_bytes;
size_t data_bytes;
};
// Internal version of Read used by all the public interface versions. T
// should be of type ReadOutput.
template <typename T>
Status InternalRead(T read_output, bool get_preamble);
// Push back implementation, which optionally discards front elements to fit
// the incoming element.
Status InternalPushBack(std::span<const std::byte> data,
std::byte user_preamble_data,
bool pop_front_if_needed);
// Get info struct with the size of the preamble and data chunk for the next
// entry to be read.
EntryInfo FrontEntryInfo();
// Get the raw number of available bytes free in the ring buffer. This is
// not available bytes for data, since there is a variable size preamble for
// each entry.
size_t RawAvailableBytes();
// Do the basic write of the specified number of bytes starting at the last
// write index of the ring buffer to the destination, handing any wrap-around
// of the ring buffer. This is basic, raw operation with no safety checks.
void RawWrite(std::span<const std::byte> source);
// Do the basic read of the specified number of bytes starting at the given
// index of the ring buffer to the destination, handing any wrap-around of
// the ring buffer. This is basic, raw operation with no safety checks.
void RawRead(std::byte* destination, size_t source_idx, size_t length_bytes);
size_t IncrementIndex(size_t index, size_t count);
std::byte* buffer_;
size_t buffer_bytes_;
size_t write_idx_;
size_t read_idx_;
size_t entry_count_;
const bool user_preamble_;
// Worst case size for the variable-sized preable that is prepended to
// each entry.
static constexpr size_t kMaxEntryPreambleBytes = sizeof(size_t) + 1;
// Maximum bufer size allowed. Restricted to this to allow index aliasing to
// not overflow.
static constexpr size_t kMaxBufferBytes =
std::numeric_limits<size_t>::max() / 2;
};
} // namespace ring_buffer
} // namespace pw