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
This commit is contained in:
Markus Ewald 2009-07-14 21:08:10 +00:00
parent 3ee5fdfc67
commit 0b23b1f7c4
6 changed files with 120 additions and 31 deletions

View File

@ -23,14 +23,14 @@ namespace Nuclex.Support.Collections {
/// <summary>Appends an item to the end of the double-ended queue</summary>
/// <param name="item">Item that will be appended to the queue</param>
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

View File

@ -61,7 +61,7 @@ namespace Nuclex.Support.Collections {
/// <summary>Whether the deque is read-only</summary>
bool IList.IsReadOnly {
get { throw new NotImplementedException(); }
get { return false; }
}
/// <summary>Removes the specified item from the deque</summary>
@ -106,7 +106,11 @@ namespace Nuclex.Support.Collections {
/// <param name="array">Array the contents of the deque will be copied into</param>
/// <param name="index">Index at which writing into the array will begin</param>
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);
}
/// <summary>Whether the deque is thread-synchronized</summary>

View File

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

View File

@ -13,7 +13,7 @@ namespace Nuclex.Support.Collections {
/// <returns>The index of the item or -1 if it wasn't found</returns>
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<ItemType>(
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<ItemType>(
this.blocks[lastBlock], item, 0, this.lastBlockCount
this.blocks[lastBlock], item, 0, this.lastBlockEndIndex
);
if(index == -1) {
return -1;

View File

@ -606,13 +606,54 @@ namespace Nuclex.Support.Collections {
/// <summary>Tests the non-typesafe CopyTo() method</summary>
[Test]
public void TestCopyToObjectArray() {
// TODO Write a unit test for the non-typesafe CopyTo() method
Deque<int> intDeque = createNonNormalizedDeque(40);
int[] array = new int[40];
((ICollection)intDeque).CopyTo(array, 0);
Assert.AreEqual(intDeque, array);
}
/// <summary>Tests the CopyTo() method</summary>
[Test]
public void TestCopyToArray() {
// TODO Write a unit test for the typesafe CopyTo() method
Deque<int> 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
);
}
/// <summary>
/// Verifies that the non-typesafe CopyTo() method throws an exception if
/// the array is of an incompatible type
/// </summary>
[Test]
public void TestThrowOnCopyToIncompatibleObjectArray() {
Deque<int> intDeque = createDeque(4);
short[] array = new short[4];
Assert.Throws<ArgumentException>(
delegate() { ((ICollection)intDeque).CopyTo(array, 4); }
);
}
/// <summary>
/// Verifies that the CopyTo() method throws an exception if the target array
/// is too small
/// </summary>
[Test]
public void TestThrowOnCopyToTooSmallArray() {
Deque<int> intDeque = createDeque(8);
Assert.Throws<ArgumentException>(
delegate() { intDeque.CopyTo(new int[7], 0); }
);
}
/// <summary>

View File

@ -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 {
/// <param name="array">Array the contents of the deque will be copied into</param>
/// <param name="arrayIndex">Array index the deque contents will begin at</param>
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
);
}
}
/// <summary>Obtains a new enumerator for the contents of the deque</summary>
@ -238,7 +282,7 @@ namespace Nuclex.Support.Collections {
/// <summary>Starting index of data in the first block</summary>
private int firstBlockStartIndex;
/// <summary>End index of data in the last block</summary>
private int lastBlockCount;
private int lastBlockEndIndex;
}