1144 lines
27 KiB
C#
1144 lines
27 KiB
C#
/* Copyright 2013-2014 Daikon Forge */
|
|
|
|
namespace DaikonForge.VoIP
|
|
{
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
/// <summary>
|
|
/// Defines a simplified Generic List customized specifically for
|
|
/// game development - Implements object pooling, minimizes memory
|
|
/// allocations during common operations, replaces common extension
|
|
/// methods with bespoke implementations that do not allocate
|
|
/// iterators, etc.
|
|
/// </summary>
|
|
public class FastList<T> : IList<T>, IDisposable
|
|
{
|
|
#region Object pooling
|
|
|
|
#region Static variables
|
|
|
|
// NOTE: Switched to Queue<object> in an attempt to work around
|
|
// a bug in the version of Mono used by Unity on iOS - http://stackoverflow.com/q/16542915
|
|
private static Queue<object> pool = new Queue<object>( 1024 );
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Releases all instances in the object pool.
|
|
/// </summary>
|
|
public static void ClearPool()
|
|
{
|
|
lock( pool )
|
|
{
|
|
pool.Clear();
|
|
pool.TrimExcess();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a reference to a <see cref="dfList<T>"/> instance. If there are
|
|
/// available instances in the object pool, the first available instance will
|
|
/// be returned. If there are no instances available, then a new instance
|
|
/// will be created. Use the <see cref="Release"/> function to return the instance
|
|
/// to the object pool.
|
|
/// </summary>
|
|
public static FastList<T> Obtain()
|
|
{
|
|
lock( pool )
|
|
{
|
|
if( pool.Count == 0 )
|
|
return new FastList<T>();
|
|
|
|
return (FastList<T>)pool.Dequeue();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a reference to a <see cref="dfList<T>"/> instance. If there are
|
|
/// available instances in the object pool, the first available instance will
|
|
/// be returned. If there are no instances available, then a new instance
|
|
/// will be created. Use the <see cref="Release"/> function to return the instance
|
|
/// to the object pool.
|
|
/// </summary>
|
|
public static FastList<T> Obtain( int capacity )
|
|
{
|
|
var list = Obtain();
|
|
|
|
list.EnsureCapacity( capacity );
|
|
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases the <see cref="FastList"/> back to the object pool
|
|
/// </summary>
|
|
public void Release()
|
|
{
|
|
Clear();
|
|
|
|
lock( pool )
|
|
{
|
|
pool.Enqueue( this );
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private instance fields
|
|
|
|
private const int DEFAULT_CAPACITY = 128;
|
|
|
|
private T[] items = new T[ DEFAULT_CAPACITY ];
|
|
private int count = 0;
|
|
|
|
private bool isElementTypeValueType = false;
|
|
//private bool isElementTypePoolable = false;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
internal FastList()
|
|
{
|
|
#if !UNITY_EDITOR && UNITY_METRO
|
|
isElementTypeValueType = typeof( T ).GetTypeInfo().IsValueType;
|
|
isElementTypePoolable = typeof( IPoolable ).GetTypeInfo().IsAssignableFrom( typeof( T ).GetTypeInfo() );
|
|
#else
|
|
isElementTypeValueType = typeof( T ).IsValueType;
|
|
//isElementTypePoolable = typeof( IPoolable ).IsAssignableFrom( typeof( T ) );
|
|
#endif
|
|
}
|
|
|
|
internal FastList( IList<T> listToClone )
|
|
: this()
|
|
{
|
|
AddRange( listToClone );
|
|
}
|
|
|
|
internal FastList( int capacity )
|
|
: this()
|
|
{
|
|
EnsureCapacity( capacity );
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public properties
|
|
|
|
/// <summary>
|
|
/// Returns the number of items in the list
|
|
/// </summary>
|
|
public int Count
|
|
{
|
|
get { return this.count; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of items this list can hold without needing to
|
|
/// resize the internal array (for internal use only)
|
|
/// </summary>
|
|
internal int Capacity
|
|
{
|
|
get { return this.items.Length; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the list is read-only. Inherited from IList<>
|
|
/// </summary>
|
|
public bool IsReadOnly
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets/Sets the item at the specified index
|
|
/// </summary>
|
|
public T this[ int index ]
|
|
{
|
|
get
|
|
{
|
|
if( index < 0 || index > this.count - 1 )
|
|
throw new IndexOutOfRangeException();
|
|
return this.items[ index ];
|
|
}
|
|
set
|
|
{
|
|
if( index < 0 || index > this.count - 1 )
|
|
throw new IndexOutOfRangeException();
|
|
this.items[ index ] = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows direct access to the underlying <see cref="System.Array"/>
|
|
/// containing this list's data. This array will most likely contain more
|
|
/// elements than the list reports via the <see cref="Count"/> property.
|
|
/// This property is intended for internal use by the UI library and should
|
|
/// not be accessed by other code.
|
|
/// </summary>
|
|
internal T[] Items
|
|
{
|
|
get { return this.items; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public methods
|
|
|
|
/// <summary>
|
|
/// Adds a new item to the end of the list. Provided only for call-level
|
|
/// compatability with code that treats this collection as a Queue.
|
|
/// </summary>
|
|
public void Enqueue( T item )
|
|
{
|
|
lock( items )
|
|
{
|
|
this.Add( item );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the first item in the collection and removes it from the list.
|
|
/// Provided only for call-level compatability with code that treats this
|
|
/// collection as a Queue.
|
|
/// </summary>
|
|
public T Dequeue()
|
|
{
|
|
lock( items )
|
|
{
|
|
if( this.count == 0 )
|
|
throw new IndexOutOfRangeException();
|
|
|
|
var item = this.items[ 0 ];
|
|
|
|
this.RemoveAt( 0 );
|
|
|
|
return item;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the last item in the collection and removes it frm the list.
|
|
/// Provided only for call-level compatibility with code that treats this
|
|
/// collection as a Stack.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public T Pop()
|
|
{
|
|
lock( items )
|
|
{
|
|
if( this.count == 0 )
|
|
throw new IndexOutOfRangeException();
|
|
|
|
var item = this.items[ this.count - 1 ];
|
|
|
|
this.count -= 1;
|
|
|
|
return item;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a shallow copy of this <see cref="dfList<T>"/> instance
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public FastList<T> Clone()
|
|
{
|
|
var clone = Obtain( this.count );
|
|
|
|
Array.Copy( this.items, 0, clone.items, 0, this.count );
|
|
|
|
clone.count = this.count;
|
|
|
|
return clone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reverses the order of the elements in the list
|
|
/// </summary>
|
|
public void Reverse()
|
|
{
|
|
Array.Reverse( this.items, 0, this.count );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts the elements in the entire <see cref="FastList" /> using the default comparer.
|
|
/// </summary>
|
|
public void Sort()
|
|
{
|
|
Array.Sort( this.items, 0, this.count, null );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts the elements in the entire <see cref="FastList" /> using the specified comparer.
|
|
/// </summary>
|
|
public void Sort( IComparer<T> comparer )
|
|
{
|
|
Array.Sort( this.items, 0, this.count, comparer );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts the elements in the entire <see cref="System.Collections.Generic.List" /> using the specified <see cref="System.Comparison" />.
|
|
/// </summary>
|
|
/// <param name="comparison">The <see cref="System.Comparison" /> to use when comparing elements.</param>
|
|
public void Sort( Comparison<T> comparison )
|
|
{
|
|
if( comparison == null )
|
|
{
|
|
throw new ArgumentNullException( "comparison" );
|
|
}
|
|
if( this.count > 0 )
|
|
{
|
|
using( var comparer = FunctorComparer.Obtain( comparison ) )
|
|
{
|
|
Array.Sort<T>( this.items, 0, this.count, comparer );
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures that the <see cref="FastList" /> has enough capacity to store <paramref name="Size"/> elements
|
|
/// </summary>
|
|
/// <param name="Size"></param>
|
|
public void EnsureCapacity( int Size )
|
|
{
|
|
if( items.Length < Size )
|
|
{
|
|
var newSize = ( Size / DEFAULT_CAPACITY ) * DEFAULT_CAPACITY + DEFAULT_CAPACITY;
|
|
Array.Resize<T>( ref this.items, newSize );
|
|
}
|
|
}
|
|
|
|
public void ForceCount( int count )
|
|
{
|
|
this.count = count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the elements of the specified collection to the end of the <see cref="FastList"/>
|
|
/// </summary>
|
|
public void AddRange( FastList<T> list )
|
|
{
|
|
var listCount = list.count;
|
|
|
|
EnsureCapacity( this.count + listCount );
|
|
Array.Copy( list.items, 0, this.items, this.count, listCount );
|
|
this.count += listCount;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the elements of the specified collection to the end of the <see cref="FastList"/>
|
|
/// </summary>
|
|
public void AddRange( IList<T> list )
|
|
{
|
|
var listCount = list.Count;
|
|
|
|
EnsureCapacity( this.count + listCount );
|
|
|
|
for( int i = 0; i < listCount; i++ )
|
|
{
|
|
this.items[ count++ ] = list[ i ];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the elements of the specified collection to the end of the <see cref="FastList"/>
|
|
/// </summary>
|
|
public void AddRange( T[] list )
|
|
{
|
|
var listLength = list.Length;
|
|
|
|
EnsureCapacity( this.count + listLength );
|
|
Array.Copy( list, 0, this.items, this.count, listLength );
|
|
this.count += listLength;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines the index of a specific item in the collection
|
|
/// </summary>
|
|
public int IndexOf( T item )
|
|
{
|
|
return Array.IndexOf<T>( this.items, item, 0, this.count );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inserts an item to the collection at the specified index
|
|
/// </summary>
|
|
/// <param name="index"></param>
|
|
/// <param name="item"></param>
|
|
public void Insert( int index, T item )
|
|
{
|
|
EnsureCapacity( this.count + 1 );
|
|
|
|
if( index < this.count )
|
|
{
|
|
Array.Copy( this.items, index, this.items, index + 1, this.count - index );
|
|
}
|
|
|
|
this.items[ index ] = item;
|
|
this.count += 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inserts an array of items at the specified index
|
|
/// </summary>
|
|
public void InsertRange( int index, T[] array )
|
|
{
|
|
if( array == null )
|
|
throw new ArgumentNullException( "items" );
|
|
|
|
if( index < 0 || index > this.count )
|
|
throw new ArgumentOutOfRangeException( "index" );
|
|
|
|
EnsureCapacity( this.count + array.Length );
|
|
|
|
if( index < this.count )
|
|
{
|
|
Array.Copy( this.items, index, this.items, index + array.Length, this.count - index );
|
|
}
|
|
|
|
array.CopyTo( this.items, index );
|
|
|
|
this.count += array.Length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inserts a collection of items at the specified index
|
|
/// </summary>
|
|
public void InsertRange( int index, FastList<T> list )
|
|
{
|
|
if( list == null )
|
|
throw new ArgumentNullException( "items" );
|
|
|
|
if( index < 0 || index > this.count )
|
|
throw new ArgumentOutOfRangeException( "index" );
|
|
|
|
EnsureCapacity( this.count + list.count );
|
|
|
|
if( index < this.count )
|
|
{
|
|
Array.Copy( this.items, index, this.items, index + list.count, this.count - index );
|
|
}
|
|
|
|
Array.Copy( list.items, 0, this.items, index, list.count );
|
|
|
|
this.count += list.count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all items matching the predicate condition from the list
|
|
/// </summary>
|
|
public void RemoveAll( Predicate<T> predicate )
|
|
{
|
|
var index = 0;
|
|
while( index < this.count )
|
|
{
|
|
if( predicate( items[ index ] ) )
|
|
{
|
|
RemoveAt( index );
|
|
}
|
|
else
|
|
{
|
|
index += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the item at the specified index
|
|
/// </summary>
|
|
public void RemoveAt( int index )
|
|
{
|
|
if( index >= this.count )
|
|
{
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
this.count -= 1;
|
|
|
|
if( index < this.count )
|
|
{
|
|
Array.Copy( this.items, index + 1, this.items, index, this.count - index );
|
|
}
|
|
|
|
this.items[ this.count ] = default( T );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes <paramref name="length"/> items from the collection at the specified index
|
|
/// </summary>
|
|
public void RemoveRange( int index, int length )
|
|
{
|
|
if( index < 0 || length < 0 || this.count - index < length )
|
|
{
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
if( count > 0 )
|
|
{
|
|
this.count -= length;
|
|
if( index < this.count )
|
|
{
|
|
Array.Copy( this.items, index + length, this.items, index, this.count - index );
|
|
}
|
|
|
|
Array.Clear( this.items, this.count, length );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an item to the collection
|
|
/// </summary>
|
|
public void Add( T item )
|
|
{
|
|
EnsureCapacity( this.count + 1 );
|
|
this.items[ this.count++ ] = item;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds two items to the collection
|
|
/// </summary>
|
|
public void Add( T item0, T item1 )
|
|
{
|
|
EnsureCapacity( this.count + 2 );
|
|
this.items[ this.count++ ] = item0;
|
|
this.items[ this.count++ ] = item1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds three items to the collection
|
|
/// </summary>
|
|
public void Add( T item0, T item1, T item2 )
|
|
{
|
|
EnsureCapacity( this.count + 3 );
|
|
this.items[ this.count++ ] = item0;
|
|
this.items[ this.count++ ] = item1;
|
|
this.items[ this.count++ ] = item2;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds four items to the collection
|
|
/// </summary>
|
|
public void Add( T item0, T item1, T item2, T item3 )
|
|
{
|
|
EnsureCapacity( this.count + 3 );
|
|
this.items[ this.count++ ] = item0;
|
|
this.items[ this.count++ ] = item1;
|
|
this.items[ this.count++ ] = item2;
|
|
this.items[ this.count++ ] = item3;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all items from the collection
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
if( !isElementTypeValueType )
|
|
{
|
|
Array.Clear( this.items, 0, this.items.Length );
|
|
}
|
|
|
|
this.count = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resizes the internal buffer to exactly match the number of elements in the collection
|
|
/// </summary>
|
|
public void TrimExcess()
|
|
{
|
|
Array.Resize( ref this.items, this.count );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether the collection contains the specified value
|
|
/// </summary>
|
|
public bool Contains( T item )
|
|
{
|
|
if( item == null )
|
|
{
|
|
for( int i = 0; i < this.count; i++ )
|
|
{
|
|
if( this.items[ i ] == null )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
|
|
|
|
for( int j = 0; j < this.count; j++ )
|
|
{
|
|
if( comparer.Equals( this.items[ j ], item ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies the elements of the collection to a <see cref="System.Array"/> instance
|
|
/// </summary>
|
|
/// <param name="array"></param>
|
|
public void CopyTo( T[] array )
|
|
{
|
|
CopyTo( array, 0 );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies the elements of the collection to an <see cref="System.Array"/> starting at the specified index
|
|
/// </summary>
|
|
public void CopyTo( T[] array, int arrayIndex )
|
|
{
|
|
Array.Copy( this.items, 0, array, arrayIndex, this.count );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies the elements of the collection to an <see cref="System.Array"/>
|
|
/// </summary>
|
|
/// <param name="sourceIndex">The starting position in the collection</param>
|
|
/// <param name="dest">The destination array</param>
|
|
/// <param name="destIndex">The position in the array to start copying to</param>
|
|
/// <param name="length">How many elements to copy</param>
|
|
public void CopyTo( int sourceIndex, T[] dest, int destIndex, int length )
|
|
{
|
|
if( sourceIndex + length > this.count )
|
|
throw new IndexOutOfRangeException( "sourceIndex" );
|
|
|
|
if( dest == null )
|
|
throw new ArgumentNullException( "dest" );
|
|
|
|
if( destIndex + length > dest.Length )
|
|
throw new IndexOutOfRangeException( "destIndex" );
|
|
|
|
Array.Copy( this.items, sourceIndex, dest, destIndex, length );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the first occurrence of a specific object from the collection
|
|
/// </summary>
|
|
public bool Remove( T item )
|
|
{
|
|
var index = IndexOf( item );
|
|
if( index == -1 )
|
|
return false;
|
|
|
|
RemoveAt( index );
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a List<T> collection containing all elements of this collection
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public List<T> ToList()
|
|
{
|
|
var list = new List<T>( this.count );
|
|
list.AddRange( this.ToArray() );
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// For internal use only.
|
|
/// </summary>
|
|
// @private
|
|
internal T[] ToTempArray()
|
|
{
|
|
var result = TempArray<T>.Obtain( this.count );
|
|
Array.Copy( items, 0, result, 0, this.count );
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an array containing all elements of this collection
|
|
/// </summary>
|
|
public T[] ToArray()
|
|
{
|
|
var array = new T[ this.count ];
|
|
|
|
Array.Copy( this.items, 0, array, 0, this.count );
|
|
|
|
return array;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a subset of the collection's items as an array
|
|
/// </summary>
|
|
public T[] ToArray( int index, int length )
|
|
{
|
|
var array = new T[ this.count ];
|
|
|
|
if( this.count > 0 )
|
|
{
|
|
CopyTo( index, array, 0, length );
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a subset of the collection's items as another dfList
|
|
/// </summary>
|
|
public FastList<T> GetRange( int index, int length )
|
|
{
|
|
var range = Obtain( length );
|
|
CopyTo( 0, range.items, index, length );
|
|
return range;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region LINQ replacement methods (avoids allocating enumerators)
|
|
|
|
/// <summary>
|
|
/// Returns whether any items in the collection match the condition
|
|
/// defined by the predicate.
|
|
/// </summary>
|
|
/// <param name="predicate">A function to test each element for a condition.</param>
|
|
/// <returns>true if any elements in the source sequence pass the test in the specified
|
|
/// predicate; otherwise, false.</returns>
|
|
public bool Any( Func<T, bool> predicate )
|
|
{
|
|
for( int i = 0; i < this.count; i++ )
|
|
{
|
|
if( predicate( this.items[ i ] ) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the first element in the list. Throws an exception if the list is empty.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public T First()
|
|
{
|
|
if( this.count == 0 )
|
|
{
|
|
throw new IndexOutOfRangeException();
|
|
}
|
|
|
|
return this.items[ 0 ];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the first element of the collection, or a default value
|
|
/// if the collection contains no elements.
|
|
/// </summary>
|
|
public T FirstOrDefault()
|
|
{
|
|
if( this.count > 0 )
|
|
return this.items[ 0 ];
|
|
|
|
return default( T );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the first element of the collection matching the condition defined by
|
|
/// <paramref name="predicate"/>, or the default value for the element type if the
|
|
/// collection contains no elements.
|
|
/// </summary>
|
|
public T FirstOrDefault( Func<T, bool> predicate )
|
|
{
|
|
for( int i = 0; i < this.count; i++ )
|
|
{
|
|
if( predicate( items[ i ] ) )
|
|
return items[ i ];
|
|
}
|
|
|
|
return default( T );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the last element in the list. Throws an exception if the list is empty.
|
|
/// </summary>
|
|
public T Last()
|
|
{
|
|
if( this.count == 0 )
|
|
{
|
|
throw new IndexOutOfRangeException();
|
|
}
|
|
|
|
return this.items[ this.count - 1 ];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the last element of the collection, or a default value
|
|
/// if the collection contains no elements.
|
|
/// </summary>
|
|
public T LastOrDefault()
|
|
{
|
|
if( this.count == 0 )
|
|
{
|
|
return default( T );
|
|
}
|
|
|
|
return this.items[ this.count - 1 ];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the last element of the collection matching the condition defined by
|
|
/// <paramref name="predicate"/>, or the default value for the element type if the
|
|
/// collection contains no elements.
|
|
/// </summary>
|
|
public T LastOrDefault( Func<T, bool> predicate )
|
|
{
|
|
var result = default( T );
|
|
|
|
for( int i = 0; i < this.count; i++ )
|
|
{
|
|
if( predicate( items[ i ] ) )
|
|
{
|
|
result = items[ i ];
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a <see cref="FastList"/> list containing all elements
|
|
/// of the collection matching the condition specified by <paramref name="predicate"/>
|
|
/// </summary>
|
|
public FastList<T> Where( Func<T, bool> predicate )
|
|
{
|
|
var result = FastList<T>.Obtain( this.count );
|
|
|
|
for( int i = 0; i < this.count; i++ )
|
|
{
|
|
if( predicate( items[ i ] ) )
|
|
{
|
|
result.Add( items[ i ] );
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the count of elements in the list that satisfy the
|
|
/// condition defined by <paramref name="predicate"/>
|
|
/// </summary>
|
|
public int Matching( Func<T, bool> predicate )
|
|
{
|
|
var matching = 0;
|
|
|
|
for( int i = 0; i < this.count; i++ )
|
|
{
|
|
if( predicate( items[ i ] ) )
|
|
matching += 1;
|
|
}
|
|
|
|
return matching;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Projects each element of a sequence into a new form defined by <paramref name="selector"/>
|
|
/// </summary>
|
|
public FastList<TResult> Select<TResult>( Func<T, TResult> selector )
|
|
{
|
|
var result = FastList<TResult>.Obtain( this.count );
|
|
|
|
for( int i = 0; i < this.count; i++ )
|
|
{
|
|
result.Add( selector( items[ i ] ) );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a concatenated list containing all elements both lists
|
|
/// </summary>
|
|
public FastList<T> Concat( FastList<T> list )
|
|
{
|
|
var result = FastList<T>.Obtain( this.count + list.count );
|
|
|
|
result.AddRange( this );
|
|
result.AddRange( list );
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts all elements of the list to the specified target type
|
|
/// </summary>
|
|
public FastList<TResult> Convert<TResult>()
|
|
{
|
|
var result = FastList<TResult>.Obtain( this.count );
|
|
|
|
for( int i = 0; i < this.count; i++ )
|
|
{
|
|
result.Add( (TResult)System.Convert.ChangeType( this.items[ i ], typeof( TResult ) ) );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs an action on each element of the list
|
|
/// </summary>
|
|
/// <param name="action">The action to be performed on each element</param>
|
|
public void ForEach( Action<T> action )
|
|
{
|
|
var index = 0;
|
|
while( index < this.Count )
|
|
{
|
|
action( items[ index++ ] );
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable<T> implementation
|
|
|
|
// NOTE: The IEnumerable<T> implementation here is horribly broken on iOS, and until
|
|
// I can figure out a way to implement typed enumerators that do work on iOS, please
|
|
// use a for(;;) loop instead of foreach(). Note that this may also apply to using
|
|
// LINQ queries, which may use foreach() or an GetEnumerator() internally.
|
|
|
|
/// <summary>
|
|
/// Returns an IEnumerator instance that can be used to iterate through
|
|
/// the elements in this list.
|
|
/// </summary>
|
|
public IEnumerator<T> GetEnumerator()
|
|
{
|
|
return PooledEnumerator.Obtain( this, null );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an IEnumerator instance that can be used to iterate through
|
|
/// the elements in this list.
|
|
/// </summary>
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return PooledEnumerator.Obtain( this, null );
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable implementation
|
|
|
|
/// <summary>
|
|
/// Releases the memory used by this object and returns it to the object pool
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
Release();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested classes
|
|
|
|
/// <summary>
|
|
/// This custom enumerator class implements object pooling in order
|
|
/// to reduce the number and frequency of memory allocations. It is
|
|
/// primarily expected to be used by framework and library code which
|
|
/// treats the associated collection as an IEnumerable<T> rather
|
|
/// than as a <see cref="dfList"/>. For instance, LINQ contains
|
|
/// several extension methods which will treat the collection as
|
|
/// an IEnumerable<T> and will create, use, and Dispose() of
|
|
/// the enumerator while performing the query. Similarly, many
|
|
/// third-party libraries will use a foreach() loop over a collection
|
|
/// rather than using for() with numeric indices, and the object pooling
|
|
/// implemented by this class will help to mitigate the number of
|
|
/// allocations typically caused by using such code.
|
|
/// </summary>
|
|
private class PooledEnumerator : IEnumerator<T>, IEnumerable<T>
|
|
{
|
|
|
|
#region Static variables
|
|
|
|
private static Queue<PooledEnumerator> pool = new Queue<PooledEnumerator>();
|
|
|
|
#endregion
|
|
|
|
#region Private variables
|
|
|
|
private FastList<T> list;
|
|
private Func<T, bool> predicate;
|
|
private int currentIndex;
|
|
private T currentValue;
|
|
private bool isValid = false;
|
|
|
|
#endregion
|
|
|
|
#region Pooling
|
|
|
|
public static PooledEnumerator Obtain( FastList<T> list, Func<T, bool> predicate )
|
|
{
|
|
var enumerator = ( pool.Count > 0 ) ? pool.Dequeue() : new PooledEnumerator();
|
|
enumerator.ResetInternal( list, predicate );
|
|
|
|
return enumerator;
|
|
}
|
|
|
|
public void Release()
|
|
{
|
|
if( this.isValid )
|
|
{
|
|
this.isValid = false;
|
|
pool.Enqueue( this );
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerator<T> Members
|
|
|
|
public T Current
|
|
{
|
|
get
|
|
{
|
|
if( !this.isValid )
|
|
throw new InvalidOperationException( "The enumerator is no longer valid" );
|
|
|
|
return this.currentValue;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private utility methods
|
|
|
|
private void ResetInternal( FastList<T> list, Func<T, bool> predicate )
|
|
{
|
|
this.isValid = true;
|
|
this.list = list;
|
|
this.predicate = predicate;
|
|
this.currentIndex = 0;
|
|
this.currentValue = default( T );
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable Members
|
|
|
|
public void Dispose()
|
|
{
|
|
Release();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerator Members
|
|
|
|
object IEnumerator.Current
|
|
{
|
|
get
|
|
{
|
|
return this.Current;
|
|
}
|
|
}
|
|
|
|
public bool MoveNext()
|
|
{
|
|
if( !this.isValid )
|
|
throw new InvalidOperationException( "The enumerator is no longer valid" );
|
|
|
|
while( this.currentIndex < this.list.Count )
|
|
{
|
|
var valueAtIndex = this.list[ currentIndex++ ];
|
|
if( predicate != null )
|
|
{
|
|
if( !predicate( valueAtIndex ) )
|
|
continue;
|
|
}
|
|
|
|
this.currentValue = valueAtIndex;
|
|
return true;
|
|
}
|
|
|
|
Release();
|
|
|
|
this.currentValue = default( T );
|
|
return false;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable Members
|
|
|
|
public IEnumerator<T> GetEnumerator()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
private class FunctorComparer : IComparer<T>, IDisposable
|
|
{
|
|
#region Static variables
|
|
|
|
private static Queue<FunctorComparer> pool = new Queue<FunctorComparer>();
|
|
|
|
#endregion
|
|
|
|
#region Private instance variables
|
|
|
|
private Comparison<T> comparison;
|
|
|
|
#endregion
|
|
|
|
#region Object pooling
|
|
|
|
public static FunctorComparer Obtain( Comparison<T> comparison )
|
|
{
|
|
var comparer = ( pool.Count > 0 ) ? pool.Dequeue() : new FunctorComparer();
|
|
comparer.comparison = comparison;
|
|
return comparer;
|
|
}
|
|
|
|
public void Release()
|
|
{
|
|
this.comparison = null;
|
|
|
|
if( !pool.Contains( this ) )
|
|
{
|
|
pool.Enqueue( this );
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IComparer<T> implementation
|
|
|
|
public int Compare( T x, T y )
|
|
{
|
|
return this.comparison( x, y );
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable implementation
|
|
|
|
public void Dispose()
|
|
{
|
|
this.Release();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |