namespace SRF
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine;
///
/// IList implementation which does not release the buffer when clearing/removing elements. Based on the NGUI BetterList
///
[Serializable]
public class SRList : IList, ISerializationCallbackReceiver
{
[SerializeField] private T[] _buffer;
[SerializeField] private int _count;
private EqualityComparer _equalityComparer;
private ReadOnlyCollection _readOnlyWrapper;
public SRList() {}
public SRList(int capacity)
{
Buffer = new T[capacity];
}
///
/// Create a new list with the range of values. Contains a foreach loop, which will allocate garbage when used with most
/// generic collection types.
///
public SRList(IEnumerable source)
{
AddRange(source);
}
public T[] Buffer
{
get { return _buffer; }
private set { _buffer = value; }
}
private EqualityComparer EqualityComparer
{
get
{
if (_equalityComparer == null)
{
_equalityComparer = EqualityComparer.Default;
}
return _equalityComparer;
}
}
public int Count
{
get { return _count; }
private set { _count = value; }
}
public IEnumerator GetEnumerator()
{
if (Buffer != null)
{
for (var i = 0; i < Count; ++i)
{
yield return Buffer[i];
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(T item)
{
if (Buffer == null || Count == Buffer.Length)
{
Expand();
}
Buffer[Count++] = item;
}
public void Clear()
{
Count = 0;
}
public bool Contains(T item)
{
if (Buffer == null)
{
return false;
}
for (var i = 0; i < Count; ++i)
{
if (EqualityComparer.Equals(Buffer[i], item))
{
return true;
}
}
return false;
}
public void CopyTo(T[] array, int arrayIndex)
{
Trim();
Buffer.CopyTo(array, arrayIndex);
}
public bool Remove(T item)
{
if (Buffer == null)
{
return false;
}
var index = IndexOf(item);
if (index < 0)
{
return false;
}
RemoveAt(index);
return true;
}
public bool IsReadOnly
{
get { return false; }
}
public int IndexOf(T item)
{
if (Buffer == null)
{
return -1;
}
for (var i = 0; i < Count; ++i)
{
if (EqualityComparer.Equals(Buffer[i], item))
{
return i;
}
}
return -1;
}
public void Insert(int index, T item)
{
if (Buffer == null || Count == Buffer.Length)
{
Expand();
}
if (index < Count)
{
for (var i = Count; i > index; --i)
{
Buffer[i] = Buffer[i - 1];
}
Buffer[index] = item;
++Count;
}
else
{
Add(item);
}
}
public void RemoveAt(int index)
{
if (Buffer != null && index < Count)
{
--Count;
Buffer[index] = default(T);
for (var b = index; b < Count; ++b)
{
Buffer[b] = Buffer[b + 1];
}
}
}
public T this[int index]
{
get
{
if (Buffer == null)
{
throw new IndexOutOfRangeException();
}
return Buffer[index];
}
set
{
if (Buffer == null)
{
throw new IndexOutOfRangeException();
}
Buffer[index] = value;
}
}
public void OnBeforeSerialize()
{
// Clean buffer of unused elements before serializing
Clean();
}
public void OnAfterDeserialize()
{
}
///
/// Add range of values to the list. Contains a foreach loop, which will allocate garbage when used with most
/// generic collection types.
///
///
public void AddRange(IEnumerable range)
{
foreach (var item in range)
{
Add(item);
}
}
///
/// Clear the list, optionally setting each element to default(T)
///
public void Clear(bool clean)
{
Clear();
if (!clean)
{
return;
}
Clean();
}
public void Clean()
{
if (Buffer == null)
{
return;
}
for (var i = Count; i < _buffer.Length; i++)
{
_buffer[i] = default(T);
}
}
///
/// Get a read-only wrapper of this list. This is cached, so very little cost after first called.
///
///
public ReadOnlyCollection AsReadOnly()
{
if (_readOnlyWrapper == null)
{
_readOnlyWrapper = new ReadOnlyCollection(this);
}
return _readOnlyWrapper;
}
///
/// Helper function that expands the size of the array, maintaining the content.
///
private void Expand()
{
var newList = (Buffer != null) ? new T[Mathf.Max(Buffer.Length << 1, 32)] : new T[32];
if (Buffer != null && Count > 0)
{
Buffer.CopyTo(newList, 0);
}
Buffer = newList;
}
///
/// Trim the unnecessary memory, resizing the buffer to be of 'Length' size.
/// Call this function only if you are sure that the buffer won't need to resize anytime soon.
///
public void Trim()
{
if (Count > 0)
{
if (Count >= Buffer.Length)
{
return;
}
var newList = new T[Count];
for (var i = 0; i < Count; ++i)
{
newList[i] = Buffer[i];
}
Buffer = newList;
}
else
{
Buffer = new T[0];
}
}
///
/// List.Sort equivalent.
///
public void Sort(Comparison comparer)
{
var changed = true;
while (changed)
{
changed = false;
for (var i = 1; i < Count; ++i)
{
if (comparer.Invoke(Buffer[i - 1], Buffer[i]) > 0)
{
var temp = Buffer[i];
Buffer[i] = Buffer[i - 1];
Buffer[i - 1] = temp;
changed = true;
}
}
}
}
}
}