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.
308 lines
9.4 KiB
308 lines
9.4 KiB
5 years ago
|
#if !NET_4_6
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Threading;
|
||
|
|
||
|
namespace LinqInternal.Collections.ThreadSafe
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Represent a thread-safe wait-free bucket.
|
||
|
/// </summary>
|
||
|
/// <typeparam name="T">The type of the item.</typeparam>
|
||
|
[Serializable]
|
||
|
internal sealed class Bucket<T> : IBucket<T>
|
||
|
{
|
||
|
private readonly BucketCore _bucketCore;
|
||
|
private int _count;
|
||
|
|
||
|
public Bucket()
|
||
|
{
|
||
|
_bucketCore = new BucketCore(7);
|
||
|
}
|
||
|
|
||
|
public Bucket(IEnumerable<T> source)
|
||
|
{
|
||
|
if (source == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("source");
|
||
|
}
|
||
|
_bucketCore = new BucketCore(7);
|
||
|
var index = 0;
|
||
|
foreach (var item in source)
|
||
|
{
|
||
|
var copy = item;
|
||
|
_bucketCore.DoMayIncrement
|
||
|
(
|
||
|
index,
|
||
|
(ref object target) => Interlocked.Exchange(ref target, (object)copy ?? BucketHelper.Null) == null
|
||
|
);
|
||
|
index++;
|
||
|
_count++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int Count
|
||
|
{
|
||
|
get { return _count; }
|
||
|
}
|
||
|
|
||
|
public void CopyTo(T[] array, int arrayIndex)
|
||
|
{
|
||
|
Extensions.CopyTo(this, array, arrayIndex);
|
||
|
}
|
||
|
|
||
|
public IEnumerable<T> EnumerateRange(int indexFrom, int indexTo)
|
||
|
{
|
||
|
foreach (var value in _bucketCore.EnumerateRange(indexFrom, indexTo))
|
||
|
{
|
||
|
yield return value == BucketHelper.Null ? default(T) : (T)value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool Exchange(int index, T item, out T previous)
|
||
|
{
|
||
|
var found = BucketHelper.Null;
|
||
|
previous = default(T);
|
||
|
var result = _bucketCore.DoMayIncrement
|
||
|
(
|
||
|
index,
|
||
|
(ref object target) =>
|
||
|
{
|
||
|
found = Interlocked.Exchange(ref target, (object)item ?? BucketHelper.Null);
|
||
|
return found == null;
|
||
|
}
|
||
|
);
|
||
|
if (result)
|
||
|
{
|
||
|
Interlocked.Increment(ref _count);
|
||
|
return true;
|
||
|
}
|
||
|
if (found != BucketHelper.Null)
|
||
|
{
|
||
|
previous = (T)found;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public IEnumerator<T> GetEnumerator()
|
||
|
{
|
||
|
foreach (var value in _bucketCore)
|
||
|
{
|
||
|
yield return value == BucketHelper.Null ? default(T) : (T)value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IEnumerator IEnumerable.GetEnumerator()
|
||
|
{
|
||
|
return GetEnumerator();
|
||
|
}
|
||
|
|
||
|
public bool Insert(int index, T item)
|
||
|
{
|
||
|
var result = _bucketCore.DoMayIncrement
|
||
|
(
|
||
|
index,
|
||
|
(ref object target) =>
|
||
|
{
|
||
|
var found = Interlocked.CompareExchange(ref target, (object)item ?? BucketHelper.Null, null);
|
||
|
return found == null;
|
||
|
}
|
||
|
);
|
||
|
if (result)
|
||
|
{
|
||
|
Interlocked.Increment(ref _count);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public bool Insert(int index, T item, out T previous)
|
||
|
{
|
||
|
var found = BucketHelper.Null;
|
||
|
previous = default(T);
|
||
|
var result = _bucketCore.DoMayIncrement
|
||
|
(
|
||
|
index,
|
||
|
(ref object target) =>
|
||
|
{
|
||
|
found = Interlocked.CompareExchange(ref target, (object)item ?? BucketHelper.Null, null);
|
||
|
return found == null;
|
||
|
}
|
||
|
);
|
||
|
if (result)
|
||
|
{
|
||
|
Interlocked.Increment(ref _count);
|
||
|
return true;
|
||
|
}
|
||
|
if (found != BucketHelper.Null)
|
||
|
{
|
||
|
previous = (T)found;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool RemoveAt(int index)
|
||
|
{
|
||
|
var result = _bucketCore.DoMayDecrement
|
||
|
(
|
||
|
index,
|
||
|
(ref object target) => Interlocked.Exchange(ref target, null) != null
|
||
|
);
|
||
|
if (result)
|
||
|
{
|
||
|
Interlocked.Decrement(ref _count);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public bool RemoveAt(int index, out T previous)
|
||
|
{
|
||
|
var found = BucketHelper.Null;
|
||
|
previous = default(T);
|
||
|
var result = _bucketCore.DoMayDecrement
|
||
|
(
|
||
|
index,
|
||
|
(ref object target) =>
|
||
|
{
|
||
|
found = Interlocked.Exchange(ref target, null);
|
||
|
return found != null;
|
||
|
}
|
||
|
);
|
||
|
if (!result)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
Interlocked.Decrement(ref _count);
|
||
|
if (found != BucketHelper.Null)
|
||
|
{
|
||
|
previous = (T)found;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public bool RemoveAt(int index, Predicate<T> check)
|
||
|
{
|
||
|
if (check == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("check");
|
||
|
}
|
||
|
return _bucketCore.DoMayDecrement
|
||
|
(
|
||
|
index,
|
||
|
(ref object target) =>
|
||
|
{
|
||
|
var found = Interlocked.CompareExchange(ref target, null, null);
|
||
|
if (found != null)
|
||
|
{
|
||
|
var comparisonItem = found == BucketHelper.Null ? default(T) : (T)found;
|
||
|
if (check(comparisonItem))
|
||
|
{
|
||
|
var compare = Interlocked.CompareExchange(ref target, null, found);
|
||
|
if (found == compare)
|
||
|
{
|
||
|
Interlocked.Decrement(ref _count);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
public void Set(int index, T item, out bool isNew)
|
||
|
{
|
||
|
isNew = _bucketCore.DoMayIncrement
|
||
|
(
|
||
|
index,
|
||
|
(ref object target) => Interlocked.Exchange(ref target, (object)item ?? BucketHelper.Null) == null
|
||
|
);
|
||
|
if (isNew)
|
||
|
{
|
||
|
Interlocked.Increment(ref _count);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool TryGet(int index, out T value)
|
||
|
{
|
||
|
var found = BucketHelper.Null;
|
||
|
value = default(T);
|
||
|
var done = _bucketCore.Do
|
||
|
(
|
||
|
index,
|
||
|
(ref object target) =>
|
||
|
{
|
||
|
found = Interlocked.CompareExchange(ref target, null, null);
|
||
|
return true;
|
||
|
}
|
||
|
);
|
||
|
if (!done || found == null)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
if (found != BucketHelper.Null)
|
||
|
{
|
||
|
value = (T)found;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public bool Update(int index, Func<T, T> itemUpdateFactory, Predicate<T> check, out bool isEmpty)
|
||
|
{
|
||
|
if (itemUpdateFactory == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("itemUpdateFactory");
|
||
|
}
|
||
|
if (check == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("check");
|
||
|
}
|
||
|
var found = BucketHelper.Null;
|
||
|
var compare = BucketHelper.Null;
|
||
|
var result = false;
|
||
|
var done = _bucketCore.Do
|
||
|
(
|
||
|
index,
|
||
|
(ref object target) =>
|
||
|
{
|
||
|
found = Interlocked.CompareExchange(ref target, null, null);
|
||
|
if (found != null)
|
||
|
{
|
||
|
var comparisonItem = found == BucketHelper.Null ? default(T) : (T)found;
|
||
|
if (check(comparisonItem))
|
||
|
{
|
||
|
var item = itemUpdateFactory(comparisonItem);
|
||
|
compare = Interlocked.CompareExchange(ref target, (object)item ?? BucketHelper.Null, found);
|
||
|
result = found == compare;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
);
|
||
|
if (!done)
|
||
|
{
|
||
|
isEmpty = true;
|
||
|
return false;
|
||
|
}
|
||
|
isEmpty = found == null || compare == null;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public IEnumerable<T> Where(Predicate<T> check)
|
||
|
{
|
||
|
if (check == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("check");
|
||
|
}
|
||
|
foreach (var value in _bucketCore)
|
||
|
{
|
||
|
var castedValue = value == BucketHelper.Null ? default(T) : (T)value;
|
||
|
if (check(castedValue))
|
||
|
{
|
||
|
yield return castedValue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|