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+
2332namespace 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
6995private:
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+
139173template <typename T>
140174void 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
167320VERSION:
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
172326LICENSE:
0 commit comments