#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
{
///
/// Represent a thread-safe wait-free fixed size bucket with lazy initialization.
///
/// The type of the item.
/// THe type of the needles
///
/// Consider wrapping this class to implement or any other desired interface.
///
[Serializable]
internal sealed class NeedleBucket : IEnumerable, IBucket
where TNeedle : class, IRecyclableNeedle
{
private readonly FixedSizeBucket _entries;
private readonly Func _needleFactory;
private readonly Func _needleIndexFactory;
[NonSerialized]
private NeedleReservoir _reservoir;
///
/// Initializes a new instance of the class.
///
/// The delegate that is invoked to do the lazy initialization of the items given their index.
/// The delegate that is invoked to create a needle
/// The capacity.
///
/// is null.
public NeedleBucket(Func valueFactory, Func needleFactory, int capacity)
{
if (valueFactory == null)
{
throw new ArgumentNullException("valueFactory");
}
if (needleFactory == null)
{
throw new ArgumentNullException("needleFactory");
}
_needleFactory = needleFactory;
_reservoir = new NeedleReservoir(_needleFactory);
_needleIndexFactory = index => Reservoir.GetNeedle(new ValueFuncClosure(valueFactory, index).InvokeReturn());
_entries = new FixedSizeBucket(capacity);
}
///
/// Initializes a new instance of the class.
///
/// The delegate that is invoked to do the lazy initialization of the items.
/// The delegate that is invoked to create a needle
/// The capacity.
///
/// is null.
public NeedleBucket(Func valueFactory, Func needleFactory, int capacity)
{
if (valueFactory == null)
{
throw new ArgumentNullException("valueFactory");
}
if (needleFactory == null)
{
throw new ArgumentNullException("needleFactory");
}
_needleFactory = needleFactory;
_reservoir = new NeedleReservoir(_needleFactory);
_needleIndexFactory = index => Reservoir.GetNeedle(new ValueFuncClosure(valueFactory).InvokeReturn());
_entries = new FixedSizeBucket(capacity);
}
///
/// Gets the capacity.
///
public int Capacity
{
get { return _entries.Capacity; }
}
///
/// Gets the number of items actually contained.
///
public int Count
{
get { return _entries.Count; }
}
public NeedleReservoir Reservoir
{
get
{
var found = Interlocked.CompareExchange(ref _reservoir, null, null);
if (found != null)
{
return found;
}
var created = new NeedleReservoir(_needleFactory);
found = Interlocked.CompareExchange(ref _reservoir, created, null);
return found ?? created;
}
}
///
/// Copies the items to a compatible one-dimensional array, starting at the specified index of the target array.
///
/// The array.
/// Index of the array.
/// array
/// arrayIndex;Non-negative number is required.
/// array;The array can not contain the number of elements.
public void CopyTo(T[] array, int arrayIndex)
{
_entries.ToArray().CopyTo(array, arrayIndex);
}
///
/// Sets the item at the specified index.
///
/// The index.
/// The item.
/// The previous item in the specified index.
///
/// true if the item was new; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity
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;
}
///
/// Sets the needle at the specified index.
///
/// The index.
/// The needle.
/// The previous item in the specified index.
///
/// true if the item was new; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity
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);
}
///
/// Retrieve or creates a new item at the specified index.
///
/// The index.
/// The value.
/// index;index must be greater or equal to 0 and less than capacity
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;
}
///
/// Returns an enumerator that iterates through a collection.
///
///
/// An object that can be used to iterate through the collection.
///
public IEnumerator 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();
}
///
/// Retrieve or creates a new needle at the specified index.
///
/// The index.
/// The needle.
/// index;index must be greater or equal to 0 and less than capacity
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;
}
///
/// Inserts the item at the specified index.
///
/// The index.
/// The item.
///
/// true if the item was inserted; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity.
///
/// 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.
///
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;
}
///
/// Inserts the item at the specified index.
///
/// The index.
/// The item.
/// The previous item in the specified index.
///
/// true if the item was inserted; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity
///
/// 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.
///
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;
}
///
/// Inserts the needle at the specified index.
///
/// The index.
/// The needle.
///
/// true if the needle was inserted; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity.
///
/// 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.
///
public bool InsertNeedle(int index, TNeedle needle)
{
// This may allow null to enter
return _entries.Insert(index, needle);
}
///
/// Inserts the needle at the specified index.
///
/// The index.
/// The needle.
/// The previous needle in the specified index.
///
/// true if the needle was inserted; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity
///
/// 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.
///
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);
}
///
/// Removes the item at the specified index.
///
/// The index.
///
/// true if the item was removed; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity
public bool RemoveAt(int index)
{
return _entries.RemoveAt(index);
}
///
/// Removes the item at the specified index.
///
/// The index.
/// The previous item in the specified index.
///
/// true if the item was removed; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity
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 check)
{
if (check == null)
{
throw new ArgumentNullException("check");
}
TNeedle found = null;
Predicate replacementCheck = needle =>
{
found = needle;
return check(needle.Value);
};
if (_entries.RemoveAt(index, replacementCheck))
{
// Donate it
Reservoir.DonateNeedle(found);
return true;
}
return false;
}
///
/// Removes the needle at the specified index.
///
/// The index.
/// The previous needle in the specified index.
///
/// true if the needle was removed; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity
public bool RemoveNeedleAt(int index, out TNeedle previous)
{
return _entries.RemoveAt(index, out previous);
}
///
/// Sets the item at the specified index.
///
/// The index.
/// The item.
/// if set to true the index was not previously used.
/// index;index must be greater or equal to 0 and less than capacity
public void Set(int index, T item, out bool isNew)
{
// This may have replaced something
_entries.Set(index, Reservoir.GetNeedle(item), out isNew);
}
///
/// Sets the needle at the specified index.
///
/// The index.
/// The needle.
/// if set to true the index was not previously used.
/// index;index must be greater or equal to 0 and less than capacity
public void SetNeedle(int index, TNeedle needle, out bool isNew)
{
// This may allow null to enter
_entries.Set(index, needle, out isNew);
}
///
/// Tries to retrieve the item at the specified index.
///
/// The index.
/// The value.
///
/// true if the item was retrieved; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity
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;
}
///
/// Tries to retrieve the needle at the specified index.
///
/// The index.
/// The value.
///
/// true if the needle was retrieved; otherwise, false.
///
/// index;index must be greater or equal to 0 and less than capacity
public bool TryGetNeedle(int index, out TNeedle value)
{
return _entries.TryGet(index, out value);
}
public bool Update(int index, Func itemUpdateFactory, Predicate check, out bool isEmpty)
{
if (itemUpdateFactory == null)
{
throw new ArgumentNullException("check");
}
if (check == null)
{
throw new ArgumentNullException("check");
}
TNeedle newNeedle = null;
Func replacementFactory = needle => newNeedle = Reservoir.GetNeedle(itemUpdateFactory(needle.Value));
Predicate 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 Where(Predicate check)
{
if (check == null)
{
throw new ArgumentNullException("check");
}
foreach (var needle in _entries.Where(needle => check(needle.Value)))
{
yield return needle.Value;
}
}
}
}
#endif