48template <
typename T,
size_t Capacity>
50 static_assert((Capacity & (Capacity - 1)) == 0,
51 "FixedStorage capacity must be power of 2 for efficient modulo. "
52 "Use 64, 128, 256, 512, 1024, 2048, 4096, 8192, etc.");
60 [[nodiscard]]
constexpr size_t capacity() const noexcept {
return Capacity; }
164 template <
typename T,
typename Storage>
166 static_assert(!Storage::is_resizable,
167 "LockFreePolicy requires FixedStorage<T, N>. "
168 "For dynamic capacity, use SingleThreadedPolicy with DynamicStorage.");
173 static constexpr size_t increment(
size_t index,
size_t capacity)
noexcept
175 return (index + 1) & (capacity - 1);
221 template <
typename T,
typename Storage>
226 static constexpr size_t increment(
size_t index,
size_t capacity)
noexcept
228 return (index + 1) % capacity;
266 static constexpr const char*
name =
"Queue (FIFO)";
308 static constexpr const char*
name =
"HistoryBuffer (newest-first)";
425 return m_data[oldest_idx];
431 return m_data[oldest_idx];
456 for (
size_t i = 0; i <
m_count; ++i) {
467 for (
size_t i = 0; i <
m_count; ++i) {
478 void update(
size_t index,
const T& value)
494 std::ranges::fill(
m_data, T {});
508 std::ranges::fill(
m_data, T {});
511 for (
size_t i = 0; i <
count; ++i) {
532 std::vector<T> current_data;
534 for (
size_t i = 0; i <
m_count; ++i) {
539 m_data.resize(new_capacity, T {});
542 size_t to_copy = std::min(current_data.size(), new_capacity);
543 for (
size_t i = 0; i < to_copy; ++i) {
544 m_data[i] = current_data[i];
566 [[nodiscard]]
bool empty()
const {
return false; }
574 std::vector<T> state;
576 for (
size_t i = 0; i <
m_count; ++i) {
588 std::ranges::fill(
m_data, T {});
591 for (
size_t i = 0; i <
count; ++i) {
663 typename StoragePolicy,
668 static_assert(!ConcurrencyPolicy::requires_fixed_storage || !StoragePolicy::is_resizable,
669 "Selected ConcurrencyPolicy requires FixedStorage<T, N>. "
670 "Either: (1) Use SingleThreadedPolicy, or (2) Use FixedStorage.");
680 static constexpr bool is_lock_free = ConcurrencyPolicy::is_thread_safe;
788 size_t oldest_idx = (
m_state.write_index + cap -
count + 1) % cap;
804 size_t actual_idx = (
m_state.write_index + cap - index) % cap;
844 if constexpr (AccessPattern::push_front) {
845 for (
size_t i = 0; i <
count; ++i) {
846 size_t idx = (
m_state.write_index + cap - i) % cap;
850 for (
size_t i = 0; i <
count; ++i) {
851 size_t idx = (
m_state.read_index + i) % cap;
879 if constexpr (AccessPattern::push_front) {
880 for (
size_t i = 0; i <
count; ++i) {
881 size_t idx = (
m_state.write_index + cap - i) % cap;
885 for (
size_t i = 0; i <
count; ++i) {
886 size_t idx = (
m_state.read_index + i) % cap;
915 std::vector<T> result;
919 auto read =
m_state.read_index.load(std::memory_order_acquire);
920 auto write =
m_state.write_index.load(std::memory_order_acquire);
923 while (read != write) {
924 result.push_back(
m_storage.buffer[read]);
925 read = State::increment(read, cap);
929 result.assign(view.begin(), view.end());
939 [[nodiscard]]
bool empty() const noexcept
942 return m_state.read_index.load(std::memory_order_acquire) ==
m_state.write_index.load(std::memory_order_acquire);
955 [[nodiscard]]
size_t size() const noexcept
960 auto write =
m_state.write_index.load(std::memory_order_acquire);
961 auto read =
m_state.read_index.load(std::memory_order_acquire);
962 return (write >= read) ? (write - read) : (cap - read + write);
989 if (new_capacity ==
m_storage.capacity())
1000 size_t to_copy = std::min(current_data.size(), new_capacity);
1001 for (
size_t i = 0; i < to_copy; ++i) {
1005 if constexpr (AccessPattern::push_front) {
1006 m_state.write_index = (to_copy > 0) ? to_copy - 1 : 0;
1008 m_state.write_index = to_copy;
1020 m_state.write_index.store(0, std::memory_order_release);
1021 m_state.read_index.store(0, std::memory_order_release);
1031 const size_t cap =
m_storage.capacity();
1032 auto write =
m_state.write_index.load(std::memory_order_relaxed);
1033 auto next_write = State::increment(write, cap);
1035 if (next_write ==
m_state.read_index.load(std::memory_order_acquire)) {
1040 m_state.write_index.store(next_write, std::memory_order_release);
1047 auto read =
m_state.read_index.load(std::memory_order_relaxed);
1049 if (read ==
m_state.write_index.load(std::memory_order_acquire)) {
1050 return std::nullopt;
1055 State::increment(read,
m_storage.capacity()),
1056 std::memory_order_release);
1063 const size_t cap =
m_storage.capacity();
1064 auto next_write = State::increment(
m_state.write_index, cap);
1066 if (next_write ==
m_state.read_index) {
1070 if constexpr (AccessPattern::push_front) {
1077 m_state.write_index = next_write;
1086 return std::nullopt;
1126template <
typename T,
size_t Capacity>
1139template <
typename T,
size_t Capacity>
1151template <
typename T>
const_reference oldest() const
const T & const_reference
void resize(size_t new_capacity)
Resize buffer capacity.
void restore_state(const std::vector< T > &state)
Restore previously saved state.
std::span< T > linearized_view()
Get mutable linearized view of entire history.
reference operator[](size_t index)
Access element by temporal offset.
std::vector< T > m_linear_view
std::span< const T > linearized_view() const
Get const linearized view.
bool empty() const
Check if buffer is empty (always false for HistoryBuffer)
void overwrite_newest(const T &value)
Overwrite the newest element without advancing position.
void push(const T &value)
Push new value to front of history.
reference oldest()
Get oldest element (same as [capacity-1])
void reset()
Reset buffer to initial state (all zeros)
const_reference newest() const
HistoryBuffer(size_t capacity)
Construct history buffer with specified capacity.
void update(size_t index, const T &value)
Update element at specific index.
const_reference operator[](size_t index) const
size_t capacity() const
Get buffer capacity.
size_t size() const
Get current count (always equals capacity for HistoryBuffer)
std::vector< T > save_state() const
Save current state for later restoration.
void set_initial_conditions(const std::vector< T > &values)
Set initial conditions.
reference newest()
Get newest element (same as [0])
History buffer for difference equations and recursive relations.
RingBuffer()=default
Default construct ring buffer (FixedStorage only) Capacity determined by template parameter.
bool push(const T &value) noexcept(is_lock_free)
Push element into buffer.
const_reference peek_oldest() const
Peek at oldest element without removing.
void reset() noexcept
Clear buffer contents and reset indices.
bool push_lockfree(const T &value) noexcept
RingBuffer(size_t initial_capacity=64)
Construct ring buffer with runtime capacity (DynamicStorage only)
std::span< T > linearized_view() const
Get linearized view of buffer contents.
std::optional< T > pop_singlethread() noexcept
static constexpr bool is_resizable
StoragePolicy storage_type
size_t size() const noexcept
Get approximate element count.
std::span< T > linearized_view_mut()
Get mutable linearized view for modification.
const_reference peek_newest() const
Peek at newest element without removing.
static constexpr bool is_lock_free
std::optional< T > pop() noexcept(is_lock_free)
Pop element from buffer.
std::optional< T > pop_lockfree() noexcept
bool empty() const noexcept
Check if buffer is empty.
bool push_singlethread(const T &value) noexcept
void resize(size_t new_capacity)
Resize buffer capacity (DynamicStorage only)
std::vector< T > m_linearized
void overwrite_newest(const T &value)
Overwrite newest element without advancing write position.
const T & const_reference
std::vector< T > snapshot() const
Thread-safe snapshot of buffer contents.
typename ConcurrencyPolicy::template State< T, StoragePolicy > State
size_t capacity() const noexcept
Get buffer capacity.
static constexpr bool is_delay_line
const_reference operator[](size_t index) const
Access element by index (delay line style)
Policy-driven unified circular buffer implementation.
Constraint for types that can be safely copied bitwise.
size_t capacity() const noexcept
void resize(size_t new_capacity)
std::vector< T > storage_type
static constexpr bool is_resizable
DynamicStorage(size_t initial_capacity=64)
Runtime-resizable storage using std::vector.
std::array< T, Capacity > storage_type
static constexpr size_t capacity_value
static constexpr bool is_resizable
constexpr size_t capacity() const noexcept
Compile-time fixed-capacity storage using std::array.
static constexpr bool pop_front
static constexpr bool push_front
static constexpr const char * name
History buffer semantics (newest sample first)
std::atomic< size_t > write_index
static constexpr size_t increment(size_t index, size_t capacity) noexcept
std::atomic< size_t > read_index
static constexpr bool is_thread_safe
static constexpr bool requires_trivial_copyable
static constexpr bool requires_fixed_storage
Lock-free SPSC (Single Producer Single Consumer) concurrency.
static constexpr const char * name
static constexpr bool pop_front
static constexpr bool push_front
FIFO queue semantics (oldest data first)
static constexpr size_t increment(size_t index, size_t capacity) noexcept
static constexpr bool requires_trivial_copyable
static constexpr bool is_thread_safe
static constexpr bool requires_fixed_storage
Non-atomic single-threaded operation.