/* Copyright 2008 The 'A Concurrent Hashtable' development team (http://www.codeplex.com/CH/People/ProjectPeople.aspx) This library is licensed under the GNU Library General Public License (LGPL). You should have received a copy of the license along with the source code. If not, an online copy of the license can be found at http://www.codeplex.com/CH/license. */ using System; using System.Collections.Generic; using System.Linq; using System.Text; #if !SILVERLIGHT using System.Collections.Concurrent; #endif namespace Orvid.Concurrent.Collections { internal abstract class InternalWeakDictionaryWeakValueBase : ConcurrentDictionary, IMaintainable, IDictionary, ICollection>, IEnumerable> where IK : ITrashable //For IV we do not use the WeakValueRef struct directly in order to support unittesting. where IV : IWeakValueRef, IEquatable where EV : class where SK : struct { protected InternalWeakDictionaryWeakValueBase(int concurrencyLevel, int capacity, IEqualityComparer keyComparer) #if SILVERLIGHT : base(keyComparer) #else : base(concurrencyLevel, capacity, keyComparer) #endif { } protected InternalWeakDictionaryWeakValueBase(IEqualityComparer keyComparer) : base(keyComparer) { } protected abstract IK FromExternalKeyToSearchKey(EK externalKey); protected abstract IK FromExternalKeyToStorageKey(EK externalKey); protected abstract IK FromStackKeyToSearchKey(SK externalKey); protected abstract IK FromStackKeyToStorageKey(SK externalKey); protected abstract bool FromInternalKeyToExternalKey(IK internalKey, out EK externalKey); protected abstract bool FromInternalKeyToStackKey(IK internalKey, out SK externalKey); protected abstract IV FromExternalValueToInternalValue(EV externalValue); bool FromInternalValueToExternalValue(IV internalValue, out EV externalValue) { return internalValue.GetValue(out externalValue); } #region IMaintainable Members void IMaintainable.DoMaintenance() { foreach (var kvp in (IEnumerable>)this) if (kvp.Key.IsGarbage || kvp.Value.IsGarbage) ((ICollection>)this).Remove(kvp); } #endregion #region IDictionary,TValue> Members void IDictionary.Add(EK key, EV value) { var newItm = FromExternalValueToInternalValue(value); var storedItm = base.AddOrUpdate( FromExternalKeyToStorageKey(key), newItm, (sKey, oldItm) => { if (!sKey.IsGarbage && !oldItm.IsGarbage) return oldItm; else { //boyscout RemoveIKVP(sKey, oldItm); return newItm; } } ) ; if (!object.ReferenceEquals(newItm.Reference, storedItm.Reference)) throw new ArgumentException(); } bool IDictionary.ContainsKey(EK key) { EV dummy; return ((IDictionary)this).TryGetValue(key, out dummy); } ICollection IDictionary.Keys { get { return new TransformedCollection { _source = ((IEnumerable>)this).Select(kvp => kvp.Key) }; } } bool IDictionary.Remove(EK key) { IV itm; bool res = this.TryRemove(FromExternalKeyToSearchKey(key), out itm); return res && !itm.IsGarbage; } bool IDictionary.TryGetValue(EK key, out EV value) { IV internalValue; IK searchKey = FromExternalKeyToSearchKey(key); if (((IDictionary)this).TryGetValue(searchKey, out internalValue)) { if (FromInternalValueToExternalValue(internalValue, out value)) return true; //boyscout RemoveIKVP(searchKey, internalValue); } value = default(EV); return false; } ICollection IDictionary.Values { get { return new TransformedCollection { _source = ((IEnumerable>)this).Select(kvp => kvp.Value) }; } } EV IDictionary.this[EK key] { get { EV externalValue; if (!((IDictionary)this).TryGetValue(key, out externalValue)) throw new KeyNotFoundException(); return externalValue; } set { ((IDictionary)this)[FromExternalKeyToStorageKey(key)] = FromExternalValueToInternalValue(value); } } #endregion #region ICollection,TValue>> Members void ICollection>.Add(KeyValuePair item) { ((IDictionary)this).Add(item.Key, item.Value); } void ICollection>.Clear() { Clear(); } bool ICollection>.Contains(KeyValuePair item) { EV value; if (((IDictionary)this).TryGetValue(item.Key, out value)) return EqualityComparer.Default.Equals(value, item.Value); return false; } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException("array"); if (arrayIndex < 0 || arrayIndex > array.Length) throw new IndexOutOfRangeException(); int i = 0; int end = array.Length - arrayIndex; var buffer = new KeyValuePair[end]; using (var it = ((IEnumerable>)this).GetEnumerator()) while (it.MoveNext()) { if (i == end) throw new ArgumentException(); buffer[i++] = it.Current; } buffer.CopyTo(array, arrayIndex); } int ICollection>.Count { get { int ct = 0; using (var it = ((IEnumerable>)this).GetEnumerator()) while (it.MoveNext()) ++ct; return ct; } } bool ICollection>.IsReadOnly { get { return false; } } bool ICollection>.Remove(KeyValuePair item) { return RemoveIKVP(FromExternalKeyToSearchKey(item.Key),FromExternalValueToInternalValue(item.Value)) ; } #endregion #region IEnumerable> Members public new IEnumerator> GetEnumerator() { foreach (var kvp in (IEnumerable>)this) { EK externalKey; EV externalValue; if ( FromInternalKeyToExternalKey(kvp.Key, out externalKey) && FromInternalValueToExternalValue(kvp.Value, out externalValue) ) yield return new KeyValuePair(externalKey, externalValue); else //boyscout ((ICollection>)this).Remove(kvp); } } #endregion public bool ContainsKey(SK key) { IV itm; return ((IDictionary)this).TryGetValue(FromStackKeyToSearchKey(key), out itm) && !itm.IsGarbage; } public bool TryGetValue(SK key, out EV value) { IV itm; IK internalKey = FromStackKeyToSearchKey(key); if (((IDictionary)this).TryGetValue(FromStackKeyToSearchKey(key), out itm)) { if (FromInternalValueToExternalValue(itm, out value)) return true; //boyscout RemoveIKVP(internalKey, itm); } value = default(EV); return false; } public EV GetItem(SK key) { EV externalValue; if (!TryGetValue(key, out externalValue)) throw new KeyNotFoundException(); return externalValue; } public void SetItem(SK key, EV value) { ((IDictionary)this)[FromStackKeyToStorageKey(key)] = FromExternalValueToInternalValue(value); } public new bool IsEmpty { get { return !((ICollection>)this).GetEnumerator().MoveNext(); } } public EV AddOrUpdate(SK key, Func addValueFactory, Func updateValueFactory) { while (true) { //variables to hold references to newly created values so they will remain alive EV hold1; EV hold2; if ( FromInternalValueToExternalValue( base.AddOrUpdate( FromStackKeyToStorageKey(key), sKey => FromExternalValueToInternalValue(hold1 = addValueFactory(key)), (sKey, oldItm) => { EV oldValue; SK oldKey; EV newValue; if ( FromInternalKeyToStackKey(sKey, out oldKey) && FromInternalValueToExternalValue(oldItm, out oldValue) ) newValue = updateValueFactory(oldKey, oldValue); else { //boyscout RemoveIKVP(sKey, oldItm); newValue = addValueFactory(key); } return FromExternalValueToInternalValue(hold2 = newValue); } ), out hold1 ) ) return hold1; } } public EV AddOrUpdate(SK key, EV addValue, Func updateValueFactory) { while (true) { //this inside loop to prevent optimizer from optimizing away our reference to addValue. var newItm = FromExternalValueToInternalValue(addValue); var internalKey = FromStackKeyToStorageKey(key); if (base.TryAdd(internalKey, newItm)) return addValue; EV hold = default(EV); if( FromInternalValueToExternalValue( base.AddOrUpdate( FromStackKeyToStorageKey(key), newItm, (sKey, oldItm) => { EV oldValue; SK oldKey; if ( FromInternalKeyToStackKey(sKey, out oldKey) && FromInternalValueToExternalValue(oldItm, out oldValue) ) return FromExternalValueToInternalValue(hold = updateValueFactory(oldKey, oldValue)); else { //boyscout RemoveIKVP(sKey, oldItm); return newItm; } } ), out hold ) ) return hold; } } public EV GetOrAdd(SK key, EV value) { while (true) { //this inside loop to prevent optimizer from optimizing away our reference to value. var newItm = FromExternalValueToInternalValue(value); var internalKey = FromStackKeyToStorageKey(key); EV hold; IV item = base.GetOrAdd(internalKey, newItm); if (FromInternalValueToExternalValue(item, out hold)) return hold; //boyscout RemoveIKVP(internalKey, item); } } private bool RemoveIKVP(IK internalKey, IV item) { return ((ICollection>)this).Remove(new KeyValuePair(internalKey, item)); } public EV GetOrAdd(SK key, Func valueFactory) { EV hold; return this.TryGetValue(key, out hold) ? hold : GetOrAdd(key, valueFactory(key)); } public new KeyValuePair[] ToArray() { return ((IEnumerable>)this).ToArray(); } public bool TryAdd(SK key, EV value) { var newItm = FromExternalValueToInternalValue(value); var internalKey = FromStackKeyToStorageKey(key); if (base.TryAdd(internalKey, newItm)) return true; var storedItm = base.AddOrUpdate( internalKey, newItm, (k, itm) => { if (k.IsGarbage || itm.IsGarbage) { //boyscout RemoveIKVP(k, itm); return newItm; } else return itm; } ) ; return object.ReferenceEquals(storedItm.Reference, newItm.Reference); } public bool TryRemove(SK key, out EV value) { IV hold; if (base.TryRemove(FromStackKeyToSearchKey(key), out hold)) return FromInternalValueToExternalValue(hold, out value); value = default(EV); return false; } public bool TryUpdate(SK key, EV newValue, EV comparisonValue) { return base.TryUpdate( FromStackKeyToSearchKey(key), FromExternalValueToInternalValue(newValue), FromExternalValueToInternalValue(comparisonValue) ) ; } public List> GetContents() { return ((IEnumerable>)this).ToList(); } public void InsertContents(IEnumerable> collection) { if (null == collection) throw new ArgumentNullException("collection"); foreach (var kvp in collection) ((IDictionary)this).Add(kvp); } } internal abstract class InternalWeakDictionaryWeakValueBase : InternalWeakDictionaryWeakValueBase, EK, EV, SK> where IK : ITrashable where EV : class where SK : struct { protected InternalWeakDictionaryWeakValueBase(int concurrencyLevel, int capacity, IEqualityComparer keyComparer) #if SILVERLIGHT : base(keyComparer) #else : base(concurrencyLevel, capacity, keyComparer) #endif { } protected InternalWeakDictionaryWeakValueBase(IEqualityComparer keyComparer) : base(keyComparer) { } protected override WeakValueRef FromExternalValueToInternalValue(EV externalValue) { return WeakValueRef.Create( externalValue ); } } }