1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Reflection; 6 #if MONO 7 using System.Diagnostics.Private; 8 #endif 9 using System.Runtime.InteropServices; 10 using System.Runtime.CompilerServices; 11 12 namespace System 13 { 14 internal static partial class SpanHelpers 15 { 16 /// <summary> 17 /// Implements the copy functionality used by Span and ReadOnlySpan. 18 /// 19 /// NOTE: Fast span implements TryCopyTo in corelib and therefore this implementation 20 /// is only used by portable span. The code must live in code that only compiles 21 /// for portable span which means either each individual span implementation 22 /// of this shared code file. Other shared SpanHelper.X.cs files are compiled 23 /// for both portable and fast span implementations. 24 /// </summary> CopyTo(ref T dst, int dstLength, ref T src, int srcLength)25 public static unsafe void CopyTo<T>(ref T dst, int dstLength, ref T src, int srcLength) 26 { 27 Debug.Assert(dstLength != 0); 28 29 IntPtr srcByteCount = Unsafe.ByteOffset(ref src, ref Unsafe.Add(ref src, srcLength)); 30 IntPtr dstByteCount = Unsafe.ByteOffset(ref dst, ref Unsafe.Add(ref dst, dstLength)); 31 32 IntPtr diff = Unsafe.ByteOffset(ref src, ref dst); 33 34 bool isOverlapped = (sizeof(IntPtr) == sizeof(int)) 35 ? (uint)diff < (uint)srcByteCount || (uint)diff > (uint)-(int)dstByteCount 36 : (ulong)diff < (ulong)srcByteCount || (ulong)diff > (ulong)-(long)dstByteCount; 37 38 if (!isOverlapped && !SpanHelpers.IsReferenceOrContainsReferences<T>()) 39 { 40 ref byte dstBytes = ref Unsafe.As<T, byte>(ref dst); 41 ref byte srcBytes = ref Unsafe.As<T, byte>(ref src); 42 ulong byteCount = (ulong)srcByteCount; 43 ulong index = 0; 44 45 while (index < byteCount) 46 { 47 uint blockSize = (byteCount - index) > uint.MaxValue ? uint.MaxValue : (uint)(byteCount - index); 48 Unsafe.CopyBlock( 49 ref Unsafe.Add(ref dstBytes, (IntPtr)index), 50 ref Unsafe.Add(ref srcBytes, (IntPtr)index), 51 blockSize); 52 index += blockSize; 53 } 54 } 55 else 56 { 57 bool srcGreaterThanDst = (sizeof(IntPtr) == sizeof(int)) 58 ? (uint)diff > (uint)-(int)dstByteCount 59 : (ulong)diff > (ulong)-(long)dstByteCount; 60 61 int direction = srcGreaterThanDst ? 1 : -1; 62 int runCount = srcGreaterThanDst ? 0 : srcLength - 1; 63 64 int loopCount = 0; 65 for (; loopCount < (srcLength & ~7); loopCount += 8) 66 { 67 Unsafe.Add<T>(ref dst, runCount + direction * 0) = Unsafe.Add<T>(ref src, runCount + direction * 0); 68 Unsafe.Add<T>(ref dst, runCount + direction * 1) = Unsafe.Add<T>(ref src, runCount + direction * 1); 69 Unsafe.Add<T>(ref dst, runCount + direction * 2) = Unsafe.Add<T>(ref src, runCount + direction * 2); 70 Unsafe.Add<T>(ref dst, runCount + direction * 3) = Unsafe.Add<T>(ref src, runCount + direction * 3); 71 Unsafe.Add<T>(ref dst, runCount + direction * 4) = Unsafe.Add<T>(ref src, runCount + direction * 4); 72 Unsafe.Add<T>(ref dst, runCount + direction * 5) = Unsafe.Add<T>(ref src, runCount + direction * 5); 73 Unsafe.Add<T>(ref dst, runCount + direction * 6) = Unsafe.Add<T>(ref src, runCount + direction * 6); 74 Unsafe.Add<T>(ref dst, runCount + direction * 7) = Unsafe.Add<T>(ref src, runCount + direction * 7); 75 runCount += direction * 8; 76 } 77 if (loopCount < (srcLength & ~3)) 78 { 79 Unsafe.Add<T>(ref dst, runCount + direction * 0) = Unsafe.Add<T>(ref src, runCount + direction * 0); 80 Unsafe.Add<T>(ref dst, runCount + direction * 1) = Unsafe.Add<T>(ref src, runCount + direction * 1); 81 Unsafe.Add<T>(ref dst, runCount + direction * 2) = Unsafe.Add<T>(ref src, runCount + direction * 2); 82 Unsafe.Add<T>(ref dst, runCount + direction * 3) = Unsafe.Add<T>(ref src, runCount + direction * 3); 83 runCount += direction * 4; 84 loopCount += 4; 85 } 86 for (; loopCount < srcLength; ++loopCount) 87 { 88 Unsafe.Add<T>(ref dst, runCount) = Unsafe.Add<T>(ref src, runCount); 89 runCount += direction; 90 } 91 } 92 } 93 94 /// <summary> 95 /// Computes "start + index * sizeof(T)", using the unsigned IntPtr-sized multiplication for 32 and 64 bits. 96 /// 97 /// Assumptions: 98 /// Start and index are non-negative, and already pre-validated to be within the valid range of their containing Span. 99 /// 100 /// If the byte length (Span.Length * sizeof(T)) does an unsigned overflow (i.e. the buffer wraps or is too big to fit within the address space), 101 /// the behavior is undefined. 102 /// 103 /// </summary> 104 [MethodImpl(MethodImplOptions.AggressiveInlining)] Add(this IntPtr start, int index)105 public static IntPtr Add<T>(this IntPtr start, int index) 106 { 107 Debug.Assert(start.ToInt64() >= 0); 108 Debug.Assert(index >= 0); 109 110 unsafe 111 { 112 if (sizeof(IntPtr) == sizeof(int)) 113 { 114 // 32-bit path. 115 uint byteLength = (uint)index * (uint)Unsafe.SizeOf<T>(); 116 return (IntPtr)(((byte*)start) + byteLength); 117 } 118 else 119 { 120 // 64-bit path. 121 ulong byteLength = (ulong)index * (ulong)Unsafe.SizeOf<T>(); 122 return (IntPtr)(((byte*)start) + byteLength); 123 } 124 } 125 } 126 127 /// <summary> 128 /// Determine if a type is eligible for storage in unmanaged memory. 129 /// Portable equivalent of RuntimeHelpers.IsReferenceOrContainsReferences{T}() 130 /// </summary> IsReferenceOrContainsReferences()131 public static bool IsReferenceOrContainsReferences<T>() => PerTypeValues<T>.IsReferenceOrContainsReferences; 132 IsReferenceOrContainsReferencesCore(Type type)133 private static bool IsReferenceOrContainsReferencesCore(Type type) 134 { 135 if (type.GetTypeInfo().IsPrimitive) // This is hopefully the common case. All types that return true for this are value types w/out embedded references. 136 return false; 137 138 if (!type.GetTypeInfo().IsValueType) 139 return true; 140 141 // If type is a Nullable<> of something, unwrap it first. 142 Type underlyingNullable = Nullable.GetUnderlyingType(type); 143 if (underlyingNullable != null) 144 type = underlyingNullable; 145 146 if (type.GetTypeInfo().IsEnum) 147 return false; 148 149 foreach (FieldInfo field in type.GetTypeInfo().DeclaredFields) 150 { 151 if (field.IsStatic) 152 continue; 153 if (IsReferenceOrContainsReferencesCore(field.FieldType)) 154 return true; 155 } 156 return false; 157 } 158 159 public static class PerTypeValues<T> 160 { 161 // 162 // Latch to ensure that excruciatingly expensive validation check for constructing a Span around a raw pointer is done 163 // only once per type. 164 // 165 public static readonly bool IsReferenceOrContainsReferences = IsReferenceOrContainsReferencesCore(typeof(T)); 166 167 public static readonly T[] EmptyArray = new T[0]; 168 169 public static readonly IntPtr ArrayAdjustment = MeasureArrayAdjustment(); 170 171 // Array header sizes are a runtime implementation detail and aren't the same across all runtimes. (The CLR made a tweak after 4.5, and Mono has an extra Bounds pointer.) MeasureArrayAdjustment()172 private static IntPtr MeasureArrayAdjustment() 173 { 174 T[] sampleArray = new T[1]; 175 return Unsafe.ByteOffset<T>(ref Unsafe.As<Pinnable<T>>(sampleArray).Data, ref sampleArray[0]); 176 } 177 } 178 } 179 } 180