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;
238template <
typename Policy>
247template <
typename Policy>
248 requires requires { Policy::is_mpsc; }
290 template <
typename T,
typename Storage>
292 static_assert(!Storage::is_resizable,
293 "MPSCPolicy requires FixedStorage<T, N>. "
294 "For dynamic capacity, use SingleThreadedPolicy with DynamicStorage.");
296 static constexpr size_t N = Storage::capacity_value;
305 f.store(
false, std::memory_order_relaxed);
308 static constexpr size_t increment(
size_t index,
size_t capacity)
noexcept
310 return (index + 1) & (capacity - 1);
348 static constexpr const char*
name =
"Queue (FIFO)";
390 static constexpr const char*
name =
"HistoryBuffer (newest-first)";
507 return m_data[oldest_idx];
513 return m_data[oldest_idx];
538 for (
size_t i = 0; i <
m_count; ++i) {
549 for (
size_t i = 0; i <
m_count; ++i) {
576 std::ranges::fill(
m_data, T {});
590 std::ranges::fill(
m_data, T {});
593 for (
size_t i = 0; i <
count; ++i) {
614 std::vector<T> current_data;
616 for (
size_t i = 0; i <
m_count; ++i) {
621 m_data.resize(new_capacity, T {});
624 size_t to_copy = std::min(current_data.size(), new_capacity);
625 for (
size_t i = 0; i < to_copy; ++i) {
626 m_data[i] = current_data[i];
648 [[nodiscard]]
bool empty()
const {
return false; }
656 std::vector<T> state;
658 for (
size_t i = 0; i <
m_count; ++i) {
670 std::ranges::fill(
m_data, T {});
673 for (
size_t i = 0; i <
count; ++i) {
745 typename StoragePolicy,
750 static_assert(!ConcurrencyPolicy::requires_fixed_storage || !StoragePolicy::is_resizable,
751 "Selected ConcurrencyPolicy requires FixedStorage<T, N>. "
752 "Either: (1) Use SingleThreadedPolicy, or (2) Use FixedStorage.");
762 static constexpr bool is_lock_free = ConcurrencyPolicy::is_thread_safe;
763 static constexpr bool is_mpsc = policy_is_mpsc<ConcurrencyPolicy>;
835 [[nodiscard]] std::optional<T>
pop() noexcept
875 size_t oldest_idx = (
m_state.write_index + cap -
count + 1) % cap;
891 size_t actual_idx = (
m_state.write_index + cap - index) % cap;
931 if constexpr (AccessPattern::push_front) {
932 for (
size_t i = 0; i <
count; ++i) {
933 size_t idx = (
m_state.write_index + cap - i) % cap;
937 for (
size_t i = 0; i <
count; ++i) {
938 size_t idx = (
m_state.read_index + i) % cap;
966 if constexpr (AccessPattern::push_front) {
967 for (
size_t i = 0; i <
count; ++i) {
968 size_t idx = (
m_state.write_index + cap - i) % cap;
972 for (
size_t i = 0; i <
count; ++i) {
973 size_t idx = (
m_state.read_index + i) % cap;
1005 std::vector<T> result;
1012 const size_t cap =
m_storage.capacity();
1013 auto read =
m_state.read_index.load(std::memory_order_acquire);
1014 auto write =
m_state.write_index.load(std::memory_order_acquire);
1016 result.reserve(cap);
1017 while (read != write) {
1018 result.push_back(
m_storage.buffer[read]);
1019 read = State::increment(read, cap);
1023 result.assign(view.begin(), view.end());
1033 [[nodiscard]]
bool empty() const noexcept
1036 return m_state.read_index.load(std::memory_order_acquire)
1037 ==
m_state.claim_index.load(std::memory_order_acquire);
1039 return m_state.read_index.load(std::memory_order_acquire)
1040 ==
m_state.write_index.load(std::memory_order_acquire);
1053 [[nodiscard]]
size_t size() const noexcept
1055 const size_t cap =
m_storage.capacity();
1058 auto claim =
m_state.claim_index.load(std::memory_order_acquire);
1059 auto read =
m_state.read_index.load(std::memory_order_acquire);
1060 return (claim - read) & (cap - 1);
1062 auto write =
m_state.write_index.load(std::memory_order_acquire);
1063 auto read =
m_state.read_index.load(std::memory_order_acquire);
1064 return (write >= read) ? (write - read) : (cap - read + write);
1091 if (new_capacity ==
m_storage.capacity())
1102 size_t to_copy = std::min(current_data.size(), new_capacity);
1103 for (
size_t i = 0; i < to_copy; ++i) {
1107 if constexpr (AccessPattern::push_front) {
1108 m_state.write_index = (to_copy > 0) ? to_copy - 1 : 0;
1110 m_state.write_index = to_copy;
1122 m_state.claim_index.store(0, std::memory_order_release);
1123 m_state.read_index.store(0, std::memory_order_release);
1124 for (
auto& f :
m_state.ready_flags)
1125 f.store(
false, std::memory_order_release);
1127 m_state.write_index.store(0, std::memory_order_release);
1128 m_state.read_index.store(0, std::memory_order_release);
1138 const size_t cap =
m_storage.capacity();
1139 auto write =
m_state.write_index.load(std::memory_order_relaxed);
1140 auto next_write = State::increment(write, cap);
1142 if (next_write ==
m_state.read_index.load(std::memory_order_acquire)) {
1147 m_state.write_index.store(next_write, std::memory_order_release);
1154 auto read =
m_state.read_index.load(std::memory_order_relaxed);
1156 if (read ==
m_state.write_index.load(std::memory_order_acquire)) {
1157 return std::nullopt;
1162 State::increment(read,
m_storage.capacity()),
1163 std::memory_order_release);
1170 const size_t cap =
m_storage.capacity();
1171 auto next_write = State::increment(
m_state.write_index, cap);
1173 if (next_write ==
m_state.read_index) {
1177 if constexpr (AccessPattern::push_front) {
1184 m_state.write_index = next_write;
1193 return std::nullopt;
1204 const size_t cap =
m_storage.capacity();
1206 size_t slot =
m_state.claim_index.fetch_add(1, std::memory_order_relaxed) & (cap - 1);
1208 if (
m_state.ready_flags[slot].load(std::memory_order_acquire))
1212 m_state.ready_flags[slot].store(
true, std::memory_order_release);
1219 const size_t cap =
m_storage.capacity();
1220 const size_t slot =
m_state.read_index.load(std::memory_order_relaxed);
1222 if (!
m_state.ready_flags[slot].load(std::memory_order_acquire))
1223 return std::nullopt;
1227 m_state.ready_flags[slot].store(
false, std::memory_order_release);
1228 m_state.read_index.store(State::increment(slot, cap), std::memory_order_release);
1264template <
typename T,
size_t Capacity>
1279template <
typename T,
size_t Capacity>
1292template <
typename T,
size_t Capacity>
1304template <
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_mpsc(const T &value) noexcept
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
bool push(const T &value) noexcept
Push element into buffer.
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_mpsc() noexcept
std::optional< T > pop_lockfree() noexcept
std::optional< T > pop() noexcept
Pop element from buffer.
static constexpr bool is_mpsc
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.
static constexpr bool policy_is_mpsc< Policy >
Specialization for MPSCPolicy.
static constexpr bool policy_is_mpsc
Helper to detect if a policy is MPSC.
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.
std::array< std::atomic< bool >, N > ready_flags
std::atomic< size_t > claim_index
std::atomic< size_t > read_index
static constexpr size_t N
static constexpr size_t increment(size_t index, size_t capacity) noexcept
static constexpr bool is_thread_safe
static constexpr bool requires_fixed_storage
static constexpr bool requires_trivial_copyable
static constexpr bool is_mpsc
Lock-free MPSC (Multi-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.