Files
libsst/Lib/Include/ZSTL/ZRingBuffer.hpp
2026-04-03 00:22:39 -05:00

1379 lines
42 KiB
C++

/*
ZRingBuffer.hpp
Author: Chris Ertel <crertel@762studios.com>
James Russell <jcrussell@762studios.com>
Created: 12/01/2011
Purpose:
Templated array-backed resizable circular buffer implementation.
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#pragma once
#ifndef _ZRINGBUFFER_HPP
#define _ZRINGBUFFER_HPP
#include <ZSTL/ZArray.hpp>
//Default local storage for ring buffer
#ifndef ZRINGBUFFER_DEFAULT_LOCAL_STORAGE
#define ZRINGBUFFER_DEFAULT_LOCAL_STORAGE (100)
#endif
//Default capacity for ring buffer
#ifndef ZRINGBUFFER_DEFAULT_CAPACITY
#define ZRINGBUFFER_DEFAULT_CAPACITY (100)
#endif
//Resize factor for ZRingBuffer when operations are performed that require an increase in capacity
#ifndef ZRINGBUFFER_CAPACITY_RESIZE_FACTOR
#define ZRINGBUFFER_CAPACITY_RESIZE_FACTOR (2.0)
#endif
////////////////////////////////
/* ZRingBuffer Implementation */
////////////////////////////////
//Forward declaration of ZRingBuffer
template <typename T, typename P, typename A>
class ZRingBuffer;
/*
Forward Declaration of ZRingBuffer Method Implementation Structures
Existence of these structures allows for template specialization of
individual methods of the ZRingBuffer class. In order to specialize
a single method of ZRingBuffer, simply specialize the corresponding
method implementation structure.
*/
//Forward declaration of ZRingBuffer<T, P, A>::At struct
template <typename T, typename P, typename A>
struct ZRingBuffer_AtImpl {
inline static T& Call( const ZRingBuffer<T, P, A>& _self, const size_t _index);
};
//Forward declaration of ZRingBuffer<T, P, A>::Back struct
template <typename T, typename P, typename A>
struct ZRingBuffer_BackImpl {
inline static T& Call( const ZRingBuffer<T, P, A>& _self );
};
//Forward declaration of ZRingBuffer<T, P, A>::Capacity struct
template <typename T, typename P, typename A>
struct ZRingBuffer_CapacityImpl {
inline static size_t Call( const ZRingBuffer<T, P, A>& _self );
};
//Forward declaration of ZRingBuffer<T, P, A>::Clear struct
template <typename T, typename P, typename A>
struct ZRingBuffer_ClearImpl {
inline static void Call( ZRingBuffer<T, P, A>& _self );
inline static void Call( ZRingBuffer<T, P, A>& _self, size_t _newCapacity );
};
//Forward declaration of ZRingBuffer<T, P, A>::Copy struct
template <typename T, typename P, typename A>
struct ZRingBuffer_CopyImpl {
template <typename O, typename B> inline static void Call( ZRingBuffer<T, P, A>& _self, const ZRingBuffer<T, O, B>& _other );
};
//Forward declaration of ZRingBuffer<T, P, A>::Empty struct
template <typename T, typename P, typename A>
struct ZRingBuffer_EmptyImpl {
inline static bool Call( const ZRingBuffer<T, P, A>& _self );
};
//Forward declaration of ZRingBuffer<T, P, A>::Equals struct
template <typename T, typename P, typename A>
struct ZRingBuffer_EqualsImpl {
template <typename O, typename B> inline static bool Call( const ZRingBuffer<T, P, A>& _self, const ZRingBuffer<T, O, B>& _other );
};
//Forward declaration of ZRingBuffer<T, P, A>::Erase struct
template <typename T, typename P, typename A>
struct ZRingBuffer_EraseImpl {
inline static T Call( ZRingBuffer<T, P, A>& _self, size_t _index);
inline static void Call( ZRingBuffer<T, P, A>& _self, size_t _i, size_t _j );
};
//Forward declaration of ZRingBuffer<T, P, A>::Front struct
template <typename T, typename P, typename A>
struct ZRingBuffer_FrontImpl {
inline static T& Call( const ZRingBuffer<T, P, A>& _self );
};
//Forward declaration of ZRingBuffer<T, P, A>::Full struct
template <typename T, typename P, typename A>
struct ZRingBuffer_FullImpl {
inline static bool Call( const ZRingBuffer<T, P, A>& _self );
};
//Forward declaration of ZRingBuffer<T, P, A>::Insert struct
template <typename T, typename P, typename A>
struct ZRingBuffer_InsertImpl {
inline static void Call ( ZRingBuffer<T, P, A>& _self, size_t _index, const T& _value, size_t _count );
template <typename B> inline static void Call( ZRingBuffer<T, P, A>& _self, size_t _index, const ZArray<T, B>& _array, size_t _start, size_t _count );
template <typename O, typename B> inline static void Call( ZRingBuffer<T, P, A>& _self, size_t _index, const ZRingBuffer<T, O, B>& _other, size_t _start, size_t _count );
};
//Forward declaration of ZRingBuffer<T, P, A>::PopBack struct
template <typename T, typename P, typename A>
struct ZRingBuffer_PopBackImpl {
inline static T Call( ZRingBuffer<T, P, A>& _self );
};
//Forward declaration of ZRingBuffer<T, P, A>::PopFront struct
template <typename T, typename P, typename A>
struct ZRingBuffer_PopFrontImpl {
inline static T Call( ZRingBuffer<T, P, A>& _self );
};
//Forward declaration of ZRingBuffer<T, P, A>::PushBack struct
template <typename T, typename P, typename A>
struct ZRingBuffer_PushBackImpl {
inline static void Call( ZRingBuffer<T, P, A>& _self, const T& _value );
};
//Forward declaration of ZRingBuffer<T, P, A>::PushFront struct
template <typename T, typename P, typename A>
struct ZRingBuffer_PushFrontImpl {
inline static void Call( ZRingBuffer<T, P, A>& _self, const T& _value );
};
//Forward declaration of ZRingBuffer<T, P, A>::Reserve struct
template <typename T, typename P, typename A>
struct ZRingBuffer_ReserveImpl {
inline static void Call( ZRingBuffer<T, P, A>& _self, size_t _newCapacity );
};
//Forward declaration of ZRingBuffer<T, P, A>::Size struct
template <typename T, typename P, typename A>
struct ZRingBuffer_SizeImpl {
inline static size_t Call( const ZRingBuffer<T, P, A>& _self );
};
//Forward declaration of ZRingBuffer<T, P, A>::TryPushBack struct
template <typename T, typename P, typename A>
struct ZRingBuffer_TryPushBackImpl {
inline static bool Call( ZRingBuffer<T, P, A>& _self, const T& _value );
};
//Forward declaration of ZRingBuffer<T, P, A>::TryPushFront struct
template <typename T, typename P, typename A>
struct ZRingBuffer_TryPushFrontImpl {
inline static bool Call( ZRingBuffer<T, P, A>& _self, const T& _value );
};
/////////////////////////////////////////
/* ZRingBuffer Overflow Policy Structs */
/////////////////////////////////////////
//When used for template parameter P, then overflow is never checked for
struct ZRingBuffer_OverflowUnsafe {};
//When used for template parameter P, then overflow causes an assert
struct ZRingBuffer_OverflowAssert {};
//When used for template parameter P, then overflow is ignored and the new element is dropped
struct ZRingBuffer_OverflowIgnore {};
//When used for template parameter P, then overflow causes the front element to be dropped
struct ZRingBuffer_OverflowDropFront {};
//When used for template parameter P, then overflow causes the back element to be dropped
struct ZRingBuffer_OverflowDropBack {};
//When used for template parameter P, then overflow causes the current element to be overwritten
struct ZRingBuffer_OverflowOverwrite {};
//When used for template parameter P, then overflow evicts the first element (PushBack) or last element (PushFront)
struct ZRingBuffer_OverflowEvict {};
//When used for template parameter P, then overflow causes the storage to grow
struct ZRingBuffer_OverflowGrow {};
/////////////////////////////
/* ZRingBuffer Declaration */
/////////////////////////////
/*
Templated array-backed circular buffer implementation.
The template parameter T is the type contained in the buffer.
The template parameter P is the overflow policy for the ring buffer. Overflow only occurs during
push and pop operations - other methods of adding (such as insert) to the ring buffer will always grow the
buffer.
The template parameter A is the allocator that will be used by the underlying array.
*/
template <typename T, typename P = ZRingBuffer_OverflowGrow, typename A = ZArrayAllocator<T, ZRINGBUFFER_DEFAULT_LOCAL_STORAGE> >
class ZRingBuffer
{
friend struct ZRingBuffer_AtImpl<T, P, A>;
friend struct ZRingBuffer_BackImpl<T, P, A>;
friend struct ZRingBuffer_CapacityImpl<T, P, A>;
friend struct ZRingBuffer_ClearImpl<T, P, A>;
friend struct ZRingBuffer_CopyImpl<T, P, A>;
friend struct ZRingBuffer_EmptyImpl<T, P, A>;
friend struct ZRingBuffer_EqualsImpl<T, P, A>;
friend struct ZRingBuffer_EraseImpl<T, P, A>;
friend struct ZRingBuffer_FrontImpl<T, P, A>;
friend struct ZRingBuffer_FullImpl<T, P, A>;
friend struct ZRingBuffer_InsertImpl<T, P, A>;
friend struct ZRingBuffer_PopBackImpl<T, P, A>;
friend struct ZRingBuffer_PopFrontImpl<T, P, A>;
friend struct ZRingBuffer_PushBackImpl<T, P, A>;
friend struct ZRingBuffer_PushFrontImpl<T, P, A>;
friend struct ZRingBuffer_ReserveImpl<T, P, A>;
friend struct ZRingBuffer_SizeImpl<T, P, A>;
friend struct ZRingBuffer_TryPushFrontImpl<T, P, A>;
friend struct ZRingBuffer_TryPushBackImpl<T, P, A>;
protected:
//Backing array of the ring buffer
ZArray<T, A> Buffer;
//Current number of elements in the ring buffer
size_t BufferSize;
//Index into the backing array of the front element of the ring buffer
size_t FrontIndex;
//Index into the backing array of the back element of the ring buffer
size_t BackIndex;
//Aligns the buffer with front index at zero
inline void AlignBuffer()
{
//If FrontIndex is set to zero, then we are aligned
if (FrontIndex == 0)
return;
ZArray<T, A> copy(Buffer.Capacity());
copy.Resize(Buffer.Size());
for (size_t num = 0; num < (size_t)BufferSize; num++)
copy.Data()[num] = At(num);
Buffer = copy;
FrontIndex = 0;
BackIndex = BufferSize - 1;
}
//Integrity Check
inline void CheckIntegrity() const
{
#if ZSTL_CHECK_INTEGRITY
ZSTL_ASSERT(FrontIndex < Buffer.Size(), "ZRingBuffer Error: Front index exceeds capacity!");
ZSTL_ASSERT(BackIndex < Buffer.Size(), "ZRingBuffer Error: Back index exceeds capacity!");
#endif
}
//Decrements the BackIndex (decreases size)
inline ZRingBuffer<T, P, A>& DecrementBack()
{
//The back index does not move if we remove the last element
if (BufferSize != 1)
{
if (BackIndex == 0)
BackIndex = Buffer.Size() - 1;
else
BackIndex--;
}
BufferSize--;
return *this;
}
//Decrements the FrontIndex (increases size)
inline ZRingBuffer<T, P, A>& DecrementFront()
{
//The first element push does not require moving the back index
if (BufferSize != 0)
{
if (FrontIndex == 0)
FrontIndex = Buffer.Size() - 1;
else
FrontIndex--;
}
BufferSize++;
return *this;
}
//Increments the BackIndex (increases size)
inline ZRingBuffer<T, P, A>& IncrementBack()
{
//The first element push does not require moving the back index
if (BufferSize != 0)
{
if (BackIndex == Buffer.Size() - 1)
BackIndex = 0;
else
BackIndex++;
}
BufferSize++;
return *this;
}
//Increments the FrontIndex (decreases size)
inline ZRingBuffer<T, P, A>& IncrementFront()
{
//The front index does not move if we remove the last element
if (BufferSize != 1)
{
if (FrontIndex == Buffer.Size() - 1)
FrontIndex = 0;
else
FrontIndex++;
}
BufferSize--;
return *this;
}
public:
/*
Default constructor. Initializes an empty ring buffer with a default capacity.
*/
ZRingBuffer()
: Buffer(ZRINGBUFFER_DEFAULT_CAPACITY),
BufferSize(0), FrontIndex(0), BackIndex(0)
{ Buffer.Resize(ZRINGBUFFER_DEFAULT_CAPACITY); }
/*
Parameterized constructor for ZRingBuffer. Initializes empty, but allows specification of capacity.
@param _capacity - initial capacity for the ring buffer
*/
ZRingBuffer(size_t _capacity)
: Buffer(_capacity),
BufferSize(0), FrontIndex(0), BackIndex(0)
{ Buffer.Resize(_capacity); }
/*
Parameterized constructor to use for ZRingBuffer. Allows default assignment of
contained elements. The starting size of the buffer is equal to the array size. The
starting capacity of the buffer is equal to the array capacity.
@param _storage - ZArray containing default set of elements
*/
ZRingBuffer(const ZArray<T, A>& _storage)
: Buffer(_storage),
BufferSize(_storage.Size()), FrontIndex(0), BackIndex(_storage.Size() - 1)
{ Buffer.Resize(_storage.Capacity()); }
/*
Destructor.
*/
~ZRingBuffer() { }
/*
[] Operator overload. Gets a value from the buffer. Equivalent to
a call to At.
@param _index - the integer index into the buffer
@return (T&) - reference to a value contained in the buffer
*/
T& operator [] (const size_t _index) const
{ return At(_index); }
/*
= Operator overload. Sets this buffer equal to the other by making
a copy. Equivalent to a call to Copy.
Even though the code is the same as the more generic version below,
this is needed to prevent a default assignment operator from being constructed.
@param _other - the buffer to be set equal to
@return (void)
*/
ZRingBuffer<T, P, A>& operator = (const ZRingBuffer<T, P, A>& _other)
{ Copy(_other); return *this; }
/*
= Operator overload. Sets this buffer equal to the other by making
a copy. Equivalent to a call to Copy.
@param _other - the buffer to be set equal to
@return (void)
*/
template <typename B>
ZRingBuffer<T, P, A>& operator = (const ZRingBuffer<T, B>& _other)
{ Copy(_other); return *this; }
/*
== Operator overload. Performs an element comparison on the two buffers.
Equivalent to a call to Equals.
@param B - the type of allocator for the other buffer
@param _other - the buffer to compare to
@return (bool) - true if this buffer is equivalent to the other
*/
template <typename B>
bool operator == (const ZRingBuffer<T, B>& _other) const
{ return Equals(_other); }
/*
!= Operator overload. Performs an element comparison on the two buffers.
Equivalent to a call to !Equals.
@param B - the type of the allocator for the other buffer
@param _other - the buffer to compare to
@return (bool) - false if this buffer is equivalent to the other
*/
template <typename B>
bool operator != (const ZRingBuffer<T, B>& _other) const
{ return !Equals(_other); }
/*
public ZRingBuffer<T, P, A>::Actual
Function that gives an actual index into the underlying array given an index into
the ring buffer. Will also perform bounds checking and assert if out of bounds.
@param _index - index
@return (size_t) - unsigned index into the backing array
*/
size_t ActualIndex(size_t _index) const
{
size_t index;
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(_index < BufferSize, "ZRingBuffer: Out of bounds access!");
#endif
index = ((size_t)_index + FrontIndex) % Buffer.Size();
#if !ZSTL_DISABLE_RUNTIME_CHECKS
if (FrontIndex < BackIndex)
{
ZSTL_ASSERT(((index >= FrontIndex) && (index <= BackIndex)), "ZRingBuffer: Out of bounds access!");
}
else //FrontIndex >= BackIndex
{
ZSTL_ASSERT(((index >= FrontIndex) || (index <= BackIndex)), "ZRingBuffer: Out of bounds access!");
}
#endif
return index;
}
/*
public ZRingBuffer<T, P, A>::Allocator
Gets a reference to the allocator used by this buffer.
@return (A&) - the allocator used by this buffer
*/
A& Allocator()
{ return Buffer.Allocator(); }
/*
public ZRingBuffer<T, P, A>::Array
Gets a reference to the array that backs this buffer. The elements are not guaranteed to
be stored in order.
@return (ZArray<T, P, A>&) - the array backing this buffer
*/
const ZArray<T, A>& Array() const
{ return Buffer; }
/*
public ZRingBuffer<T, P, A>::At
Gets a reference to the element indexed by the provided index. The index 0 refers
to the front element in the buffer, the index 1 the following, and so on up to the back
element. This allows random access of the buffer, though more overhead is involved than
random access of ZArray.
@param _index - index into the buffer
@return (T&) - element at the indexed location
@assert - if _index is out of bounds
*/
T& At(const size_t _index) const
{ return ZRingBuffer_AtImpl<T, P, A>::Call(*this, _index); }
/*
public ZRingBuffer<T, P, A>::Back
Gets a reference to the back element of the buffer.
@return (T&) - reference to back element in buffer
@assert - if the buffer is empty
*/
T& Back() const
{ return ZRingBuffer_BackImpl<T, P, A>::Call(*this); }
/*
public ZRingBuffer<T, P, A>::Capacity
Gets the capacity of the ring buffer, which is the maximum number of elements that
can be stored before a resize is required.
@return (size_t) - the capacity of the ring buffer
*/
size_t Capacity() const
{ return ZRingBuffer_CapacityImpl<T, P, A>::Call(*this); }
/*
public ZRingBuffer<T, P, A>::Clear
Clears out all elements from the buffer, keeping the currently allocated storage.
@return (void)
*/
void Clear()
{ ZRingBuffer_ClearImpl<T, P, A>::Call(*this); }
/*
public ZRingBuffer<T, P, A>::Clear
Clears out all elements from the buffer and will ensure that the capacity is
at least as much as the provided value. Reallocates the internal storage if necessary.
@param _newCapacity - the new capacity value
@return (void)
*/
void Clear(size_t _newCapacity)
{ ZRingBuffer_ClearImpl<T, P, A>::Call(*this, _newCapacity); }
/*
public ZRingBuffer<T, P, A>::Copy
Copies the contents of another buffer into this one.
@param O - overflow policy for the other buffer
@param B - allocator type for the other buffer
@param _other - the ring buffer to copy
@return (void)
*/
template <typename O, typename B>
void Copy(const ZRingBuffer<T, O, B>& _other)
{ ZRingBuffer_CopyImpl<T, P, A>::template Call<O, B>(*this, _other); }
/*
public ZRingBuffer<T, P, A>::Empty
Tests whether or not the ring buffer is empty.
@return (bool) - true is empty, false if not empty
*/
bool Empty() const
{ return ZRingBuffer_EmptyImpl<T, P, A>::Call(*this); }
/*
public ZRingBuffer<T, P, A>::Equals
Tests whether or not this buffer is equivalent to another. The two are considered
equivalent if they contain equivalent elements in the same order.
@param O - overflow policy for the other buffer
@param B - allocator type for the other buffer
@param _other - the other buffer
@return (bool) - true if equal, false otherwise
*/
template <typename O, typename B>
bool Equals(const ZRingBuffer<T, O, B>& _other)
{ return ZRingBuffer_EqualsImpl<T, P, A>::template Call<O, B>(*this, _other); }
/*
public ZRingBuffer<T, P, A>::Erase
Erases a value present in the buffer at the given index.
@param _index - the index at which to remove the element from the array
@return (T) - the element removed
@assert - if the index is out of range
*/
T Erase(size_t _index)
{ return ZRingBuffer_EraseImpl<T, P, A>::Call(*this, _index); }
/*
public ZRingBuffer<T, P, A>::Erase
Erase function. Removes elements from the buffer between the given indices.
@param _i - first index
@param _j - second index (exclusive)
@return (void)
@assert - if either index is out of range or if _j < _i
*/
void Erase(size_t _i, size_t _j)
{ ZRingBuffer_EraseImpl<T, P, A>::Call(*this, _i, _j); }
/*
public ZRingBuffer<T, P, A>::Front
Gets a reference to the front element of the buffer.
@return (T&) - reference to front element in buffer.
@assert - if the buffer is empty
*/
T& Front() const
{ return ZRingBuffer_FrontImpl<T, P, A>::Call(*this); }
/*
public ZRingBuffer<T, P, A>::Full
Tests whether or not the ring buffer is full. If full, the overflow policy
determines what is done whenever any operation that adds elements to the
buffer.
@return (bool) - true if full, false if not full
*/
bool Full() const
{ return ZRingBuffer_FullImpl<T, P, A>::Call(*this); }
/*
public ZRingBuffer<T, P, A>::Insert
Inserts the given value at the specified location. Ring buffers are
not well suited to perform this operation quickly, so use sparingly.
@param _index - the index at which to perform the insertion
@param _value - the value to insert
@return (void)
@assert - if _index is out of range
*/
void Insert(size_t _index, const T& _value)
{ ZRingBuffer_InsertImpl<T, P, A>::Call(*this, _index, _value, 1); }
/*
public ZRingBuffer<T, P, A>::Insert
Inserts the given value at the specified location. Ring buffers are
not well suited to perform this operation quickly, so use sparingly.
@param _index - the index at which to perform the insertion
@param _value - the value to insert
@param _count - the number of times to insert _value
@return (void)
@assert - if _index is out of range
*/
void Insert(size_t _index, const T& _value, size_t _count)
{ ZRingBuffer_InsertImpl<T, P, A>::Call(*this, _index, _value, _count); }
/*
public ZRingBuffer<T, P, A>::Insert
Inserts the given array at the specified location. Copies
the values into the buffer. Ring buffers are not well suited
to perform this operation quickly, so use sparingly.
@param _index - the index at which to perform the insertion
@param _array - the array to insert
@return (void)
@assert - if _index is out of range
*/
template <typename B>
void Insert(size_t _index, const ZArray<T, B>& _array)
{ ZRingBuffer_InsertImpl<T, P, A>::template Call<B>(*this, _index, _array, 0, _array.Size()); }
/*
public ZRingBuffer<T, P, A>::Insert
Inserts data from the given array at the specified location. Copies
the values into the buffer. Ring buffers are not well suited
to perform this operation quickly, so use sparingly.
@param _index - the index at which to perform the insertion
@param _array - the array to insert
@param _start - the index to start getting values from
@param _count - the number of values to get
@return (void)
@assert - if _index is out of range
*/
template <typename B>
void Insert(size_t _index, const ZArray<T, B>& _array, size_t _start, size_t _count)
{ ZRingBuffer_InsertImpl<T, P, A>::template Call<B>(*this, _index, _array, _start, _count); }
/*
public ZRingBuffer<T, P, A>::Insert
Inserts the given buffer at the specified location. Copies
the values into the buffer. Ring buffers are not well suited
to perform this operation quickly, so use sparingly.
@param O - overflow policy for the other buffer
@param B - allocator type for the other buffer
@param _index - the index at which to perform the insertion
@param _other - the buffer to insert
@return (void)
@assert - if _index is out of range
*/
template <typename O, typename B>
void Insert(size_t _index, const ZRingBuffer<T, O, B>& _other)
{ ZRingBuffer_InsertImpl<T, P, A>::template Call<O, B>(*this, _index, _other, 0, _other.Size()); }
/*
public ZRingBuffer<T, P, A>::Insert
Inserts data from the given buffer at the specified location. Copies
the values into the buffer. Ring buffers are not well suited
to perform this operation quickly, so use sparingly.
@param O - overflow policy for the other buffer
@param B - allocator type for the other buffer
@param _index - the index at which to perform the insertion
@param _other - the buffer to insert
@param _start - the index to start getting values from
@param _count - the number of values to get
@return (void)
@assert - if _index is out of range
*/
template <typename O, typename B>
void Insert(size_t _index, const ZRingBuffer<T, O, B>& _other, size_t _start, size_t _count)
{ ZRingBuffer_InsertImpl<T, P, A>::template Call<O, B>(*this, _index, _other, _start, _count); }
/*
public ZRingBuffer<T, P, A>::PopBack
Function to pop the back element off of the ring buffer.
@return (T) - the back element in the buffer
@assert - if the buffer is empty
*/
T PopBack()
{ return ZRingBuffer_PopBackImpl<T, P, A>::Call(*this); }
/*
public ZRingBuffer<T, P, A>::PopFront
Function to pop the front element off of the ring buffer.
@return (T) - the front element in the buffer
@assert - if the buffer is empty
*/
T PopFront()
{ return ZRingBuffer_PopFrontImpl<T, P, A>::Call(*this); }
/*
public ZRingBuffer<T, P, A>::PushBack
Pushes an element onto the back of this ring buffer.
@param _value - element to push onto back of ring buffer
@return (void)
@assert - if the buffer is full
*/
void PushBack(const T& _value)
{ ZRingBuffer_PushBackImpl<T, P, A>::Call(*this, _value); }
/*
public ZRingBuffer<T, P, A>::PushFront
Pushes an element onto the front of this ring buffer.
@param _value - element to push onto front of ring buffer
@return (void)
@assert - if the buffer is full
*/
void PushFront(const T& _value)
{ ZRingBuffer_PushFrontImpl<T, P, A>::Call(*this, _value); }
/*
public ZRingBuffer<T, P, A>::Reserve
Changes the capacity of the backing array to accommodate the given size. Existing items in the
buffer remain in order and unaffected, though copy assignment may be required.
@param _newCapacity - new capacity to reserve in ring buffer
@return (void)
@assert - if the new capacity is smaller than the current size
*/
void Reserve(size_t _newCapacity)
{ ZRingBuffer_ReserveImpl<T, P, A>::Call(*this, _newCapacity); }
/*
public ZRingBuffer<T, P, A>::Size
Gets the size of the ring buffer, which is the current number of contained elements.
@return (size_t) - the number of elements in the ring buffer.
*/
size_t Size() const
{ return ZRingBuffer_SizeImpl<T, P, A>::Call(*this); }
/*
public ZRingBuffer<T, P, A>::TryPushFront
Tries to push an element onto the ring buffer, returning success or failure.
If the element is unable to be pushed onto the buffer, no change is made to the buffer.
@param _value - element to push onto front of ring buffer.
@return (bool) - true if push was successful, false if not.
*/
bool TryPushFront(const T& _value)
{ return ZRingBuffer_TryPushFrontImpl<T, P, A>::Call(*this, _value); }
/*
public ZRingBuffer<T, P, A>::TryPushBack
Tries to push an element onto the ring buffer, returning success or failure.
If the element is unable to be pushed onto the buffer, no change is made to the buffer.
@param _value - element to push onto the back of the ring buffer.
@return (bool) - true if push was successful, false if not.
*/
bool TryPushBack(const T& _value)
{ return ZRingBuffer_TryPushBackImpl<T, P, A>::Call(*this, _value); }
};
////////////////////////////////////////////
/* Non-Specialized Method Implementations */
////////////////////////////////////////////
template <typename T, typename P, typename A>
T& ZRingBuffer_AtImpl<T, P, A>::Call(const ZRingBuffer<T, P, A>& _self, const size_t _index)
{
return _self.Buffer.Data()[_self.ActualIndex(_index)]; //Bounds checking will be done by ActualIndex
}
/*************************************************************************/
template <typename T, typename P, typename A>
T& ZRingBuffer_BackImpl<T, P, A>::Call(const ZRingBuffer<T, P, A>& _self)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT( _self.BufferSize > 0, "ZRingBuffer::Back() called with no elements present!");
#endif
return _self.Buffer.Data()[_self.BackIndex];
}
/*************************************************************************/
template <typename T, typename P, typename A>
size_t ZRingBuffer_CapacityImpl<T, P, A>::Call(const ZRingBuffer<T, P, A>& _self)
{
return _self.Buffer.Size();
}
/*************************************************************************/
template <typename T, typename P, typename A>
void ZRingBuffer_ClearImpl<T, P, A>::Call( ZRingBuffer<T, P, A>& _self )
{
_self.BufferSize = 0;
_self.FrontIndex = 0;
_self.BackIndex = 0;
}
/*************************************************************************/
template <typename T, typename P, typename A>
void ZRingBuffer_ClearImpl<T, P, A>::Call( ZRingBuffer<T, P, A>& _self, size_t _newCapacity )
{
_self.Buffer.Resize(_newCapacity);
_self.BufferSize = 0;
_self.FrontIndex = 0;
_self.BackIndex = 0;
}
/*************************************************************************/
template <typename T, typename P, typename A> template <typename O, typename B>
void ZRingBuffer_CopyImpl<T, P, A>::Call( ZRingBuffer<T, P, A>& _self, const ZRingBuffer<T, O, B>& _other )
{
_self.Buffer.Copy(_other.Buffer);
_self.BufferSize = _other.BufferSize;
_self.FrontIndex = _other.FrontIndex;
_self.BackIndex = _other.BackIndex;
}
/*************************************************************************/
template <typename T, typename P, typename A>
bool ZRingBuffer_EmptyImpl<T, P, A>::Call(const ZRingBuffer<T, P, A>& _self)
{
return _self.BufferSize == 0;
}
/*************************************************************************/
template <typename T, typename P, typename A> template <typename O, typename B>
bool ZRingBuffer_EqualsImpl<T, P, A>::Call( const ZRingBuffer<T, P, A>& _self, const ZRingBuffer<T, O, B>& _other )
{
size_t i;
//Easy out #1
if (&_self == &_other)
return true;
//Easy out #2
if (_self.Size() != _other.Size())
return false;
//Element by element comparison
for (i = 0; i < _self.Size(); i++)
{
if (_self.At(i) != _other.At(i))
return false;
}
return true;
}
/*************************************************************************/
template <typename T, typename P, typename A>
T ZRingBuffer_EraseImpl<T, P, A>::Call(ZRingBuffer<T, P, A>& _self, const size_t _index)
{
//We are going to do this in a very naive fashion, as this container is not intended for these
//kinds of operations anyhow. Anyone who wishes to optimize this at a later date is welcome.
_self.AlignBuffer();
_self.BufferSize--;
_self.BackIndex--;
return _self.Buffer.Erase(_index);
}
/*************************************************************************/
template <typename T, typename P, typename A>
void ZRingBuffer_EraseImpl<T, P, A>::Call( ZRingBuffer<T, P, A>& _self, size_t _i, size_t _j )
{
//We are going to do this in a very naive fashion, as this container is not intended for these
//kinds of operations anyhow. Anyone who wishes to optimize this at a later date is welcome.
size_t count;
_self.AlignBuffer();
count = (size_t)(_j - _i);
_self.BufferSize = _self.BufferSize - count;
_self.BackIndex = _self.BackIndex - count;
_self.Buffer.Erase(_i, _j);
}
/*************************************************************************/
template <typename T, typename P, typename A>
T& ZRingBuffer_FrontImpl<T, P, A>::Call(const ZRingBuffer<T, P, A>& _self)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT( _self.BufferSize > 0, "ZRingBuffer::Front() called with no elements present!");
#endif
return _self.Buffer.Data()[_self.FrontIndex];
}
/*************************************************************************/
template <typename T, typename P, typename A>
bool ZRingBuffer_FullImpl<T, P, A>::Call(const ZRingBuffer<T, P, A>& _self)
{
return _self.BufferSize == _self.Buffer.Size();
}
/*************************************************************************/
template <typename T, typename P, typename A>
void ZRingBuffer_InsertImpl<T, P, A>::Call( ZRingBuffer<T, P, A>& _self, size_t _index, const T& _value, size_t _count )
{
//We are going to do this in a very naive fashion, as this container is not intended for these
//kinds of operations anyhow. Anyone who wishes to optimize this at a later date is welcome.
_self.AlignBuffer();
_self.Buffer.Insert(_index, _value, _count);
_self.Buffer.Resize(_self.Buffer.Capacity()); //In case the insert caused a grow
_self.BackIndex = _self.BackIndex + _count;
_self.BufferSize = _self.BufferSize + _count;
}
/*************************************************************************/
template <typename T, typename P, typename A> template <typename B>
void ZRingBuffer_InsertImpl<T, P, A>::Call( ZRingBuffer<T, P, A>& _self, size_t _index, const ZArray<T, B>& _array, size_t _start, size_t _count )
{
//We are going to do this in a very naive fashion, as this container is not intended for these
//kinds of operations anyhow. Anyone who wishes to optimize this at a later date is welcome.
_self.AlignBuffer();
_self.Buffer.Insert(_index, _array, _start, _count);
_self.Buffer.Resize(_self.Buffer.Capacity()); //In case the insert caused a grow
_self.BackIndex = _self.BackIndex + _count;
_self.BufferSize = _self.BufferSize + _count;
}
/*************************************************************************/
template <typename T, typename P, typename A> template <typename O, typename B>
void ZRingBuffer_InsertImpl<T, P, A>::Call( ZRingBuffer<T, P, A>& _self, size_t _index, const ZRingBuffer<T, O, B>& _other, size_t _start, size_t _count )
{
//We are going to do this in a very naive fashion, as this container is not intended for these
//kinds of operations anyhow. Anyone who wishes to optimize this at a later date is welcome.
ZRingBuffer<T, P, A> copy(_other);
copy.AlignBuffer();
_self.AlignBuffer();
_self.Buffer.Insert(_index, copy.Array(), _start, _count);
_self.Buffer.Resize(_self.Buffer.Capacity()); //In case the insert caused a grow
_self.BackIndex = _self.BackIndex + _count;
_self.BufferSize = _self.BufferSize + _count;
}
/*************************************************************************/
template <typename T, typename P, typename A>
T ZRingBuffer_PopBackImpl<T, P, A>::Call(ZRingBuffer<T, P, A>& _self)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT( _self.BufferSize > 0, "ZRingBuffer::PopBack() caused underflow!");
#endif
size_t index = _self.BackIndex;
_self.DecrementBack();
_self.CheckIntegrity();
return _self.Buffer.Data()[index];
}
/*************************************************************************/
template <typename T, typename P, typename A>
T ZRingBuffer_PopFrontImpl<T, P, A>::Call(ZRingBuffer<T, P, A>& _self)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT( _self.BufferSize > 0, "ZRingBuffer::PopFront() caused underflow!");
#endif
size_t index = _self.FrontIndex;
_self.IncrementFront();
_self.CheckIntegrity();
return _self.Buffer.Data()[index];
}
/*************************************************************************/
template <typename T, typename P, typename A>
void ZRingBuffer_PushBackImpl<T, P, A>::Call(ZRingBuffer<T, P, A>& _self, const T& _value)
{
URFP(_self); URFP(_value);
P::UnimplementedMethod; //This will cause a compiler error if P is of invalid type
}
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushBackImpl <T, ZRingBuffer_OverflowUnsafe, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowUnsafe, A>& _self, const T& _value)
{
_self.IncrementBack().Buffer.Data()[_self.BackIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushBackImpl <T, ZRingBuffer_OverflowAssert, A>{
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowAssert, A>& _self, const T& _value)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT( _self.BufferSize < _self.Buffer.Size(), "ZRingBuffer::PushBack() caused overflow!");
#endif
_self.IncrementBack().Buffer.Data()[_self.BackIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushBackImpl <T, ZRingBuffer_OverflowIgnore, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowIgnore, A>& _self, const T& _value)
{
//See if we should be ignoring this call
if (_self.Full())
return;
_self.IncrementBack().Buffer.Data()[_self.BackIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushBackImpl <T, ZRingBuffer_OverflowDropBack, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowDropBack, A>& _self, const T& _value)
{
//See if we need to drop the back element
if (_self.Full())
_self.DecrementBack();
_self.IncrementBack().Buffer.Data()[_self.BackIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushBackImpl <T, ZRingBuffer_OverflowDropFront, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowDropFront, A>& _self, const T& _value)
{
//See if we need to drop the front element
if (_self.Full())
_self.IncrementFront();
_self.IncrementBack().Buffer.Data()[_self.BackIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushBackImpl <T, ZRingBuffer_OverflowOverwrite, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowOverwrite, A>& _self, const T& _value)
{
//See if we need to overwrite the current element
if (_self.Full())
_self.Buffer.Data()[_self.BackIndex] = _value;
else
_self.IncrementBack().Buffer.Data()[_self.BackIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushBackImpl <T, ZRingBuffer_OverflowEvict, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowEvict, A>& _self, const T& _value)
{
//See if we need to evict the opposite element
if (_self.Full())
_self.IncrementFront();
_self.IncrementBack().Buffer.Data()[_self.BackIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushBackImpl <T, ZRingBuffer_OverflowGrow, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowGrow, A>& _self, const T& _value)
{
//See if we need to reserve more space
if (_self.Full())
_self.Reserve((size_t)(_self.BufferSize * ZRINGBUFFER_CAPACITY_RESIZE_FACTOR));
_self.IncrementBack().Buffer.Data()[_self.BackIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename P, typename A>
void ZRingBuffer_PushFrontImpl<T, P, A>::Call(ZRingBuffer<T, P, A>& _self, const T& _value)
{
URFP(_self); URFP(_value);
P::UnimplementedMethod; //This will cause a compiler error if P is of invalid type
}
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushFrontImpl <T, ZRingBuffer_OverflowUnsafe, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowUnsafe, A>& _self, const T& _value)
{
_self.DecrementFront().Buffer.Data()[_self.FrontIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushFrontImpl <T, ZRingBuffer_OverflowAssert, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowAssert, A>& _self, const T& _value)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT( _self.BufferSize < _self.Buffer.Size(), "ZRingBuffer::PushFront() caused overflow!");
#endif
_self.DecrementFront().Buffer.Data()[_self.FrontIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushFrontImpl <T, ZRingBuffer_OverflowIgnore, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowIgnore, A>& _self, const T& _value)
{
//See if we should be ignoring this call
if (_self.Full())
return;
_self.DecrementFront().Buffer.Data()[_self.FrontIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushFrontImpl <T, ZRingBuffer_OverflowDropBack, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowDropBack, A>& _self, const T& _value)
{
//See if we need to drop the back element
if (_self.Full())
_self.DecrementBack();
_self.DecrementFront().Buffer.Data()[_self.FrontIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushFrontImpl <T, ZRingBuffer_OverflowDropFront, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowDropFront, A>& _self, const T& _value)
{
//See if we need to drop the front element
if (_self.Full())
_self.IncrementFront();
_self.DecrementFront().Buffer.Data()[_self.FrontIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushFrontImpl <T, ZRingBuffer_OverflowOverwrite, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowOverwrite, A>& _self, const T& _value)
{
//See if we need to overwrite the current element
if (_self.Full())
_self.Buffer.Data()[_self.FrontIndex] = _value;
else
_self.DecrementFront().Buffer.Data()[_self.FrontIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushFrontImpl <T, ZRingBuffer_OverflowEvict, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowEvict, A>& _self, const T& _value)
{
//See if we need to evict the opposite element
if (_self.Full())
_self.DecrementBack();
_self.DecrementFront().Buffer.Data()[_self.FrontIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename A>
struct ZRingBuffer_PushFrontImpl <T, ZRingBuffer_OverflowGrow, A> {
inline static void Call(ZRingBuffer<T, ZRingBuffer_OverflowGrow, A>& _self, const T& _value)
{
//See if we need to reserve more space
if (_self.Full())
_self.Reserve((size_t)(_self.BufferSize * ZRINGBUFFER_CAPACITY_RESIZE_FACTOR));
_self.DecrementFront().Buffer.Data()[_self.FrontIndex] = _value;
}
};
/*************************************************************************/
template <typename T, typename P, typename A>
void ZRingBuffer_ReserveImpl<T, P, A>::Call(ZRingBuffer<T, P, A>& _self, size_t _newCapacity)
{
//If the new and current capacities are equal, do nothing.
if (_self.Buffer.Size() == _newCapacity)
return;
//If the new capacity is smaller than the current size, assert.
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT( _newCapacity >= _self.BufferSize, "ZRingBuffer::Reserve() called with new capacity below size!");
#endif
//Align the buffer
_self.AlignBuffer();
//Resize the buffer
_self.Buffer.Resize(_newCapacity);
}
/*************************************************************************/
template <typename T, typename P, typename A>
size_t ZRingBuffer_SizeImpl<T, P, A>::Call(const ZRingBuffer<T, P, A>& _self)
{
return _self.BufferSize;
}
/*************************************************************************/
template <typename T, typename P, typename A>
bool ZRingBuffer_TryPushFrontImpl<T, P, A>::Call(ZRingBuffer<T, P, A>& _self, const T& _value)
{
if (_self.Full())
return false;
_self.PushFront(_value);
return true;
}
/*************************************************************************/
template <typename T, typename P, typename A>
bool ZRingBuffer_TryPushBackImpl<T, P, A>::Call(ZRingBuffer<T, P, A>& _self, const T& _value)
{
if (_self.Full())
return false;
_self.PushBack(_value);
return true;
}
#endif