// Needed for NET40
#if !NET_4_6
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using LinqInternal.Collections.Specialized;
namespace LinqInternal.Collections.ThreadSafe
{
///
/// Represent a thread-safe lock-free hash based dictionary.
///
/// The type of the key.
/// The type of the value.
///
/// Consider wrapping this class to implement or any other desired interface.
///
[Serializable]
internal sealed partial class SafeDictionary : IDictionary
{
private const int _defaultProbing = 1;
private readonly KeyCollection _keyCollection;
private readonly IEqualityComparer _keyComparer;
private readonly ValueCollection _valueCollection;
private readonly IEqualityComparer _valueComparer;
private Bucket> _bucket;
private int _probing;
///
/// Initializes a new instance of the class.
///
public SafeDictionary()
: this(EqualityComparer.Default, _defaultProbing)
{
// Empty
}
///
/// Initializes a new instance of the class.
///
/// The number of steps in linear probing.
public SafeDictionary(int initialProbing)
: this(EqualityComparer.Default, initialProbing)
{
// Empty
}
///
/// Initializes a new instance of the class.
///
/// The key comparer.
public SafeDictionary(IEqualityComparer comparer)
: this(comparer, _defaultProbing)
{
// Empty
}
///
/// Initializes a new instance of the class.
///
/// The key comparer.
/// The number of steps in linear probing.
public SafeDictionary(IEqualityComparer comparer, int initialProbing)
{
_keyComparer = comparer ?? EqualityComparer.Default;
_valueComparer = EqualityComparer.Default;
_bucket = new Bucket>();
_probing = initialProbing;
_keyCollection = new KeyCollection(this);
_valueCollection = new ValueCollection(this);
}
public int Count
{
get { return _bucket.Count; }
}
bool ICollection>.IsReadOnly
{
get { return false; }
}
public IEqualityComparer KeyComparer
{
get { return _keyComparer; }
}
public ICollection Keys
{
get { return _keyCollection; }
}
public ICollection Values
{
get { return _valueCollection; }
}
public TValue this[TKey key]
{
get
{
TValue value;
if (TryGetValue(key, out value))
{
return value;
}
throw new KeyNotFoundException();
}
set { Set(key, value); }
}
///
/// Adds the specified key and associated value.
///
/// The key.
/// The value.
/// An item with the same key has already been added
public void AddNew(TKey key, TValue value)
{
var insertPair = new KeyValuePair(key, value);
var hashCode = GetHashCode(key);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
KeyValuePair found;
if (_bucket.Insert(hashCode + attempts, insertPair, out found))
{
return;
}
if (_keyComparer.Equals(found.Key, key))
{
throw new ArgumentException("An item with the same key has already been added", "key");
}
attempts++;
}
}
///
/// Removes all the elements.
///
public void Clear()
{
Interlocked.Exchange(ref _bucket, _bucket = new Bucket>());
}
///
/// Removes all the elements.
///
/// Returns the removed pairs.
public IEnumerable> ClearEnumerable()
{
return Interlocked.Exchange(ref _bucket, _bucket = new Bucket>());
}
///
/// Determines whether the specified key is contained.
///
/// The key.
///
/// true if the specified key is contained; otherwise, false.
///
public bool ContainsKey(TKey key)
{
var hashCode = GetHashCode(key);
for (var attempts = 0; attempts < _probing; attempts++)
{
KeyValuePair found;
if (_bucket.TryGet(hashCode + attempts, out found))
{
if (_keyComparer.Equals(found.Key, key))
{
return true;
}
}
}
return false;
}
///
/// Determines whether the specified key is contained.
///
/// The hash code to look for.
/// The key predicate.
///
/// true if the specified key is contained; otherwise, false.
///
public bool ContainsKey(int hashCode, Predicate keyCheck)
{
if (keyCheck == null)
{
throw new ArgumentNullException("keyCheck");
}
for (var attempts = 0; attempts < _probing; attempts++)
{
KeyValuePair found;
if (_bucket.TryGet(hashCode + attempts, out found))
{
if (GetHashCode(found.Key) == hashCode && keyCheck(found.Key))
{
return true;
}
}
}
return false;
}
///
/// Determines whether the specified key is contained.
///
/// The hash code to look for.
/// The key predicate.
/// The value predicate.
///
/// true if the specified key is contained; otherwise, false.
///
public bool ContainsKey(int hashCode, Predicate keyCheck, Predicate valueCheck)
{
if (keyCheck == null)
{
throw new ArgumentNullException("keyCheck");
}
if (valueCheck == null)
{
throw new ArgumentNullException("valueCheck");
}
for (var attempts = 0; attempts < _probing; attempts++)
{
KeyValuePair found;
if (_bucket.TryGet(hashCode + attempts, out found))
{
if (GetHashCode(found.Key) == hashCode && keyCheck(found.Key) && valueCheck(found.Value))
{
return true;
}
}
}
return false;
}
///
/// 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(KeyValuePair[] array, int arrayIndex)
{
_bucket.CopyTo(array, arrayIndex);
}
///
/// Returns an that allows to iterate through the collection.
///
///
/// An object that can be used to iterate through the collection.
///
public IEnumerator> GetEnumerator()
{
return _bucket.GetEnumerator();
}
public TValue GetOrAdd(TKey key, Func valueFactory)
{
if (valueFactory == null)
{
throw new ArgumentNullException("valueFactory");
}
var hashCode = GetHashCode(key);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
KeyValuePair storedPair;
if (_bucket.TryGetOrInsert(hashCode + attempts, () => new KeyValuePair(key, valueFactory(key)), out storedPair))
{
return storedPair.Value;
}
if (_keyComparer.Equals(storedPair.Key, key))
{
return storedPair.Value;
}
attempts++;
}
}
public TValue GetOrAdd(TKey key, TValue value)
{
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
KeyValuePair storedPair;
if (_bucket.TryGetOrInsert(hashCode + attempts, insertPair, out storedPair))
{
return storedPair.Value;
}
if (_keyComparer.Equals(storedPair.Key, key))
{
return storedPair.Value;
}
attempts++;
}
}
///
/// Gets the pairs contained in this object.
///
/// The pairs contained in this object
public IList> GetPairs()
{
var result = new List>(_bucket.Count);
result.AddRange(_bucket);
return result;
}
void ICollection>.Add(KeyValuePair item)
{
AddNew(item.Key, item.Value);
}
bool ICollection>.Contains(KeyValuePair item)
{
var hashCode = GetHashCode(item.Key);
for (var attempts = 0; attempts < _probing; attempts++)
{
KeyValuePair found;
if (_bucket.TryGet(hashCode + attempts, out found))
{
if (_keyComparer.Equals(found.Key, item.Key))
{
if (_valueComparer.Equals(found.Value, item.Value))
{
return true;
}
return false;
}
}
}
return false;
}
bool ICollection>.Remove(KeyValuePair item)
{
var hashCode = GetHashCode(item.Key);
for (var attempts = 0; attempts < _probing; attempts++)
{
var done = false;
var result = _bucket.RemoveAt
(
hashCode + attempts,
found =>
{
if (_keyComparer.Equals(found.Key, item.Key))
{
done = true;
if (_valueComparer.Equals(found.Value, item.Value))
{
return true;
}
}
return false;
}
);
if (done)
{
return result;
}
}
return false;
}
void IDictionary.Add(TKey key, TValue value)
{
AddNew(key, value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
/// Removes the specified key.
///
/// The key.
///
/// true if the specified key was removed; otherwise, false.
///
public bool Remove(TKey key)
{
var hashCode = GetHashCode(key);
for (var attempts = 0; attempts < _probing; attempts++)
{
var done = false;
Predicate> check = found =>
{
if (_keyComparer.Equals(found.Key, key))
{
done = true;
return true;
}
return false;
};
var result = _bucket.RemoveAt
(
hashCode + attempts,
check
);
if (done)
{
return result;
}
}
return false;
}
///
/// Removes the specified key.
///
/// The key.
/// The value.
///
/// true if the specified key was removed; otherwise, false.
///
public bool Remove(TKey key, out TValue value)
{
value = default(TValue);
var hashCode = GetHashCode(key);
for (var attempts = 0; attempts < _probing; attempts++)
{
var done = false;
var previous = default(KeyValuePair);
Predicate> check = found =>
{
previous = found;
if (_keyComparer.Equals(found.Key, key))
{
done = true;
return true;
}
return false;
};
var result = _bucket.RemoveAt
(
hashCode + attempts,
check
);
if (done)
{
value = previous.Value;
return result;
}
}
return false;
}
///
/// Removes a key by hash code and a key predicate.
///
/// The hash code to look for.
/// The key predicate.
/// The value.
///
/// true if the specified key was removed; otherwise, false.
///
public bool Remove(int hashCode, Predicate keyCheck, out TValue value)
{
if (keyCheck == null)
{
throw new ArgumentNullException("keyCheck");
}
value = default(TValue);
for (var attempts = 0; attempts < _probing; attempts++)
{
var done = false;
var previous = default(KeyValuePair);
Predicate> check = found =>
{
previous = found;
if (GetHashCode(found.Key) == hashCode && keyCheck(found.Key))
{
done = true;
return true;
}
return false;
};
var result = _bucket.RemoveAt
(
hashCode + attempts,
check
);
if (done)
{
value = previous.Value;
return result;
}
}
return false;
}
///
/// Removes a key by hash code, key predicate and value predicate.
///
/// The key.
/// The value predicate.
/// The value.
///
/// true if the specified key was removed; otherwise, false.
///
public bool Remove(TKey key, Predicate valueCheck, out TValue value)
{
if (valueCheck == null)
{
throw new ArgumentNullException("valueCheck");
}
value = default(TValue);
var hashCode = GetHashCode(key);
for (var attempts = 0; attempts < _probing; attempts++)
{
var done = false;
var previous = default(KeyValuePair);
Predicate> check = found =>
{
previous = found;
if (_keyComparer.Equals(found.Key, key))
{
done = true;
if (valueCheck(found.Value))
{
return true;
}
}
return false;
};
var result = _bucket.RemoveAt
(
hashCode + attempts,
check
);
if (done)
{
value = previous.Value;
return result;
}
}
return false;
}
///
/// Removes a key by hash code, key predicate and value predicate.
///
/// The hash code to look for.
/// The key predicate.
/// The value predicate.
/// The value.
///
/// true if the specified key was removed; otherwise, false.
///
public bool Remove(int hashCode, Predicate keyCheck, Predicate valueCheck, out TValue value)
{
if (keyCheck == null)
{
throw new ArgumentNullException("keyCheck");
}
if (valueCheck == null)
{
throw new ArgumentNullException("valueCheck");
}
value = default(TValue);
for (var attempts = 0; attempts < _probing; attempts++)
{
var done = false;
var previous = default(KeyValuePair);
Predicate> check = found =>
{
previous = found;
if (GetHashCode(found.Key) == hashCode && keyCheck(found.Key))
{
done = true;
if (valueCheck(found.Value))
{
return true;
}
}
return false;
};
var result = _bucket.RemoveAt
(
hashCode + attempts,
check
);
if (done)
{
value = previous.Value;
return result;
}
}
return false;
}
///
/// Removes the keys and associated values where the key satisfies the predicate.
///
/// The predicate.
///
/// The number or removed pairs of keys and associated values.
///
///
/// It is not guaranteed that all the pairs of keys and associated values that satisfies the predicate will be removed.
///
public int RemoveWhereKey(Predicate keyCheck)
{
if (keyCheck == null)
{
throw new ArgumentNullException("keyCheck");
}
var matches = _bucket.Where(pair => keyCheck(pair.Key));
return matches.Count(pair => Remove(pair.Key));
}
///
/// Removes the keys and associated values where the key satisfies the predicate.
///
/// The predicate.
///
/// An that allows to iterate over the values of the removed pairs.
///
///
/// It is not guaranteed that all the pairs of keys and associated values that satisfies the predicate will be removed.
///
public IEnumerable RemoveWhereKeyEnumerable(Predicate keyCheck)
{
if (keyCheck == null)
{
throw new ArgumentNullException("keyCheck");
}
var matches = _bucket.Where(pair => keyCheck(pair.Key));
return from pair in matches where Remove(pair.Key) select pair.Value;
}
///
/// Removes the keys and associated values where the value satisfies the predicate.
///
/// The predicate.
///
/// The number or removed pairs of keys and associated values.
///
///
/// It is not guaranteed that all the pairs of keys and associated values that satisfies the predicate will be removed.
///
public int RemoveWhereValue(Predicate valueCheck)
{
if (valueCheck == null)
{
throw new ArgumentNullException("valueCheck");
}
var matches = _bucket.Where(pair => valueCheck(pair.Value));
return matches.Count(pair => Remove(pair.Key));
}
///
/// Removes the keys and associated values where the value satisfies the predicate.
///
/// The predicate.
///
/// An that allows to iterate over the values of the removed pairs.
///
///
/// It is not guaranteed that all the pairs of keys and associated values that satisfies the predicate will be removed.
///
public IEnumerable RemoveWhereValueEnumerable(Predicate valueCheck)
{
if (valueCheck == null)
{
throw new ArgumentNullException("valueCheck");
}
var matches = _bucket.Where(pair => valueCheck(pair.Value));
return from pair in matches where Remove(pair.Key) select pair.Value;
}
///
/// Sets the value associated with the specified key.
///
/// The key.
/// The value.
public void Set(TKey key, TValue value)
{
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
bool isNew;
if (_bucket.InsertOrUpdate(hashCode + attempts, insertPair, found => _keyComparer.Equals(found.Key, key), out isNew))
{
return;
}
attempts++;
}
}
///
/// Sets the value associated with the specified key.
///
/// The key.
/// The value.
/// if set to true the item value was set.
public void Set(TKey key, TValue value, out bool isNew)
{
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
if (_bucket.InsertOrUpdate(hashCode + attempts, insertPair, found => _keyComparer.Equals(found.Key, key), out isNew))
{
return;
}
attempts++;
}
}
///
/// Attempts to add the specified key and associated value. The value is added if the key is not found.
///
/// The key.
/// The value.
///
/// true if the specified key and associated value were added; otherwise, false.
///
public bool TryAdd(TKey key, TValue value)
{
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
KeyValuePair found;
if (_bucket.Insert(hashCode + attempts, insertPair, out found))
{
return true;
}
if (_keyComparer.Equals(found.Key, key))
{
return false;
}
attempts++;
}
}
///
/// Attempts to add the specified key and associated value. The value is added if the key is not found.
///
/// The key.
/// The value.
/// The stored pair independently of success.
///
/// true if the specified key and associated value were added; otherwise, false.
///
public bool TryAdd(TKey key, TValue value, out KeyValuePair stored)
{
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
if (_bucket.Insert(hashCode + attempts, insertPair, out stored))
{
stored = insertPair;
return true;
}
if (_keyComparer.Equals(stored.Key, key))
{
return false;
}
attempts++;
}
}
public bool TryGetOrAdd(TKey key, TValue value, out TValue stored)
{
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
KeyValuePair storedPair;
if (_bucket.TryGetOrInsert(hashCode + attempts, insertPair, out storedPair))
{
stored = storedPair.Value;
return true;
}
if (_keyComparer.Equals(storedPair.Key, key))
{
stored = storedPair.Value;
return false;
}
attempts++;
}
}
public bool TryGetOrAdd(TKey key, Func valueFactory, out TValue stored)
{
if (valueFactory == null)
{
throw new ArgumentException("valueFactory");
}
var hashCode = GetHashCode(key);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
KeyValuePair storedPair;
if (_bucket.TryGetOrInsert(hashCode + attempts, () => new KeyValuePair(key, valueFactory(key)), out storedPair))
{
stored = storedPair.Value;
return true;
}
if (_keyComparer.Equals(storedPair.Key, key))
{
stored = storedPair.Value;
return false;
}
attempts++;
}
}
///
/// Tries to retrieve the value associated with the specified key.
///
/// The key.
/// The value.
///
/// true if the value was retrieved; otherwise, false.
///
public bool TryGetValue(TKey key, out TValue value)
{
value = default(TValue);
var hashCode = GetHashCode(key);
for (var attempts = 0; attempts < _probing; attempts++)
{
KeyValuePair found;
if (_bucket.TryGet(hashCode + attempts, out found))
{
if (_keyComparer.Equals(found.Key, key))
{
value = found.Value;
return true;
}
}
}
return false;
}
public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue)
{
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, newValue);
for (var attempts = 0; attempts < _probing; attempts++)
{
var keyMatch = false;
ExtendProbingIfNeeded(attempts);
Predicate> check = found =>
{
keyMatch = _keyComparer.Equals(found.Key, key);
return keyMatch && _valueComparer.Equals(found.Value, comparisonValue);
};
if (_bucket.Update(hashCode + attempts, insertPair, check))
{
return true;
}
if (keyMatch)
{
return false;
}
}
return false;
}
public bool TryUpdate(TKey key, TValue newValue, Predicate valueCheck)
{
if (valueCheck == null)
{
throw new ArgumentNullException("valueCheck");
}
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, newValue);
for (var attempts = 0; attempts < _probing; attempts++)
{
var keyMatch = false;
ExtendProbingIfNeeded(attempts);
Predicate> check = found =>
{
keyMatch = _keyComparer.Equals(found.Key, key);
return keyMatch && valueCheck(found.Value);
};
bool isEmpty;
if (_bucket.Update(hashCode + attempts, _ => insertPair, check, out isEmpty))
{
return true;
}
if (keyMatch)
{
return false;
}
}
return false;
}
///
/// Returns the values where the key satisfies the predicate.
///
/// The predicate.
///
/// An that allows to iterate over the values of the matched pairs.
///
///
/// It is not guaranteed that all the pairs of keys and associated values that satisfies the predicate will be returned.
///
public IEnumerable Where(Predicate keyCheck)
{
if (keyCheck == null)
{
throw new ArgumentNullException("keyCheck");
}
var matches = _bucket.Where(pair => keyCheck(pair.Key));
return matches.Select(pair => pair.Value);
}
///
/// Adds the specified key and associated value.
///
/// The key.
/// The key predicate to approve overwriting.
/// The value.
/// An item with the same key has already been added
internal void AddNew(TKey key, Predicate keyOverwriteCheck, TValue value)
{
// NOTICE this method has no null check
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
Predicate> check = found =>
{
if (_keyComparer.Equals(found.Key, key))
{
// This is the item that has been stored with the key
// Throw to abort overwrite
throw CreateKeyArgumentException(null); // This exception will buble up to the context where "key" is an argument.
}
// This is not the key, overwrite?
return keyOverwriteCheck(found.Key);
};
// No try-catch - let the exception go.
bool isNew;
// InsertOrUpdate will add if no item is found, otherwise it calls check
_bucket.InsertOrUpdate(hashCode + attempts, insertPair, check, out isNew);
if (isNew)
{
// It added a new item
return;
}
attempts++;
}
}
///
/// Sets the value associated with the specified key.
///
/// The key.
/// The key predicate to approve overwriting.
/// The value.
internal void Set(TKey key, Predicate keyOverwriteCheck, TValue value)
{
// NOTICE this method has no null check
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
bool isNew;
Predicate> check = found => _keyComparer.Equals(found.Key, key) || keyOverwriteCheck(found.Key);
if (_bucket.InsertOrUpdate(hashCode + attempts, insertPair, check, out isNew))
{
return;
}
attempts++;
}
}
///
/// Sets the value associated with the specified key.
///
/// The key.
/// The key predicate to approve overwriting.
/// The value.
/// if set to true the item value was set.
internal void Set(TKey key, Predicate keyOverwriteCheck, TValue value, out bool isNew)
{
// NOTICE this method has no null check
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
Predicate> check = found => _keyComparer.Equals(found.Key, key) || keyOverwriteCheck(found.Key);
if (_bucket.InsertOrUpdate(hashCode + attempts, insertPair, check, out isNew))
{
return;
}
attempts++;
}
}
///
/// Attempts to add the specified key and associated value. The value is added if the key is not found.
///
/// The key.
/// The key predicate to approve overwriting.
/// The value.
///
/// true if the specified key and associated value were added; otherwise, false.
///
internal bool TryAdd(TKey key, Predicate keyOverwriteCheck, TValue value)
{
// NOTICE this method has no null check
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
Predicate> check = found =>
{
if (_keyComparer.Equals(found.Key, key))
{
// This is the item that has been stored with the key
// Throw to abort overwrite
throw CreateKeyArgumentException(null); // This exception will buble up to the context where "key" is an argument.
}
// This is not the key, overwrite?
return keyOverwriteCheck(found.Key);
};
try
{
bool isNew;
// InsertOrUpdate will add if no item is found, otherwise it calls check
_bucket.InsertOrUpdate(hashCode + attempts, insertPair, check, out isNew);
if (isNew)
{
// It added a new item
return true;
}
}
catch (ArgumentException)
{
// An item with the same key has already been added
return false;
}
attempts++;
}
}
internal bool TryGetOrAdd(TKey key, Predicate keyOverwriteCheck, TValue value, out TValue stored)
{
// NOTICE this method has no null check
var hashCode = GetHashCode(key);
var insertPair = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
ExtendProbingIfNeeded(attempts);
Predicate> check = found =>
{
if (_keyComparer.Equals(found.Key, key))
{
// This is the item that has been stored with the key
value = found.Value;
// Throw to abort overwrite
throw CreateKeyArgumentException(null); // This exception will buble up to the context where "key" is an argument.
}
// This is not the key, overwrite?
return keyOverwriteCheck(found.Key);
};
try
{
bool isNew;
// InsertOrUpdate will add if no item is found, otherwise it calls check
_bucket.InsertOrUpdate(hashCode + attempts, insertPair, check, out isNew);
if (isNew)
{
// It added a new item
stored = value;
return true;
}
}
catch (ArgumentException)
{
// An item with the same key has already been added
// Return it
stored = value;
return false;
}
attempts++;
}
}
private void ExtendProbingIfNeeded(int attempts)
{
var diff = 1 + attempts - _probing;
if (diff > 0)
{
Interlocked.Add(ref _probing, diff);
}
}
private int GetHashCode(TKey key)
{
var hashCode = _keyComparer.GetHashCode(key);
if (hashCode < 0)
{
hashCode = -hashCode;
}
// -int.MinValue == int.MinValue
if (hashCode < 0)
{
hashCode = 0;
}
return hashCode;
}
}
internal sealed partial class SafeDictionary
{
public TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory)
{
if (addValueFactory == null)
{
throw new ArgumentNullException("addValueFactory");
}
if (updateValueFactory == null)
{
throw new ArgumentNullException("updateValueFactory");
}
var hashCode = GetHashCode(key);
var attempts = 0;
var insertPair = default(KeyValuePair);
var updatePair = default(KeyValuePair);
while (true)
{
ExtendProbingIfNeeded(attempts);
bool isNew;
Func> itemFactory = () =>
{
return insertPair = new KeyValuePair(key, addValueFactory(key));
};
Func, KeyValuePair> itemUpdateFactory = found =>
{
return updatePair = new KeyValuePair(key, updateValueFactory(found.Key, found.Value));
};
Predicate> check = found => _keyComparer.Equals(key, found.Key);
var result = _bucket.InsertOrUpdate
(
hashCode + attempts,
itemFactory,
itemUpdateFactory,
check,
out isNew
);
if (result)
{
return isNew ? insertPair.Value : updatePair.Value;
}
attempts++;
}
}
public TValue AddOrUpdate(TKey key, TValue addValue, Func updateValueFactory)
{
if (updateValueFactory == null)
{
throw new ArgumentNullException("updateValueFactory");
}
var hashCode = GetHashCode(key);
var attempts = 0;
var insertPair = new KeyValuePair(key, addValue);
var updatePair = default(KeyValuePair);
while (true)
{
ExtendProbingIfNeeded(attempts);
bool isNew;
Func, KeyValuePair> updateFactory = found =>
{
return updatePair = new KeyValuePair(key, updateValueFactory(found.Key, found.Value));
};
Predicate> check = found => _keyComparer.Equals(key, found.Key);
var result = _bucket.InsertOrUpdate
(
hashCode + attempts,
insertPair,
updateFactory,
check,
out isNew
);
if (result)
{
return isNew ? insertPair.Value : updatePair.Value;
}
attempts++;
}
}
public TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory, out bool isNew)
{
if (addValueFactory == null)
{
throw new ArgumentNullException("addValueFactory");
}
if (updateValueFactory == null)
{
throw new ArgumentNullException("updateValueFactory");
}
var hashCode = GetHashCode(key);
var attempts = 0;
var insertPair = default(KeyValuePair);
var updatePair = default(KeyValuePair);
while (true)
{
ExtendProbingIfNeeded(attempts);
Func> valueFactory = () =>
{
return insertPair = new KeyValuePair(key, addValueFactory(key));
};
Func, KeyValuePair> updateFactory = found =>
{
return updatePair = new KeyValuePair(key, updateValueFactory(found.Key, found.Value));
};
Predicate> check = found => _keyComparer.Equals(key, found.Key);
var result = _bucket.InsertOrUpdate
(
hashCode + attempts,
valueFactory,
updateFactory,
check,
out isNew
);
if (result)
{
return isNew ? insertPair.Value : updatePair.Value;
}
attempts++;
}
}
public TValue AddOrUpdate(TKey key, TValue addValue, Func updateValueFactory, out bool isNew)
{
if (ReferenceEquals(updateValueFactory, null))
{
throw new ArgumentNullException("updateValueFactory");
}
var hashCode = GetHashCode(key);
var attempts = 0;
var insertPair = new KeyValuePair(key, addValue);
var updatePair = default(KeyValuePair);
while (true)
{
ExtendProbingIfNeeded(attempts);
Func, KeyValuePair> updateFactory = found =>
{
return updatePair = new KeyValuePair(key, updateValueFactory(found.Key, found.Value));
};
Predicate> check = found => _keyComparer.Equals(key, found.Key);
var result = _bucket.InsertOrUpdate
(
hashCode + attempts,
insertPair,
updateFactory,
check,
out isNew
);
if (result)
{
return isNew ? insertPair.Value : updatePair.Value;
}
attempts++;
}
}
///
/// Tries to retrieve the value by hash code and key predicate.
///
/// The hash code to look for.
/// The key predicate.
/// The value.
///
/// true if the value was retrieved; otherwise, false.
///
public bool TryGetValue(int hashCode, Predicate keyCheck, out TValue value)
{
if (keyCheck == null)
{
throw new ArgumentNullException("keyCheck");
}
value = default(TValue);
for (var attempts = 0; attempts < _probing; attempts++)
{
KeyValuePair found;
if (_bucket.TryGet(hashCode + attempts, out found))
{
if (GetHashCode(found.Key) == hashCode && keyCheck(found.Key))
{
value = found.Value;
return true;
}
}
}
return false;
}
///
/// Attempts to add the specified key and associated value. The value is added if the key is not found.
///
/// The key.
/// The key predicate to approve overwriting.
/// The value.
/// The stored pair independently of success.
///
/// true if the specified key and associated value were added; otherwise, false.
///
internal bool TryAdd(TKey key, Predicate keyOverwriteCheck, TValue value, out KeyValuePair stored)
{
// NOTICE this method has no null check
var hashCode = GetHashCode(key);
var created = new KeyValuePair(key, value);
var attempts = 0;
while (true)
{
var foundPair = created;
ExtendProbingIfNeeded(attempts);
Predicate> check = found =>
{
foundPair = found;
if (_keyComparer.Equals(foundPair.Key, key))
{
// This is the item that has been stored with the key
// Throw to abort overwrite
throw CreateKeyArgumentException(null); // This exception will buble up to the context where "key" is an argument.
}
// This is not the key, overwrite?
return keyOverwriteCheck(foundPair.Key);
};
try
{
bool isNew;
// InsertOrUpdate will add if no item is found, otherwise it calls check
_bucket.InsertOrUpdate(hashCode + attempts, created, check, out isNew);
if (isNew)
{
// It added a new item
stored = created;
return true;
}
}
catch (ArgumentException)
{
// An item with the same key has already been added
stored = foundPair;
return false;
}
attempts++;
}
}
internal bool TryGetOrAdd(TKey key, Func addValueFactory, Func updateValueFactory, out TValue stored)
{
// NOTICE this method has no null check
var hashCode = GetHashCode(key);
var attempts = 0;
while (true)
{
var value = default(TValue);
ExtendProbingIfNeeded(attempts);
Func> itemFactory = () => new KeyValuePair(key, value = addValueFactory());
Func, KeyValuePair> itemUpdateFactory = found =>
{
if (_keyComparer.Equals(found.Key, key))
{
// This is the item that has been stored with the key
value = found.Value;
// Throw to abort overwrite
throw CreateKeyArgumentException(null); // This exception will buble up to the context where "key" is an argument.
}
value = updateValueFactory(found.Key, found.Value);
return new KeyValuePair(key, value);
};
try
{
bool isNew;
_bucket.InsertOrUpdate(hashCode + attempts, itemFactory, itemUpdateFactory, out isNew);
if (isNew)
{
// It added a new item
stored = value;
return true;
}
}
catch (ArgumentException)
{
// An item with the same key has already been added
// Return it
stored = value;
return false;
}
attempts++;
}
}
private static ArgumentException CreateKeyArgumentException(object key)
{
GC.KeepAlive(key);
return new ArgumentException("An item with the same key has already been added", "key");
}
}
}
#endif