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