using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Unity.Mathematics;
namespace Unity.Collections.LowLevel.Unsafe
{
///
/// Provides utility methods for unsafe, untyped buffers.
///
[GenerateTestsForBurstCompatibility]
public unsafe static class UnsafeUtilityExtensions
{
///
/// Swaps bytes between two buffers.
///
/// A buffer.
/// Another buffer.
/// The number of bytes to swap.
/// Thrown if the two ranges of bytes to swap overlap in memory.
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;
}
}
///
/// Reads an element from a buffer after bounds checking.
///
/// The type of element.
/// The buffer to read from.
/// The index of the element.
/// The buffer capacity (in number of elements). Used for the bounds checking.
/// The element read from the buffer.
/// Thrown if the index is out of bounds.
[GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
public unsafe static T ReadArrayElementBoundsChecked(void* source, int index, int capacity)
where T : unmanaged
{
CheckIndexRange(index, capacity);
return UnsafeUtility.ReadArrayElement(source, index);
}
///
/// Writes an element to a buffer after bounds checking.
///
/// The type of element.
/// The buffer to write to.
/// The value to write.
/// The index at which to store the element.
/// The buffer capacity (in number of elements). Used for the bounds checking.
/// Thrown if the index is out of bounds.
[GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
public unsafe static void WriteArrayElementBoundsChecked(void* destination, int index, T value, int capacity)
where T : unmanaged
{
CheckIndexRange(index, capacity);
UnsafeUtility.WriteArrayElement(destination, index, value);
}
///
/// Returns the address of a read-only reference.
///
/// The type of referenced value.
/// A read-only reference.
/// A pointer to the referenced value.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
public static void* AddressOf(in T value)
where T : unmanaged
{
return ILSupport.AddressOf(in value);
}
///
/// Returns a read-write reference from a read-only reference.
/// 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.
///
/// The type of referenced value.
/// A read-only reference.
/// A read-write reference to the value referenced by `item`.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
public static ref T AsRef(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."
);
}
}
}
}