diff options
author | Robert Anderson <shiftylogic@users.noreply.github.com> | 2017-03-15 20:26:11 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-15 20:26:11 +0300 |
commit | 1014d2f774476038bf93a4dc5df5a00d3989372a (patch) | |
tree | 9d937d0b284dfa30f39d6fe0acd8eb3971d991a9 /src/System.Memory | |
parent | 4fe54ee3e028be5b0f8f2683c5f71e38bf7c2251 (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.cs | 70 |
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; } } |