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:
parent
3ee5fdfc67
commit
0b23b1f7c4
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user