A game about forced loneliness, made by TACStudios
1using System;
2using Unity.Collections;
3using System.Diagnostics;
4using System.Runtime.InteropServices;
5using Unity.Collections.LowLevel.Unsafe;
6
7namespace UnityEngine.U2D.Common.UTess
8{
9
10 [StructLayout(LayoutKind.Sequential)]
11 [DebuggerDisplay("Length = {Length}")]
12 [DebuggerTypeProxy(typeof(ArraySliceDebugView<>))]
13 internal unsafe struct ArraySlice<T> : System.IEquatable<ArraySlice<T>> where T : struct
14 {
15 [NativeDisableUnsafePtrRestriction] internal byte* m_Buffer;
16 internal int m_Stride;
17 internal int m_Length;
18
19#if ENABLE_UNITY_COLLECTIONS_CHECKS
20 internal int m_MinIndex;
21 internal int m_MaxIndex;
22 internal AtomicSafetyHandle m_Safety;
23#endif
24
25 public ArraySlice(NativeArray<T> array, int start, int length)
26 {
27#if ENABLE_UNITY_COLLECTIONS_CHECKS
28 if (start < 0)
29 throw new ArgumentOutOfRangeException(nameof(start), $"Slice start {start} < 0.");
30 if (length < 0)
31 throw new ArgumentOutOfRangeException(nameof(length), $"Slice length {length} < 0.");
32 if (start + length > array.Length)
33 throw new ArgumentException(
34 $"Slice start + length ({start + length}) range must be <= array.Length ({array.Length})");
35 m_MinIndex = 0;
36 m_MaxIndex = length - 1;
37 m_Safety = Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array);
38#endif
39
40 m_Stride = UnsafeUtility.SizeOf<T>();
41 var ptr = (byte*)array.GetUnsafePtr() + m_Stride * start;
42 m_Buffer = ptr;
43 m_Length = length;
44 }
45
46 public ArraySlice(Array<T> array, int start, int length)
47 {
48#if ENABLE_UNITY_COLLECTIONS_CHECKS
49 if (start < 0)
50 throw new ArgumentOutOfRangeException(nameof(start), $"Slice start {start} < 0.");
51 if (length < 0)
52 throw new ArgumentOutOfRangeException(nameof(length), $"Slice length {length} < 0.");
53 if (start + length > array.Length)
54 throw new ArgumentException(
55 $"Slice start + length ({start + length}) range must be <= array.Length ({array.Length})");
56 m_MinIndex = 0;
57 m_MaxIndex = length - 1;
58 m_Safety = Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array.m_Array);
59#endif
60
61 m_Stride = UnsafeUtility.SizeOf<T>();
62 var ptr = (byte*)array.UnsafePtr + m_Stride * start;
63 m_Buffer = ptr;
64 m_Length = length;
65 }
66
67 public bool Equals(ArraySlice<T> other)
68 {
69 return m_Buffer == other.m_Buffer && m_Stride == other.m_Stride && m_Length == other.m_Length;
70 }
71
72 public override bool Equals(object obj)
73 {
74 if (ReferenceEquals(null, obj)) return false;
75 return obj is ArraySlice<T> && Equals((ArraySlice<T>)obj);
76 }
77
78 public override int GetHashCode()
79 {
80 unchecked
81 {
82 var hashCode = (int)m_Buffer;
83 hashCode = (hashCode * 397) ^ m_Stride;
84 hashCode = (hashCode * 397) ^ m_Length;
85 return hashCode;
86 }
87 }
88
89 public static bool operator ==(ArraySlice<T> left, ArraySlice<T> right)
90 {
91 return left.Equals(right);
92 }
93
94 public static bool operator !=(ArraySlice<T> left, ArraySlice<T> right)
95 {
96 return !left.Equals(right);
97 }
98
99#if ENABLE_UNITY_COLLECTIONS_CHECKS
100 // These are double-whammy excluded to we can elide bounds checks in the Burst disassembly view
101 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
102 void CheckReadIndex(int index)
103 {
104#if ENABLE_UNITY_COLLECTIONS_CHECKS
105 if (index < m_MinIndex || index > m_MaxIndex)
106 FailOutOfRangeError(index);
107#endif
108 }
109
110 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
111 void CheckWriteIndex(int index)
112 {
113#if ENABLE_UNITY_COLLECTIONS_CHECKS
114 if (index < m_MinIndex || index > m_MaxIndex)
115 FailOutOfRangeError(index);
116#endif
117 }
118
119 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
120 private void FailOutOfRangeError(int index)
121 {
122 if (index < Length && (m_MinIndex != 0 || m_MaxIndex != Length - 1))
123 throw new System.IndexOutOfRangeException(
124 $"Index {index} is out of restricted IJobParallelFor range [{m_MinIndex}...{m_MaxIndex}] in ReadWriteBuffer.\n" +
125 "ReadWriteBuffers are restricted to only read & write the element at the job index. " +
126 "You can use double buffering strategies to avoid race conditions due to " +
127 "reading & writing in parallel to the same elements from a job.");
128
129 throw new System.IndexOutOfRangeException($"Index {index} is out of range of '{Length}' Length.");
130 }
131
132#endif
133
134 public static unsafe ArraySlice<T> ConvertExistingDataToArraySlice(void* dataPointer, int stride, int length)
135 {
136 if (length < 0)
137 throw new System.ArgumentException($"Invalid length of '{length}'. It must be greater than 0.",
138 nameof(length));
139 if (stride < 0)
140 throw new System.ArgumentException($"Invalid stride '{stride}'. It must be greater than 0.",
141 nameof(stride));
142
143 var newSlice = new ArraySlice<T>
144 {
145 m_Stride = stride,
146 m_Buffer = (byte*)dataPointer,
147 m_Length = length,
148#if ENABLE_UNITY_COLLECTIONS_CHECKS
149 m_MinIndex = 0,
150 m_MaxIndex = length - 1,
151#endif
152 };
153
154 return newSlice;
155 }
156
157 public T this[int index]
158 {
159 get
160 {
161#if ENABLE_UNITY_COLLECTIONS_CHECKS
162 CheckReadIndex(index);
163#endif
164 return UnsafeUtility.ReadArrayElementWithStride<T>(m_Buffer, index, m_Stride);
165 }
166
167 [WriteAccessRequired]
168 set
169 {
170#if ENABLE_UNITY_COLLECTIONS_CHECKS
171 CheckWriteIndex(index);
172#endif
173 UnsafeUtility.WriteArrayElementWithStride(m_Buffer, index, m_Stride, value);
174 }
175 }
176
177 internal unsafe void* GetUnsafeReadOnlyPtr()
178 {
179#if ENABLE_UNITY_COLLECTIONS_CHECKS
180 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
181#endif
182 return m_Buffer;
183 }
184
185 internal void CopyTo(T[] array)
186 {
187#if ENABLE_UNITY_COLLECTIONS_CHECKS
188 if (Length != array.Length)
189 throw new ArgumentException($"array.Length ({array.Length}) does not match the Length of this instance ({Length}).", nameof(array));
190#endif
191 unsafe
192 {
193 GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned);
194 IntPtr addr = handle.AddrOfPinnedObject();
195
196 var sizeOf = UnsafeUtility.SizeOf<T>();
197 UnsafeUtility.MemCpyStride((byte*)addr, sizeOf, this.GetUnsafeReadOnlyPtr(), Stride, sizeOf, m_Length);
198
199 handle.Free();
200 }
201 }
202
203 internal T[] ToArray()
204 {
205 var array = new T[Length];
206 CopyTo(array);
207 return array;
208 }
209
210 public int Stride => m_Stride;
211 public int Length => m_Length;
212
213 }
214
215 /// <summary>
216 /// DebuggerTypeProxy for <see cref="ArraySlice{T}"/>
217 /// </summary>
218 internal sealed class ArraySliceDebugView<T> where T : struct
219 {
220 ArraySlice<T> m_Slice;
221
222 public ArraySliceDebugView(ArraySlice<T> slice)
223 {
224 m_Slice = slice;
225 }
226
227 public T[] Items
228 {
229 get { return m_Slice.ToArray(); }
230 }
231 }
232
233}