Files
SoulliesOfficial d09b58fd80 架构大更
2026-03-20 11:56:50 -04:00

318 lines
11 KiB
C#

using System;
using UnityEngine;
namespace IngameDebugConsole
{
public class CircularBuffer<T>
{
private readonly T[] array;
private int startIndex;
public CircularBuffer(int capacity)
{
array = new T[capacity];
}
public int Count { get; private set; }
public T this[int index] => array[(startIndex + index) % array.Length];
// Old elements are overwritten when capacity is reached
public void Add(T value)
{
if (Count < array.Length)
{
array[Count++] = value;
}
else
{
array[startIndex] = value;
if (++startIndex >= array.Length)
startIndex = 0;
}
}
public T[] ToArray()
{
var result = new T[Count];
for (var i = 0; i < Count; i++)
result[i] = this[i];
return result;
}
}
public class DynamicCircularBuffer<T>
{
private T[] array;
private int startIndex;
public DynamicCircularBuffer(int initialCapacity = 2)
{
array = new T[initialCapacity];
}
public int Count { get; private set; }
public int Capacity => array.Length;
public T this[int index]
{
get => array[(startIndex + index) % array.Length];
set => array[(startIndex + index) % array.Length] = value;
}
private void SetCapacity(int capacity)
{
var newArray = new T[capacity];
if (Count > 0)
{
var elementsBeforeWrap = Mathf.Min(Count, array.Length - startIndex);
Array.Copy(array, startIndex, newArray, 0, elementsBeforeWrap);
if (elementsBeforeWrap < Count)
Array.Copy(array, 0, newArray, elementsBeforeWrap, Count - elementsBeforeWrap);
}
array = newArray;
startIndex = 0;
}
/// <summary>Inserts the value to the beginning of the collection.</summary>
public void AddFirst(T value)
{
if (array.Length == Count)
SetCapacity(Mathf.Max(array.Length * 2, 4));
startIndex = startIndex > 0 ? startIndex - 1 : array.Length - 1;
array[startIndex] = value;
Count++;
}
/// <summary>Adds the value to the end of the collection.</summary>
public void Add(T value)
{
if (array.Length == Count)
SetCapacity(Mathf.Max(array.Length * 2, 4));
this[Count++] = value;
}
public void AddRange(DynamicCircularBuffer<T> other)
{
if (other.Count == 0)
return;
if (array.Length < Count + other.Count)
SetCapacity(Mathf.Max(array.Length * 2, Count + other.Count));
var insertStartIndex = (startIndex + Count) % array.Length;
var elementsBeforeWrap = Mathf.Min(other.Count, array.Length - insertStartIndex);
var otherElementsBeforeWrap = Mathf.Min(other.Count, other.array.Length - other.startIndex);
Array.Copy(other.array, other.startIndex, array, insertStartIndex,
Mathf.Min(elementsBeforeWrap, otherElementsBeforeWrap));
if (elementsBeforeWrap < otherElementsBeforeWrap) // This array wrapped before the other array
Array.Copy(other.array, other.startIndex + elementsBeforeWrap, array, 0,
otherElementsBeforeWrap - elementsBeforeWrap);
else if (elementsBeforeWrap > otherElementsBeforeWrap) // The other array wrapped before this array
Array.Copy(other.array, 0, array, insertStartIndex + otherElementsBeforeWrap,
elementsBeforeWrap - otherElementsBeforeWrap);
var copiedElements = Mathf.Max(elementsBeforeWrap, otherElementsBeforeWrap);
if (copiedElements < other.Count) // Both arrays wrapped and there's still some elements left to copy
Array.Copy(other.array, copiedElements - otherElementsBeforeWrap, array,
copiedElements - elementsBeforeWrap, other.Count - copiedElements);
Count += other.Count;
}
public T RemoveFirst()
{
var element = array[startIndex];
array[startIndex] = default;
if (++startIndex == array.Length)
startIndex = 0;
Count--;
return element;
}
public T RemoveLast()
{
var index = (startIndex + Count - 1) % array.Length;
var element = array[index];
array[index] = default;
Count--;
return element;
}
public int RemoveAll(Predicate<T> shouldRemoveElement)
{
return RemoveAll<T>(shouldRemoveElement, null, null);
}
public int RemoveAll<Y>(Predicate<T> shouldRemoveElement, Action<T, int> onElementIndexChanged,
DynamicCircularBuffer<Y> synchronizedBuffer)
{
var synchronizedArray = synchronizedBuffer != null ? synchronizedBuffer.array : null;
var elementsBeforeWrap = Mathf.Min(Count, array.Length - startIndex);
var removedElements = 0;
int i = startIndex, newIndex = startIndex, endIndex = startIndex + elementsBeforeWrap;
for (; i < endIndex; i++)
if (shouldRemoveElement(array[i]))
{
removedElements++;
}
else
{
if (removedElements > 0)
{
var element = array[i];
array[newIndex] = element;
if (synchronizedArray != null)
synchronizedArray[newIndex] = synchronizedArray[i];
if (onElementIndexChanged != null)
onElementIndexChanged(element, newIndex - startIndex);
}
newIndex++;
}
i = 0;
endIndex = Count - elementsBeforeWrap;
if (newIndex < array.Length)
for (; i < endIndex; i++)
if (shouldRemoveElement(array[i]))
{
removedElements++;
}
else
{
var element = array[i];
array[newIndex] = element;
if (synchronizedArray != null)
synchronizedArray[newIndex] = synchronizedArray[i];
if (onElementIndexChanged != null)
onElementIndexChanged(element, newIndex - startIndex);
if (++newIndex == array.Length)
{
i++;
break;
}
}
if (newIndex == array.Length)
{
newIndex = 0;
for (; i < endIndex; i++)
if (shouldRemoveElement(array[i]))
{
removedElements++;
}
else
{
if (removedElements > 0)
{
var element = array[i];
array[newIndex] = element;
if (synchronizedArray != null)
synchronizedArray[newIndex] = synchronizedArray[i];
if (onElementIndexChanged != null)
onElementIndexChanged(element, newIndex + elementsBeforeWrap);
}
newIndex++;
}
}
TrimEnd(removedElements);
if (synchronizedBuffer != null)
synchronizedBuffer.TrimEnd(removedElements);
return removedElements;
}
public void TrimStart(int trimCount, Action<T> perElementCallback = null)
{
TrimInternal(trimCount, startIndex, perElementCallback);
startIndex = (startIndex + trimCount) % array.Length;
}
public void TrimEnd(int trimCount, Action<T> perElementCallback = null)
{
TrimInternal(trimCount, (startIndex + Count - trimCount) % array.Length, perElementCallback);
}
private void TrimInternal(int trimCount, int startIndex, Action<T> perElementCallback)
{
var elementsBeforeWrap = Mathf.Min(trimCount, array.Length - startIndex);
if (perElementCallback == null)
{
Array.Clear(array, startIndex, elementsBeforeWrap);
if (elementsBeforeWrap < trimCount)
Array.Clear(array, 0, trimCount - elementsBeforeWrap);
}
else
{
for (int i = startIndex, endIndex = startIndex + elementsBeforeWrap; i < endIndex; i++)
{
perElementCallback(array[i]);
array[i] = default;
}
for (int i = 0, endIndex = trimCount - elementsBeforeWrap; i < endIndex; i++)
{
perElementCallback(array[i]);
array[i] = default;
}
}
Count -= trimCount;
}
public void Clear()
{
var elementsBeforeWrap = Mathf.Min(Count, array.Length - startIndex);
Array.Clear(array, startIndex, elementsBeforeWrap);
if (elementsBeforeWrap < Count)
Array.Clear(array, 0, Count - elementsBeforeWrap);
startIndex = 0;
Count = 0;
}
public int IndexOf(T value)
{
var elementsBeforeWrap = Mathf.Min(Count, array.Length - startIndex);
var index = Array.IndexOf(array, value, startIndex, elementsBeforeWrap);
if (index >= 0)
return index - startIndex;
if (elementsBeforeWrap < Count)
{
index = Array.IndexOf(array, value, 0, Count - elementsBeforeWrap);
if (index >= 0)
return index + elementsBeforeWrap;
}
return -1;
}
public void ForEach(Action<T> action)
{
var elementsBeforeWrap = Mathf.Min(Count, array.Length - startIndex);
for (int i = startIndex, endIndex = startIndex + elementsBeforeWrap; i < endIndex; i++)
action(array[i]);
for (int i = 0, endIndex = Count - elementsBeforeWrap; i < endIndex; i++)
action(array[i]);
}
}
}