From 0b23b1f7c4e82e577187cf6ba240155551a2f334 Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Tue, 14 Jul 2009 21:08:10 +0000 Subject: [PATCH] lastBlockCount is called lastBlockEndIndex again (anyone got a better term - it's an index one past the last entry, not a count); implemented the CopyTo() method; added unit tests for the CopyTo() method git-svn-id: file:///srv/devel/repo-conversion/nusu@164 d2e56fa2-650e-0410-a79f-9358c0239efd --- Source/Collections/Deque.Insertion.cs | 16 ++++---- Source/Collections/Deque.Interfaces.cs | 8 +++- Source/Collections/Deque.Removal.cs | 26 ++++++------- Source/Collections/Deque.Search.cs | 4 +- Source/Collections/Deque.Test.cs | 45 +++++++++++++++++++++- Source/Collections/Deque.cs | 52 ++++++++++++++++++++++++-- 6 files changed, 120 insertions(+), 31 deletions(-) diff --git a/Source/Collections/Deque.Insertion.cs b/Source/Collections/Deque.Insertion.cs index d96f123..7d9b99e 100644 --- a/Source/Collections/Deque.Insertion.cs +++ b/Source/Collections/Deque.Insertion.cs @@ -23,14 +23,14 @@ namespace Nuclex.Support.Collections { /// Appends an item to the end of the double-ended queue /// Item that will be appended to the queue public void AddLast(ItemType item) { - if(this.lastBlockCount < this.blockSize) { - ++this.lastBlockCount; + if(this.lastBlockEndIndex < this.blockSize) { + ++this.lastBlockEndIndex; } else { // Need to allocate a new block this.blocks.Add(new ItemType[this.blockSize]); - this.lastBlockCount = 1; + this.lastBlockEndIndex = 1; } - this.blocks[this.blocks.Count - 1][this.lastBlockCount - 1] = item; + this.blocks[this.blocks.Count - 1][this.lastBlockEndIndex - 1] = item; ++this.count; } @@ -137,15 +137,15 @@ namespace Nuclex.Support.Collections { int blockLength; // If the lastmost block is full, we need to add another block - if(this.lastBlockCount == this.blockSize) { + if(this.lastBlockEndIndex == this.blockSize) { this.blocks.Add(new ItemType[this.blockSize]); this.blocks[lastBlock + 1][0] = this.blocks[lastBlock][this.blockSize - 1]; - this.lastBlockCount = 1; + this.lastBlockEndIndex = 1; blockLength = this.blockSize - 1; } else { - blockLength = this.lastBlockCount; - ++this.lastBlockCount; + blockLength = this.lastBlockEndIndex; + ++this.lastBlockEndIndex; } // If the insertion point is not in the lastmost block diff --git a/Source/Collections/Deque.Interfaces.cs b/Source/Collections/Deque.Interfaces.cs index 17c8341..3d7c5bc 100644 --- a/Source/Collections/Deque.Interfaces.cs +++ b/Source/Collections/Deque.Interfaces.cs @@ -61,7 +61,7 @@ namespace Nuclex.Support.Collections { /// Whether the deque is read-only bool IList.IsReadOnly { - get { throw new NotImplementedException(); } + get { return false; } } /// Removes the specified item from the deque @@ -106,7 +106,11 @@ namespace Nuclex.Support.Collections { /// Array the contents of the deque will be copied into /// Index at which writing into the array will begin void ICollection.CopyTo(Array array, int index) { - throw new NotImplementedException(); + if(!(array is ItemType[])) { + throw new ArgumentException("Incompatible array type", "array"); + } + + CopyTo((ItemType[])array, index); } /// Whether the deque is thread-synchronized diff --git a/Source/Collections/Deque.Removal.cs b/Source/Collections/Deque.Removal.cs index 61b34e8..0aacb8f 100644 --- a/Source/Collections/Deque.Removal.cs +++ b/Source/Collections/Deque.Removal.cs @@ -23,7 +23,7 @@ namespace Nuclex.Support.Collections { // Clear the items in the block to release any reference we may be keeping alive for( - int index = this.firstBlockStartIndex; index < this.lastBlockCount; ++index + int index = this.firstBlockStartIndex; index < this.lastBlockEndIndex; ++index ) { this.blocks[0][index] = default(ItemType); } @@ -32,7 +32,7 @@ namespace Nuclex.Support.Collections { // Reset the counters to restart the deque from scratch this.firstBlockStartIndex = 0; - this.lastBlockCount = 0; + this.lastBlockEndIndex = 0; this.count = 0; } @@ -68,7 +68,7 @@ namespace Nuclex.Support.Collections { this.firstBlockStartIndex = 0; } else { // Last block - do not remove this.firstBlockStartIndex = 0; - this.lastBlockCount = 0; + this.lastBlockEndIndex = 0; } } --this.count; @@ -83,18 +83,18 @@ namespace Nuclex.Support.Collections { // This is necessary to make sure the deque doesn't hold dead objects alive // in unreachable spaces of its memory. int lastBlock = this.blocks.Count - 1; - this.blocks[lastBlock][this.lastBlockCount - 1] = default(ItemType); + this.blocks[lastBlock][this.lastBlockEndIndex - 1] = default(ItemType); // Cut off the last item in the last block. If the block became empty and it's // not the last remaining block, remove it as well. - --this.lastBlockCount; - if(this.lastBlockCount == 0) { // Block became empty + --this.lastBlockEndIndex; + if(this.lastBlockEndIndex == 0) { // Block became empty if(this.count > 1) { this.blocks.RemoveAt(lastBlock); - this.lastBlockCount = this.blockSize; + this.lastBlockEndIndex = this.blockSize; } else { // Last block - do not remove this.firstBlockStartIndex = 0; - this.lastBlockCount = 0; + this.lastBlockEndIndex = 0; } } --this.count; @@ -206,15 +206,15 @@ namespace Nuclex.Support.Collections { Array.Copy( this.blocks[lastBlock], startIndex + 1, this.blocks[lastBlock], startIndex, - this.lastBlockCount - startIndex - 1 + this.lastBlockEndIndex - startIndex - 1 ); - if(this.lastBlockCount == 1) { + if(this.lastBlockEndIndex == 1) { this.blocks.RemoveAt(lastBlock); - this.lastBlockCount = this.blockSize; + this.lastBlockEndIndex = this.blockSize; } else { - this.blocks[lastBlock][this.lastBlockCount - 1] = default(ItemType); - --this.lastBlockCount; + this.blocks[lastBlock][this.lastBlockEndIndex - 1] = default(ItemType); + --this.lastBlockEndIndex; } --this.count; diff --git a/Source/Collections/Deque.Search.cs b/Source/Collections/Deque.Search.cs index 4a4fcf0..011d3d6 100644 --- a/Source/Collections/Deque.Search.cs +++ b/Source/Collections/Deque.Search.cs @@ -13,7 +13,7 @@ namespace Nuclex.Support.Collections { /// The index of the item or -1 if it wasn't found public int IndexOf(ItemType item) { if(this.blocks.Count == 1) { // Only one block to scan? - int length = this.lastBlockCount - this.firstBlockStartIndex; + int length = this.lastBlockEndIndex - this.firstBlockStartIndex; int index = Array.IndexOf( this.blocks[0], item, this.firstBlockStartIndex, length ); @@ -50,7 +50,7 @@ namespace Nuclex.Support.Collections { // Nothing found, continue the search in the index = Array.IndexOf( - this.blocks[lastBlock], item, 0, this.lastBlockCount + this.blocks[lastBlock], item, 0, this.lastBlockEndIndex ); if(index == -1) { return -1; diff --git a/Source/Collections/Deque.Test.cs b/Source/Collections/Deque.Test.cs index 4f1d4ec..96561d4 100644 --- a/Source/Collections/Deque.Test.cs +++ b/Source/Collections/Deque.Test.cs @@ -606,13 +606,54 @@ namespace Nuclex.Support.Collections { /// Tests the non-typesafe CopyTo() method [Test] public void TestCopyToObjectArray() { - // TODO Write a unit test for the non-typesafe CopyTo() method + Deque intDeque = createNonNormalizedDeque(40); + + int[] array = new int[40]; + ((ICollection)intDeque).CopyTo(array, 0); + + Assert.AreEqual(intDeque, array); } /// Tests the CopyTo() method [Test] public void TestCopyToArray() { - // TODO Write a unit test for the typesafe CopyTo() method + Deque intDeque = createDeque(12); + intDeque.RemoveFirst(); + intDeque.RemoveFirst(); + + int[] array = new int[14]; + intDeque.CopyTo(array, 4); + + Assert.AreEqual( + new int[] { 0, 0, 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + array + ); + } + + /// + /// Verifies that the non-typesafe CopyTo() method throws an exception if + /// the array is of an incompatible type + /// + [Test] + public void TestThrowOnCopyToIncompatibleObjectArray() { + Deque intDeque = createDeque(4); + + short[] array = new short[4]; + Assert.Throws( + delegate() { ((ICollection)intDeque).CopyTo(array, 4); } + ); + } + + /// + /// Verifies that the CopyTo() method throws an exception if the target array + /// is too small + /// + [Test] + public void TestThrowOnCopyToTooSmallArray() { + Deque intDeque = createDeque(8); + Assert.Throws( + delegate() { intDeque.CopyTo(new int[7], 0); } + ); } /// diff --git a/Source/Collections/Deque.cs b/Source/Collections/Deque.cs index 2201a68..f0c7a50 100644 --- a/Source/Collections/Deque.cs +++ b/Source/Collections/Deque.cs @@ -31,7 +31,7 @@ namespace Nuclex.Support.Collections { this.deque = deque; this.blockSize = this.deque.blockSize; this.lastBlock = this.deque.blocks.Count - 1; - this.lastBlockEndIndex = this.deque.lastBlockCount - 1; + this.lastBlockEndIndex = this.deque.lastBlockEndIndex - 1; Reset(); } @@ -175,7 +175,7 @@ namespace Nuclex.Support.Collections { if(this.count == 0) { throw new InvalidOperationException("The deque is empty"); } - return this.blocks[this.blocks.Count - 1][this.lastBlockCount - 1]; + return this.blocks[this.blocks.Count - 1][this.lastBlockEndIndex - 1]; } } @@ -190,7 +190,51 @@ namespace Nuclex.Support.Collections { /// Array the contents of the deque will be copied into /// Array index the deque contents will begin at public void CopyTo(ItemType[] array, int arrayIndex) { - throw new NotImplementedException(); + if(this.count > (array.Length - arrayIndex)) { + throw new ArgumentException( + "Array too small to hold the collection items starting at the specified index" + ); + } + + if(this.blocks.Count == 1) { // Does only one block exist? + + // Copy the one and only block there is + Array.Copy( + this.blocks[0], this.firstBlockStartIndex, + array, arrayIndex, + this.lastBlockEndIndex - this.firstBlockStartIndex + ); + + } else { // Multiple blocks exist + + // Copy the first block which is filled from the start index to its end + int length = this.blockSize - this.firstBlockStartIndex; + Array.Copy( + this.blocks[0], this.firstBlockStartIndex, + array, arrayIndex, + length + ); + arrayIndex += length; + + // Copy all intermediate blocks (if there are any). These are completely filled + int lastBlock = this.blocks.Count - 1; + for(int index = 1; index < lastBlock; ++index) { + Array.Copy( + this.blocks[index], 0, + array, arrayIndex, + this.blockSize + ); + arrayIndex += this.blockSize; + } + + // Copy the final block which is filled from the beginning to the end index + Array.Copy( + this.blocks[lastBlock], 0, + array, arrayIndex, + this.lastBlockEndIndex + ); + + } } /// Obtains a new enumerator for the contents of the deque @@ -238,7 +282,7 @@ namespace Nuclex.Support.Collections { /// Starting index of data in the first block private int firstBlockStartIndex; /// End index of data in the last block - private int lastBlockCount; + private int lastBlockEndIndex; }