using System; using System.Collections; using System.Collections.Generic; ////REVIEW: switch to something that doesn't require the backing store to be an actual array? //// (maybe switch m_Array to an InlinedArray and extend InlinedArray to allow having three configs: //// 1. firstValue only, 2. firstValue + additionalValues, 3. everything in additionalValues) namespace UnityEngine.InputSystem.Utilities { /// /// Read-only access to an array or to a slice of an array. /// /// Type of values stored in the array. /// /// The purpose of this struct is to allow exposing internal arrays directly such that no /// boxing and no going through interfaces is required but at the same time not allowing /// the internal arrays to be modified. /// /// It differs from ReadOnlySpan<T> in that it can be stored on the heap and differs /// from ReadOnlyCollection<T> in that it supports slices directly without needing /// an intermediate object representing the slice. /// /// Note that in most cases, the ReadOnlyArray instance should be treated as a temporary. /// The actual array referred to by a ReadOnlyArray instance is usually owned and probably mutated /// by another piece of code. When that code makes changes to the array, the ReadOnlyArray /// instance will not get updated. /// public struct ReadOnlyArray : IReadOnlyList { internal TValue[] m_Array; internal int m_StartIndex; internal int m_Length; /// /// Construct a read-only array covering all of the given array. /// /// Array to index. public ReadOnlyArray(TValue[] array) { m_Array = array; m_StartIndex = 0; m_Length = array?.Length ?? 0; } /// /// Construct a read-only array that covers only the given slice of . /// /// Array to index. /// Index at which to start indexing . The given element /// becomes index #0 for the read-only array. /// Length of the slice to index from . public ReadOnlyArray(TValue[] array, int index, int length) { m_Array = array; m_StartIndex = index; m_Length = length; } /// /// Convert to array. /// /// A new array containing a copy of the contents of the read-only array. public TValue[] ToArray() { var result = new TValue[m_Length]; if (m_Length > 0) Array.Copy(m_Array, m_StartIndex, result, 0, m_Length); return result; } public int IndexOf(Predicate predicate) { if (predicate == null) throw new ArgumentNullException(nameof(predicate)); for (var i = 0; i < m_Length; ++i) if (predicate(m_Array[m_StartIndex + i])) return i; return -1; } /// public IEnumerator GetEnumerator() { return new Enumerator(m_Array, m_StartIndex, m_Length); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "`ToXXX` message only really makes sense as static, which is not recommended for generic types.")] public static implicit operator ReadOnlyArray(TValue[] array) { return new ReadOnlyArray(array); } /// /// Number of elements in the array. /// public int Count => m_Length; /// /// Return the element at the given index. /// /// Index into the array. /// is less than 0 or greater than . /// public TValue this[int index] { get { if (index < 0 || index >= m_Length) throw new ArgumentOutOfRangeException(nameof(index)); // We allow array to be null as we are patching up ReadOnlyArrays in a separate // path in several places. if (m_Array == null) throw new InvalidOperationException(); return m_Array[m_StartIndex + index]; } } internal class Enumerator : IEnumerator { private readonly T[] m_Array; private readonly int m_IndexStart; private readonly int m_IndexEnd; private int m_Index; public Enumerator(T[] array, int index, int length) { m_Array = array; m_IndexStart = index - 1; // First call to MoveNext() moves us to first valid index. m_IndexEnd = index + length; m_Index = m_IndexStart; } public void Dispose() { } public bool MoveNext() { if (m_Index < m_IndexEnd) ++m_Index; return (m_Index != m_IndexEnd); } public void Reset() { m_Index = m_IndexStart; } public T Current { get { if (m_Index == m_IndexEnd) throw new InvalidOperationException("Iterated beyond end"); return m_Array[m_Index]; } } object IEnumerator.Current => Current; } } /// /// Extension methods to help with contents. /// public static class ReadOnlyArrayExtensions { public static bool Contains(this ReadOnlyArray array, TValue value) where TValue : IComparable { for (var i = 0; i < array.m_Length; ++i) if (array.m_Array[array.m_StartIndex + i].CompareTo(value) == 0) return true; return false; } public static bool ContainsReference(this ReadOnlyArray array, TValue value) where TValue : class { return IndexOfReference(array, value) != -1; } public static int IndexOfReference(this ReadOnlyArray array, TValue value) where TValue : class { for (var i = 0; i < array.m_Length; ++i) if (ReferenceEquals(array.m_Array[array.m_StartIndex + i], value)) return i; return -1; } } }