Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corefx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Anderson <shiftylogic@users.noreply.github.com>2017-03-15 20:26:11 +0300
committerGitHub <noreply@github.com>2017-03-15 20:26:11 +0300
commit1014d2f774476038bf93a4dc5df5a00d3989372a (patch)
tree9d937d0b284dfa30f39d6fe0acd8eb3971d991a9 /src/System.Memory
parent4fe54ee3e028be5b0f8f2683c5f71e38bf7c2251 (diff)
Make use of CopyBlock for non-overlapping spans (#17063)
* Make use of CopyBlock for non-overlapping spans * sizeof(IntPtr) needs unsafe * Making call to ref byte version of CopyBlock * Block copy that handles buffers larger than 4GB * Skip fast path for reference types and types containing references * Consolidating code and simplifying the logic for Span::TryCopyTo fast path * FastCopy comments, asserts, and a fast exit for zero length source. * Merging methods back into a single method for trycopyto.
Diffstat (limited to 'src/System.Memory')
-rw-r--r--src/System.Memory/src/System/Span.cs70
1 files changed, 56 insertions, 14 deletions
diff --git a/src/System.Memory/src/System/Span.cs b/src/System.Memory/src/System/Span.cs
index 5a7aa8ba0d..02b5e484aa 100644
--- a/src/System.Memory/src/System/Span.cs
+++ b/src/System.Memory/src/System/Span.cs
@@ -204,12 +204,12 @@ namespace System
var byteLength = (UIntPtr)((uint)length * Unsafe.SizeOf<T>());
- if ((Unsafe.SizeOf<T>() & (sizeof(IntPtr) - 1)) != 0)
+ if ((Unsafe.SizeOf<T>() & (sizeof(IntPtr) - 1)) != 0)
{
if (_pinnable == null)
{
var ptr = (byte*)_byteOffset.ToPointer();
-
+
SpanHelpers.ClearLessThanPointerSized(ptr, byteLength);
}
else
@@ -311,7 +311,6 @@ namespace System
ThrowHelper.ThrowArgumentException_DestinationTooShort();
}
-
/// <summary>
/// Copies the contents of this span into destination span. If the source
/// and destinations overlap, this method behaves as if the original values in
@@ -323,35 +322,78 @@ namespace System
/// <param name="destination">The span to copy items into.</param>
public bool TryCopyTo(Span<T> destination)
{
- if ((uint)_length > (uint)destination._length)
+ int length = _length;
+ int destLength = destination._length;
+
+ if ((uint)length == 0)
+ return true;
+
+ if ((uint)length > (uint)destLength)
return false;
- // TODO: This is a tide-over implementation as we plan to add a overlap-safe cpblk-based api to Unsafe. (https://github.com/dotnet/corefx/issues/13427)
unsafe
{
ref T src = ref DangerousGetPinnableReference();
ref T dst = ref destination.DangerousGetPinnableReference();
- IntPtr srcMinusDst = Unsafe.ByteOffset<T>(ref dst, ref src);
- int length = _length;
+ IntPtr srcMinusDst = Unsafe.ByteOffset<T>(ref dst, ref src);
bool srcGreaterThanDst = (sizeof(IntPtr) == sizeof(int)) ? srcMinusDst.ToInt32() >= 0 : srcMinusDst.ToInt64() >= 0;
+ IntPtr tailDiff;
+
if (srcGreaterThanDst)
{
- // Source address greater than or equal to destination address. Can do normal copy.
- for (int i = 0; i < length; i++)
+ // If the start of source is greater than the start of destination, then we need to calculate
+ // the different between the end of destination relative to the start of source.
+ tailDiff = Unsafe.ByteOffset<T>(ref Unsafe.Add<T>(ref dst, destLength), ref src);
+ }
+ else
+ {
+ // If the start of source is less than the start of destination, then we need to calculate
+ // the different between the end of source relative to the start of destunation.
+ tailDiff = Unsafe.ByteOffset<T>(ref Unsafe.Add<T>(ref src, length), ref dst);
+ }
+
+ // If the source is entirely before or entirely after the destination and the type inside the span is not
+ // itself a reference type or containing reference types, then we can do a simple block copy of the data.
+ bool isOverlapped = (sizeof(IntPtr) == sizeof(int)) ? tailDiff.ToInt32() < 0 : tailDiff.ToInt64() < 0;
+ if (!isOverlapped && !SpanHelpers.IsReferenceOrContainsReferences<T>())
+ {
+ ref byte dstBytes = ref Unsafe.As<T, byte>(ref dst);
+ ref byte srcBytes = ref Unsafe.As<T, byte>(ref src);
+ ulong byteCount = (ulong)length * (ulong)Unsafe.SizeOf<T>();
+ ulong index = 0;
+
+ while (index < byteCount)
{
- Unsafe.Add<T>(ref dst, i) = Unsafe.Add<T>(ref src, i);
+ uint blockSize = byteCount > uint.MaxValue ? uint.MaxValue : (uint)byteCount;
+ Unsafe.CopyBlock(
+ ref Unsafe.Add(ref dstBytes, (IntPtr)index),
+ ref Unsafe.Add(ref srcBytes, (IntPtr)index),
+ blockSize);
+ index += blockSize;
}
}
else
{
- // Source address less than destination address. Must do backward copy.
- int i = length;
- while (i-- != 0)
+ if (srcGreaterThanDst)
+ {
+ // Source address greater than or equal to destination address. Can do normal copy.
+ for (int i = 0; i < length; i++)
+ {
+ Unsafe.Add<T>(ref dst, i) = Unsafe.Add<T>(ref src, i);
+ }
+ }
+ else
{
- Unsafe.Add<T>(ref dst, i) = Unsafe.Add<T>(ref src, i);
+ // Source address less than destination address. Must do backward copy.
+ int i = length;
+ while (i-- != 0)
+ {
+ Unsafe.Add<T>(ref dst, i) = Unsafe.Add<T>(ref src, i);
+ }
}
}
+
return true;
}
}