src/Proton/Utilities/LinkedSplayedDictionary.cs (395 lines of code) (raw):

/* * 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. */ using System; using System.Collections; using System.Collections.Generic; namespace Apache.Qpid.Proton.Utilities { /// <summary> /// A Linked node version of the Splayed Dictionary that provides /// consisted enumeration of dictionary entries based on insertion /// order over natural or comparer based enumeration from the default /// splayed dictionary implementation. /// </summary> /// <typeparam name="T">The type contained in the Queue</typeparam> public sealed class LinkedSplayedDictionary<K, V> : SplayedDictionary<K, V> { /// <summary> /// A dummy entry in the circular linked list of entries in the map. /// The first real entry is root.next, and the last is header.pervious. /// If the map is empty, root.next == root and root.previous == root. /// </summary> private readonly SplayedEntry entries = new(); public LinkedSplayedDictionary() { } public LinkedSplayedDictionary(IComparer<K> comparer) : base(comparer) { } public LinkedSplayedDictionary(IDictionary<K, V> source) : base(source) { } public override void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex) { if (array == null) { throw new ArgumentNullException(nameof(array), "The provided array cannot be null"); } if (arrayIndex < 0) { throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Array index must be greater than zero"); } if (array.Length - arrayIndex > size) { throw new ArgumentException("Not enough space in array to store the dictionary entries"); } for (SplayedEntry entry = entries.linkNext; entry != entries; entry = entry.linkNext) { array[arrayIndex++] = entry.ToKeyValuePair(); } } public override void CopyTo(Array array, int arrayIndex) { if (array == null) { throw new ArgumentNullException(nameof(array), "The provided array cannot be null"); } if (arrayIndex < 0) { throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Array index must be greater than zero"); } if (array.Length - arrayIndex > size) { throw new ArgumentException("Not enough space in array to store the dictionary entries"); } for (SplayedEntry entry = entries.linkNext; entry != entries; entry = entry.linkNext) { array.SetValue(entry.ToKeyValuePair(), arrayIndex++); } } public override void Clear() { base.Clear(); // Unlink all the entries and reset to no insertions state entries.linkNext = entries.linkPrev = entries; } public override IEnumerator<KeyValuePair<K, V>> GetEnumerator() { return new LinkedSplayedDictionaryEntryEnumerator(this); } public override ICollection<K> Keys { get { if (keySet == null) { keySet = new LinkedSplayedDictionaryKeys(this); } return keySet; } } public override ICollection<V> Values { get { if (values == null) { values = new LinkedSplayedDictionaryValues(this); } return values; } } protected override void EntryAdded(SplayedEntry newEntry) { // Insertion ordering of the splayed map entries recorded here // and the list of entries doesn't change until an entry is removed. newEntry.linkNext = entries; newEntry.linkPrev = entries.linkPrev; entries.linkPrev.linkNext = newEntry; entries.linkPrev = newEntry; } protected override void EntryDeleted(SplayedEntry deletedEntry) { // Remove the entry from the insertion ordered entry list. deletedEntry.linkNext.linkPrev = deletedEntry.linkPrev; deletedEntry.linkPrev.linkNext = deletedEntry.linkNext; deletedEntry.linkNext = deletedEntry.linkPrev = null; } #region Dictionary Enumerators private abstract class LinkedSplayedDictionaryEnumerator<TResult> : IEnumerator<TResult>, IEnumerator { protected readonly LinkedSplayedDictionary<K, V> parent; protected readonly int expectedModCount; protected SplayedEntry current; protected bool disposed; public LinkedSplayedDictionaryEnumerator(LinkedSplayedDictionary<K, V> parent) { this.parent = parent; this.expectedModCount = parent.modCount; this.current = parent.entries; } object IEnumerator.Current => this.Current; public abstract TResult Current { get; } public void Dispose() { current = parent.entries; disposed = true; } public bool MoveNext() { CheckNotModified(); return (current = current.linkNext) != parent.entries; } public void Reset() { current = parent.entries; } protected void CheckNotModified() { if (expectedModCount != parent.modCount) { throw new InvalidOperationException("Parent Dictionary was modified during enumeration."); } } } private sealed class LinkedSplayedDictionaryKeyEnumerator : LinkedSplayedDictionaryEnumerator<K> { public LinkedSplayedDictionaryKeyEnumerator(LinkedSplayedDictionary<K, V> parent) : base(parent) { } public override K Current { get { if (current != parent.entries) { return current.Key; } throw new InvalidOperationException("Current position is undefined."); } } } private sealed class LinkedSplayedDictionaryValueEnumerator : LinkedSplayedDictionaryEnumerator<V> { public LinkedSplayedDictionaryValueEnumerator(LinkedSplayedDictionary<K, V> parent) : base(parent) { } public override V Current { get { if (current != parent.entries) { return current.Value; } throw new InvalidOperationException("Current position is undefined."); } } } private sealed class LinkedSplayedDictionaryEntryEnumerator : LinkedSplayedDictionaryEnumerator<KeyValuePair<K, V>>, IDictionaryEnumerator { public LinkedSplayedDictionaryEntryEnumerator(LinkedSplayedDictionary<K, V> parent) : base(parent) { } public override KeyValuePair<K, V> Current { get { if (current != parent.entries) { return current.ToKeyValuePair(); } throw new InvalidOperationException("Current position is undefined."); } } public DictionaryEntry Entry { get { if (current != parent.entries) { return current.ToDictionaryEntry(); } throw new InvalidOperationException("Current position is undefined."); } } public object Key { get { if (current != parent.entries) { return current.Key; } throw new InvalidOperationException("Current position is undefined."); } } public object Value { get { if (current != parent.entries) { return current.Value; } throw new InvalidOperationException("Current position is undefined."); } } } #endregion #region Dictionary Collection Views private sealed class LinkedSplayedDictionaryValues : ICollection<V>, ICollection { private readonly LinkedSplayedDictionary<K, V> parent; public LinkedSplayedDictionaryValues(LinkedSplayedDictionary<K, V> parent) : base() { this.parent = parent; } public int Count => parent.Count; public bool IsReadOnly => parent.IsReadOnly; public bool IsSynchronized => false; public object SyncRoot => parent.entryPool; public void Add(V item) { throw new NotSupportedException("Cannot add a value only entry to the parent Dictionary"); } public void Clear() { parent.Clear(); } public bool Contains(V item) { return parent.ContainsValue(item); } public bool Remove(V item) { SplayedEntry root = parent.entries; for (SplayedEntry entry = root.linkNext; entry != root; entry = entry.linkNext) { if (entry.ValueEquals(item)) { parent.Delete(entry); return true; } } return false; } public void CopyTo(V[] array, int arrayIndex) { if (array == null) { throw new ArgumentNullException(nameof(array), "The provided array cannot be null"); } if (arrayIndex < 0) { throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Array index must be greater than zero"); } if (array.Length - arrayIndex > parent.size) { throw new ArgumentException("Not enough space in array to store the dictionary values"); } SplayedEntry root = parent.entries; for (SplayedEntry entry = root.linkNext; entry != root; entry = entry.linkNext) { array[arrayIndex++] = entry.Value; } } public void CopyTo(Array array, int arrayIndex) { if (array == null) { throw new ArgumentNullException(nameof(array), "The provided array cannot be null"); } if (arrayIndex < 0) { throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Array index must be greater than zero"); } if (array.Length - arrayIndex > parent.size) { throw new ArgumentException("Not enough space in array to store the dictionary values"); } SplayedEntry root = parent.entries; for (SplayedEntry entry = root.linkNext; entry != root; entry = entry.linkNext) { array.SetValue(entry.Value, arrayIndex++); } } public IEnumerator<V> GetEnumerator() { return new LinkedSplayedDictionaryValueEnumerator(parent); } IEnumerator IEnumerable.GetEnumerator() { return new LinkedSplayedDictionaryValueEnumerator(parent); } } private sealed class LinkedSplayedDictionaryKeys : ICollection<K>, ICollection { private readonly LinkedSplayedDictionary<K, V> parent; public LinkedSplayedDictionaryKeys(LinkedSplayedDictionary<K, V> parent) : base() { this.parent = parent; } public int Count => parent.Count; public bool IsReadOnly => parent.IsReadOnly; public bool IsSynchronized => false; public object SyncRoot => parent.entryPool; public void Add(K item) { throw new NotSupportedException("Cannot add a key only entry to the parent Dictionary"); } public void Clear() { parent.Clear(); } public bool Contains(K item) { return parent.ContainsKey(item); } public void CopyTo(K[] array, int arrayIndex) { if (array == null) { throw new ArgumentNullException(nameof(array), "The provided array cannot be null"); } if (arrayIndex < 0) { throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Array index must be greater than zero"); } if (array.Length - arrayIndex > parent.size) { throw new ArgumentException("Not enough space in array to store the dictionary keys"); } SplayedEntry root = parent.entries; for (SplayedEntry entry = root.linkNext; entry != root; entry = entry.linkNext) { array[arrayIndex++] = entry.Key; } } public void CopyTo(Array array, int arrayIndex) { if (array == null) { throw new ArgumentNullException(nameof(array), "The provided array cannot be null"); } if (arrayIndex < 0) { throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Array index must be greater than zero"); } if (array.Length - arrayIndex > parent.size) { throw new ArgumentException("Not enough space in array to store the dictionary values"); } SplayedEntry root = parent.entries; for (SplayedEntry entry = root.linkNext; entry != root; entry = entry.linkNext) { array.SetValue(entry.Key, arrayIndex++); } } public bool Remove(K item) { SplayedEntry root = parent.entries; for (SplayedEntry entry = root.linkNext; entry != root; entry = entry.linkNext) { if (entry.KeyEquals(item)) { parent.Delete(entry); return true; } } return false; } public IEnumerator<K> GetEnumerator() { return new LinkedSplayedDictionaryKeyEnumerator(parent); } IEnumerator IEnumerable.GetEnumerator() { return new LinkedSplayedDictionaryKeyEnumerator(parent); } } #endregion } }