persistence/Persistence.Repository/CyclicArray.cs

200 lines
5.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Persistence.Repository;
/// <summary>
/// Цикличный массив
/// </summary>
/// <typeparam name="T"></typeparam>
public class CyclicArray<T> : IEnumerable<T>
{
readonly T[] array;
int used, current = -1;
/// <summary>
/// constructor
/// </summary>
/// <param name="capacity"></param>
public CyclicArray(int capacity)
{
array = new T[capacity];
}
/// <summary>
/// Количество элементов в массиве
/// </summary>
public int Count => used;
/// <summary>
/// Добавить новый элемент<br/>
/// Если capacity достигнуто, то вытеснит самый первый элемент
/// </summary>
/// <param name="item"></param>
public void Add(T item)
{
current = (++current) % array.Length;
array[current] = item;
if (used < array.Length)
used++;
UpdatedInvoke(current, item);
}
/// <summary>
/// Добавить новые элементы.<br/>
/// Если capacity достигнуто, то вытеснит самые первые элементы.<br/>
/// Не вызывает Updated!
/// </summary>
/// <param name="items"></param>
public void AddRange(IEnumerable<T> items)
{
var capacity = array.Length;
var newItems = items.TakeLast(capacity).ToArray();
if (newItems.Length == capacity)
{
Array.Copy(newItems, array, capacity);
current = capacity - 1;
}
else
{
current = (++current) % capacity;
var countToEndOfArray = capacity - current;
if (newItems.Length <= countToEndOfArray)
{
Array.Copy(newItems, 0, array, current, newItems.Length);
current += newItems.Length - 1;
}
else
{
var firstStepLength = countToEndOfArray;
Array.Copy(newItems, 0, array, current, firstStepLength);
var secondStepCount = newItems.Length - firstStepLength;
Array.Copy(newItems, firstStepLength, array, 0, secondStepCount);
current = secondStepCount - 1;
}
}
if (used < capacity)
{
used += newItems.Length;
used = used > capacity ? capacity : used;
}
}
/// <summary>
/// Индекс
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T this[int index]
{
get
{
if (used == 0)
throw new IndexOutOfRangeException();
var i = (current + 1 + index) % used;
return array[i];
}
set
{
var devider = used > 0 ? used : array.Length;
var i = (current + 1 + index) % devider;
array[i] = value;
UpdatedInvoke(current, value);
}
}
/// <summary>
/// событие на изменение элемента в массиве
/// </summary>
public event EventHandler<(int index, T value)>? Updated;
private void UpdatedInvoke(int index, T value)
{
Updated?.Invoke(this, (index, value));
}
/// <summary>
/// Агрегирование значения по всему массиву
/// </summary>
/// <typeparam name="Tout"></typeparam>
/// <param name="func"></param>
/// <param name="startValue"></param>
/// <returns></returns>
public Tout Aggregate<Tout>(Func<T, Tout, Tout> func, Tout startValue)
{
Tout result = startValue;
for (int i = 0; i < used; i++)
result = func(this[i], result);
return result;
}
/// <inheritdoc/>
public IEnumerator<T> GetEnumerator()
=> new CyclycListEnumerator<T>(array, current, used);
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
class CyclycListEnumerator<Te> : IEnumerator<Te>
{
private readonly Te[] array;
private readonly int used;
private readonly int first;
private int current = -1;
public CyclycListEnumerator(Te[] array, int first, int used)
{
this.array = new Te[array.Length];
array.CopyTo(this.array, 0);
this.used = used;
this.first = first;
}
public Te Current
{
get
{
if (IsCurrentOk())
{
var i = (current + first + 1) % used;
return array[i];
}
else
return default!;
}
}
object? IEnumerator.Current => Current;
public void Dispose() {; }
private bool IsCurrentOk() => current >= 0 && current < used;
public bool MoveNext()
{
if (current < used)
current++;
return IsCurrentOk();
}
public void Reset()
{
current = -1;
}
}
/// <summary>
/// Очистить весь массив
/// </summary>
public void Clear()
{
used = 0;
current = -1;
}
}