using System;
using System.Collections;
using System.Collections.Generic;
namespace MemoryPools.Collections.Specialized
{
///
/// Use this class when you need to store 1 or 2 items in class fields and in very rare scenarios
/// more then 2 items. In super-rare scenarios - to pass it as IList, ICollection or as IEnumerable.
///
public struct LocalList : IList
{
private static readonly EqualityComparer ItemComparer = EqualityComparer.Default;
private (T, T) _items;
private IList _other;
// Static lists to store real length (-1 field in struct)
private static readonly IList LengthIs1 = new List {default!};
private static readonly IList LengthIs2 = new List {default!, default!};
private const int DefaultListCapacity = 8;
public const int LocalStoreCapacity = 2;
public IEnumerator GetEnumerator()
{
// empty
if (_other == null) yield break;
if (_other.Count <= LocalStoreCapacity)
{
yield return _items.Item1;
if (ReferenceEquals(_other, LengthIs2)) yield return _items.Item2;
yield break;
}
using(var enumerator = _other.GetEnumerator())
{
while (enumerator.MoveNext()) yield return enumerator.Current;
}
}
public void Add(T item)
{
// empty
if (_other == null)
{
_items.Item1 = item;
_other = LengthIs1;
}
// count=1
else if (ReferenceEquals(_other, LengthIs1))
{
_items.Item2 = item;
_other = LengthIs2;
}
// count=2
else
{
if (ReferenceEquals(_other, LengthIs2))
{
_other = new List(DefaultListCapacity);
_other.Add(_items.Item1);
_items.Item1 = default;
_other.Add(_items.Item2);
_items.Item2 = default;
}
_other.Add(item);
}
}
public void Clear()
{
_other = null;
_items.Item1 = default;
_items.Item2 = default;
}
public bool Contains(T item)
{
return IndexOf(item) >= 0;
}
public void CopyTo(T[] array, int arrayIndex)
{
if (_other == null) return;
if (_other.Count > LocalStoreCapacity)
{
_other.CopyTo(array, arrayIndex);
}
else
{
if (_other.Count > 0) array[arrayIndex++] = _items.Item1;
if (_other.Count > 1) array[arrayIndex] = _items.Item2;
}
}
///
/// Removes first occurrence of given item
///
public bool Remove(T item)
{
if (_other == null) return false;
if (_other.Count > LocalStoreCapacity)
{
var done = _other.Remove(item);
if (done && _other.Count == 2)
{
_items.Item1 = _other[0];
_items.Item2 = _other[1];
_other = LengthIs2;
}
return done;
}
if (ReferenceEquals(_other, LengthIs1) && ItemComparer.Equals(_items.Item1, item))
{
_items.Item1 = default;
_other = null;
return true;
}
if (ReferenceEquals(_other, LengthIs2))
{
if (ItemComparer.Equals(_items.Item2, item))
{
_items.Item2 = default;
_other = LengthIs1;
return true;
}
if (ItemComparer.Equals(_items.Item1, item))
{
_items.Item1 = _items.Item2;
_other = LengthIs1;
return true;
}
}
return false;
}
public int Count => _other?.Count ?? 0;
public bool IsReadOnly => false;
public int IndexOf(T item)
{
if (_other == null)
return -1;
if (_other.Count > LocalStoreCapacity) return _other.IndexOf(item);
if (_other.Count > 0 && ItemComparer.Equals(_items.Item1, item)) return 0;
if (_other.Count > 1 && ItemComparer.Equals(_items.Item2, item)) return 1;
return -1;
}
public void Insert(int index, T item)
{
// 2nd of 1, 3rd of 2, 4th of 3..
if (index == Count)
{
Add(item);
return;
}
// Asked non-first when empty
if (_other == null) throw new IndexOutOfRangeException();
// If list already created
if (_other.Count > LocalStoreCapacity) _other.Insert(index, item);
if (index == 0)
{
if (ReferenceEquals(_other, LengthIs1))
{
_items = (item, _items.Item1);
_other = LengthIs2;
}
else
{
(_items.Item1, _items.Item2, item) = (item, _items.Item1, _items.Item2);
Add(item);
}
return;
}
// (item0, item1), list(nothing) ->> (def, def), list(item0, item ,item1)
if (index == 1)
{
(_items.Item2, item) = (item, _items.Item2);
Add(item);
}
}
public void RemoveAt(int index)
{
if (_other == null || _other.Count <= index || index < 0)
throw new IndexOutOfRangeException();
if (_other.Count < LocalStoreCapacity)
{
if (index == 0)
{
_items.Item1 = _items.Item2;
_other = ReferenceEquals(_other, LengthIs1) ? null : LengthIs1;
}
else
{
_items.Item2 = default;
_other = LengthIs1;
}
}
else
{
_other.RemoveAt(index);
if (_other.Count == 2)
{
_items.Item1 = _other[0];
_items.Item2 = _other[1];
_other = LengthIs2;
}
}
}
public T this[int index]
{
get
{
if (_other == null || index >= Count || index < 0)
throw new IndexOutOfRangeException();
if (_other?.Count > LocalStoreCapacity) return _other[index];
if (_other!.Count > 0 && index == 0) return _items.Item1;
if (_other.Count > 1 && index == 1) return _items.Item2;
throw new InvalidOperationException("Uncovered branch");
}
set
{
if (_other == null || index >= Count || index < 0)
throw new IndexOutOfRangeException();
if (_other.Count > LocalStoreCapacity) _other[index] = value;
if (_other.Count > 0 && index == 0) _items.Item1 = value;
if (_other.Count > 1 && index == 1) _items.Item2 = value;
throw new InvalidOperationException("Uncovered branch");
}
}
public override int GetHashCode()
{
return (_items.GetHashCode() * 37) & _other.GetHashCode();
}
public override bool Equals(object obj)
{
return obj is LocalList other && Equals(other);
}
public bool Equals(LocalList other)
{
return _items.Equals(other._items) && Equals(_other, other._other);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}