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;
}
///
/// Searches for the first element in the array for which the given is true and returns the index of that element.
///
/// The predicate to be evaluated for each element which defines the condition for the search.
/// Index of the first element for which is truex or -1 if no such element exists.
/// If predicate is null.
///
///
/// // Searches for the first element in an integer array that is greater or equal to 5.
/// var haystack = new ReadOnlyArray<int>(new[] { 1, 2, 3, 4, 5, 6, 7 });
/// var index = haystack.IndexOf((value) => value >= 5); // index == 4
///
///
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;
}
///
/// Returns an enumerator that iterates through the read-only array.
///
///
/// An enumerator for the read-only array.
///
///
public Enumerator GetEnumerator()
{
return new Enumerator(m_Array, m_StartIndex, m_Length);
}
///
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
/// Constructs a read-only array containing elements .
///
/// An existing array containing elements to be wrapped as a read-only array.
[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];
}
}
///
///
/// Enumerates the elements of a read-only array.
///
public struct Enumerator : IEnumerator
{
private readonly TValue[] m_Array;
private readonly int m_IndexStart;
private readonly int m_IndexEnd;
private int m_Index;
internal Enumerator(TValue[] 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 TValue 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
{
///
/// Evaluates whether contains an element that compares equal to .
///
/// The array element type.
/// Reference to the read-only array to be searched.
/// The value to be searched for in .
/// true if contains , else false.
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;
}
///
/// Evaluates whether contains a reference to .
///
/// The array element type.
/// Reference to the read-only array to be searched.
/// The reference to be searched for in .
/// true if contains a reference to , else false.
public static bool ContainsReference(this ReadOnlyArray array, TValue value)
where TValue : class
{
return IndexOfReference(array, value) != -1;
}
///
/// Retrieves the index of in .
///
/// The array element type.
/// Reference to the read-only array to be searched.
/// The reference to be searched for in .
/// The zero-based index of element in if such a reference could be found and -1 if it do not exist.
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;
}
///
/// Evaluates whether whether and contain the same sequence of references.
///
/// The array element type.
/// The first array to be evaluated.
/// The second array to be evaluated.
/// The maximum number of elements to be compared.
/// true if the first elements of and contain the same references, else false.
internal static bool HaveEqualReferences(this ReadOnlyArray array1, IReadOnlyList array2, int count = int.MaxValue)
{
var length1 = Math.Min(array1.Count, count);
var length2 = Math.Min(array2.Count, count);
if (length1 != length2)
return false;
for (var i = 0; i < length1; ++i)
if (!ReferenceEquals(array1[i], array2[i]))
return false;
return true;
}
}
}