diff --git a/Nuclex.Support (PC).csproj b/Nuclex.Support (PC).csproj index 71a9d57..d7bbfeb 100644 --- a/Nuclex.Support (PC).csproj +++ b/Nuclex.Support (PC).csproj @@ -78,6 +78,10 @@ ObservableCollection.Test ObservableCollection.cs + + false + PriorityItemPair + false TransformingReadOnlyCollection @@ -87,9 +91,9 @@ TransformingReadOnlyCollection.Interfaces TransformingReadOnlyCollection.cs - + false - UnintrusivePriorityQueue + PairPriorityQueue false @@ -121,10 +125,10 @@ RingMemoryStream.Test RingMemoryStream.cs - + false - UnintrusivePriorityQueue.Test - UnintrusivePriorityQueue.cs + PairPriorityQueue.Test + PairPriorityQueue.cs false diff --git a/Source/Collections/UnintrusivePriorityQueue.Test.cs b/Source/Collections/PairPriorityQueue.Test.cs similarity index 55% rename from Source/Collections/UnintrusivePriorityQueue.Test.cs rename to Source/Collections/PairPriorityQueue.Test.cs index 148b1ab..251dd99 100644 --- a/Source/Collections/UnintrusivePriorityQueue.Test.cs +++ b/Source/Collections/PairPriorityQueue.Test.cs @@ -29,22 +29,22 @@ namespace Nuclex.Support.Collections { /// Unit Test for the priority queue class [TestFixture] - public class UnintrusivePriorityQueueTest { + public class PairPriorityQueueTest { /// Tests to ensure the count property is properly updated [Test] public void TestCount() { - UnintrusivePriorityQueue testQueue = - new UnintrusivePriorityQueue(); + PairPriorityQueue testQueue = + new PairPriorityQueue(); Assert.AreEqual(0, testQueue.Count); - testQueue.Enqueue("a", 12.34f); + testQueue.Enqueue(12.34f, "a"); Assert.AreEqual(1, testQueue.Count); - testQueue.Enqueue("b", 56.78f); + testQueue.Enqueue(56.78f, "b"); Assert.AreEqual(2, testQueue.Count); testQueue.Dequeue(); Assert.AreEqual(1, testQueue.Count); - testQueue.Enqueue("c", 9.0f); + testQueue.Enqueue(9.0f, "c"); Assert.AreEqual(2, testQueue.Count); testQueue.Clear(); Assert.AreEqual(0, testQueue.Count); @@ -53,28 +53,28 @@ namespace Nuclex.Support.Collections { /// Tests to ensure that the priority collection actually sorts items [Test] public void TestOrdering() { - UnintrusivePriorityQueue testQueue = - new UnintrusivePriorityQueue(); + PairPriorityQueue testQueue = + new PairPriorityQueue(); - testQueue.Enqueue("a", 1.0f); - testQueue.Enqueue("i", 9.0f); - testQueue.Enqueue("b", 2.0f); - testQueue.Enqueue("h", 8.0f); - testQueue.Enqueue("c", 3.0f); - testQueue.Enqueue("g", 7.0f); - testQueue.Enqueue("d", 4.0f); - testQueue.Enqueue("f", 6.0f); - testQueue.Enqueue("e", 5.0f); + testQueue.Enqueue(1.0f, "a"); + testQueue.Enqueue(9.0f, "i"); + testQueue.Enqueue(2.0f, "b"); + testQueue.Enqueue(8.0f, "h"); + testQueue.Enqueue(3.0f, "c"); + testQueue.Enqueue(7.0f, "g"); + testQueue.Enqueue(4.0f, "d"); + testQueue.Enqueue(6.0f, "f"); + testQueue.Enqueue(5.0f, "e"); - Assert.AreEqual("i", testQueue.Dequeue()); - Assert.AreEqual("h", testQueue.Dequeue()); - Assert.AreEqual("g", testQueue.Dequeue()); - Assert.AreEqual("f", testQueue.Dequeue()); - Assert.AreEqual("e", testQueue.Dequeue()); - Assert.AreEqual("d", testQueue.Dequeue()); - Assert.AreEqual("c", testQueue.Dequeue()); - Assert.AreEqual("b", testQueue.Dequeue()); - Assert.AreEqual("a", testQueue.Dequeue()); + Assert.AreEqual("i", testQueue.Dequeue().Item); + Assert.AreEqual("h", testQueue.Dequeue().Item); + Assert.AreEqual("g", testQueue.Dequeue().Item); + Assert.AreEqual("f", testQueue.Dequeue().Item); + Assert.AreEqual("e", testQueue.Dequeue().Item); + Assert.AreEqual("d", testQueue.Dequeue().Item); + Assert.AreEqual("c", testQueue.Dequeue().Item); + Assert.AreEqual("b", testQueue.Dequeue().Item); + Assert.AreEqual("a", testQueue.Dequeue().Item); } } diff --git a/Source/Collections/PairPriorityQueue.cs b/Source/Collections/PairPriorityQueue.cs new file mode 100644 index 0000000..dde43de --- /dev/null +++ b/Source/Collections/PairPriorityQueue.cs @@ -0,0 +1,138 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2007 Nuclex Development Labs + +This library is free software; you can redistribute it and/or +modify it under the terms of the IBM Common Public License as +published by the IBM Corporation; either version 1.0 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +IBM Common Public License for more details. + +You should have received a copy of the IBM Common Public +License along with this library +*/ +#endregion + +using System; +using System.Collections.Generic; +using System.Collections; + +namespace Nuclex.Support.Collections { + + /// Queue that dequeues items in order of their priority + /// + /// This variant of the priority queue uses an external priority value. If the + /// priority data type implements the IComparable interface, the user does not + /// even + /// + public class PairPriorityQueue + : ICollection, IEnumerable> { + + #region class PairComparer + + /// Compares two priority queue entries based on their priority + private class PairComparer : IComparer> { + + /// Initializes a new entry comparer + /// Comparer used to compare entry priorities + public PairComparer(IComparer priorityComparer) { + this.priorityComparer = priorityComparer; + } + + /// Compares the left entry to the right entry + /// Entry on the left side + /// Entry on the right side + /// The relationship of the two entries + public int Compare( + PriorityItemPair left, + PriorityItemPair right + ) { + return this.priorityComparer.Compare(left.Priority, right.Priority); + } + + /// Comparer used to compare the priorities of the entries + private IComparer priorityComparer; + + } + + #endregion // class EntryComparer + + /// Initializes a new non-intrusive priority queue + public PairPriorityQueue() : this(Comparer.Default) { } + + /// Initializes a new non-intrusive priority queue + /// Comparer used to compare the item priorities + public PairPriorityQueue(IComparer priorityComparer) { + this.internalQueue = new PriorityQueue>( + new PairComparer(priorityComparer) + ); + } + + /// Takes the item with the highest priority off from the queue + /// The item with the highest priority in the list + public PriorityItemPair Dequeue() { + return this.internalQueue.Dequeue(); + } + + /// Puts an item into the priority queue + /// Priority of the item to be queued + /// Item to be queued + public void Enqueue(PriorityType priority, ItemType item) { + this.internalQueue.Enqueue( + new PriorityItemPair(priority, item) + ); + } + + /// Removes all items from the priority queue + public void Clear() { + this.internalQueue.Clear(); + } + + /// Total number of items in the priority queue + public int Count { + get { return this.internalQueue.Count; } + } + + /// Copies the contents of the priority queue into an array + /// Array to copy the priority queue into + /// Starting index for the destination array + public void CopyTo(Array array, int index) { + this.internalQueue.CopyTo(array, index); + } + + /// + /// Obtains an object that can be used to synchronize accesses to the priority queue + /// from different threads + /// + public object SyncRoot { + get { return this.internalQueue.SyncRoot; } + } + + /// Whether operations performed on this priority queue are thread safe + public bool IsSynchronized { + get { return this.internalQueue.IsSynchronized; } + } + + /// Returns a typesafe enumerator for the priority queue + /// A new enumerator for the priority queue + public IEnumerator> GetEnumerator() { + return this.internalQueue.GetEnumerator(); + } + + /// Returns an enumerator for the priority queue + /// A new enumerator for the priority queue + IEnumerator IEnumerable.GetEnumerator() { + return this.internalQueue.GetEnumerator(); + } + + /// Intrusive priority queue being wrapped by this class + private PriorityQueue> internalQueue; + + } + +} // namespace Nuclex.Support.Collections diff --git a/Source/Collections/PriorityItemPair.cs b/Source/Collections/PriorityItemPair.cs new file mode 100644 index 0000000..ca8ebe8 --- /dev/null +++ b/Source/Collections/PriorityItemPair.cs @@ -0,0 +1,76 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2007 Nuclex Development Labs + +This library is free software; you can redistribute it and/or +modify it under the terms of the IBM Common Public License as +published by the IBM Corporation; either version 1.0 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +IBM Common Public License for more details. + +You should have received a copy of the IBM Common Public +License along with this library +*/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nuclex.Support.Collections { + + /// An pair of a priority and an item + public struct PriorityItemPair { + + /// Initializes a new priority / item pair + /// Priority of the item in the pair + /// Item to be stored in the pair + public PriorityItemPair(PriorityType priority, ItemType item) { + this.Priority = priority; + this.Item = item; + } + + /// Priority assigned to this priority / item pair + public PriorityType Priority; + /// Item contained in this priority / item pair + public ItemType Item; + + /// Converts the priority / item pair into a string + /// A string describing the priority / item pair + public override string ToString() { + int length = 4; + + // Convert the priority value into a string or use the empty string + // constant if the ToString() overload returns null + string priorityString = this.Priority.ToString(); + if(priorityString != null) + length += priorityString.Length; + else + priorityString = string.Empty; + + // Convert the item value into a string or use the empty string + // constant if the ToString() overload returns null + string itemString = this.Item.ToString(); + if(itemString != null) + length += itemString.Length; + else + itemString = string.Empty; + + // Concatenate priority and item into a single string + StringBuilder builder = new StringBuilder(length); + builder.Append('['); + builder.Append(priorityString); + builder.Append(", "); + builder.Append(itemString); + builder.Append(']'); + return builder.ToString(); + } + + } + +} // namespace Nuclex.Support.Collections diff --git a/Source/Collections/PriorityQueue.cs b/Source/Collections/PriorityQueue.cs index 1519fcd..2917492 100644 --- a/Source/Collections/PriorityQueue.cs +++ b/Source/Collections/PriorityQueue.cs @@ -94,6 +94,11 @@ namespace Nuclex.Support.Collections { #endregion // class Enumerator + /// + /// Initializes a new priority queue using IComparable for comparing items + /// + public PriorityQueue() : this(Comparer.Default) { } + /// Initializes a new priority queue /// Comparer to use for ordering the items public PriorityQueue(IComparer comparer) { diff --git a/Source/Collections/RingMemoryStream.cs b/Source/Collections/RingMemoryStream.cs index 0d029d0..3a3b13c 100644 --- a/Source/Collections/RingMemoryStream.cs +++ b/Source/Collections/RingMemoryStream.cs @@ -123,8 +123,8 @@ namespace Nuclex.Support.Collections { setEmpty(); } - // If the end index lies before the start index, the data in the - // ring memory stream is fragmented. Example: |#####>-------<#####| + // The end index lies before the start index, so the data in the + // ring memory stream is fragmented. Example: |#####>-------<#####| } else { int linearAvailable = (int)this.ringBuffer.Length - this.startIndex; @@ -142,8 +142,8 @@ namespace Nuclex.Support.Collections { this.startIndex = count - linearAvailable; this.ringBuffer.Read(buffer, offset + linearAvailable, this.startIndex); - // Nope, the amount of requested data can be read in one piece without - // crossing the end of the ring buffer + // Nope, the amount of requested data can be read in one piece without + // crossing the end of the ring buffer } else { this.ringBuffer.Position = this.startIndex; this.ringBuffer.Read(buffer, offset, count); @@ -166,7 +166,7 @@ namespace Nuclex.Support.Collections { public override void Write(byte[] buffer, int offset, int count) { // The end index lies behind the start index (usual case), so the - // ring memory is not fragmented. Example: |-----<#######>-----| + // unused buffer space is fragmented. Example: |-----<#######>-----| if((this.startIndex < this.endIndex) || this.empty) { int linearAvailable = (int)(this.ringBuffer.Length - this.endIndex); @@ -183,8 +183,8 @@ namespace Nuclex.Support.Collections { this.endIndex = count - linearAvailable; this.ringBuffer.Write(buffer, offset + linearAvailable, this.endIndex); - // All data can be appended at the current stream position without - // crossing the ring memory stream's end + // All data can be appended at the current stream position without + // crossing the ring memory stream's end } else { this.ringBuffer.Position = this.endIndex; this.ringBuffer.Write(buffer, offset, count); @@ -193,9 +193,9 @@ namespace Nuclex.Support.Collections { this.empty = false; - // If the end index lies before the start index, the ring memory stream - // has been fragmented. Hence, this means the gap into which we are about - // to write cannot be fragmented. Example: |#####>-------<#####| + // The end index lies before the start index, so the data in the ring memory + // stream has been fragmented. This means the gap into which we are about + // to write is not fragmented. Example: |#####>-------<#####| } else { if(count > (this.startIndex - this.endIndex)) throw new OverflowException("Data does not fit in buffer"); diff --git a/Source/Collections/UnintrusivePriorityQueue.cs b/Source/Collections/UnintrusivePriorityQueue.cs deleted file mode 100644 index ded3c61..0000000 --- a/Source/Collections/UnintrusivePriorityQueue.cs +++ /dev/null @@ -1,190 +0,0 @@ -#region CPL License -/* -Nuclex Framework -Copyright (C) 2002-2007 Nuclex Development Labs - -This library is free software; you can redistribute it and/or -modify it under the terms of the IBM Common Public License as -published by the IBM Corporation; either version 1.0 of the -License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -IBM Common Public License for more details. - -You should have received a copy of the IBM Common Public -License along with this library -*/ -#endregion - -using System; -using System.Collections.Generic; -using System.Collections; - -namespace Nuclex.Support.Collections { - - /// Queue that dequeues items in order of their priority - public class UnintrusivePriorityQueue - : ICollection, IEnumerable - where PriorityType : IComparable { - - #region struct Entry - - /// An entry in the priority queue - private struct Entry { - - /// Initializes a new priority queue entry - /// Item to be stored in the entry - /// Priority of the item in the entry - public Entry(ItemType item, PriorityType priority) { - this.Item = item; - this.Priority = priority; - } - - /// Item contained in this priority queue entry - public ItemType Item; - /// Priority assigned to this entry - public PriorityType Priority; - - } - - #endregion // struct Entry - - #region class EntryComparer - - /// Compares two priority queue entries based on their priority - private class EntryComparer : IComparer { - - /// Compares the left entry to the right entry - /// Entry on the left side - /// Entry on the right side - /// The relationship of the two entries - public int Compare(Entry left, Entry right) { - return left.Priority.CompareTo(right.Priority); - } - - /// Default instance for this comparer - public static readonly EntryComparer Instance = new EntryComparer(); - - } - - #endregion // class EntryComparer - - #region class UnwrappingEnumerator - - /// Enumerates all items contained in a priority queue - private class UnwrappingEnumerator : IEnumerator { - - /// Initializes a new priority queue enumerator - /// Enumerator of entries to unwrap - public UnwrappingEnumerator(IEnumerator entryEnumerator) { - this.entryEnumerator = entryEnumerator; - } - - /// Resets the enumerator to its initial state - public void Reset() { - this.entryEnumerator.Reset(); - } - - /// The current item being enumerated - ItemType IEnumerator.Current { - get { return this.entryEnumerator.Current.Item; } - } - - /// Releases all resources used by the enumerator - public void Dispose() { - this.entryEnumerator.Dispose(); - } - - /// Moves to the next item in the priority queue - /// True if a next item was found, false if the end has been reached - public bool MoveNext() { - return this.entryEnumerator.MoveNext(); - } - - /// The current item being enumerated - object IEnumerator.Current { - get { return this.entryEnumerator.Current.Item; } - } - - /// Enumerator for entries to be unwrapped by this enumerator - private IEnumerator entryEnumerator; - - } - - #endregion // class Enumerator - - /// Initializes a new non-intrusive priority queue - public UnintrusivePriorityQueue() { - this.intrusiveQueue = new PriorityQueue(EntryComparer.Instance); - } - - /// Takes the item with the highest priority off from the queue - /// The item with the highest priority in the list - public ItemType Dequeue() { - return this.intrusiveQueue.Dequeue().Item; - } - - /// Puts an item into the priority queue - /// Item to be queued - /// Priority of the item to be queued - public void Enqueue(ItemType item, PriorityType priority) { - this.intrusiveQueue.Enqueue(new Entry(item, priority)); - } - - /// Removes all items from the priority queue - public void Clear() { - this.intrusiveQueue.Clear(); - } - - /// Total number of items in the priority queue - public int Count { - get { return this.intrusiveQueue.Count; } - } - - /// Copies the contents of the priority queue into an array - /// Array to copy the priority queue into - /// Starting index for the destination array - public void CopyTo(Array array, int index) { - - Entry[] entries = new Entry[this.intrusiveQueue.Count]; - - this.intrusiveQueue.CopyTo(array, 0); - - for(int entryIndex = 0; entryIndex < this.intrusiveQueue.Count; ++entryIndex) - array.SetValue(entries[entryIndex], entryIndex + index); - - } - - /// - /// Obtains an object that can be used to synchronize accesses to the priority queue - /// from different threads - /// - public object SyncRoot { - get { return this.intrusiveQueue.SyncRoot; } - } - - /// Whether operations performed on this priority queue are thread safe - public bool IsSynchronized { - get { return this.intrusiveQueue.IsSynchronized; } - } - - /// Returns a typesafe enumerator for the priority queue - /// A new enumerator for the priority queue - public IEnumerator GetEnumerator() { - return new UnwrappingEnumerator(this.intrusiveQueue.GetEnumerator()); - } - - /// Returns an enumerator for the priority queue - /// A new enumerator for the priority queue - IEnumerator IEnumerable.GetEnumerator() { - return new UnwrappingEnumerator(this.intrusiveQueue.GetEnumerator()); - } - - /// Intrusive priority queue being wrapped by this class - private PriorityQueue intrusiveQueue; - - } - -} // namespace Nuclex.Support.Collections