using System; using System.Collections.Generic; namespace Unity.MLAgents { /// /// An array-like object that stores up to four elements. /// This is a value type that does not allocate any additional memory. /// /// /// This does not implement any interfaces such as IList, in order to avoid any accidental boxing allocations. /// /// public struct InplaceArray : IEquatable> where T : struct { private const int k_MaxLength = 4; private readonly int m_Length; private T m_Elem0; private T m_Elem1; private T m_Elem2; private T m_Elem3; /// /// Create a length-1 array. /// /// public InplaceArray(T elem0) { m_Length = 1; m_Elem0 = elem0; m_Elem1 = new T { }; m_Elem2 = new T { }; m_Elem3 = new T { }; } /// /// Create a length-2 array. /// /// /// public InplaceArray(T elem0, T elem1) { m_Length = 2; m_Elem0 = elem0; m_Elem1 = elem1; m_Elem2 = new T { }; m_Elem3 = new T { }; } /// /// Create a length-3 array. /// /// /// /// public InplaceArray(T elem0, T elem1, T elem2) { m_Length = 3; m_Elem0 = elem0; m_Elem1 = elem1; m_Elem2 = elem2; m_Elem3 = new T { }; } /// /// Create a length-3 array. /// /// /// /// /// public InplaceArray(T elem0, T elem1, T elem2, T elem3) { m_Length = 4; m_Elem0 = elem0; m_Elem1 = elem1; m_Elem2 = elem2; m_Elem3 = elem3; } /// /// Construct an InplaceArray from an IList (e.g. Array or List). /// The source must be non-empty and have at most 4 elements. /// /// /// /// public static InplaceArray FromList(IList elems) { switch (elems.Count) { case 1: return new InplaceArray(elems[0]); case 2: return new InplaceArray(elems[0], elems[1]); case 3: return new InplaceArray(elems[0], elems[1], elems[2]); case 4: return new InplaceArray(elems[0], elems[1], elems[2], elems[3]); default: throw new ArgumentOutOfRangeException(); } } /// /// Per-element access. /// /// /// public T this[int index] { get { if (index >= Length) { throw new IndexOutOfRangeException(); } switch (index) { case 0: return m_Elem0; case 1: return m_Elem1; case 2: return m_Elem2; case 3: return m_Elem3; default: throw new IndexOutOfRangeException(); } } set { if (index >= Length) { throw new IndexOutOfRangeException(); } switch (index) { case 0: m_Elem0 = value; break; case 1: m_Elem1 = value; break; case 2: m_Elem2 = value; break; case 3: m_Elem3 = value; break; default: throw new IndexOutOfRangeException(); } } } /// /// The length of the array. /// public int Length { get => m_Length; } /// /// Returns a string representation of the array's elements. /// /// /// public override string ToString() { switch (m_Length) { case 1: return $"[{m_Elem0}]"; case 2: return $"[{m_Elem0}, {m_Elem1}]"; case 3: return $"[{m_Elem0}, {m_Elem1}, {m_Elem2}]"; case 4: return $"[{m_Elem0}, {m_Elem1}, {m_Elem2}, {m_Elem3}]"; default: throw new IndexOutOfRangeException(); } } /// /// Check that the arrays have the same length and have all equal values. /// /// /// /// Whether the arrays are equivalent. public static bool operator ==(InplaceArray lhs, InplaceArray rhs) { return lhs.Equals(rhs); } /// /// Check that the arrays are not equivalent. /// /// /// /// Whether the arrays are not equivalent public static bool operator !=(InplaceArray lhs, InplaceArray rhs) => !lhs.Equals(rhs); /// /// Check that the arrays are equivalent. /// /// /// Whether the arrays are not equivalent public override bool Equals(object other) => other is InplaceArray other1 && this.Equals(other1); /// /// Check that the arrays are equivalent. /// /// /// Whether the arrays are not equivalent public bool Equals(InplaceArray other) { // See https://montemagno.com/optimizing-c-struct-equality-with-iequatable/ var thisTuple = (m_Elem0, m_Elem1, m_Elem2, m_Elem3, Length); var otherTuple = (other.m_Elem0, other.m_Elem1, other.m_Elem2, other.m_Elem3, other.Length); return thisTuple.Equals(otherTuple); } /// /// Get a hashcode for the array. /// /// public override int GetHashCode() { return (m_Elem0, m_Elem1, m_Elem2, m_Elem3, Length).GetHashCode(); } } }