A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections; 3using System.Collections.Generic; 4 5////REVIEW: switch to something that doesn't require the backing store to be an actual array? 6//// (maybe switch m_Array to an InlinedArray and extend InlinedArray to allow having three configs: 7//// 1. firstValue only, 2. firstValue + additionalValues, 3. everything in additionalValues) 8 9namespace UnityEngine.InputSystem.Utilities 10{ 11 /// <summary> 12 /// Read-only access to an array or to a slice of an array. 13 /// </summary> 14 /// <typeparam name="TValue">Type of values stored in the array.</typeparam> 15 /// <remarks> 16 /// The purpose of this struct is to allow exposing internal arrays directly such that no 17 /// boxing and no going through interfaces is required but at the same time not allowing 18 /// the internal arrays to be modified. 19 /// 20 /// It differs from <c>ReadOnlySpan&lt;T&gt;</c> in that it can be stored on the heap and differs 21 /// from <c>ReadOnlyCollection&lt;T&gt;</c> in that it supports slices directly without needing 22 /// an intermediate object representing the slice. 23 /// 24 /// Note that in most cases, the ReadOnlyArray instance should be treated as a <em>temporary</em>. 25 /// The actual array referred to by a ReadOnlyArray instance is usually owned and probably mutated 26 /// by another piece of code. When that code makes changes to the array, the ReadOnlyArray 27 /// instance will not get updated. 28 /// </remarks> 29 public struct ReadOnlyArray<TValue> : IReadOnlyList<TValue> 30 { 31 internal TValue[] m_Array; 32 internal int m_StartIndex; 33 internal int m_Length; 34 35 /// <summary> 36 /// Construct a read-only array covering all of the given array. 37 /// </summary> 38 /// <param name="array">Array to index.</param> 39 public ReadOnlyArray(TValue[] array) 40 { 41 m_Array = array; 42 m_StartIndex = 0; 43 m_Length = array?.Length ?? 0; 44 } 45 46 /// <summary> 47 /// Construct a read-only array that covers only the given slice of <paramref name="array"/>. 48 /// </summary> 49 /// <param name="array">Array to index.</param> 50 /// <param name="index">Index at which to start indexing <paramref name="array"/>. The given element 51 /// becomes index #0 for the read-only array.</param> 52 /// <param name="length">Length of the slice to index from <paramref name="array"/>.</param> 53 public ReadOnlyArray(TValue[] array, int index, int length) 54 { 55 m_Array = array; 56 m_StartIndex = index; 57 m_Length = length; 58 } 59 60 /// <summary> 61 /// Convert to array. 62 /// </summary> 63 /// <returns>A new array containing a copy of the contents of the read-only array.</returns> 64 public TValue[] ToArray() 65 { 66 var result = new TValue[m_Length]; 67 if (m_Length > 0) 68 Array.Copy(m_Array, m_StartIndex, result, 0, m_Length); 69 return result; 70 } 71 72 /// <summary> 73 /// Searches for the first element in the array for which the given <paramref name="predicate"/> is <c>true</c> and returns the index of that element. 74 /// </summary> 75 /// <param name="predicate">The predicate to be evaluated for each element which defines the condition for the search.</param> 76 /// <returns>Index of the first element for which <paramref name="predicate"/> is <c>true</c>x or -1 if no such element exists.</returns> 77 /// <exception cref="ArgumentNullException">If predicate is <c>null</c>.</exception> 78 /// <example> 79 /// <code> 80 /// // Searches for the first element in an integer array that is greater or equal to 5. 81 /// var haystack = new ReadOnlyArray&lt;int&gt;(new[] { 1, 2, 3, 4, 5, 6, 7 }); 82 /// var index = haystack.IndexOf((value) => value >= 5); // index == 4 83 /// </code> 84 /// </example> 85 public int IndexOf(Predicate<TValue> predicate) 86 { 87 if (predicate == null) 88 throw new ArgumentNullException(nameof(predicate)); 89 90 for (var i = 0; i < m_Length; ++i) 91 if (predicate(m_Array[m_StartIndex + i])) 92 return i; 93 94 return -1; 95 } 96 97 /// <summary> 98 /// Returns an enumerator that iterates through the read-only array. 99 /// <returns> 100 /// <see cref="ReadOnlyArray{TValue}.Enumerator"/> 101 /// An enumerator for the read-only array. 102 /// </returns> 103 /// </summary> 104 public Enumerator GetEnumerator() 105 { 106 return new Enumerator(m_Array, m_StartIndex, m_Length); 107 } 108 109 /// <inheritdoc/> 110 IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() 111 { 112 return GetEnumerator(); 113 } 114 115 /// <inheritdoc/> 116 IEnumerator IEnumerable.GetEnumerator() 117 { 118 return GetEnumerator(); 119 } 120 121 /// <summary> 122 /// Constructs a read-only array containing elements <paramref name="array"/>. 123 /// </summary> 124 /// <param name="array">An existing array containing elements to be wrapped as a read-only array.</param> 125 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "`ToXXX` message only really makes sense as static, which is not recommended for generic types.")] 126 public static implicit operator ReadOnlyArray<TValue>(TValue[] array) 127 { 128 return new ReadOnlyArray<TValue>(array); 129 } 130 131 /// <summary> 132 /// Number of elements in the array. 133 /// </summary> 134 public int Count => m_Length; 135 136 /// <summary> 137 /// Return the element at the given index. 138 /// </summary> 139 /// <param name="index">Index into the array.</param> 140 /// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is less than 0 or greater than <see cref="Count"/>.</exception> 141 /// <exception cref="InvalidOperationException"></exception> 142 public TValue this[int index] 143 { 144 get 145 { 146 if (index < 0 || index >= m_Length) 147 throw new ArgumentOutOfRangeException(nameof(index)); 148 // We allow array to be null as we are patching up ReadOnlyArrays in a separate 149 // path in several places. 150 if (m_Array == null) 151 throw new InvalidOperationException(); 152 return m_Array[m_StartIndex + index]; 153 } 154 } 155 156 /// <summary> 157 /// <see cref="ReadOnlyArray{TValue}"/> 158 /// Enumerates the elements of a read-only array. 159 /// </summary> 160 public struct Enumerator : IEnumerator<TValue> 161 { 162 private readonly TValue[] m_Array; 163 private readonly int m_IndexStart; 164 private readonly int m_IndexEnd; 165 private int m_Index; 166 167 internal Enumerator(TValue[] array, int index, int length) 168 { 169 m_Array = array; 170 m_IndexStart = index - 1; // First call to MoveNext() moves us to first valid index. 171 m_IndexEnd = index + length; 172 m_Index = m_IndexStart; 173 } 174 175 /// <inheritdoc/> 176 public void Dispose() 177 { 178 } 179 180 /// <inheritdoc/> 181 public bool MoveNext() 182 { 183 if (m_Index < m_IndexEnd) 184 ++m_Index; 185 return m_Index != m_IndexEnd; 186 } 187 188 /// <inheritdoc/> 189 public void Reset() 190 { 191 m_Index = m_IndexStart; 192 } 193 194 /// <inheritdoc/> 195 public TValue Current 196 { 197 get 198 { 199 if (m_Index == m_IndexEnd) 200 throw new InvalidOperationException("Iterated beyond end"); 201 return m_Array[m_Index]; 202 } 203 } 204 205 object IEnumerator.Current => Current; 206 } 207 } 208 209 /// <summary> 210 /// Extension methods to help with <see cref="ReadOnlyArrayExtensions"/> contents. 211 /// </summary> 212 public static class ReadOnlyArrayExtensions 213 { 214 /// <summary> 215 /// Evaluates whether <paramref name="array"/> contains an element that compares equal to <paramref name="value"/>. 216 /// </summary> 217 /// <typeparam name="TValue">The array element type.</typeparam> 218 /// <param name="array">Reference to the read-only array to be searched.</param> 219 /// <param name="value">The value to be searched for in <paramref name="array"/>.</param> 220 /// <returns><c>true</c> if <paramref name="array"/> contains <paramref name="value"/>, else <c>false</c>.</returns> 221 public static bool Contains<TValue>(this ReadOnlyArray<TValue> array, TValue value) 222 where TValue : IComparable<TValue> 223 { 224 for (var i = 0; i < array.m_Length; ++i) 225 if (array.m_Array[array.m_StartIndex + i].CompareTo(value) == 0) 226 return true; 227 return false; 228 } 229 230 /// <summary> 231 /// Evaluates whether <paramref name="array"/> contains a reference to <paramref name="value"/>. 232 /// </summary> 233 /// <typeparam name="TValue">The array element type.</typeparam> 234 /// <param name="array">Reference to the read-only array to be searched.</param> 235 /// <param name="value">The reference to be searched for in <paramref name="array"/>.</param> 236 /// <returns><c>true</c> if <paramref name="array"/> contains a reference to <paramref name="value"/>, else <c>false</c>.</returns> 237 public static bool ContainsReference<TValue>(this ReadOnlyArray<TValue> array, TValue value) 238 where TValue : class 239 { 240 return IndexOfReference(array, value) != -1; 241 } 242 243 /// <summary> 244 /// Retrieves the index of <paramref name="value"/> in <paramref name="array"/>. 245 /// </summary> 246 /// <typeparam name="TValue">The array element type.</typeparam> 247 /// <param name="array">Reference to the read-only array to be searched.</param> 248 /// <param name="value">The reference to be searched for in <paramref name="array"/>.</param> 249 /// <returns>The zero-based index of element <paramref name="value"/> in <paramref name="array"/> if such a reference could be found and -1 if it do not exist.</returns> 250 public static int IndexOfReference<TValue>(this ReadOnlyArray<TValue> array, TValue value) 251 where TValue : class 252 { 253 for (var i = 0; i < array.m_Length; ++i) 254 if (ReferenceEquals(array.m_Array[array.m_StartIndex + i], value)) 255 return i; 256 return -1; 257 } 258 259 /// <summary> 260 /// Evaluates whether whether <paramref name="array1"/> and <paramref name="array2"/> contain the same sequence of references. 261 /// </summary> 262 /// <typeparam name="TValue">The array element type.</typeparam> 263 /// <param name="array1">The first array to be evaluated.</param> 264 /// <param name="array2">The second array to be evaluated.</param> 265 /// <param name="count">The maximum number of elements to be compared.</param> 266 /// <returns><c>true</c> if the <paramref name="count"/> first elements of <paramref name="array1"/> and <paramref name="array2"/> contain the same references, else false.</returns> 267 internal static bool HaveEqualReferences<TValue>(this ReadOnlyArray<TValue> array1, IReadOnlyList<TValue> array2, int count = int.MaxValue) 268 { 269 var length1 = Math.Min(array1.Count, count); 270 var length2 = Math.Min(array2.Count, count); 271 272 if (length1 != length2) 273 return false; 274 275 for (var i = 0; i < length1; ++i) 276 if (!ReferenceEquals(array1[i], array2[i])) 277 return false; 278 279 return true; 280 } 281 } 282}