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}