| // LUCENENET specific - factored out this class and replaced it with ConditionalWeakTable<TKey, TValue>. |
| // ConditionalWeakTable<TKey, TValue> is thread-safe and internally uses RuntimeHelpers.GetHashCode() |
| // to lookup the key, so it can be used as a direct replacement for WeakIdentityMap<TKey, TValue> |
| // in most cases. |
| |
| //using System; |
| //using System.Collections.Concurrent; |
| //using System.Collections.Generic; |
| //using System.Runtime.CompilerServices; |
| //using System.Collections; |
| |
| //namespace Lucene.Net.Util |
| //{ |
| // /* |
| // * Licensed to the Apache Software Foundation (ASF) under one or more |
| // * contributor license agreements. See the NOTICE file distributed with |
| // * this work for additional information regarding copyright ownership. |
| // * The ASF licenses this file to You under the Apache License, Version 2.0 |
| // * (the "License"); you may not use this file except in compliance with |
| // * the License. You may obtain a copy of the License at |
| // * |
| // * http://www.apache.org/licenses/LICENSE-2.0 |
| // * |
| // * Unless required by applicable law or agreed to in writing, software |
| // * distributed under the License is distributed on an "AS IS" BASIS, |
| // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // * See the License for the specific language governing permissions and |
| // * limitations under the License. |
| // */ |
| |
| // /// <summary> |
| // /// Implements a combination of <c>java.util.WeakHashMap</c> and |
| // /// <c>java.util.IdentityHashMap</c>. |
| // /// Useful for caches that need to key off of a <c>==</c> comparison |
| // /// instead of a <c>.Equals(object)</c>. |
| // /// |
| // /// <para/>This class is not a general-purpose <see cref="IDictionary{TKey, TValue}"/> |
| // /// implementation! It intentionally violates |
| // /// <see cref="IDictionary{TKey, TValue}"/>'s general contract, which mandates the use of the <see cref="object.Equals(object)"/> method |
| // /// when comparing objects. This class is designed for use only in the |
| // /// rare cases wherein reference-equality semantics are required. |
| // /// |
| // /// <para/>This implementation was forked from <a href="http://cxf.apache.org/">Apache CXF</a> |
| // /// but modified to <b>not</b> implement the <see cref="IDictionary{TKey, TValue}"/> interface and |
| // /// without any set views on it, as those are error-prone and inefficient, |
| // /// if not implemented carefully. The map only contains <see cref="IEnumerable{T}.GetEnumerator()"/> implementations |
| // /// on the values and not-GCed keys. Lucene's implementation also supports <c>null</c> |
| // /// keys, but those are never weak! |
| // /// |
| // /// <para/><a name="reapInfo" />The map supports two modes of operation: |
| // /// <list type="bullet"> |
| // /// <item><term><c>reapOnRead = true</c>:</term><description> This behaves identical to a <c>java.util.WeakHashMap</c> |
| // /// where it also cleans up the reference queue on every read operation (<see cref="Get(object)"/>, |
| // /// <see cref="ContainsKey(object)"/>, <see cref="Count"/>, <see cref="GetValueEnumerator()"/>), freeing map entries |
| // /// of already GCed keys.</description></item> |
| // /// <item><term><c>reapOnRead = false</c>:</term><description> This mode does not call <see cref="Reap()"/> on every read |
| // /// operation. In this case, the reference queue is only cleaned up on write operations |
| // /// (like <see cref="Put(TKey, TValue)"/>). This is ideal for maps with few entries where |
| // /// the keys are unlikely be garbage collected, but there are lots of <see cref="Get(object)"/> |
| // /// operations. The code can still call <see cref="Reap()"/> to manually clean up the queue without |
| // /// doing a write operation.</description></item> |
| // /// </list> |
| // /// <para/> |
| // /// @lucene.internal |
| // /// </summary> |
| // public sealed class WeakIdentityMap<TKey, TValue> |
| // where TKey : class |
| // { |
| // private readonly IDictionary<IdentityWeakReference, TValue> backingStore; |
| |
| // private readonly bool reapOnRead; |
| |
| // /// <summary> |
| // /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a non-synchronized <see cref="Dictionary{TKey, TValue}"/>. |
| // /// The map <a href="#reapInfo">cleans up the reference queue on every read operation</a>. |
| // /// </summary> |
| // public static WeakIdentityMap<TKey, TValue> NewHashMap() |
| // { |
| // return NewHashMap(false); |
| // } |
| |
| // /// <summary> |
| // /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a non-synchronized <see cref="Dictionary{TKey, TValue}"/>. </summary> |
| // /// <param name="reapOnRead"> controls if the map <a href="#reapInfo">cleans up the reference queue on every read operation</a>. </param> |
| // public static WeakIdentityMap<TKey, TValue> NewHashMap(bool reapOnRead) |
| // { |
| // return new WeakIdentityMap<TKey, TValue>(new Dictionary<IdentityWeakReference, TValue>(), reapOnRead); |
| // } |
| |
| // /// <summary> |
| // /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a <see cref="ConcurrentDictionary{TKey, TValue}"/>. |
| // /// The map <a href="#reapInfo">cleans up the reference queue on every read operation</a>. |
| // /// </summary> |
| // public static WeakIdentityMap<TKey, TValue> NewConcurrentHashMap() |
| // { |
| // return NewConcurrentHashMap(true); |
| // } |
| |
| // /// <summary> |
| // /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a <see cref="ConcurrentDictionary{TKey, TValue}"/>. </summary> |
| // /// <param name="reapOnRead"> controls if the map <a href="#reapInfo">cleans up the reference queue on every read operation</a>. </param> |
| // public static WeakIdentityMap<TKey, TValue> NewConcurrentHashMap(bool reapOnRead) |
| // { |
| // return new WeakIdentityMap<TKey, TValue>(new ConcurrentDictionary<IdentityWeakReference, TValue>(), reapOnRead); |
| // } |
| |
| // /// <summary> |
| // /// Private only constructor, to create use the static factory methods. </summary> |
| // private WeakIdentityMap(IDictionary<IdentityWeakReference, TValue> backingStore, bool reapOnRead) |
| // { |
| // this.backingStore = backingStore; |
| // this.reapOnRead = reapOnRead; |
| // } |
| |
| // /// <summary> |
| // /// Removes all of the mappings from this map. </summary> |
| // public void Clear() |
| // { |
| // backingStore.Clear(); |
| // Reap(); |
| // } |
| |
| // /// <summary> |
| // /// Returns <c>true</c> if this map contains a mapping for the specified key. </summary> |
| // public bool ContainsKey(object key) |
| // { |
| // if (reapOnRead) |
| // { |
| // Reap(); |
| // } |
| // return backingStore.ContainsKey(new IdentityWeakReference(key)); |
| // } |
| |
| // /// <summary> |
| // /// Returns the value to which the specified key is mapped. </summary> |
| // public TValue Get(object key) |
| // { |
| // if (reapOnRead) |
| // { |
| // Reap(); |
| // } |
| |
| // TValue val; |
| // if (backingStore.TryGetValue(new IdentityWeakReference(key), out val)) |
| // { |
| // return val; |
| // } |
| // else |
| // { |
| // return default; |
| // } |
| // } |
| |
| // /// <summary> |
| // /// Associates the specified value with the specified key in this map. |
| // /// If the map previously contained a mapping for this key, the old value |
| // /// is replaced. |
| // /// </summary> |
| // public TValue Put(TKey key, TValue value) |
| // { |
| // Reap(); |
| // return backingStore[new IdentityWeakReference(key)] = value; |
| // } |
| |
| // /// <summary> |
| // /// Gets an <see cref="IEnumerable{TKey}"/> object containing the keys of the <see cref="WeakIdentityMap{TKey, TValue}"/>. |
| // /// </summary> |
| // public IEnumerable<TKey> Keys |
| // { |
| // get |
| // { |
| // return new KeyWrapper(this); |
| // } |
| // } |
| |
| // /// <summary> |
| // /// LUCENENET specific class to allow the |
| // /// GetEnumerator() method to be overridden |
| // /// for the keys so we can return an enumerator |
| // /// that is smart enough to clean up the dead keys |
| // /// and also so that MoveNext() returns false in the |
| // /// event there are no more values left (instead of returning |
| // /// a null value in an extra enumeration). |
| // /// </summary> |
| // private class KeyWrapper : IEnumerable<TKey> |
| // { |
| // private readonly WeakIdentityMap<TKey, TValue> outerInstance; |
| // public KeyWrapper(WeakIdentityMap<TKey, TValue> outerInstance) |
| // { |
| // this.outerInstance = outerInstance; |
| // } |
| // public IEnumerator<TKey> GetEnumerator() |
| // { |
| // outerInstance.Reap(); |
| // return new IteratorAnonymousClass(outerInstance); |
| // } |
| |
| // IEnumerator IEnumerable.GetEnumerator() |
| // { |
| // return GetEnumerator(); |
| // } |
| // } |
| |
| // /// <summary> |
| // /// Gets an <see cref="IEnumerable{TKey}"/> object containing the values of the <see cref="WeakIdentityMap{TKey, TValue}"/>. |
| // /// </summary> |
| // public IEnumerable<TValue> Values |
| // { |
| // get |
| // { |
| // if (reapOnRead) Reap(); |
| // return backingStore.Values; |
| // } |
| // } |
| |
| // /// <summary> |
| // /// Returns <c>true</c> if this map contains no key-value mappings. </summary> |
| // public bool IsEmpty |
| // { |
| // get |
| // { |
| // return Count == 0; |
| // } |
| // } |
| |
| // /// <summary> |
| // /// Removes the mapping for a key from this weak hash map if it is present. |
| // /// Returns the value to which this map previously associated the key, |
| // /// or <c>null</c> if the map contained no mapping for the key. |
| // /// A return value of <c>null</c> does not necessarily indicate that |
| // /// the map contained. |
| // /// </summary> |
| // public bool Remove(object key) |
| // { |
| // Reap(); |
| // return backingStore.Remove(new IdentityWeakReference(key)); |
| // } |
| |
| // /// <summary> |
| // /// Returns the number of key-value mappings in this map. This result is a snapshot, |
| // /// and may not reflect unprocessed entries that will be removed before next |
| // /// attempted access because they are no longer referenced. |
| // /// <para/> |
| // /// NOTE: This was size() in Lucene. |
| // /// </summary> |
| // public int Count |
| // { |
| // get |
| // { |
| // if (backingStore.Count == 0) |
| // { |
| // return 0; |
| // } |
| // if (reapOnRead) |
| // { |
| // Reap(); |
| // } |
| // return backingStore.Count; |
| // } |
| // } |
| |
| // private class IteratorAnonymousClass : IEnumerator<TKey> |
| // { |
| // private readonly WeakIdentityMap<TKey, TValue> outerInstance; |
| // private readonly IEnumerator<KeyValuePair<IdentityWeakReference, TValue>> enumerator; |
| |
| // public IteratorAnonymousClass(WeakIdentityMap<TKey, TValue> outerInstance) |
| // { |
| // this.outerInstance = outerInstance; |
| // enumerator = outerInstance.backingStore.GetEnumerator(); |
| // } |
| |
| // // holds strong reference to next element in backing iterator: |
| // private object next = null; |
| |
| // public TKey Current |
| // { |
| // get |
| // { |
| // return (TKey)next; |
| // } |
| // } |
| |
| // object IEnumerator.Current |
| // { |
| // get |
| // { |
| // return Current; |
| // } |
| // } |
| |
| |
| // public void Dispose() |
| // { |
| // enumerator.Dispose(); |
| // } |
| |
| |
| // public bool MoveNext() |
| // { |
| // while (enumerator.MoveNext()) |
| // { |
| // next = enumerator.Current.Key.Target; |
| // if (next != null) |
| // { |
| // // unfold "null" special value: |
| // if (next == NULL) |
| // next = null; |
| // return true; |
| // } |
| // } |
| // return false; |
| // } |
| |
| // public void Reset() |
| // { |
| // enumerator.Reset(); |
| // } |
| // } |
| |
| // /// <summary> |
| // /// Returns an iterator over all values of this map. |
| // /// This iterator may return values whose key is already |
| // /// garbage collected while iterator is consumed, |
| // /// especially if <see cref="reapOnRead"/> is <c>false</c>. |
| // /// <para/> |
| // /// NOTE: This was valueIterator() in Lucene. |
| // /// </summary> |
| // public IEnumerator<TValue> GetValueEnumerator() |
| // { |
| // if (reapOnRead) |
| // { |
| // Reap(); |
| // } |
| // return backingStore.Values.GetEnumerator(); |
| // } |
| |
| // /// <summary> |
| // /// This method manually cleans up the reference queue to remove all garbage |
| // /// collected key/value pairs from the map. Calling this method is not needed |
| // /// if <c>reapOnRead = true</c>. Otherwise it might be a good idea |
| // /// to call this method when there is spare time (e.g. from a background thread). |
| // /// <a href="#reapInfo">Information about the <c>reapOnRead</c> setting</a> |
| // /// </summary> |
| // public void Reap() |
| // { |
| // List<IdentityWeakReference> keysToRemove = null; |
| // foreach (var item in backingStore) |
| // { |
| // if (!item.Key.IsAlive) |
| // { |
| // // create the list of keys to remove only if there are keys to remove. |
| // // this reduces heap pressure |
| // if (keysToRemove == null) |
| // keysToRemove = new List<IdentityWeakReference>(); |
| // keysToRemove.Add(item.Key); |
| // } |
| // } |
| |
| // if (keysToRemove != null) |
| // foreach (var key in keysToRemove) |
| // { |
| // backingStore.Remove(key); |
| // } |
| // } |
| |
| // // we keep a hard reference to our NULL key, so map supports null keys that never get GCed: |
| // internal static readonly object NULL = new object(); |
| |
| // private sealed class IdentityWeakReference : WeakReference |
| // { |
| // private readonly int hash; |
| |
| // internal IdentityWeakReference(object obj/*, ReferenceQueue<object> queue*/) |
| // : base(obj == null ? NULL : obj/*, queue*/) |
| // { |
| // hash = RuntimeHelpers.GetHashCode(obj); |
| // } |
| |
| // public override int GetHashCode() |
| // { |
| // return hash; |
| // } |
| |
| // public override bool Equals(object o) |
| // { |
| // if (this == o) |
| // { |
| // return true; |
| // } |
| // IdentityWeakReference @ref = o as IdentityWeakReference; |
| // if (@ref != null) |
| // { |
| // if (this.Target == @ref.Target) |
| // { |
| // return true; |
| // } |
| // } |
| // return false; |
| // } |
| // } |
| // } |
| //} |