You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
512 lines
21 KiB
512 lines
21 KiB
#if FAT |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Threading; |
|
using LinqInternal.Core; |
|
using LinqInternal.Threading.Needles; |
|
|
|
namespace LinqInternal.Collections.ThreadSafe |
|
{ |
|
/// <summary> |
|
/// Represent a thread-safe wait-free fixed size bucket with lazy initialization. |
|
/// </summary> |
|
/// <typeparam name="T">The type of the item.</typeparam> |
|
/// <typeparam name="TNeedle">THe type of the needles</typeparam> |
|
/// <remarks> |
|
/// Consider wrapping this class to implement <see cref="ICollection{T}" /> or any other desired interface. |
|
/// </remarks> |
|
[Serializable] |
|
internal sealed class NeedleBucket<T, TNeedle> : IEnumerable<T>, IBucket<T> |
|
where TNeedle : class, IRecyclableNeedle<T> |
|
{ |
|
private readonly FixedSizeBucket<TNeedle> _entries; |
|
private readonly Func<T, TNeedle> _needleFactory; |
|
private readonly Func<int, TNeedle> _needleIndexFactory; |
|
|
|
[NonSerialized] |
|
private NeedleReservoir<T, TNeedle> _reservoir; |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="NeedleBucket{T, TNeedle}" /> class. |
|
/// </summary> |
|
/// <param name = "valueFactory">The delegate that is invoked to do the lazy initialization of the items given their index.</param> |
|
/// <param name="needleFactory">The delegate that is invoked to create a needle</param> |
|
/// <param name="capacity">The capacity.</param> |
|
/// <exception cref="InvalidOperationException"></exception> |
|
/// <exception cref="ArgumentNullException"><paramref name="valueFactory"/> is <c>null</c>.</exception> |
|
public NeedleBucket(Func<int, T> valueFactory, Func<T, TNeedle> needleFactory, int capacity) |
|
{ |
|
if (valueFactory == null) |
|
{ |
|
throw new ArgumentNullException("valueFactory"); |
|
} |
|
if (needleFactory == null) |
|
{ |
|
throw new ArgumentNullException("needleFactory"); |
|
} |
|
_needleFactory = needleFactory; |
|
_reservoir = new NeedleReservoir<T, TNeedle>(_needleFactory); |
|
_needleIndexFactory = index => Reservoir.GetNeedle(new ValueFuncClosure<int, T>(valueFactory, index).InvokeReturn()); |
|
_entries = new FixedSizeBucket<TNeedle>(capacity); |
|
} |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="NeedleBucket{T, TNeedle}" /> class. |
|
/// </summary> |
|
/// <param name = "valueFactory">The delegate that is invoked to do the lazy initialization of the items.</param> |
|
/// <param name="needleFactory">The delegate that is invoked to create a needle</param> |
|
/// <param name="capacity">The capacity.</param> |
|
/// <exception cref="InvalidOperationException"></exception> |
|
/// <exception cref="ArgumentNullException"><paramref name="valueFactory"/> is <c>null</c>.</exception> |
|
public NeedleBucket(Func<T> valueFactory, Func<T, TNeedle> needleFactory, int capacity) |
|
{ |
|
if (valueFactory == null) |
|
{ |
|
throw new ArgumentNullException("valueFactory"); |
|
} |
|
if (needleFactory == null) |
|
{ |
|
throw new ArgumentNullException("needleFactory"); |
|
} |
|
_needleFactory = needleFactory; |
|
_reservoir = new NeedleReservoir<T, TNeedle>(_needleFactory); |
|
_needleIndexFactory = index => Reservoir.GetNeedle(new ValueFuncClosure<T>(valueFactory).InvokeReturn()); |
|
_entries = new FixedSizeBucket<TNeedle>(capacity); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the capacity. |
|
/// </summary> |
|
public int Capacity |
|
{ |
|
get { return _entries.Capacity; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the number of items actually contained. |
|
/// </summary> |
|
public int Count |
|
{ |
|
get { return _entries.Count; } |
|
} |
|
|
|
public NeedleReservoir<T, TNeedle> Reservoir |
|
{ |
|
get |
|
{ |
|
var found = Interlocked.CompareExchange(ref _reservoir, null, null); |
|
if (found != null) |
|
{ |
|
return found; |
|
} |
|
var created = new NeedleReservoir<T, TNeedle>(_needleFactory); |
|
found = Interlocked.CompareExchange(ref _reservoir, created, null); |
|
return found ?? created; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Copies the items to a compatible one-dimensional array, starting at the specified index of the target array. |
|
/// </summary> |
|
/// <param name="array">The array.</param> |
|
/// <param name="arrayIndex">Index of the array.</param> |
|
/// <exception cref="System.ArgumentNullException">array</exception> |
|
/// <exception cref="System.ArgumentOutOfRangeException">arrayIndex;Non-negative number is required.</exception> |
|
/// <exception cref="System.ArgumentException">array;The array can not contain the number of elements.</exception> |
|
public void CopyTo(T[] array, int arrayIndex) |
|
{ |
|
_entries.ToArray().CopyTo(array, arrayIndex); |
|
} |
|
|
|
/// <summary> |
|
/// Sets the item at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="item">The item.</param> |
|
/// <param name="previous">The previous item in the specified index.</param> |
|
/// <returns> |
|
/// <c>true</c> if the item was new; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public bool Exchange(int index, T item, out T previous) |
|
{ |
|
TNeedle found; |
|
if (_entries.Exchange(index, Reservoir.GetNeedle(item), out found)) |
|
{ |
|
previous = default(T); |
|
return true; |
|
} |
|
// TryGetValue is null resistant |
|
found.TryGetValue(out previous); |
|
// This is a needle that is no longer referenced, we donate it |
|
Reservoir.DonateNeedle(found); |
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Sets the needle at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="needle">The needle.</param> |
|
/// <param name="previous">The previous item in the specified index.</param> |
|
/// <returns> |
|
/// <c>true</c> if the item was new; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public bool ExchangeNeedle(int index, TNeedle needle, out TNeedle previous) |
|
{ |
|
// This may allow null to enter, this may also give null as previous if null was allowed to enter |
|
// We don't donate because we return the needle |
|
return _entries.Exchange(index, needle, out previous); |
|
} |
|
|
|
/// <summary> |
|
/// Retrieve or creates a new item at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <returns>The value.</returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public T Get(int index) |
|
{ |
|
if (index < 0 || index >= _entries.Capacity) |
|
{ |
|
throw new ArgumentOutOfRangeException("index", "index must be greater or equal to 0 and less than capacity"); |
|
} |
|
// Using TryGetValue first just avoid wasting a needle |
|
TNeedle found; |
|
if (_entries.TryGetInternal(index, out found)) |
|
{ |
|
// Null resistant |
|
T previous; |
|
found.TryGetValue(out previous); |
|
return previous; |
|
// We don't donate because we didn't remove |
|
} |
|
var newNeedle = _needleIndexFactory(index); |
|
if (_entries.InsertInternal(index, newNeedle, out found)) |
|
{ |
|
return newNeedle.Value; |
|
} |
|
// we just failed to insert, meaning that we created a usless needle |
|
// donate it |
|
Reservoir.DonateNeedle(newNeedle); |
|
return found.Value; |
|
} |
|
|
|
/// <summary> |
|
/// Returns an enumerator that iterates through a collection. |
|
/// </summary> |
|
/// <returns> |
|
/// An <see cref="System.Collections.Generic.IEnumerator{T}" /> object that can be used to iterate through the collection. |
|
/// </returns> |
|
public IEnumerator<T> GetEnumerator() |
|
{ |
|
foreach (var needle in _entries) |
|
{ |
|
T item; |
|
if (needle.TryGetValue(out item)) |
|
{ |
|
yield return item; |
|
} |
|
} |
|
} |
|
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
|
{ |
|
return GetEnumerator(); |
|
} |
|
|
|
/// <summary> |
|
/// Retrieve or creates a new needle at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <returns>The needle.</returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public TNeedle GetNeedle(int index) |
|
{ |
|
var newNeedle = _needleIndexFactory(index); |
|
TNeedle found; |
|
if (_entries.Insert(index, newNeedle, out found)) |
|
{ |
|
return newNeedle; |
|
} |
|
// we just failed to insert, meaning that we created a usless needle |
|
// donate it |
|
Reservoir.DonateNeedle(newNeedle); |
|
return found; |
|
} |
|
|
|
/// <summary> |
|
/// Inserts the item at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="item">The item.</param> |
|
/// <returns> |
|
/// <c>true</c> if the item was inserted; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity.</exception> |
|
/// <remarks> |
|
/// The insertion can fail if the index is already used or is being written by another thread. |
|
/// If the index is being written it can be understood that the insert operation happened before but the item was overwritten or removed. |
|
/// </remarks> |
|
public bool Insert(int index, T item) |
|
{ |
|
// Only succeeds if there was nothing there before |
|
// meaning that if this succeeds it replaced nothing |
|
// If this fails whatever was there is still there |
|
var newNeedle = Reservoir.GetNeedle(item); |
|
if (_entries.Insert(index, newNeedle)) |
|
{ |
|
return true; |
|
} |
|
Reservoir.DonateNeedle(newNeedle); |
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Inserts the item at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="item">The item.</param> |
|
/// <param name="previous">The previous item in the specified index.</param> |
|
/// <returns> |
|
/// <c>true</c> if the item was inserted; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
/// <remarks> |
|
/// The insertion can fail if the index is already used or is being written by another thread. |
|
/// If the index is being written it can be understood that the insert operation happened before but the item was overwritten or removed. |
|
/// </remarks> |
|
public bool Insert(int index, T item, out T previous) |
|
{ |
|
// Only succeeds if there was nothing there before |
|
// meaning that if this succeeds it replaced nothing |
|
// If this fails whatever was there is still there |
|
var newNeedle = Reservoir.GetNeedle(item); |
|
TNeedle found; |
|
if (_entries.Insert(index, newNeedle, out found)) |
|
{ |
|
previous = default(T); |
|
return true; |
|
} |
|
Reservoir.DonateNeedle(newNeedle); |
|
// TryGetValue is null resistant |
|
found.TryGetValue(out previous); |
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Inserts the needle at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="needle">The needle.</param> |
|
/// <returns> |
|
/// <c>true</c> if the needle was inserted; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity.</exception> |
|
/// <remarks> |
|
/// The insertion can fail if the index is already used or is being written by another thread. |
|
/// If the index is being written it can be understood that the insert operation happened before but the needle was overwritten or removed. |
|
/// </remarks> |
|
public bool InsertNeedle(int index, TNeedle needle) |
|
{ |
|
// This may allow null to enter |
|
return _entries.Insert(index, needle); |
|
} |
|
|
|
/// <summary> |
|
/// Inserts the needle at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="needle">The needle.</param> |
|
/// <param name="previous">The previous needle in the specified index.</param> |
|
/// <returns> |
|
/// <c>true</c> if the needle was inserted; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
/// <remarks> |
|
/// The insertion can fail if the index is already used or is being written by another thread. |
|
/// If the index is being written it can be understood that the insert operation happened before but the needle was overwritten or removed. |
|
/// </remarks> |
|
public bool InsertNeedle(int index, TNeedle needle, out TNeedle previous) |
|
{ |
|
// This may allow null to enter, this may also give null as previous if null was allowed to enter |
|
return _entries.Insert(index, needle, out previous); |
|
} |
|
|
|
/// <summary> |
|
/// Removes the item at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <returns> |
|
/// <c>true</c> if the item was removed; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public bool RemoveAt(int index) |
|
{ |
|
return _entries.RemoveAt(index); |
|
} |
|
|
|
/// <summary> |
|
/// Removes the item at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="previous">The previous item in the specified index.</param> |
|
/// <returns> |
|
/// <c>true</c> if the item was removed; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public bool RemoveAt(int index, out T previous) |
|
{ |
|
TNeedle found; |
|
if (_entries.RemoveAt(index, out found)) |
|
{ |
|
// TryGetValue is null resistant |
|
found.TryGetValue(out previous); |
|
// Donate it |
|
Reservoir.DonateNeedle(found); |
|
return true; |
|
} |
|
previous = default(T); |
|
return false; |
|
} |
|
|
|
public bool RemoveAt(int index, Predicate<T> check) |
|
{ |
|
if (check == null) |
|
{ |
|
throw new ArgumentNullException("check"); |
|
} |
|
TNeedle found = null; |
|
Predicate<TNeedle> replacementCheck = needle => |
|
{ |
|
found = needle; |
|
return check(needle.Value); |
|
}; |
|
if (_entries.RemoveAt(index, replacementCheck)) |
|
{ |
|
// Donate it |
|
Reservoir.DonateNeedle(found); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Removes the needle at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="previous">The previous needle in the specified index.</param> |
|
/// <returns> |
|
/// <c>true</c> if the needle was removed; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public bool RemoveNeedleAt(int index, out TNeedle previous) |
|
{ |
|
return _entries.RemoveAt(index, out previous); |
|
} |
|
|
|
/// <summary> |
|
/// Sets the item at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="item">The item.</param> |
|
/// <param name="isNew">if set to <c>true</c> the index was not previously used.</param> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public void Set(int index, T item, out bool isNew) |
|
{ |
|
// This may have replaced something |
|
_entries.Set(index, Reservoir.GetNeedle(item), out isNew); |
|
} |
|
|
|
/// <summary> |
|
/// Sets the needle at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="needle">The needle.</param> |
|
/// <param name="isNew">if set to <c>true</c> the index was not previously used.</param> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public void SetNeedle(int index, TNeedle needle, out bool isNew) |
|
{ |
|
// This may allow null to enter |
|
_entries.Set(index, needle, out isNew); |
|
} |
|
|
|
/// <summary> |
|
/// Tries to retrieve the item at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="value">The value.</param> |
|
/// <returns> |
|
/// <c>true</c> if the item was retrieved; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public bool TryGet(int index, out T value) |
|
{ |
|
TNeedle found; |
|
if (_entries.TryGet(index, out found)) |
|
{ |
|
// Null resistant |
|
found.TryGetValue(out value); |
|
// We don't donate because we didn't remove |
|
return true; |
|
} |
|
value = default(T); |
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Tries to retrieve the needle at the specified index. |
|
/// </summary> |
|
/// <param name="index">The index.</param> |
|
/// <param name="value">The value.</param> |
|
/// <returns> |
|
/// <c>true</c> if the needle was retrieved; otherwise, <c>false</c>. |
|
/// </returns> |
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception> |
|
public bool TryGetNeedle(int index, out TNeedle value) |
|
{ |
|
return _entries.TryGet(index, out value); |
|
} |
|
|
|
public bool Update(int index, Func<T, T> itemUpdateFactory, Predicate<T> check, out bool isEmpty) |
|
{ |
|
if (itemUpdateFactory == null) |
|
{ |
|
throw new ArgumentNullException("check"); |
|
} |
|
if (check == null) |
|
{ |
|
throw new ArgumentNullException("check"); |
|
} |
|
TNeedle newNeedle = null; |
|
Func<TNeedle, TNeedle> replacementFactory = needle => newNeedle = Reservoir.GetNeedle(itemUpdateFactory(needle.Value)); |
|
Predicate<TNeedle> replacementCheck = needle => check(needle.Value); |
|
if (_entries.Update(index, replacementFactory, replacementCheck, out isEmpty)) |
|
{ |
|
return true; |
|
} |
|
if (newNeedle != null) |
|
{ |
|
Reservoir.DonateNeedle(newNeedle); |
|
} |
|
return false; |
|
} |
|
|
|
public IEnumerable<T> Where(Predicate<T> check) |
|
{ |
|
if (check == null) |
|
{ |
|
throw new ArgumentNullException("check"); |
|
} |
|
foreach (var needle in _entries.Where(needle => check(needle.Value))) |
|
{ |
|
yield return needle.Value; |
|
} |
|
} |
|
} |
|
} |
|
|
|
#endif |