Circular buffer implementation

This commit is contained in:
2024-04-18 17:45:00 +02:00
parent b7244b2bdb
commit 791387fb29
3 changed files with 604 additions and 1 deletions
@@ -0,0 +1,208 @@
using System.Linq;
using System.Threading;
namespace System.Collections.Generic;
public sealed class CircularBuffer<T> : IList<T>
{
private readonly T[] buffer;
private int head;
private int tail;
public int Count { get; private set; }
public bool IsReadOnly { get; } = false;
public T this[int index]
{
get
{
if (index > this.Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return this.buffer[(this.head + index) % this.buffer.Length];
}
set
{
if (index > this.Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
this.buffer[(this.head + index) % this.buffer.Length] = value;
}
}
public CircularBuffer(int capacity)
{
if (capacity < 1) throw new ArgumentException($"{nameof(capacity)} cannot be smaller than 1");
this.buffer = new T[capacity];
this.head = 0;
this.tail = 0;
}
public CircularBuffer(T[] buffer, int head, int tail)
{
if (buffer.Length == 0) throw new ArgumentException($"{nameof(buffer)} cannot be of length 0");
this.buffer = buffer;
this.head = head;
this.tail = tail;
this.Count = this.tail < this.head ?
this.tail + this.buffer.Length - this.head + 1 :
this.tail - this.head + 1;
}
public CircularBuffer(T[] buffer)
{
if (buffer.Length == 0) throw new ArgumentException($"{nameof(buffer)} cannot be of length 0");
this.buffer = buffer;
this.head = 0;
this.tail = this.buffer.Length - 1;
this.Count = this.buffer.Length;
}
public void Add(T item)
{
if (this.Count == this.buffer.Length)
{
this.buffer[this.tail] = item;
this.tail = (this.tail + 1) % this.buffer.Length;
this.head = (this.head + 1) % this.buffer.Length;
}
else
{
this.buffer[this.tail] = item;
this.tail = (this.tail + 1) % this.buffer.Length;
this.Count++;
}
}
public void Clear()
{
this.head = 0;
this.tail = 0;
this.Count = 0;
}
public void DeepClear()
{
this.Clear();
Array.Clear(this.buffer, 0, this.buffer.Length);
}
public bool Contains(T item)
{
var comparer = EqualityComparer<T>.Default;
foreach(var itItem in this)
{
if (comparer.Equals(itItem, item))
{
return true;
}
}
return false;
}
public bool Remove(T item)
{
var index = this.head;
var comparer = EqualityComparer<T>.Default;
for (var i = 0; i < this.Count; i++)
{
if (comparer.Equals(this.buffer[index % this.buffer.Length], item))
{
int nextIndex;
for (var j = 0; j < this.Count - i - 1; j++)
{
nextIndex = (index + 1) % this.buffer.Length;
this.buffer[index] = this.buffer[nextIndex];
index = nextIndex;
}
this.Count--;
this.tail = this.tail == 0 ? this.buffer.Length - 1 : this.tail - 1;
return true;
}
index = (index + 1) % this.buffer.Length;
}
return false;
}
public void CopyTo(T[] array, int arrayIndex)
{
foreach(var item in this)
{
array[arrayIndex++] = item;
}
}
public IEnumerator<T> GetEnumerator()
{
var index = this.head;
for (var i = 0; i < this.Count; i++)
{
yield return this.buffer[index];
index = (index + 1) % this.buffer.Length;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public int IndexOf(T item)
{
var comparer = EqualityComparer<T>.Default;
for (var i = 0; i < this.Count; i++)
{
if (comparer.Equals(this[i], item))
{
return i;
}
}
return -1;
}
public void Insert(int index, T item)
{
if (index < 0 || index > this.Count) throw new ArgumentOutOfRangeException(nameof(index));
if (this.Count == this.buffer.Length) throw new InvalidOperationException("Cannot insert into a full buffer.");
if (index == this.Count)
{
this.Add(item);
return;
}
for (var i = this.Count; i > index; i--)
{
this.buffer[(this.head + i) % this.buffer.Length] = this.buffer[(this.head + i - 1) % this.buffer.Length];
}
this.buffer[(this.head + index) % this.buffer.Length] = item;
this.tail = (this.tail + 1) % this.buffer.Length;
this.Count++;
}
public void RemoveAt(int index)
{
if (index < 0 || index >= this.Count) throw new ArgumentOutOfRangeException(nameof(index));
for (var i = index; i < this.Count - 1; i++)
{
this.buffer[(this.head + i) % this.buffer.Length] = this.buffer[(this.head + i + 1) % this.buffer.Length];
}
this.tail = (this.tail - 1 + this.buffer.Length) % this.buffer.Length;
this.Count--;
}
}
@@ -7,7 +7,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<RootNamespace>System</RootNamespace>
<Version>1.6.2</Version>
<Version>1.6.3</Version>
<Authors>Alexandru Macocian</Authors>
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
<Description>Extensions for the System namespace</Description>