A generic dynamic array implementation used by the HumanoidRetargeter package, ported from Bullet/ BulletXNA. It implements IList<T>, non-generic IList, and related collection operations, and adds custom methods like Resize, RemoveQuick, PopBack, QuickSort with an IQSComparer, and the ability to auto-allocate new T instances when growing.
/* * C# / XNA port of Bullet (c) 2011 Mark Neale <[email protected]> * * Bullet Continuous Collision Detection and Physics Library * Copyright (c) 2003-2008 Erwin Coumans http://www.bulletphysics.com/ * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ // Adapted from BulletXNA 73b2e1b9: namespace isolation, no XNA rendering, one type per file. #nullable disable #pragma warning disable CS1591, CS0168, CS0219, CS0414, CS0660, CS0661, CS0659, CS0693, CS8981, CS0108, CS0114 using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime.InteropServices; using System.Threading; using System.Diagnostics; namespace HumanoidRetargeter.Core.Formats.Mmd.Bullet.LinearMath; internal class ObjectArray<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable where T : new() { // Fields private const int _defaultCapacity = 4; private static T[] _emptyArray; private T[] _items; private int _size; private object _syncRoot; private int _version; // Methods static ObjectArray() { ObjectArray<T>._emptyArray = new T[0]; } public ObjectArray() { this._items = ObjectArray<T>._emptyArray; } public T[] GetRawArray() { return _items; } public ObjectArray(IEnumerable<T> collection) { if (collection == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection"); } ICollection<T> is2 = collection as ICollection<T>; if (is2 != null) { int count = is2.Count; this._items = new T[count]; is2.CopyTo(this._items, 0); this._size = count; } else { this._size = 0; this._items = new T[4]; using (IEnumerator<T> enumerator = collection.GetEnumerator()) { while (enumerator.MoveNext()) { this.Add(enumerator.Current); } } } } public ObjectArray(int capacity) { if (capacity < 0) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_SmallCapacity"); } this._items = new T[capacity]; } public void Add(T item) { if (this._size == this._items.Length) { this.EnsureCapacity(this._size + 1); } this._items[this._size++] = item; this._version++; } public void AddRange(IEnumerable<T> collection) { this.InsertRange(this._size, collection); } public ReadOnlyCollection<T> AsReadOnly() { return new ReadOnlyCollection<T>(this); } public void Swap(int index0, int index1) { T temp = _items[index0]; _items[index0] = _items[index1]; _items[index1] = temp; } public void Resize(int newsize) { Resize(newsize, true); } public void Resize(int newsize, bool allocateOrReset) { int curSize = Count; if (newsize < curSize) { if (allocateOrReset) { for (int i = newsize; i < curSize; i++) { if (this._items[i] == null) { this._items[i] = new T(); } } } else { for (int i = newsize; i < curSize; i++) { this._items[i] = default(T); } } } else { if (newsize > Count) { EnsureCapacity(newsize); } if (allocateOrReset) { for (int i = curSize; i < newsize; i++) { if (this._items[i] == null) { this._items[i] = new T(); } } } } this._size = newsize; this._version++; } public int BinarySearch(T item) { return this.BinarySearch(0, this.Count, item, null); } public int BinarySearch(T item, IComparer<T> comparer) { return this.BinarySearch(0, this.Count, item, comparer); } public int BinarySearch(int index, int count, T item, IComparer<T> comparer) { if ((index < 0) || (count < 0)) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException((index < 0) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum"); } if ((this._size - index) < count) { throw new Exception("ExceptionResource - Offlen"); } return Array.BinarySearch<T>(this._items, index, count, item, comparer); } public void Clear() { if (this._size > 0) { Array.Clear(this._items, 0, this._size); this._size = 0; } this._version++; } // removes the last numElements from the array ( public void Truncate(int numElements) { if (this._size > 0 && numElements <= this._size) { int newLength = this._size - numElements; Array.Clear(this._items, newLength, numElements); this._size = newLength; } this._version++; } public bool Contains(T item) { if (item == null) { for (int j = 0; j < this._size; j++) { if (this._items[j] == null) { return true; } } return false; } EqualityComparer<T> comparer = EqualityComparer<T>.Default; for (int i = 0; i < this._size; i++) { if (comparer.Equals(this._items[i], item)) { return true; } } return false; } public ObjectArray<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter) where TOutput : new() { if (converter == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter"); } ObjectArray<TOutput> list = new ObjectArray<TOutput>(this._size); for (int i = 0; i < this._size; i++) { list._items[i] = converter(this._items[i]); } list._size = this._size; return list; } public void CopyTo(T[] array) { this.CopyTo(array, 0); } public void CopyTo(T[] array, int arrayIndex) { Array.Copy(this._items, 0, array, arrayIndex, this._size); } public void CopyTo(int index, T[] array, int arrayIndex, int count) { if ((this._size - index) < count) { throw new Exception("ExceptionResource - Offlen"); } Array.Copy(this._items, index, array, arrayIndex, count); } public void EnsureCapacity(int min) { if (this._items.Length < min) { int num = (this._items.Length == 0) ? 4 : (this._items.Length * 2); if (num < min) { num = min; } this.Capacity = num; } } public bool Exists(Predicate<T> match) { return (this.FindIndex(match) != -1); } public T Find(Predicate<T> match) { if (match == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match"); } for (int i = 0; i < this._size; i++) { if (match(this._items[i])) { return this._items[i]; } } return default(T); } public ObjectArray<T> FindAll(Predicate<T> match) { if (match == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match"); } ObjectArray<T> list = new ObjectArray<T>(); for (int i = 0; i < this._size; i++) { if (match(this._items[i])) { list.Add(this._items[i]); } } return list; } public int FindIndex(Predicate<T> match) { return this.FindIndex(0, this._size, match); } public int FindIndex(int startIndex, Predicate<T> match) { return this.FindIndex(startIndex, this._size - startIndex, match); } public int FindIndex(int startIndex, int count, Predicate<T> match) { if (startIndex > this._size) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index"); } if ((count < 0) || (startIndex > (this._size - count))) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count"); } if (match == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match"); } int num = startIndex + count; for (int i = startIndex; i < num; i++) { if (match(this._items[i])) { return i; } } return -1; } public T FindLast(Predicate<T> match) { if (match == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match"); } for (int i = this._size - 1; i >= 0; i--) { if (match(this._items[i])) { return this._items[i]; } } return default(T); } public int FindLastIndex(Predicate<T> match) { return this.FindLastIndex(this._size - 1, this._size, match); } public int FindLastIndex(int startIndex, Predicate<T> match) { return this.FindLastIndex(startIndex, startIndex + 1, match); } public int FindLastIndex(int startIndex, int count, Predicate<T> match) { if (match == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match"); } if (this._size == 0) { if (startIndex != -1) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index"); } } else if (startIndex >= this._size) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index"); } if ((count < 0) || (((startIndex - count) + 1) < 0)) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count"); } int num = startIndex - count; for (int i = startIndex; i > num; i--) { if (match(this._items[i])) { return i; } } return -1; } public void ForEach(Action<T> action) { if (action == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match"); } for (int i = 0; i < this._size; i++) { action(this._items[i]); } } //public Enumerator<T> GetEnumerator() //{ // return new Enumerator<T>((ObjectArray<T>)this); //} public ObjectArray<T> GetRange(int index, int count) { if ((index < 0) || (count < 0)) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException((index < 0) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum"); } if ((this._size - index) < count) { throw new Exception("ExceptionResource - Offlen"); } ObjectArray<T> list = new ObjectArray<T>(count); Array.Copy(this._items, index, list._items, 0, count); list._size = count; return list; } public int IndexOf(T item) { return Array.IndexOf<T>(this._items, item, 0, this._size); } public int IndexOf(T item, int index) { if (index > this._size) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index"); } return Array.IndexOf<T>(this._items, item, index, this._size - index); } public int IndexOf(T item, int index, int count) { if (index > this._size) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index"); } if ((count < 0) || (index > (this._size - count))) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count"); } return Array.IndexOf<T>(this._items, item, index, count); } public void Insert(int index, T item) { if (index > this._size) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert"); } if (this._size == this._items.Length) { this.EnsureCapacity(this._size + 1); } if (index < this._size) { Array.Copy(this._items, index, this._items, index + 1, this._size - index); } this._items[index] = item; this._size++; this._version++; } public void InsertRange(int index, IEnumerable<T> collection) { if (collection == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection"); } if (index > this._size) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index"); } ICollection<T> is2 = collection as ICollection<T>; if (is2 != null) { int count = is2.Count; if (count > 0) { this.EnsureCapacity(this._size + count); if (index < this._size) { Array.Copy(this._items, index, this._items, index + count, this._size - index); } if (this == is2) { Array.Copy(this._items, 0, this._items, index, index); Array.Copy(this._items, (int)(index + count), this._items, (int)(index * 2), (int)(this._size - index)); } else { T[] array = new T[count]; is2.CopyTo(array, 0); array.CopyTo(this._items, index); } this._size += count; } } else { using (IEnumerator<T> enumerator = collection.GetEnumerator()) { while (enumerator.MoveNext()) { this.Insert(index++, enumerator.Current); } } } this._version++; } private static bool IsCompatibleObject(object value) { if (!(value is T) && ((value != null) || typeof(T).IsValueType)) { return false; } return true; } public int LastIndexOf(T item) { return this.LastIndexOf(item, this._size - 1, this._size); } public int LastIndexOf(T item, int index) { if (index >= this._size) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index"); } return this.LastIndexOf(item, index, index + 1); } public int LastIndexOf(T item, int index, int count) { if (this._size == 0) { return -1; } if ((index < 0) || (count < 0)) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException((index < 0) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum"); } if ((index >= this._size) || (count > (index + 1))) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException((index >= this._size) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_BiggerThanCollection"); } return Array.LastIndexOf<T>(this._items, item, index, count); } // Remove the item if it exits, order of the data isn't preserved and last item is copied into it's place. public bool RemoveQuick(T item) { int index = this.IndexOf(item); if (index >= 0) { if (_size > 0) { // copy the last item to this position this._items[index] = this._items[_size - 1]; } --_size; return true; } return false; } public bool RemoveAtQuick(int index) { if (index >= 0) { if (_size > 0) { // copy the last item to this position this._items[index] = this._items[_size - 1]; this._items[_size - 1] = default(T); } --_size; return true; } return false; } public bool Remove(T item) { int index = this.IndexOf(item); if (index >= 0) { this.RemoveAt(index); return true; } return false; } public int RemoveAll(Predicate<T> match) { if (match == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match"); } int index = 0; while ((index < this._size) && !match(this._items[index])) { index++; } if (index >= this._size) { return 0; } int num2 = index + 1; while (num2 < this._size) { while ((num2 < this._size) && match(this._items[num2])) { num2++; } if (num2 < this._size) { this._items[index++] = this._items[num2++]; } } Array.Clear(this._items, index, this._size - index); int num3 = this._size - index; this._size = index; this._version++; return num3; } public void RemoveAt(int index) { if (index >= this._size) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException"); } this._size--; if (index < this._size) { Array.Copy(this._items, index + 1, this._items, index, this._size - index); } this._items[this._size] = default(T); this._version++; } public void RemoveRange(int index, int count) { if ((index < 0) || (count < 0)) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException((index < 0) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum"); } if ((this._size - index) < count) { throw new Exception("ExceptionResource - Offlen"); } if (count > 0) { this._size -= count; if (index < this._size) { Array.Copy(this._items, index + count, this._items, index, this._size - index); } Array.Clear(this._items, this._size, count); this._version++; } } public void PopBack() { { } RemoveAtQuick(Count - 1); } public void Reverse() { this.Reverse(0, this.Count); } public void Reverse(int index, int count) { if ((index < 0) || (count < 0)) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException((index < 0) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum"); } if ((this._size - index) < count) { throw new Exception("ExceptionResource - Offlen"); } Array.Reverse(this._items, index, count); this._version++; } public void Sort() { this.Sort(0, this.Count, null); } public void Sort(IComparer<T> comparer) { this.Sort(0, this.Count, comparer); } public void Sort(Comparison<T> comparison) { if (comparison == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match"); } if (this._size > 0) { IComparer<T> comparer = new ObjectArray<T>.FunctorComparer<T>(comparison); Array.Sort<T>(this._items, 0, this._size, comparer); } } public void Sort(int index, int count, IComparer<T> comparer) { if ((index < 0) || (count < 0)) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException((index < 0) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum"); } if ((this._size - index) < count) { throw new Exception("ExceptionResource - Offlen"); } Array.Sort<T>(this._items, index, count, comparer); this._version++; } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return new Enumerator<T>((ObjectArray<T>)this); } void ICollection.CopyTo(Array array, int arrayIndex) { if ((array != null) && (array.Rank != 1)) { throw new Exception("ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported"); } try { Array.Copy(this._items, 0, array, arrayIndex, this._size); } catch (ArrayTypeMismatchException) { throw new Exception("ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType"); } } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator<T>((ObjectArray<T>)this); } int IList.Add(object item) { ObjectArray<T>.VerifyValueType(item); this.Add((T)item); return (this.Count - 1); } bool IList.Contains(object item) { return (ObjectArray<T>.IsCompatibleObject(item) && this.Contains((T)item)); } int IList.IndexOf(object item) { if (ObjectArray<T>.IsCompatibleObject(item)) { return this.IndexOf((T)item); } return -1; } void IList.Insert(int index, object item) { ObjectArray<T>.VerifyValueType(item); this.Insert(index, (T)item); } void IList.Remove(object item) { if (ObjectArray<T>.IsCompatibleObject(item)) { this.Remove((T)item); } } public T[] ToArray() { T[] destinationArray = new T[this._size]; Array.Copy(this._items, 0, destinationArray, 0, this._size); return destinationArray; } public void TrimExcess() { int num = (int)(this._items.Length * 0.9); if (this._size < num) { this.Capacity = this._size; } } public bool TrueForAll(Predicate<T> match) { if (match == null) { throw new Exception("ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match"); } for (int i = 0; i < this._size; i++) { if (!match(this._items[i])) { return false; } } return true; } private static void VerifyValueType(object value) { if (!ObjectArray<T>.IsCompatibleObject(value)) { throw new Exception("ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)"); } } // Properties public int Capacity { get { return this._items.Length; } set { if (value != this._items.Length) { if (value < this._size) { throw new Exception("ExceptionResource ArgumentOutOfRange_SmallCapacity"); } if (value > 0) { T[] destinationArray = new T[value]; if (this._size > 0) { Array.Copy(this._items, 0, destinationArray, 0, this._size); } this._items = destinationArray; } else { this._items = ObjectArray<T>._emptyArray; } } } } public int Count { get { return this._size; } } public T this[int index] { get { CheckAndGrow(index); //int diff = index + 1 - _size; //for (int i = 0; i < diff; ++i) //{ // Add(new T()); //} if (index >= this._size) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException()"); } return this._items[index]; } set { CheckAndGrow(index); //int diff = index + 1 - _size; //for (int i = 0; i < diff; ++i) //{ // Add(new T()); //} if (index >= this._size) { throw new Exception("ThrowHelper.ThrowArgumentOutOfRangeException()"); } this._items[index] = value; this._version++; } } bool ICollection<T>.IsReadOnly { get { return false; } } bool ICollection.IsSynchronized { get { return false; } } object ICollection.SyncRoot { get { if (this._syncRoot == null) { Interlocked.CompareExchange(ref this._syncRoot, new object (), null); } return this._syncRoot; } } bool IList.IsFixedSize { get { return false; } } bool IList.IsReadOnly { get { return false; } } object IList.this[int index] { get { CheckAndGrow(index); //int diff = index + 1 - _size; //for (int i = 0; i < diff; ++i) //{ // Add(new T()); //} return this[index]; } set { CheckAndGrow(index); //int diff = index + 1 - _size; //for (int i = 0; i < diff; ++i) //{ // Add(new T()); //} ObjectArray<T>.VerifyValueType(value); this[index] = (T)value; } } private void CheckAndGrow(int newSize) { // make space if needed... if (newSize >= this._items.Length) { this.EnsureCapacity(newSize + 1); } // only create if we have to?? int diff = newSize + 1 - _size; for (int i = 0; i < diff; ++i) { if (this._items[this._size] == null) { this._items[this._size] = new T(); } this._size++; } this._version++; } // Nested Types [StructLayout(LayoutKind.Sequential)] public struct Enumerator<T> : IEnumerator<T>, IDisposable, IEnumerator where T : new() { private ObjectArray<T> list; private int index; private int version; private T current; internal Enumerator(ObjectArray<T> list) { this.list = list; this.index = 0; this.version = list._version; this.current = default(T); } public void Dispose() { } public bool MoveNext() { ObjectArray<T> list = this.list; if ((this.version == list._version) && (this.index < list._size)) { this.current = list._items[this.index]; this.index++; return true; } return this.MoveNextRare(); } private bool MoveNextRare() { if (this.version != this.list._version) { throw new Exception("InvalidOperation EnumFailedVersion"); } this.index = this.list._size + 1; this.current = default(T); return false; } public T Current { get { return this.current; } } object IEnumerator.Current { get { if ((this.index == 0) || (this.index == (this.list._size + 1))) { throw new Exception("InvalidOperation EnumOpCantHappen"); } return this.Current; } } void IEnumerator.Reset() { if (this.version != this.list._version) { throw new Exception("InvalidOperation EnumFailedVersion"); } this.index = 0; this.current = default(T); } } internal sealed class FunctorComparer<T> : IComparer<T> { // Fields private Comparer<T> c; private Comparison<T> comparison; // Methods public FunctorComparer(Comparison<T> comparison) { this.c = Comparer<T>.Default; this.comparison = comparison; } public int Compare(T x, T y) { return this.comparison(x, y); } } public void QuickSort(IQSComparer<T> comparer) { //don't sort 0 or 1 elements if (Count > 1) { QuickSortInternal(comparer, 0, Count - 1); } } void QuickSortInternal(IQSComparer<T> comparer, int lo, int hi) { // lo is the lower index, hi is the upper index // of the region of array a that is to be sorted int i = lo, j = hi; T x = this._items[(lo + hi) / 2]; // partition do { while (comparer.Compare(this._items[i], x)) i++; while (comparer.Compare(x, this._items[j])) j--; if (i <= j) { Swap(i, j); i++; j--; } } while (i <= j); // recursion if (lo < j) QuickSortInternal(comparer, lo, j); if (i < hi) QuickSortInternal(comparer, i, hi); } //private void checkAndGrow(int newSize) //{ // int diff = newSize + 1 - _size; // for (int i = 0; i < diff; ++i) // { // Add(new T()); // } //} }