Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Use Buffer.MemoryCopy when available #1036

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void Skip(int bytesToSkip)
{
if (wasLastBlock)
{
throw new InvalidOperationException("Attempted to skip more bytes than available.");
ThrowInvalidOperation(InvalidBlockOperation.SkipMoreBytesThanAvailable);
}
else
{
Expand Down Expand Up @@ -768,7 +768,8 @@ internal static int FindFirstEqualByte(ref Vector<byte> byteEquals)
? (longValue & 0x000000ff00000000) > 0 ? 4 : 5
: (longValue & 0x00ff000000000000) > 0 ? 6 : 7);
}
throw new InvalidOperationException();
ThrowInvalidOperation(InvalidBlockOperation.EqualByteNotFound);
return default(int);
}

// Internal for testing
Expand Down Expand Up @@ -796,7 +797,8 @@ internal static int FindFirstEqualByteSlow(ref Vector<byte> byteEquals)
if (byteEquals[offset + 6] != 0) return offset + 6;
return offset + 7;
}
throw new InvalidOperationException();
ThrowInvalidOperation(InvalidBlockOperation.EqualByteNotFound);
return default(int);
}

/// <summary>
Expand Down Expand Up @@ -856,7 +858,7 @@ public int GetLength(MemoryPoolIterator end)
}
else if (block.Next == null)
{
throw new InvalidOperationException("end did not follow iterator");
ThrowInvalidOperation(InvalidBlockOperation.EndDidNotFollowIterator);
}
else
{
Expand All @@ -870,12 +872,33 @@ public int GetLength(MemoryPoolIterator end)

public MemoryPoolIterator CopyTo(byte[] array, int offset, int count, out int actual)
{
if (IsDefault)
if (count == 0 || IsDefault)
{
actual = 0;
return this;
}

if (array == null)
{
return Consume(count, out actual);
}

if (count + offset > array.Length)
{
ThrowInvalidOperation(InvalidBlockOperation.AccessingBeyondEndOfArray);
}

#if NET451
return BlockCopyTo(array, ref offset, count, out actual);
#else
return MemoryCopyTo(array, ref offset, count, out actual);
#endif
}

#if NET451
private MemoryPoolIterator BlockCopyTo(byte[] array, ref int offset, int count, out int actual)
{
// Note: Ensure for any changes in this function MemoryCopyTo & Consume are changed to match
var block = _block;
var index = _index;
var remaining = count;
Expand All @@ -890,28 +913,89 @@ public MemoryPoolIterator CopyTo(byte[] array, int offset, int count, out int ac
if (remaining <= following)
{
actual = count;
if (array != null)
{
Buffer.BlockCopy(block.Array, index, array, offset, remaining);
}
Buffer.BlockCopy(block.Array, index, array, offset, remaining);
return new MemoryPoolIterator(block, index + remaining);
}
else if (wasLastBlock)
{
actual = count - remaining + following;
if (array != null)
{
Buffer.BlockCopy(block.Array, index, array, offset, following);
}
Buffer.BlockCopy(block.Array, index, array, offset, following);
return new MemoryPoolIterator(block, index + following);
}
else
{
if (array != null)
Buffer.BlockCopy(block.Array, index, array, offset, following);
offset += following;
remaining -= following;
block = block.Next;
index = block.Start;
}
}
}
#else
private unsafe MemoryPoolIterator MemoryCopyTo(byte[] array, ref int offset, int count, out int actual)
{
// Note: Ensure for any changes in this function BlockCopyTo & Consume are changed to match
var block = _block;
var index = _index;
var remaining = count;
fixed (byte* pArray = &array[0])
{
while (true)
{
// Determine if we might attempt to copy data from block.Next before
// calculating "following" so we don't risk skipping data that could
// be added after block.End when we decide to copy from block.Next.
// block.End will always be advanced before block.Next is set.
var wasLastBlock = block.Next == null;
var following = block.End - index;
if (remaining <= following)
{
Buffer.BlockCopy(block.Array, index, array, offset, following);
actual = count;
Buffer.MemoryCopy(block.DataFixedPtr + index, pArray + offset, remaining, remaining);
return new MemoryPoolIterator(block, index + remaining);
}
offset += following;
else if (wasLastBlock)
{
actual = count - remaining + following;
Buffer.MemoryCopy(block.DataFixedPtr + index, pArray + offset, following, following);
return new MemoryPoolIterator(block, index + following);
}
else
{
Buffer.MemoryCopy(block.DataFixedPtr + index, pArray + offset, following, following);
offset += following;
remaining -= following;
block = block.Next;
index = block.Start;
}
}
}
}
#endif

private MemoryPoolIterator Consume(int count, out int actual)
{
// Note: Ensure for any changes in this function CopyToBlockCopy & CopyToMemoryCopy are changed to match
var block = _block;
var index = _index;
var remaining = count;
while (true)
{
var wasLastBlock = block.Next == null;
var following = block.End - index;
if (remaining <= following)
{
actual = count;
return new MemoryPoolIterator(block, index + remaining);
}
else if (wasLastBlock)
{
actual = count - remaining + following;
return new MemoryPoolIterator(block, index + following);
}
else
{
remaining -= following;
block = block.Next;
index = block.Start;
Expand All @@ -931,7 +1015,7 @@ public void CopyFrom(ArraySegment<byte> buffer)

public void CopyFrom(byte[] data, int offset, int count)
{
if (IsDefault)
if (count == 0 || IsDefault)
{
return;
}
Expand All @@ -940,6 +1024,22 @@ public void CopyFrom(byte[] data, int offset, int count)
Debug.Assert(_block.Next == null);
Debug.Assert(_block.End == _index);

if (count + offset > data.Length)
{
ThrowInvalidOperation(InvalidBlockOperation.AccessingBeyondEndOfArray);
}

#if NET451
BlockCopyFrom(data, offset, count);
#else
MemoryCopyFrom(data, offset, count);
#endif
}

#if NET451
private void BlockCopyFrom(byte[] data, int offset, int count)
{
// Note: Ensure for any changes in this function MemoryCopyFrom is changed to match
var pool = _block.Pool;
var block = _block;
var blockIndex = _index;
Expand Down Expand Up @@ -975,6 +1075,49 @@ public void CopyFrom(byte[] data, int offset, int count)
_block = block;
_index = blockIndex;
}
#else
private unsafe void MemoryCopyFrom(byte[] data, int offset, int count)
{
// Note: Ensure for any changes in this function BlockCopyFrom is changed to match
var pool = _block.Pool;
var block = _block;
var blockIndex = _index;

var bufferIndex = offset;
var remaining = count;
var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex;

fixed (byte* pData = &data[0])
{
while (remaining > 0)
{
if (bytesLeftInBlock == 0)
{
var nextBlock = pool.Lease();
block.End = blockIndex;
Volatile.Write(ref block.Next, nextBlock);
block = nextBlock;

blockIndex = block.Data.Offset;
bytesLeftInBlock = block.Data.Count;
}

var bytesToCopy = remaining < bytesLeftInBlock ? remaining : bytesLeftInBlock;

Buffer.MemoryCopy(pData + bufferIndex, block.DataFixedPtr + blockIndex, bytesLeftInBlock, bytesToCopy);

blockIndex += bytesToCopy;
bufferIndex += bytesToCopy;
remaining -= bytesToCopy;
bytesLeftInBlock -= bytesToCopy;
}
}

block.End = blockIndex;
_block = block;
_index = blockIndex;
}
#endif

public unsafe void CopyFromAscii(string data)
{
Expand Down Expand Up @@ -1041,5 +1184,29 @@ public unsafe void CopyFromAscii(string data)
_block = block;
_index = blockIndex;
}

private static void ThrowInvalidOperation(InvalidBlockOperation operation)
{
switch (operation)
{
case InvalidBlockOperation.AccessingBeyondEndOfArray:
throw new InvalidOperationException("Attempting to access beyond end of array.");
case InvalidBlockOperation.SkipMoreBytesThanAvailable:
throw new InvalidOperationException("Attempted to skip more bytes than available.");
case InvalidBlockOperation.EndDidNotFollowIterator:
throw new InvalidOperationException("End did not follow iterator.");
case InvalidBlockOperation.EqualByteNotFound:
throw new InvalidOperationException("Equal Byte Not Found.");

}
}

private enum InvalidBlockOperation
{
AccessingBeyondEndOfArray,
SkipMoreBytesThanAvailable,
EndDidNotFollowIterator,
EqualByteNotFound
}
}
}