/* ZRingBuffer.hpp Author: Chris Ertel James Russell 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 //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 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::At struct template struct ZRingBuffer_AtImpl { inline static T& Call( const ZRingBuffer& _self, const size_t _index); }; //Forward declaration of ZRingBuffer::Back struct template struct ZRingBuffer_BackImpl { inline static T& Call( const ZRingBuffer& _self ); }; //Forward declaration of ZRingBuffer::Capacity struct template struct ZRingBuffer_CapacityImpl { inline static size_t Call( const ZRingBuffer& _self ); }; //Forward declaration of ZRingBuffer::Clear struct template struct ZRingBuffer_ClearImpl { inline static void Call( ZRingBuffer& _self ); inline static void Call( ZRingBuffer& _self, size_t _newCapacity ); }; //Forward declaration of ZRingBuffer::Copy struct template struct ZRingBuffer_CopyImpl { template inline static void Call( ZRingBuffer& _self, const ZRingBuffer& _other ); }; //Forward declaration of ZRingBuffer::Empty struct template struct ZRingBuffer_EmptyImpl { inline static bool Call( const ZRingBuffer& _self ); }; //Forward declaration of ZRingBuffer::Equals struct template struct ZRingBuffer_EqualsImpl { template inline static bool Call( const ZRingBuffer& _self, const ZRingBuffer& _other ); }; //Forward declaration of ZRingBuffer::Erase struct template struct ZRingBuffer_EraseImpl { inline static T Call( ZRingBuffer& _self, size_t _index); inline static void Call( ZRingBuffer& _self, size_t _i, size_t _j ); }; //Forward declaration of ZRingBuffer::Front struct template struct ZRingBuffer_FrontImpl { inline static T& Call( const ZRingBuffer& _self ); }; //Forward declaration of ZRingBuffer::Full struct template struct ZRingBuffer_FullImpl { inline static bool Call( const ZRingBuffer& _self ); }; //Forward declaration of ZRingBuffer::Insert struct template struct ZRingBuffer_InsertImpl { inline static void Call ( ZRingBuffer& _self, size_t _index, const T& _value, size_t _count ); template inline static void Call( ZRingBuffer& _self, size_t _index, const ZArray& _array, size_t _start, size_t _count ); template inline static void Call( ZRingBuffer& _self, size_t _index, const ZRingBuffer& _other, size_t _start, size_t _count ); }; //Forward declaration of ZRingBuffer::PopBack struct template struct ZRingBuffer_PopBackImpl { inline static T Call( ZRingBuffer& _self ); }; //Forward declaration of ZRingBuffer::PopFront struct template struct ZRingBuffer_PopFrontImpl { inline static T Call( ZRingBuffer& _self ); }; //Forward declaration of ZRingBuffer::PushBack struct template struct ZRingBuffer_PushBackImpl { inline static void Call( ZRingBuffer& _self, const T& _value ); }; //Forward declaration of ZRingBuffer::PushFront struct template struct ZRingBuffer_PushFrontImpl { inline static void Call( ZRingBuffer& _self, const T& _value ); }; //Forward declaration of ZRingBuffer::Reserve struct template struct ZRingBuffer_ReserveImpl { inline static void Call( ZRingBuffer& _self, size_t _newCapacity ); }; //Forward declaration of ZRingBuffer::Size struct template struct ZRingBuffer_SizeImpl { inline static size_t Call( const ZRingBuffer& _self ); }; //Forward declaration of ZRingBuffer::TryPushBack struct template struct ZRingBuffer_TryPushBackImpl { inline static bool Call( ZRingBuffer& _self, const T& _value ); }; //Forward declaration of ZRingBuffer::TryPushFront struct template struct ZRingBuffer_TryPushFrontImpl { inline static bool Call( ZRingBuffer& _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 > class ZRingBuffer { friend struct ZRingBuffer_AtImpl; friend struct ZRingBuffer_BackImpl; friend struct ZRingBuffer_CapacityImpl; friend struct ZRingBuffer_ClearImpl; friend struct ZRingBuffer_CopyImpl; friend struct ZRingBuffer_EmptyImpl; friend struct ZRingBuffer_EqualsImpl; friend struct ZRingBuffer_EraseImpl; friend struct ZRingBuffer_FrontImpl; friend struct ZRingBuffer_FullImpl; friend struct ZRingBuffer_InsertImpl; friend struct ZRingBuffer_PopBackImpl; friend struct ZRingBuffer_PopFrontImpl; friend struct ZRingBuffer_PushBackImpl; friend struct ZRingBuffer_PushFrontImpl; friend struct ZRingBuffer_ReserveImpl; friend struct ZRingBuffer_SizeImpl; friend struct ZRingBuffer_TryPushFrontImpl; friend struct ZRingBuffer_TryPushBackImpl; protected: //Backing array of the ring buffer ZArray 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 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& 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& 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& 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& 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& _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& operator = (const ZRingBuffer& _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 ZRingBuffer& operator = (const ZRingBuffer& _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 bool operator == (const ZRingBuffer& _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 bool operator != (const ZRingBuffer& _other) const { return !Equals(_other); } /* public ZRingBuffer::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::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::Array Gets a reference to the array that backs this buffer. The elements are not guaranteed to be stored in order. @return (ZArray&) - the array backing this buffer */ const ZArray& Array() const { return Buffer; } /* public ZRingBuffer::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::Call(*this, _index); } /* public ZRingBuffer::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::Call(*this); } /* public ZRingBuffer::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::Call(*this); } /* public ZRingBuffer::Clear Clears out all elements from the buffer, keeping the currently allocated storage. @return (void) */ void Clear() { ZRingBuffer_ClearImpl::Call(*this); } /* public ZRingBuffer::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::Call(*this, _newCapacity); } /* public ZRingBuffer::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 void Copy(const ZRingBuffer& _other) { ZRingBuffer_CopyImpl::template Call(*this, _other); } /* public ZRingBuffer::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::Call(*this); } /* public ZRingBuffer::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 bool Equals(const ZRingBuffer& _other) { return ZRingBuffer_EqualsImpl::template Call(*this, _other); } /* public ZRingBuffer::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::Call(*this, _index); } /* public ZRingBuffer::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::Call(*this, _i, _j); } /* public ZRingBuffer::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::Call(*this); } /* public ZRingBuffer::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::Call(*this); } /* public ZRingBuffer::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::Call(*this, _index, _value, 1); } /* public ZRingBuffer::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::Call(*this, _index, _value, _count); } /* public ZRingBuffer::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 void Insert(size_t _index, const ZArray& _array) { ZRingBuffer_InsertImpl::template Call(*this, _index, _array, 0, _array.Size()); } /* public ZRingBuffer::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 void Insert(size_t _index, const ZArray& _array, size_t _start, size_t _count) { ZRingBuffer_InsertImpl::template Call(*this, _index, _array, _start, _count); } /* public ZRingBuffer::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 void Insert(size_t _index, const ZRingBuffer& _other) { ZRingBuffer_InsertImpl::template Call(*this, _index, _other, 0, _other.Size()); } /* public ZRingBuffer::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 void Insert(size_t _index, const ZRingBuffer& _other, size_t _start, size_t _count) { ZRingBuffer_InsertImpl::template Call(*this, _index, _other, _start, _count); } /* public ZRingBuffer::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::Call(*this); } /* public ZRingBuffer::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::Call(*this); } /* public ZRingBuffer::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::Call(*this, _value); } /* public ZRingBuffer::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::Call(*this, _value); } /* public ZRingBuffer::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::Call(*this, _newCapacity); } /* public ZRingBuffer::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::Call(*this); } /* public ZRingBuffer::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::Call(*this, _value); } /* public ZRingBuffer::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::Call(*this, _value); } }; //////////////////////////////////////////// /* Non-Specialized Method Implementations */ //////////////////////////////////////////// template T& ZRingBuffer_AtImpl::Call(const ZRingBuffer& _self, const size_t _index) { return _self.Buffer.Data()[_self.ActualIndex(_index)]; //Bounds checking will be done by ActualIndex } /*************************************************************************/ template T& ZRingBuffer_BackImpl::Call(const ZRingBuffer& _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 size_t ZRingBuffer_CapacityImpl::Call(const ZRingBuffer& _self) { return _self.Buffer.Size(); } /*************************************************************************/ template void ZRingBuffer_ClearImpl::Call( ZRingBuffer& _self ) { _self.BufferSize = 0; _self.FrontIndex = 0; _self.BackIndex = 0; } /*************************************************************************/ template void ZRingBuffer_ClearImpl::Call( ZRingBuffer& _self, size_t _newCapacity ) { _self.Buffer.Resize(_newCapacity); _self.BufferSize = 0; _self.FrontIndex = 0; _self.BackIndex = 0; } /*************************************************************************/ template template void ZRingBuffer_CopyImpl::Call( ZRingBuffer& _self, const ZRingBuffer& _other ) { _self.Buffer.Copy(_other.Buffer); _self.BufferSize = _other.BufferSize; _self.FrontIndex = _other.FrontIndex; _self.BackIndex = _other.BackIndex; } /*************************************************************************/ template bool ZRingBuffer_EmptyImpl::Call(const ZRingBuffer& _self) { return _self.BufferSize == 0; } /*************************************************************************/ template template bool ZRingBuffer_EqualsImpl::Call( const ZRingBuffer& _self, const ZRingBuffer& _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 T ZRingBuffer_EraseImpl::Call(ZRingBuffer& _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 void ZRingBuffer_EraseImpl::Call( ZRingBuffer& _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 T& ZRingBuffer_FrontImpl::Call(const ZRingBuffer& _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 bool ZRingBuffer_FullImpl::Call(const ZRingBuffer& _self) { return _self.BufferSize == _self.Buffer.Size(); } /*************************************************************************/ template void ZRingBuffer_InsertImpl::Call( ZRingBuffer& _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 template void ZRingBuffer_InsertImpl::Call( ZRingBuffer& _self, size_t _index, const ZArray& _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 template void ZRingBuffer_InsertImpl::Call( ZRingBuffer& _self, size_t _index, const ZRingBuffer& _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 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 T ZRingBuffer_PopBackImpl::Call(ZRingBuffer& _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 T ZRingBuffer_PopFrontImpl::Call(ZRingBuffer& _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 void ZRingBuffer_PushBackImpl::Call(ZRingBuffer& _self, const T& _value) { URFP(_self); URFP(_value); P::UnimplementedMethod; //This will cause a compiler error if P is of invalid type } /*************************************************************************/ template struct ZRingBuffer_PushBackImpl { inline static void Call(ZRingBuffer& _self, const T& _value) { _self.IncrementBack().Buffer.Data()[_self.BackIndex] = _value; } }; /*************************************************************************/ template struct ZRingBuffer_PushBackImpl { inline static void Call(ZRingBuffer& _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 struct ZRingBuffer_PushBackImpl { inline static void Call(ZRingBuffer& _self, const T& _value) { //See if we should be ignoring this call if (_self.Full()) return; _self.IncrementBack().Buffer.Data()[_self.BackIndex] = _value; } }; /*************************************************************************/ template struct ZRingBuffer_PushBackImpl { inline static void Call(ZRingBuffer& _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 struct ZRingBuffer_PushBackImpl { inline static void Call(ZRingBuffer& _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 struct ZRingBuffer_PushBackImpl { inline static void Call(ZRingBuffer& _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 struct ZRingBuffer_PushBackImpl { inline static void Call(ZRingBuffer& _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 struct ZRingBuffer_PushBackImpl { inline static void Call(ZRingBuffer& _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 void ZRingBuffer_PushFrontImpl::Call(ZRingBuffer& _self, const T& _value) { URFP(_self); URFP(_value); P::UnimplementedMethod; //This will cause a compiler error if P is of invalid type } /*************************************************************************/ template struct ZRingBuffer_PushFrontImpl { inline static void Call(ZRingBuffer& _self, const T& _value) { _self.DecrementFront().Buffer.Data()[_self.FrontIndex] = _value; } }; /*************************************************************************/ template struct ZRingBuffer_PushFrontImpl { inline static void Call(ZRingBuffer& _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 struct ZRingBuffer_PushFrontImpl { inline static void Call(ZRingBuffer& _self, const T& _value) { //See if we should be ignoring this call if (_self.Full()) return; _self.DecrementFront().Buffer.Data()[_self.FrontIndex] = _value; } }; /*************************************************************************/ template struct ZRingBuffer_PushFrontImpl { inline static void Call(ZRingBuffer& _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 struct ZRingBuffer_PushFrontImpl { inline static void Call(ZRingBuffer& _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 struct ZRingBuffer_PushFrontImpl { inline static void Call(ZRingBuffer& _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 struct ZRingBuffer_PushFrontImpl { inline static void Call(ZRingBuffer& _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 struct ZRingBuffer_PushFrontImpl { inline static void Call(ZRingBuffer& _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 void ZRingBuffer_ReserveImpl::Call(ZRingBuffer& _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 size_t ZRingBuffer_SizeImpl::Call(const ZRingBuffer& _self) { return _self.BufferSize; } /*************************************************************************/ template bool ZRingBuffer_TryPushFrontImpl::Call(ZRingBuffer& _self, const T& _value) { if (_self.Full()) return false; _self.PushFront(_value); return true; } /*************************************************************************/ template bool ZRingBuffer_TryPushBackImpl::Call(ZRingBuffer& _self, const T& _value) { if (_self.Full()) return false; _self.PushBack(_value); return true; } #endif