qamp/Qrakhen.Qamp.Memory/TailBuffer.cs

81 lines
2.3 KiB
C#

namespace Qrakhen.Qamp.Memory;
// add to qrakhen.memory package for clout
public abstract class TailBuffer<T>
{
private T[] _data;
public T[] Data {
get => _data;
}
public int Capacity => _data.Length;
public int Tail { get; private set; }
public TailBuffer(int size = 0x80)
{
_data = new T[size];
}
public TailBuffer(T[] data)
{
_data = new T[data.Length * 2];
Array.Copy(data, 0, _data, 0, data.Length);
SetTail(data.Length);
}
public void Insert(int cursor, T[] data)
{
cursor = SeekTail(cursor);
Prepare(cursor, data.Length);
int length = data.Length;
if (cursor == Tail) {
// Just append here.
Array.Copy(data, 0, _data, cursor, length);
SetTail(cursor + length);
} else {
// Shift everything to the right of insertion
Array.Copy(_data, cursor, _data, cursor + length, Tail - cursor);
Array.Copy(data, 0, _data, cursor, length);
SetTail(Tail + length);
}
}
public void Delete(Range range)
=> Delete(range.From, range.To - range.From);
public void Delete(int cursor, int count)
{
if (cursor + count >= Tail)
SetTail(cursor); // Directly terminate at cursor, nothing is behind tail.
else {
// Move everything from end of deletion until tail to start of deletion.
Array.Copy(_data, cursor + count, _data, cursor, Tail - (cursor + count));
SetTail(Tail - count);
}
}
// never write beyond tail (there might be invalid data)
private int SeekTail(int cursor) => cursor > Tail ? Tail : cursor;
private void Prepare(int index, int count)
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
if (count + Tail <= _data.Length)
return;
while (count + Tail > _data.Length) {
T[] grow = new T[_data.Length * 2];
Array.Copy(_data, grow, _data.Length);
_data = grow;
}
}
/// <summary>
/// Sets the tail, or, if omitted, seeks the tail by finding the first null terminator.
/// </summary>
/// <param name="tail"></param>
protected virtual void SetTail(int tail = -1)
{
Tail = tail;
}
}