namespace NesterovskyBros.Utils
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Threading;
///
/// A shared state container.
///
/// A key type.
/// A value type.
public class State
{
///
/// Creates an instance of a shared state.
///
///
/// true to copy and wrap input dictionaries, and
/// false to store dictionary passed.
///
/// Optional key comparer.
/// Optional value comparer.
public State(
bool protectDictionary = true,
IEqualityComparer keyComparer = null,
IEqualityComparer valueComparer = null)
{
if (keyComparer == null)
{
keyComparer = EqualityComparer.Default;
}
if (valueComparer == null)
{
valueComparer = EqualityComparer.Default;
}
this.protectDictionary = protectDictionary;
this.keyComparer = keyComparer;
this.valueComparer = valueComparer;
this.states = new WeakTable>(
new EqualityComparer { self = this });
}
///
/// Gets a hash code for a dictionary.
///
/// A dictionary to get hash code.
/// A hash code value.
public int GetHashCode(IDictionary dictionary)
{
if (dictionary == null)
{
return 0;
}
Tag tag;
if (tags.TryGetValue(dictionary, out tag))
{
return tag.hashCode;
}
var hashCode = 0;
foreach(var entry in dictionary)
{
hashCode += GetHashCode(entry.Key, entry.Value);
}
return hashCode;
}
///
/// Gets a typed value for a dictionary and a key.
///
/// A value type.
/// A dictionary instance or null.
/// A key to get a value for.
/// A value or a default if no value is available.
public T Get(IDictionary dictionary, K key)
where T: V
{
V value;
if ((dictionary == null) || !dictionary.TryGetValue(key, out value))
{
return default(T);
}
return (T)value;
}
///
/// Sets or remove a typed value for a dictionary and a key.
///
/// A value type.
/// A dictionary instance or null.
/// A key to get a value for.
/// A value to set.
/// A dictionary instance with a value set.
public IDictionary Set(
IDictionary dictionary,
K key,
T value)
where T : V
{
return value == null ? Remove(dictionary, key) :
Set(dictionary, key, (V)value);
}
///
/// Get a shared dictionary that contains the same value,
/// as an input dictionary.
///
/// An input dictionary.
/// Return a shared dictionary, or null on null input.
public IDictionary Get(IDictionary dictionary)
{
if (dictionary == null)
{
return null;
}
var hashCode = 0;
Tag tag;
if (tags.TryGetValue(dictionary, out tag))
{
if (tag.key)
{
return dictionary;
}
hashCode = tag.hashCode;
}
else
{
foreach(var entry in dictionary)
{
hashCode += GetHashCode(entry.Key, entry.Value);
}
}
var stateKey = new GetValue
{
self = this,
dictionary = dictionary,
hashCode = hashCode
};
IDictionary stateValue;
states.TryGetValue(stateKey, out stateValue);
if (stateValue != null)
{
return stateValue;
}
dictionary = new Dictionary(dictionary, keyComparer);
if (protectDictionary)
{
dictionary = new ReadOnlyDictionary(dictionary);
}
tag = tags.GetValue(
dictionary,
d => new Tag { hashCode = hashCode });
var result = states.GetValue(dictionary, d => d as IDictionary);
if (result == dictionary)
{
Volatile.Write(ref tag.key, true);
}
return result;
}
///
/// Get a shared dictionary that contains the same value,
/// as an input dictionary, with a key set to a value.
///
/// An input dictionary.
/// A key to set.
/// A value to set.
///
/// Return a shared dictionary, or null if result dictionary is empty.
///
public IDictionary Set(
IDictionary dictionary,
K key,
V value)
{
var hashCode = GetHashCode(key, value);
var hasValue = false;
Tag tag;
if (dictionary != null)
{
V oldValue;
hasValue = dictionary.TryGetValue(key, out oldValue);
hashCode = -GetHashCode(key, oldValue);
if (tags.TryGetValue(dictionary, out tag))
{
hashCode += tag.hashCode;
}
else
{
foreach (var entry in dictionary)
{
hashCode += GetHashCode(entry.Key, entry.Value);
}
}
}
var stateKey = hasValue ?
new SetValue
{
self = this,
dictionary = dictionary,
hashCode = hashCode,
key = key,
value = value
} :
dictionary != null ?
new AddValue
{
self = this,
dictionary = dictionary,
hashCode = hashCode,
key = key,
value = value
} :
new SingleValue
{
self = this,
hashCode = hashCode,
key = key,
value = value
} as Action;
IDictionary stateValue;
states.TryGetValue(stateKey, out stateValue);
if (stateValue != null)
{
return stateValue;
}
dictionary = dictionary != null ?
new Dictionary(dictionary, keyComparer) :
new Dictionary(keyComparer);
dictionary[key] = value;
if (protectDictionary)
{
dictionary = new ReadOnlyDictionary(dictionary);
}
tag = tags.GetValue(
dictionary,
d => new Tag { hashCode = hashCode });
var result = states.GetValue(dictionary, d => d as IDictionary);
if (result == dictionary)
{
Volatile.Write(ref tag.key, true);
}
return result;
}
///
/// Get a shared dictionary that contains the same value,
/// as an input dictionary, with a key removed.
///
/// An input dictionary.
/// A key to remove.
///
/// Return a shared dictionary, or null if result dictionary is empty.
///
public IDictionary Remove(
IDictionary dictionary,
K key)
{
if (dictionary == null)
{
return null;
}
V oldValue;
if (!dictionary.TryGetValue(key, out oldValue))
{
return Get(dictionary);
}
if (dictionary.Count == 1)
{
return null;
}
var hashCode = GetHashCode(key, oldValue);
Tag tag;
if (tags.TryGetValue(dictionary, out tag))
{
hashCode = +tag.hashCode;
}
else
{
foreach(var entry in dictionary)
{
hashCode += GetHashCode(entry.Key, entry.Value);
}
}
var stateKey = new RemoveValue
{
self = this,
dictionary = dictionary,
hashCode = hashCode,
key = key
};
IDictionary stateValue;
states.TryGetValue(stateKey, out stateValue);
if (stateValue != null)
{
return stateValue;
}
dictionary = new Dictionary(dictionary, keyComparer);
dictionary.Remove(key);
if (protectDictionary)
{
dictionary = new ReadOnlyDictionary(dictionary);
}
tag = tags.GetValue(
dictionary,
d => new Tag { hashCode = hashCode });
var result = states.GetValue(dictionary, d => d as IDictionary);
if (result == dictionary)
{
Volatile.Write(ref tag.key, true);
}
return result;
}
///
/// A key comparer.
///
public IEqualityComparer KeyComparer
{
get { return keyComparer; }
}
///
/// A value comparer.
///
public IEqualityComparer ValueComparer
{
get { return valueComparer; }
}
///
/// Internal equality comparer.
///
private class EqualityComparer: IEqualityComparer
{
///
/// A reference to the state.
///
public State self;
///
/// Compares two instances.
///
/// A first instance to compare.
/// A second instance to compare.
///
/// true if instances are considered equal, and false otherwise.
///
public new bool Equals(object x, object y)
{
if (x == y)
{
return true;
}
if ((x == null) || (y == null))
{
return false;
}
return ((y is Action) && y.Equals(x)) || ((x is Action) && x.Equals(y));
}
///
/// Gets a hash code for the object.
///
/// An object to get hash code for.
/// a hash code value.
public int GetHashCode(object obj)
{
var action = obj as Action;
if (action != null)
{
return action.hashCode;
}
var dictionary = obj as IDictionary;
if (dictionary == null)
{
return 0;
}
Tag tag;
if (self.tags.TryGetValue(dictionary, out tag))
{
return tag.hashCode;
}
return 0;
}
}
///
/// Internal action class to minimize copying.
///
private abstract class Action
{
///
/// A reference to the state.
///
public State self;
///
/// A reference dictionary.
///
public IDictionary dictionary;
///
/// A hash code.
///
public int hashCode;
///
/// Gets a hash code.
///
/// A hash code value.
public override int GetHashCode()
{
return hashCode;
}
///
/// Compares action with dictionary.
///
/// A dictionary instance to compare with.
///
/// true if action and dictionary are considered equal, and false otherwise.
///
public override bool Equals(object obj)
{
if (this.dictionary == obj)
{
return true;
}
var dictionary = obj as IDictionary;
if (dictionary == null)
{
return false;
}
return Equals(dictionary);
}
///
/// Compares action with dictionary.
///
/// A dictionary instance to compare with.
///
/// true if action and dictionary are considered equal, and false otherwise.
///
protected abstract bool Equals(IDictionary dictionary);
}
///
/// A get value action.
///
private class GetValue: Action
{
///
/// Compares action with dictionary.
///
/// A dictionary instance to compare with.
///
/// true if action and dictionary are considered equal, and false otherwise.
///
protected override bool Equals(IDictionary dictionary)
{
if (this.dictionary.Count != dictionary.Count)
{
return false;
}
V value;
foreach(var entry in this.dictionary)
{
if (!dictionary.TryGetValue(entry.Key, out value) ||
!self.valueComparer.Equals(entry.Value, value))
{
return false;
}
}
return true;
}
}
///
/// Init dictionary with a single value.
///
private class SingleValue : Action
{
///
/// Compares action with dictionary.
///
/// A dictionary instance to compare with.
///
/// true if action and dictionary are considered equal, and false otherwise.
///
protected override bool Equals(IDictionary dictionary)
{
if (dictionary.Count != 1)
{
return false;
}
V value;
if (!dictionary.TryGetValue(this.key, out value) ||
!self.valueComparer.Equals(this.value, value))
{
return false;
}
return true;
}
///
/// Added key.
///
public K key;
///
/// Added value.
///
public V value;
}
///
/// Add value to a dictionary.
///
private class AddValue : Action
{
///
/// Compares action with dictionary.
///
/// A dictionary instance to compare with.
///
/// true if action and dictionary are considered equal, and false otherwise.
///
protected override bool Equals(IDictionary dictionary)
{
if (this.dictionary.Count + 1 != dictionary.Count)
{
return false;
}
V value;
foreach(var entry in this.dictionary)
{
if (!dictionary.TryGetValue(entry.Key, out value) ||
!self.valueComparer.Equals(entry.Value, value))
{
return false;
}
}
if (!dictionary.TryGetValue(this.key, out value) ||
!self.valueComparer.Equals(this.value, value))
{
return false;
}
return true;
}
///
/// Added key.
///
public K key;
///
/// Added value.
///
public V value;
}
///
/// Set dictionary value.
///
private class SetValue : Action
{
///
/// Compares action with dictionary.
///
/// A dictionary instance to compare with.
///
/// true if action and dictionary are considered equal, and false otherwise.
///
protected override bool Equals(IDictionary dictionary)
{
if (this.dictionary.Count != dictionary.Count)
{
return false;
}
var found = false;
V value;
foreach(var entry in this.dictionary)
{
if (!found && self.keyComparer.Equals(entry.Key, this.key))
{
if (!dictionary.TryGetValue(this.key, out value) ||
!self.valueComparer.Equals(this.value, value))
{
return false;
}
found = true;
}
else
{
if (!dictionary.TryGetValue(entry.Key, out value) ||
!self.valueComparer.Equals(entry.Value, value))
{
return false;
}
}
}
return true;
}
///
/// Added key.
///
public K key;
///
/// Added value.
///
public V value;
}
///
/// Remove dictionary value.
///
private class RemoveValue: Action
{
///
/// Compares action with dictionary.
///
/// A dictionary instance to compare with.
///
/// true if action and dictionary are considered equal, and false otherwise.
///
protected override bool Equals(IDictionary dictionary)
{
if (this.dictionary.Count - 1 != dictionary.Count)
{
return false;
}
var found = false;
V value;
foreach (var entry in this.dictionary)
{
if (!found && self.keyComparer.Equals(entry.Key, this.key))
{
found = true;
continue;
}
if (!dictionary.TryGetValue(entry.Key, out value) ||
!self.valueComparer.Equals(entry.Value, value))
{
return false;
}
}
return true;
}
///
/// Removed key.
///
public K key;
}
///
/// Tag to cache hash code for the dictionary.
///
private class Tag
{
///
/// Dictionary hash code.
///
public int hashCode;
///
/// A key indicator.
///
public bool key;
}
///
/// Gets a hash code for a key and value.
///
/// A key.
/// A value.
/// A hash code of the (key, value) entry.
private int GetHashCode(K key, V value)
{
if ((key == null) || (value == null))
{
return 0;
}
var k = keyComparer.GetHashCode(key);
var v = valueComparer.GetHashCode(value);
k = ((k << 5) + k);
v = (v << (k >> 3)) + v;
return k ^ v;
}
///
/// Indicates whether to copy dictionaries kept in the storage.
///
private bool protectDictionary;
///
/// A key comparer.
///
private IEqualityComparer keyComparer;
///
/// A value comparer.
///
private IEqualityComparer valueComparer;
///
/// States stored in the dictionary.
///
private WeakTable> states;
///
/// Tags attached to dictionaries.
///
private ConditionalWeakTable, Tag> tags =
new ConditionalWeakTable, Tag>();
}
}