diff --git a/ExaArray/ExaArray1D.Factories.cs b/ExaArray/ExaArray1D.Factories.cs index 2e4f576..b487aa9 100644 --- a/ExaArray/ExaArray1D.Factories.cs +++ b/ExaArray/ExaArray1D.Factories.cs @@ -32,14 +32,33 @@ namespace Exa return next; } + /// + /// Creates a new ExaArray1D from another, respecting the given range. + /// + /// + /// When T is a value type, data gets copied as values. When T is a reference type, the pointers + /// to the original objects are copied. Thus, this factory method does not create a deep copy. + /// + /// Performance: O(n) + /// + /// The indices are inclusive. + /// + /// The instance from which the new instance is to be created. + /// The first source element which should be part of the new array. + /// The last source element which should be part of the new array. + /// The new instance + /// Throws, when one or both of the indices are out of range. public static ExaArray1D CreateFrom(ExaArray1D other, ulong indexFrom, ulong indexTo) { if (indexTo < indexFrom) throw new IndexOutOfRangeException("Index to must be greater than index from."); if (indexTo >= other.Length || indexFrom >= other.Length) - throw new IndexOutOfRangeException("Index to must be greater than index from."); + throw new IndexOutOfRangeException("Index to must be within the range of the source."); + // + // Determine the source start chunk and index. + // int sourceChunkIndexTo = -1; int sourceElementIndexTo = -1; switch (other.OptimizationStrategy) @@ -54,6 +73,9 @@ namespace Exa break; } + // + // Determine the source end chunk and index. + // int sourceChunkIndexFrom = -1; int sourceElementIndexFrom = -1; switch (other.OptimizationStrategy) @@ -68,7 +90,12 @@ namespace Exa break; } + // How many element we have to copy? ulong newRange = indexTo - indexFrom + 1; + + // + // Determine, how many total chunks we need for the copy. + // int destChunks = -1; switch (other.OptimizationStrategy) { @@ -80,20 +107,29 @@ namespace Exa break; } + // Create the copy and allocate the needed number of outer chunk. var next = new ExaArray1D(other.OptimizationStrategy) { Length = newRange, chunks = new T[destChunks][], }; - - int sourceChunkIndex = sourceChunkIndexFrom; - int destinChunkIndex = 0; - int sourceElementIndex = sourceElementIndexFrom; - int destinElementIndex = 0; - ulong leftOver = newRange; + // + // Variables for the copy process. + // + + int sourceChunkIndex = sourceChunkIndexFrom; + int destinationChunkIndex = 0; + int sourceElementIndex = sourceElementIndexFrom; + int destinationElementIndex = 0; + ulong leftOverTotal = newRange; + do { + // + // Determine how many elements we copy next. + // + uint numberToCopy = 0; // Case: small number of elements from first chunk only @@ -105,36 +141,47 @@ namespace Exa numberToCopy = (uint) (other.maxArrayCapacity - sourceElementIndex); // Case: multiple chunks + we are in the middle of huge copy process + next chunk does __not__ exist - else if (next.chunks[destinChunkIndex] == null && sourceElementIndex == 0 && leftOver >= other.maxArrayCapacity) + else if (next.chunks[destinationChunkIndex] == null && sourceElementIndex == 0 && leftOverTotal >= other.maxArrayCapacity) numberToCopy = other.maxArrayCapacity; // Case: multiple chunks + we are in the middle of huge copy process + next chunk does exist - else if (next.chunks[destinChunkIndex] != null && sourceElementIndex == 0 && leftOver >= other.maxArrayCapacity) - numberToCopy = (uint) (other.maxArrayCapacity - next.chunks[destinChunkIndex].Length); + else if (next.chunks[destinationChunkIndex] != null && sourceElementIndex == 0 && leftOverTotal >= other.maxArrayCapacity) + numberToCopy = (uint) (other.maxArrayCapacity - next.chunks[destinationChunkIndex].Length); // Case: multiple chunks + this seems to be the last chunk - else if (sourceElementIndex == 0 && leftOver < other.maxArrayCapacity) - numberToCopy = (uint) leftOver; + else if (sourceElementIndex == 0 && leftOverTotal < other.maxArrayCapacity) + numberToCopy = (uint) leftOverTotal; - if(next.chunks[destinChunkIndex] == null) - next.chunks[destinChunkIndex] = new T[numberToCopy]; + // + // Can we allocate an entire chunk or do we have to fill up the existing + // chunk first? + // + if(next.chunks[destinationChunkIndex] == null) + next.chunks[destinationChunkIndex] = new T[numberToCopy]; else { - var extended = new T[next.chunks[destinChunkIndex].Length + numberToCopy]; - Array.Copy(next.chunks[destinChunkIndex], extended, next.chunks[destinChunkIndex].Length); - next.chunks[destinChunkIndex] = extended; + var extended = new T[next.chunks[destinationChunkIndex].Length + numberToCopy]; + Array.Copy(next.chunks[destinationChunkIndex], extended, next.chunks[destinationChunkIndex].Length); + next.chunks[destinationChunkIndex] = extended; } - Array.Copy(other.chunks[sourceChunkIndex], sourceElementIndex, next.chunks[destinChunkIndex], destinElementIndex, numberToCopy); + // Copy the data: + Array.Copy(other.chunks[sourceChunkIndex], sourceElementIndex, next.chunks[destinationChunkIndex], destinationElementIndex, numberToCopy); - var needNewChunk = next.chunks[destinChunkIndex].Length == next.maxArrayCapacity; - leftOver -= numberToCopy; - sourceChunkIndex++; - destinChunkIndex = needNewChunk ? destinChunkIndex + 1 : destinChunkIndex; - sourceElementIndex = 0; - destinElementIndex = (int) (needNewChunk ? 0 : numberToCopy); + // + // Update the state machine. + // + var needNewDestinationChunk = next.chunks[destinationChunkIndex].Length == next.maxArrayCapacity; + var readNextSourceChunk = sourceElementIndex + numberToCopy == other.maxArrayCapacity; + var leftOverCurrentSourceChunk = (int) (readNextSourceChunk ? 0 : other.maxArrayCapacity - sourceElementIndex - numberToCopy); + + leftOverTotal -= numberToCopy; + sourceChunkIndex = readNextSourceChunk ? sourceChunkIndex + 1 : sourceChunkIndex; + destinationChunkIndex = needNewDestinationChunk ? destinationChunkIndex + 1 : destinationChunkIndex; + sourceElementIndex = leftOverCurrentSourceChunk; + destinationElementIndex = (int) (needNewDestinationChunk ? 0 : numberToCopy); - } while (leftOver > 0); + } while (leftOverTotal > 0); return next; } }