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>(); } }