131 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			131 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | ||
|  | using System.Diagnostics; | ||
|  | using System.Runtime.CompilerServices; | ||
|  | using Unity.Mathematics; | ||
|  | 
 | ||
|  | namespace Unity.Collections.LowLevel.Unsafe | ||
|  | { | ||
|  |     /// <summary> | ||
|  |     /// Provides utility methods for unsafe, untyped buffers. | ||
|  |     /// </summary> | ||
|  |     [GenerateTestsForBurstCompatibility] | ||
|  |     public unsafe static class UnsafeUtilityExtensions | ||
|  |     { | ||
|  |         /// <summary> | ||
|  |         /// Swaps bytes between two buffers. | ||
|  |         /// </summary> | ||
|  |         /// <param name="ptr">A buffer.</param> | ||
|  |         /// <param name="otherPtr">Another buffer.</param> | ||
|  |         /// <param name="size">The number of bytes to swap.</param> | ||
|  |         /// <exception cref="System.InvalidOperationException">Thrown if the two ranges of bytes to swap overlap in memory.</exception> | ||
|  |         internal static void MemSwap(void* ptr, void* otherPtr, long size) | ||
|  |         { | ||
|  |             byte* dst = (byte*) ptr; | ||
|  |             byte* src = (byte*) otherPtr; | ||
|  | 
 | ||
|  |             CheckMemSwapOverlap(dst, src, size); | ||
|  | 
 | ||
|  |             var tmp = stackalloc byte[1024]; | ||
|  | 
 | ||
|  |             while (size > 0) | ||
|  |             { | ||
|  |                 var numBytes = math.min(size, 1024); | ||
|  |                 UnsafeUtility.MemCpy(tmp, dst, numBytes); | ||
|  |                 UnsafeUtility.MemCpy(dst, src, numBytes); | ||
|  |                 UnsafeUtility.MemCpy(src, tmp, numBytes); | ||
|  | 
 | ||
|  |                 size -= numBytes; | ||
|  |                 src += numBytes; | ||
|  |                 dst += numBytes; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Reads an element from a buffer after bounds checking. | ||
|  |         /// </summary> | ||
|  |         /// <typeparam name="T">The type of element.</typeparam> | ||
|  |         /// <param name="source">The buffer to read from.</param> | ||
|  |         /// <param name="index">The index of the element.</param> | ||
|  |         /// <param name="capacity">The buffer capacity (in number of elements). Used for the bounds checking.</param> | ||
|  |         /// <returns>The element read from the buffer.</returns> | ||
|  |         /// <exception cref="IndexOutOfRangeException">Thrown if the index is out of bounds.</exception> | ||
|  |         [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })] | ||
|  |         public unsafe static T ReadArrayElementBoundsChecked<T>(void* source, int index, int capacity) | ||
|  |             where T : unmanaged | ||
|  |         { | ||
|  |             CheckIndexRange(index, capacity); | ||
|  | 
 | ||
|  |             return UnsafeUtility.ReadArrayElement<T>(source, index); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes an element to a buffer after bounds checking. | ||
|  |         /// </summary> | ||
|  |         /// <typeparam name="T">The type of element.</typeparam> | ||
|  |         /// <param name="destination">The buffer to write to.</param> | ||
|  |         /// <param name="value">The value to write.</param> | ||
|  |         /// <param name="index">The index at which to store the element.</param> | ||
|  |         /// <param name="capacity">The buffer capacity (in number of elements). Used for the bounds checking.</param> | ||
|  |         /// <exception cref="IndexOutOfRangeException">Thrown if the index is out of bounds.</exception> | ||
|  |         [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })] | ||
|  |         public unsafe static void WriteArrayElementBoundsChecked<T>(void* destination, int index, T value, int capacity) | ||
|  |             where T : unmanaged | ||
|  |         { | ||
|  |             CheckIndexRange(index, capacity); | ||
|  | 
 | ||
|  |             UnsafeUtility.WriteArrayElement<T>(destination, index, value); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Returns the address of a read-only reference. | ||
|  |         /// </summary> | ||
|  |         /// <typeparam name="T">The type of referenced value.</typeparam> | ||
|  |         /// <param name="value">A read-only reference.</param> | ||
|  |         /// <returns>A pointer to the referenced value.</returns> | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })] | ||
|  |         public static void* AddressOf<T>(in T value) | ||
|  |             where T : unmanaged | ||
|  |         { | ||
|  |             return ILSupport.AddressOf(in value); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Returns a read-write reference from a read-only reference. | ||
|  |         /// <remarks>Useful when you want to pass an `in` arg (read-only reference) where a `ref` arg (read-write reference) is expected. | ||
|  |         /// Do not mutate the referenced value, as doing so may break the runtime's assumptions.</remarks> | ||
|  |         /// </summary> | ||
|  |         /// <typeparam name="T">The type of referenced value.</typeparam> | ||
|  |         /// <param name="value">A read-only reference.</param> | ||
|  |         /// <returns>A read-write reference to the value referenced by `item`.</returns> | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })] | ||
|  |         public static ref T AsRef<T>(in T value) | ||
|  |             where T : unmanaged | ||
|  |         { | ||
|  |             return ref ILSupport.AsRef(in value); | ||
|  |         } | ||
|  | 
 | ||
|  |         [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] | ||
|  |         static unsafe void CheckMemSwapOverlap(byte* dst, byte* src, long size) | ||
|  |         { | ||
|  |             if (dst + size > src && src + size > dst) | ||
|  |             { | ||
|  |                 throw new InvalidOperationException("MemSwap memory blocks are overlapped."); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] | ||
|  |         static void CheckIndexRange(int index, int capacity) | ||
|  |         { | ||
|  |             if ((index > capacity - 1) || (index < 0)) | ||
|  |             { | ||
|  |                 throw new IndexOutOfRangeException( | ||
|  |                     $"Attempt to read or write from array index {index}, which is out of bounds. Array capacity is {capacity}. " | ||
|  |                     +"This may lead to a crash, data corruption, or reading invalid data." | ||
|  |                     ); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |