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;
}