Buffer.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  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. #if AMD64 || ARM64 || (BIT32 && !ARM)
  5. #define HAS_CUSTOM_BLOCKS
  6. #endif
  7. using System.Diagnostics;
  8. using System.Runtime;
  9. using System.Runtime.CompilerServices;
  10. using System.Runtime.InteropServices;
  11. using Internal.Runtime.CompilerServices;
  12. #pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
  13. #if BIT64
  14. using nint = System.Int64;
  15. using nuint = System.UInt64;
  16. #else
  17. using nint = System.Int32;
  18. using nuint = System.UInt32;
  19. #endif
  20. namespace System
  21. {
  22. public static partial class Buffer
  23. {
  24. // Copies from one primitive array to another primitive array without
  25. // respecting types. This calls memmove internally. The count and
  26. // offset parameters here are in bytes. If you want to use traditional
  27. // array element indices and counts, use Array.Copy.
  28. public static unsafe void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count)
  29. {
  30. if (src == null)
  31. throw new ArgumentNullException(nameof(src));
  32. if (dst == null)
  33. throw new ArgumentNullException(nameof(dst));
  34. nuint uSrcLen = (nuint)src.LongLength;
  35. if (src.GetType() != typeof(byte[]))
  36. {
  37. if (!src.GetCorElementTypeOfElementType().IsPrimitiveType())
  38. throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(src));
  39. uSrcLen *= (nuint)src.GetElementSize();
  40. }
  41. nuint uDstLen = uSrcLen;
  42. if (src != dst)
  43. {
  44. uDstLen = (nuint)dst.LongLength;
  45. if (dst.GetType() != typeof(byte[]))
  46. {
  47. if (!dst.GetCorElementTypeOfElementType().IsPrimitiveType())
  48. throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(dst));
  49. uDstLen *= (nuint)dst.GetElementSize();
  50. }
  51. }
  52. if (srcOffset < 0)
  53. throw new ArgumentOutOfRangeException(nameof(srcOffset), SR.ArgumentOutOfRange_MustBeNonNegInt32);
  54. if (dstOffset < 0)
  55. throw new ArgumentOutOfRangeException(nameof(dstOffset), SR.ArgumentOutOfRange_MustBeNonNegInt32);
  56. if (count < 0)
  57. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_MustBeNonNegInt32);
  58. nuint uCount = (nuint)count;
  59. nuint uSrcOffset = (nuint)srcOffset;
  60. nuint uDstOffset = (nuint)dstOffset;
  61. if ((uSrcLen < uSrcOffset + uCount) || (uDstLen < uDstOffset + uCount))
  62. throw new ArgumentException(SR.Argument_InvalidOffLen);
  63. Memmove(ref Unsafe.AddByteOffset(ref dst.GetRawArrayData(), uDstOffset), ref Unsafe.AddByteOffset(ref src.GetRawArrayData(), uSrcOffset), uCount);
  64. }
  65. public static int ByteLength(Array array)
  66. {
  67. // Is the array present?
  68. if (array == null)
  69. throw new ArgumentNullException(nameof(array));
  70. // Is it of primitive types?
  71. if (!array.GetCorElementTypeOfElementType().IsPrimitiveType())
  72. throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
  73. nuint byteLength = (nuint)array.LongLength * (nuint)array.GetElementSize();
  74. // This API is explosed both as Buffer.ByteLength and also used indirectly in argument
  75. // checks for Buffer.GetByte/SetByte.
  76. //
  77. // If somebody called Get/SetByte on 2GB+ arrays, there is a decent chance that
  78. // the computation of the index has overflowed. Thus we intentionally always
  79. // throw on 2GB+ arrays in Get/SetByte argument checks (even for indicies <2GB)
  80. // to prevent people from running into a trap silently.
  81. return checked((int)byteLength);
  82. }
  83. public static byte GetByte(Array array, int index)
  84. {
  85. // array argument validation done via ByteLength
  86. if ((uint)index >= (uint)ByteLength(array))
  87. throw new ArgumentOutOfRangeException(nameof(index));
  88. return Unsafe.Add<byte>(ref array.GetRawArrayData(), index);
  89. }
  90. public static void SetByte(Array array, int index, byte value)
  91. {
  92. // array argument validation done via ByteLength
  93. if ((uint)index >= (uint)ByteLength(array))
  94. throw new ArgumentOutOfRangeException(nameof(index));
  95. Unsafe.Add<byte>(ref array.GetRawArrayData(), index) = value;
  96. }
  97. // This method has different signature for x64 and other platforms and is done for performance reasons.
  98. internal static unsafe void ZeroMemory(byte* dest, nuint len)
  99. {
  100. SpanHelpers.ClearWithoutReferences(ref *dest, len);
  101. }
  102. // The attributes on this method are chosen for best JIT performance.
  103. // Please do not edit unless intentional.
  104. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  105. [CLSCompliant(false)]
  106. public static unsafe void MemoryCopy(void* source, void* destination, long destinationSizeInBytes, long sourceBytesToCopy)
  107. {
  108. if (sourceBytesToCopy > destinationSizeInBytes)
  109. {
  110. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
  111. }
  112. Memmove((byte*)destination, (byte*)source, checked((nuint)sourceBytesToCopy));
  113. }
  114. // The attributes on this method are chosen for best JIT performance.
  115. // Please do not edit unless intentional.
  116. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  117. [CLSCompliant(false)]
  118. public static unsafe void MemoryCopy(void* source, void* destination, ulong destinationSizeInBytes, ulong sourceBytesToCopy)
  119. {
  120. if (sourceBytesToCopy > destinationSizeInBytes)
  121. {
  122. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
  123. }
  124. Memmove((byte*)destination, (byte*)source, checked((nuint)sourceBytesToCopy));
  125. }
  126. // This method has different signature for x64 and other platforms and is done for performance reasons.
  127. internal static unsafe void Memmove(byte* dest, byte* src, nuint len)
  128. {
  129. // P/Invoke into the native version when the buffers are overlapping.
  130. if (((nuint)dest - (nuint)src < len) || ((nuint)src - (nuint)dest < len))
  131. {
  132. goto PInvoke;
  133. }
  134. byte* srcEnd = src + len;
  135. byte* destEnd = dest + len;
  136. if (len <= 16) goto MCPY02;
  137. if (len > 64) goto MCPY05;
  138. MCPY00:
  139. // Copy bytes which are multiples of 16 and leave the remainder for MCPY01 to handle.
  140. Debug.Assert(len > 16 && len <= 64);
  141. #if HAS_CUSTOM_BLOCKS
  142. *(Block16*)dest = *(Block16*)src; // [0,16]
  143. #elif BIT64
  144. *(long*)dest = *(long*)src;
  145. *(long*)(dest + 8) = *(long*)(src + 8); // [0,16]
  146. #else
  147. *(int*)dest = *(int*)src;
  148. *(int*)(dest + 4) = *(int*)(src + 4);
  149. *(int*)(dest + 8) = *(int*)(src + 8);
  150. *(int*)(dest + 12) = *(int*)(src + 12); // [0,16]
  151. #endif
  152. if (len <= 32) goto MCPY01;
  153. #if HAS_CUSTOM_BLOCKS
  154. *(Block16*)(dest + 16) = *(Block16*)(src + 16); // [0,32]
  155. #elif BIT64
  156. *(long*)(dest + 16) = *(long*)(src + 16);
  157. *(long*)(dest + 24) = *(long*)(src + 24); // [0,32]
  158. #else
  159. *(int*)(dest + 16) = *(int*)(src + 16);
  160. *(int*)(dest + 20) = *(int*)(src + 20);
  161. *(int*)(dest + 24) = *(int*)(src + 24);
  162. *(int*)(dest + 28) = *(int*)(src + 28); // [0,32]
  163. #endif
  164. if (len <= 48) goto MCPY01;
  165. #if HAS_CUSTOM_BLOCKS
  166. *(Block16*)(dest + 32) = *(Block16*)(src + 32); // [0,48]
  167. #elif BIT64
  168. *(long*)(dest + 32) = *(long*)(src + 32);
  169. *(long*)(dest + 40) = *(long*)(src + 40); // [0,48]
  170. #else
  171. *(int*)(dest + 32) = *(int*)(src + 32);
  172. *(int*)(dest + 36) = *(int*)(src + 36);
  173. *(int*)(dest + 40) = *(int*)(src + 40);
  174. *(int*)(dest + 44) = *(int*)(src + 44); // [0,48]
  175. #endif
  176. MCPY01:
  177. // Unconditionally copy the last 16 bytes using destEnd and srcEnd and return.
  178. Debug.Assert(len > 16 && len <= 64);
  179. #if HAS_CUSTOM_BLOCKS
  180. *(Block16*)(destEnd - 16) = *(Block16*)(srcEnd - 16);
  181. #elif BIT64
  182. *(long*)(destEnd - 16) = *(long*)(srcEnd - 16);
  183. *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
  184. #else
  185. *(int*)(destEnd - 16) = *(int*)(srcEnd - 16);
  186. *(int*)(destEnd - 12) = *(int*)(srcEnd - 12);
  187. *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
  188. *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
  189. #endif
  190. return;
  191. MCPY02:
  192. // Copy the first 8 bytes and then unconditionally copy the last 8 bytes and return.
  193. if ((len & 24) == 0) goto MCPY03;
  194. Debug.Assert(len >= 8 && len <= 16);
  195. #if BIT64
  196. *(long*)dest = *(long*)src;
  197. *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
  198. #else
  199. *(int*)dest = *(int*)src;
  200. *(int*)(dest + 4) = *(int*)(src + 4);
  201. *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
  202. *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
  203. #endif
  204. return;
  205. MCPY03:
  206. // Copy the first 4 bytes and then unconditionally copy the last 4 bytes and return.
  207. if ((len & 4) == 0) goto MCPY04;
  208. Debug.Assert(len >= 4 && len < 8);
  209. *(int*)dest = *(int*)src;
  210. *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
  211. return;
  212. MCPY04:
  213. // Copy the first byte. For pending bytes, do an unconditionally copy of the last 2 bytes and return.
  214. Debug.Assert(len < 4);
  215. if (len == 0) return;
  216. *dest = *src;
  217. if ((len & 2) == 0) return;
  218. *(short*)(destEnd - 2) = *(short*)(srcEnd - 2);
  219. return;
  220. MCPY05:
  221. // PInvoke to the native version when the copy length exceeds the threshold.
  222. if (len > MemmoveNativeThreshold)
  223. {
  224. goto PInvoke;
  225. }
  226. // Copy 64-bytes at a time until the remainder is less than 64.
  227. // If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 bytes and return.
  228. Debug.Assert(len > 64 && len <= MemmoveNativeThreshold);
  229. nuint n = len >> 6;
  230. MCPY06:
  231. #if HAS_CUSTOM_BLOCKS
  232. *(Block64*)dest = *(Block64*)src;
  233. #elif BIT64
  234. *(long*)dest = *(long*)src;
  235. *(long*)(dest + 8) = *(long*)(src + 8);
  236. *(long*)(dest + 16) = *(long*)(src + 16);
  237. *(long*)(dest + 24) = *(long*)(src + 24);
  238. *(long*)(dest + 32) = *(long*)(src + 32);
  239. *(long*)(dest + 40) = *(long*)(src + 40);
  240. *(long*)(dest + 48) = *(long*)(src + 48);
  241. *(long*)(dest + 56) = *(long*)(src + 56);
  242. #else
  243. *(int*)dest = *(int*)src;
  244. *(int*)(dest + 4) = *(int*)(src + 4);
  245. *(int*)(dest + 8) = *(int*)(src + 8);
  246. *(int*)(dest + 12) = *(int*)(src + 12);
  247. *(int*)(dest + 16) = *(int*)(src + 16);
  248. *(int*)(dest + 20) = *(int*)(src + 20);
  249. *(int*)(dest + 24) = *(int*)(src + 24);
  250. *(int*)(dest + 28) = *(int*)(src + 28);
  251. *(int*)(dest + 32) = *(int*)(src + 32);
  252. *(int*)(dest + 36) = *(int*)(src + 36);
  253. *(int*)(dest + 40) = *(int*)(src + 40);
  254. *(int*)(dest + 44) = *(int*)(src + 44);
  255. *(int*)(dest + 48) = *(int*)(src + 48);
  256. *(int*)(dest + 52) = *(int*)(src + 52);
  257. *(int*)(dest + 56) = *(int*)(src + 56);
  258. *(int*)(dest + 60) = *(int*)(src + 60);
  259. #endif
  260. dest += 64;
  261. src += 64;
  262. n--;
  263. if (n != 0) goto MCPY06;
  264. len %= 64;
  265. if (len > 16) goto MCPY00;
  266. #if HAS_CUSTOM_BLOCKS
  267. *(Block16*)(destEnd - 16) = *(Block16*)(srcEnd - 16);
  268. #elif BIT64
  269. *(long*)(destEnd - 16) = *(long*)(srcEnd - 16);
  270. *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
  271. #else
  272. *(int*)(destEnd - 16) = *(int*)(srcEnd - 16);
  273. *(int*)(destEnd - 12) = *(int*)(srcEnd - 12);
  274. *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
  275. *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
  276. #endif
  277. return;
  278. PInvoke:
  279. _Memmove(dest, src, len);
  280. }
  281. // This method has different signature for x64 and other platforms and is done for performance reasons.
  282. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  283. internal static void Memmove<T>(ref T destination, ref T source, nuint elementCount)
  284. {
  285. if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
  286. {
  287. // Blittable memmove
  288. Memmove(
  289. ref Unsafe.As<T, byte>(ref destination),
  290. ref Unsafe.As<T, byte>(ref source),
  291. elementCount * (nuint)Unsafe.SizeOf<T>());
  292. }
  293. else
  294. {
  295. // Non-blittable memmove
  296. BulkMoveWithWriteBarrier(
  297. ref Unsafe.As<T, byte>(ref destination),
  298. ref Unsafe.As<T, byte>(ref source),
  299. elementCount * (nuint)Unsafe.SizeOf<T>());
  300. }
  301. }
  302. // This method has different signature for x64 and other platforms and is done for performance reasons.
  303. private static void Memmove(ref byte dest, ref byte src, nuint len)
  304. {
  305. // P/Invoke into the native version when the buffers are overlapping.
  306. if (((nuint)Unsafe.ByteOffset(ref src, ref dest) < len) || ((nuint)Unsafe.ByteOffset(ref dest, ref src) < len))
  307. {
  308. goto BuffersOverlap;
  309. }
  310. // Use "(IntPtr)(nint)len" to avoid overflow checking on the explicit cast to IntPtr
  311. ref byte srcEnd = ref Unsafe.Add(ref src, (IntPtr)(nint)len);
  312. ref byte destEnd = ref Unsafe.Add(ref dest, (IntPtr)(nint)len);
  313. if (len <= 16)
  314. goto MCPY02;
  315. if (len > 64)
  316. goto MCPY05;
  317. MCPY00:
  318. // Copy bytes which are multiples of 16 and leave the remainder for MCPY01 to handle.
  319. Debug.Assert(len > 16 && len <= 64);
  320. #if HAS_CUSTOM_BLOCKS
  321. Unsafe.As<byte, Block16>(ref dest) = Unsafe.As<byte, Block16>(ref src); // [0,16]
  322. #elif BIT64
  323. Unsafe.As<byte, long>(ref dest) = Unsafe.As<byte, long>(ref src);
  324. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 8)); // [0,16]
  325. #else
  326. Unsafe.As<byte, int>(ref dest) = Unsafe.As<byte, int>(ref src);
  327. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 4));
  328. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 8));
  329. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 12)); // [0,16]
  330. #endif
  331. if (len <= 32)
  332. goto MCPY01;
  333. #if HAS_CUSTOM_BLOCKS
  334. Unsafe.As<byte, Block16>(ref Unsafe.Add(ref dest, 16)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref src, 16)); // [0,32]
  335. #elif BIT64
  336. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 16));
  337. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 24)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 24)); // [0,32]
  338. #else
  339. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 16));
  340. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 20)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 20));
  341. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 24)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 24));
  342. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 28)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 28)); // [0,32]
  343. #endif
  344. if (len <= 48)
  345. goto MCPY01;
  346. #if HAS_CUSTOM_BLOCKS
  347. Unsafe.As<byte, Block16>(ref Unsafe.Add(ref dest, 32)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref src, 32)); // [0,48]
  348. #elif BIT64
  349. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 32)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 32));
  350. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 40)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 40)); // [0,48]
  351. #else
  352. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 32)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 32));
  353. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 36)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 36));
  354. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 40)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 40));
  355. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 44)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 44)); // [0,48]
  356. #endif
  357. MCPY01:
  358. // Unconditionally copy the last 16 bytes using destEnd and srcEnd and return.
  359. Debug.Assert(len > 16 && len <= 64);
  360. #if HAS_CUSTOM_BLOCKS
  361. Unsafe.As<byte, Block16>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref srcEnd, -16));
  362. #elif BIT64
  363. Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -16));
  364. Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -8));
  365. #else
  366. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -16));
  367. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -12));
  368. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -8));
  369. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
  370. #endif
  371. return;
  372. MCPY02:
  373. // Copy the first 8 bytes and then unconditionally copy the last 8 bytes and return.
  374. if ((len & 24) == 0)
  375. goto MCPY03;
  376. Debug.Assert(len >= 8 && len <= 16);
  377. #if BIT64
  378. Unsafe.As<byte, long>(ref dest) = Unsafe.As<byte, long>(ref src);
  379. Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -8));
  380. #else
  381. Unsafe.As<byte, int>(ref dest) = Unsafe.As<byte, int>(ref src);
  382. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 4));
  383. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -8));
  384. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
  385. #endif
  386. return;
  387. MCPY03:
  388. // Copy the first 4 bytes and then unconditionally copy the last 4 bytes and return.
  389. if ((len & 4) == 0)
  390. goto MCPY04;
  391. Debug.Assert(len >= 4 && len < 8);
  392. Unsafe.As<byte, int>(ref dest) = Unsafe.As<byte, int>(ref src);
  393. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
  394. return;
  395. MCPY04:
  396. // Copy the first byte. For pending bytes, do an unconditionally copy of the last 2 bytes and return.
  397. Debug.Assert(len < 4);
  398. if (len == 0)
  399. return;
  400. dest = src;
  401. if ((len & 2) == 0)
  402. return;
  403. Unsafe.As<byte, short>(ref Unsafe.Add(ref destEnd, -2)) = Unsafe.As<byte, short>(ref Unsafe.Add(ref srcEnd, -2));
  404. return;
  405. MCPY05:
  406. // PInvoke to the native version when the copy length exceeds the threshold.
  407. if (len > MemmoveNativeThreshold)
  408. {
  409. goto PInvoke;
  410. }
  411. // Copy 64-bytes at a time until the remainder is less than 64.
  412. // If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 bytes and return.
  413. Debug.Assert(len > 64 && len <= MemmoveNativeThreshold);
  414. nuint n = len >> 6;
  415. MCPY06:
  416. #if HAS_CUSTOM_BLOCKS
  417. Unsafe.As<byte, Block64>(ref dest) = Unsafe.As<byte, Block64>(ref src);
  418. #elif BIT64
  419. Unsafe.As<byte, long>(ref dest) = Unsafe.As<byte, long>(ref src);
  420. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 8));
  421. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 16));
  422. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 24)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 24));
  423. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 32)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 32));
  424. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 40)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 40));
  425. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 48)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 48));
  426. Unsafe.As<byte, long>(ref Unsafe.Add(ref dest, 56)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src, 56));
  427. #else
  428. Unsafe.As<byte, int>(ref dest) = Unsafe.As<byte, int>(ref src);
  429. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 4));
  430. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 8));
  431. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 12));
  432. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 16));
  433. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 20)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 20));
  434. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 24)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 24));
  435. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 28)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 28));
  436. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 32)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 32));
  437. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 36)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 36));
  438. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 40)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 40));
  439. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 44)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 44));
  440. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 48)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 48));
  441. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 52)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 52));
  442. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 56)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 56));
  443. Unsafe.As<byte, int>(ref Unsafe.Add(ref dest, 60)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src, 60));
  444. #endif
  445. dest = ref Unsafe.Add(ref dest, 64);
  446. src = ref Unsafe.Add(ref src, 64);
  447. n--;
  448. if (n != 0)
  449. goto MCPY06;
  450. len %= 64;
  451. if (len > 16)
  452. goto MCPY00;
  453. #if HAS_CUSTOM_BLOCKS
  454. Unsafe.As<byte, Block16>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref srcEnd, -16));
  455. #elif BIT64
  456. Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -16));
  457. Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -8));
  458. #else
  459. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -16));
  460. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -12));
  461. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -8));
  462. Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
  463. #endif
  464. return;
  465. BuffersOverlap:
  466. // If the buffers overlap perfectly, there's no point to copying the data.
  467. if (Unsafe.AreSame(ref dest, ref src))
  468. {
  469. return;
  470. }
  471. PInvoke:
  472. _Memmove(ref dest, ref src, len);
  473. }
  474. // Non-inlinable wrapper around the QCall that avoids polluting the fast path
  475. // with P/Invoke prolog/epilog.
  476. [MethodImpl(MethodImplOptions.NoInlining)]
  477. private static unsafe void _Memmove(byte* dest, byte* src, nuint len)
  478. {
  479. __Memmove(dest, src, len);
  480. }
  481. // Non-inlinable wrapper around the QCall that avoids polluting the fast path
  482. // with P/Invoke prolog/epilog.
  483. [MethodImpl(MethodImplOptions.NoInlining)]
  484. private static unsafe void _Memmove(ref byte dest, ref byte src, nuint len)
  485. {
  486. fixed (byte* pDest = &dest)
  487. fixed (byte* pSrc = &src)
  488. __Memmove(pDest, pSrc, len);
  489. }
  490. #if HAS_CUSTOM_BLOCKS
  491. [StructLayout(LayoutKind.Sequential, Size = 16)]
  492. private struct Block16 { }
  493. [StructLayout(LayoutKind.Sequential, Size = 64)]
  494. private struct Block64 { }
  495. #endif // HAS_CUSTOM_BLOCKS
  496. }
  497. }