A game about forced loneliness, made by TACStudios
1using System;
2using System.Diagnostics;
3using System.Runtime.CompilerServices;
4using Unity.Mathematics;
5
6namespace Unity.Collections.LowLevel.Unsafe
7{
8 /// <summary>
9 /// Provides utility methods for unsafe, untyped buffers.
10 /// </summary>
11 [GenerateTestsForBurstCompatibility]
12 public unsafe static class UnsafeUtilityExtensions
13 {
14 /// <summary>
15 /// Swaps bytes between two buffers.
16 /// </summary>
17 /// <param name="ptr">A buffer.</param>
18 /// <param name="otherPtr">Another buffer.</param>
19 /// <param name="size">The number of bytes to swap.</param>
20 /// <exception cref="System.InvalidOperationException">Thrown if the two ranges of bytes to swap overlap in memory.</exception>
21 internal static void MemSwap(void* ptr, void* otherPtr, long size)
22 {
23 byte* dst = (byte*) ptr;
24 byte* src = (byte*) otherPtr;
25
26 CheckMemSwapOverlap(dst, src, size);
27
28 var tmp = stackalloc byte[1024];
29
30 while (size > 0)
31 {
32 var numBytes = math.min(size, 1024);
33 UnsafeUtility.MemCpy(tmp, dst, numBytes);
34 UnsafeUtility.MemCpy(dst, src, numBytes);
35 UnsafeUtility.MemCpy(src, tmp, numBytes);
36
37 size -= numBytes;
38 src += numBytes;
39 dst += numBytes;
40 }
41 }
42
43 /// <summary>
44 /// Reads an element from a buffer after bounds checking.
45 /// </summary>
46 /// <typeparam name="T">The type of element.</typeparam>
47 /// <param name="source">The buffer to read from.</param>
48 /// <param name="index">The index of the element.</param>
49 /// <param name="capacity">The buffer capacity (in number of elements). Used for the bounds checking.</param>
50 /// <returns>The element read from the buffer.</returns>
51 /// <exception cref="IndexOutOfRangeException">Thrown if the index is out of bounds.</exception>
52 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
53 public unsafe static T ReadArrayElementBoundsChecked<T>(void* source, int index, int capacity)
54 where T : unmanaged
55 {
56 CheckIndexRange(index, capacity);
57
58 return UnsafeUtility.ReadArrayElement<T>(source, index);
59 }
60
61 /// <summary>
62 /// Writes an element to a buffer after bounds checking.
63 /// </summary>
64 /// <typeparam name="T">The type of element.</typeparam>
65 /// <param name="destination">The buffer to write to.</param>
66 /// <param name="value">The value to write.</param>
67 /// <param name="index">The index at which to store the element.</param>
68 /// <param name="capacity">The buffer capacity (in number of elements). Used for the bounds checking.</param>
69 /// <exception cref="IndexOutOfRangeException">Thrown if the index is out of bounds.</exception>
70 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
71 public unsafe static void WriteArrayElementBoundsChecked<T>(void* destination, int index, T value, int capacity)
72 where T : unmanaged
73 {
74 CheckIndexRange(index, capacity);
75
76 UnsafeUtility.WriteArrayElement<T>(destination, index, value);
77 }
78
79 /// <summary>
80 /// Returns the address of a read-only reference.
81 /// </summary>
82 /// <typeparam name="T">The type of referenced value.</typeparam>
83 /// <param name="value">A read-only reference.</param>
84 /// <returns>A pointer to the referenced value.</returns>
85 [MethodImpl(MethodImplOptions.AggressiveInlining)]
86 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
87 public static void* AddressOf<T>(in T value)
88 where T : unmanaged
89 {
90 return ILSupport.AddressOf(in value);
91 }
92
93 /// <summary>
94 /// Returns a read-write reference from a read-only reference.
95 /// <remarks>Useful when you want to pass an `in` arg (read-only reference) where a `ref` arg (read-write reference) is expected.
96 /// Do not mutate the referenced value, as doing so may break the runtime's assumptions.</remarks>
97 /// </summary>
98 /// <typeparam name="T">The type of referenced value.</typeparam>
99 /// <param name="value">A read-only reference.</param>
100 /// <returns>A read-write reference to the value referenced by `item`.</returns>
101 [MethodImpl(MethodImplOptions.AggressiveInlining)]
102 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
103 public static ref T AsRef<T>(in T value)
104 where T : unmanaged
105 {
106 return ref ILSupport.AsRef(in value);
107 }
108
109 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
110 static unsafe void CheckMemSwapOverlap(byte* dst, byte* src, long size)
111 {
112 if (dst + size > src && src + size > dst)
113 {
114 throw new InvalidOperationException("MemSwap memory blocks are overlapped.");
115 }
116 }
117
118 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
119 static void CheckIndexRange(int index, int capacity)
120 {
121 if ((index > capacity - 1) || (index < 0))
122 {
123 throw new IndexOutOfRangeException(
124 $"Attempt to read or write from array index {index}, which is out of bounds. Array capacity is {capacity}. "
125 +"This may lead to a crash, data corruption, or reading invalid data."
126 );
127 }
128 }
129 }
130}