Skip to content

Commit c68acf8

Browse files
authored
Added support for fing buffer Swap(), Erase() and Flatten() functions (#18)
* Added ringbuffer::Swap() * Added support for Flatten() and FlattenUnsorted() * Added support for Erase() function * added version number
1 parent f3d9c5f commit c68acf8

2 files changed

Lines changed: 416 additions & 8 deletions

File tree

src/jc/cpp/ringbuffer.h

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@
2020
#include <stdlib.h>
2121
#include <assert.h> // disable with NDEBUG
2222

23+
#include <string.h> // memmove
24+
25+
#define JC_SWAP(type, lhs, rhs)\
26+
{\
27+
type tmp = rhs;\
28+
rhs = lhs;\
29+
lhs = tmp;\
30+
}
31+
2332
namespace jc
2433
{
2534

@@ -34,6 +43,14 @@ class RingBuffer
3443
RingBuffer(uint32_t capacity) : m_Buffer(0), m_Head(0), m_Tail(0), m_Max(0) { SetCapacity(capacity); }
3544
~RingBuffer() { Free(); }
3645

46+
void Swap(RingBuffer<T>& rhs) {
47+
JC_SWAP(T*, m_Buffer, rhs.m_Buffer);
48+
JC_SWAP(uint32_t, m_Head, rhs.m_Head);
49+
JC_SWAP(uint32_t, m_Tail, rhs.m_Tail);
50+
JC_SWAP(uint32_t, m_Max, rhs.m_Max);
51+
JC_SWAP(uint32_t, m_Full, rhs.m_Full);
52+
}
53+
3754
/// Gets the size of the buffer
3855
uint32_t Size() const {
3956
if (m_Full)
@@ -51,20 +68,29 @@ class RingBuffer
5168
uint32_t Capacity() const { return (uint32_t)m_Max; }
5269
/// Increases or decreases capacity. Invalidates pointers to elements
5370
void SetCapacity(uint32_t capacity);
71+
/// Increases or decreases capacity. Invalidates pointers to elements
72+
void OffsetCapacity(uint32_t grow);
73+
5474
/// Adds one item to the ring buffer. Asserts if the buffer is full
5575
void Push(const T& item);
5676
/// Adds one item to the ring buffer. Does not assert, If full, overwrites the value at the tail.
5777
void PushUnchecked(const T& item);
5878
/// Removes (and returns) the first inserted item from the ring buffer. Asserts if the buffer is empty
5979
T Pop();
80+
/// Removes the element at logical index (0-based) and keeps order
81+
void Erase(uint32_t index);
6082

6183
T& operator[] (size_t i) { assert(i < Size()); return m_Buffer[(m_Tail + i) % m_Max]; }
6284
const T& operator[] (size_t i) const { assert(i < Size()); return m_Buffer[(m_Tail + i) % m_Max]; }
6385

86+
// Useful for sorting
87+
void Flatten(); // in-place linearization so data starts at m_Buffer[0]
88+
void FlattenUnordered(); // compacts contiguously without preserving logical order
89+
T* Buffer() const { return m_Buffer; }
90+
6491
// Mainly for unit tests
6592
uint32_t Head() const { return m_Head; }
6693
uint32_t Tail() const { return m_Tail; }
67-
T* Buffer() const { return m_Buffer; }
6894

6995
private:
7096
T* m_Buffer;
@@ -78,6 +104,8 @@ class RingBuffer
78104
RingBuffer<T>& operator= (const RingBuffer<T>& rhs);
79105
bool operator== (const RingBuffer<T>& rhs);
80106

107+
void ReverseRange(uint32_t begin, uint32_t end_exclusive);
108+
81109
void Free() {
82110
free(m_Buffer);
83111
m_Buffer = 0;
@@ -136,6 +164,12 @@ void RingBuffer<T>::SetCapacity(uint32_t capacity)
136164
m_Full = capacity == size ? 1 : 0;
137165
}
138166

167+
template <typename T>
168+
void RingBuffer<T>::OffsetCapacity(uint32_t grow)
169+
{
170+
SetCapacity(Capacity() + grow);
171+
}
172+
139173
template <typename T>
140174
void RingBuffer<T>::Push(const T& item)
141175
{
@@ -160,13 +194,133 @@ T RingBuffer<T>::Pop()
160194
return item;
161195
}
162196

197+
template <typename T>
198+
void RingBuffer<T>::Erase(uint32_t index)
199+
{
200+
uint32_t size = Size();
201+
assert(index < size);
202+
203+
// Compute physical index of the element to erase
204+
uint32_t pos = (m_Tail + index) % m_Max;
205+
206+
// Shift elements left from pos towards head to preserve order
207+
// Stop when the next index equals m_Head (the logical end)
208+
while (true)
209+
{
210+
uint32_t next = pos + 1;
211+
if (next >= m_Max) next = 0;
212+
if (next == m_Head)
213+
break;
214+
m_Buffer[pos] = m_Buffer[next];
215+
pos = next;
216+
}
217+
218+
// One element removed: move head back by one and clear full flag
219+
if (m_Head == 0)
220+
{
221+
m_Head = m_Max - 1;
222+
}
223+
else
224+
{
225+
--m_Head;
226+
}
227+
m_Full = 0;
228+
}
229+
230+
231+
// some cases: .=free, T=Tail, H=Head, *=used
232+
// 1) [T****H....]
233+
// 2) [...T***H..]
234+
// 3) [*H...T****]
235+
236+
template <typename T>
237+
void RingBuffer<T>::FlattenUnordered()
238+
{
239+
uint32_t capacity = m_Max;
240+
uint32_t size = Size();
241+
if (size == 0 || m_Tail == 0)
242+
return; // already contiguous or empty
243+
244+
if (m_Tail < m_Head)
245+
{
246+
// Single contiguous block at offset; shift entire block to start
247+
memmove(m_Buffer, m_Buffer + m_Tail, sizeof(T) * size);
248+
}
249+
else
250+
{
251+
// Wrapped case: move the rightmost part down directly after the left part.
252+
// This compacts elements into [0..size-1] without preserving order.
253+
uint32_t right_len = capacity - m_Tail; // [m_Tail .. m_Max)
254+
memmove(m_Buffer + m_Head, m_Buffer + m_Tail, sizeof(T) * right_len);
255+
}
256+
257+
m_Tail = 0;
258+
m_Head = size;
259+
}
260+
261+
// In-place linearization. After this call, the logical sequence is contiguous
262+
// at m_Buffer[0..Size()-1]. Does not allocate temporary buffers.
263+
template <typename T>
264+
void RingBuffer<T>::Flatten()
265+
{
266+
uint32_t capacity = m_Max;
267+
uint32_t size = Size();
268+
269+
// Early out when already contiguous (tail at 0) or empty
270+
// No state changes in these cases.
271+
if (size == 0 || m_Tail == 0)
272+
return;
273+
274+
if (m_Tail < m_Head)
275+
{
276+
// Single contiguous block but offset; shift down to index 0
277+
memmove(m_Buffer, m_Buffer + m_Tail, sizeof(T) * size);
278+
}
279+
else
280+
{
281+
// Wrapped case: rotate the whole buffer left by m_Tail positions.
282+
// This makes the logical tail land at index 0. Elements outside
283+
// [0..size-1] are unspecified and may contain moved free slots.
284+
uint32_t n = capacity;
285+
uint32_t k = m_Tail % n;
286+
if (k)
287+
{
288+
ReverseRange(0, k);
289+
ReverseRange(k, n);
290+
ReverseRange(0, n);
291+
}
292+
}
293+
294+
m_Tail = 0;
295+
m_Head = size;
296+
}
297+
298+
template <typename T>
299+
void RingBuffer<T>::ReverseRange(uint32_t begin, uint32_t end_exclusive)
300+
{
301+
while (begin < end_exclusive)
302+
{
303+
--end_exclusive;
304+
if (begin >= end_exclusive)
305+
break;
306+
T tmp = m_Buffer[begin];
307+
m_Buffer[begin] = m_Buffer[end_exclusive];
308+
m_Buffer[end_exclusive] = tmp;
309+
++begin;
310+
}
311+
}
312+
313+
163314
} // namespace
164315

316+
#undef JC_SWAP
317+
165318
/*
166319
167320
VERSION:
168321
169-
1.0 Initial version
322+
1.1 2025-10-08 Added Swap(), Erase() and Flatten*() functions
323+
1.0 Initial version
170324
171325
172326
LICENSE:

0 commit comments

Comments
 (0)