// This file contains portions of code released by Microsoft under the MIT license as part // of an open-sourcing initiative in 2014 of the C# core libraries. // The original source was submitted to https://github.com/Microsoft/referencesource #if PARANOID #define VERSION_LIST #endif using System; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Threading; using System.Reflection; namespace System.Collections { interface IList { Variant this[int index] { get; set; } } public class List : IEnumerable, IList { private const int_cosize cDefaultCapacity = 4; const int_cosize SizeFlags = 0x7FFFFFFF; const int_cosize DynAllocFlag = (int_cosize)0x80000000; #if BF_ENABLE_REALTIME_LEAK_CHECK static DbgRawAllocData sRawAllocData; public static this() { sRawAllocData.mMarkFunc = null; sRawAllocData.mMaxStackTrace = 1; sRawAllocData.mType = typeof(T); } #endif private T* mItems; private int_cosize mSize; private int_cosize mAllocSizeAndFlags; #if VERSION_LIST private int32 mVersion; const String cVersionError = "List changed during enumeration"; #endif public int AllocSize { [Inline] get { return mAllocSizeAndFlags & SizeFlags; } } public bool IsDynAlloc { [Inline] get { return (mAllocSizeAndFlags & DynAllocFlag) != 0; } } public this() { if (((int)Internal.UnsafeCastToPtr(this) & 0xFFFF) == 0x4BA0) { NOP!(); } } public this(IEnumerator enumerator) { for (var item in enumerator) Add(item); } [AllowAppend] public this(int capacity) { Debug.Assert((uint)capacity <= (uint)SizeFlags); T* items = append T[capacity]*; if (capacity > 0) { mItems = items; mAllocSizeAndFlags = (int_cosize)(capacity & SizeFlags); } } /*public this(int capacity) { Debug.Assert((uint)capacity <= (uint)SizeFlags); if (capacity > 0) { mItems = Alloc(capacity); mAllocSizeAndFlags = (int_cosize)(capacity | DynAllocFlag); } }*/ public ~this() { #if DBG int typeId = typeof(T).GetTypeId(); if (typeId == sDebugTypeId) { Debug.WriteLine("Dealloc {0} {1}", scope Object[] { this, mItems } ); } #endif if (IsDynAlloc) Free(mItems); } public T* Ptr { get { return mItems; } } #if DBG static int_cosize sDebugTypeId = 470; static int_cosize sDebugIdx = 0; #endif public int Capacity { get { return mAllocSizeAndFlags & SizeFlags; } set { Debug.Assert((uint)value <= (uint)SizeFlags); if (value != AllocSize) { T* oldAlloc = Realloc(value, true); if (oldAlloc != null) Free(oldAlloc); } } } public int Count { get { return mSize; } } public bool IsEmpty { get { return mSize == 0; } } public ref T this[int index] { [Checked] get { Debug.Assert((uint)index < (uint)mSize); return ref mItems[index]; } [Checked] set { Debug.Assert((uint)index < (uint)mSize); mItems[index] = value; #if VERSION_LIST mVersion++; #endif } } public ref T this[int index] { [Unchecked, Inline] get { return ref mItems[index]; } [Unchecked, Inline] set { mItems[index] = value; #if VERSION_LIST mVersion++; #endif } } public ref T Back { get { Debug.Assert(mSize != 0); return ref mItems[mSize - 1]; } } Variant IList.this[int index] { get { return [Unbound]Variant.Create(this[index]); } set { ThrowUnimplemented(); } } protected T* Alloc(int size) { #if BF_ENABLE_REALTIME_LEAK_CHECK // We don't want to use the default mark function because it will mark the entire array, // whereas we have a custom marking routine because we only want to mark up to mSize return (T*)Internal.Dbg_RawAlloc(size * strideof(T), &sRawAllocData); #else return new T[size]*(?); #endif } T* Realloc(int newSize, bool autoFree) { T* oldAlloc = null; if (newSize > 0) { T* newItems = Alloc(newSize); #if DBG int typeId = typeof(T).GetTypeId(); if (typeId == sDebugTypeId) { Debug.WriteLine("Alloc {0} {1} {2}", scope Object[] { this, newItems, sDebugIdx } ); sDebugIdx++; } #endif if (mSize > 0) Internal.MemCpy(newItems, mItems, mSize * strideof(T), alignof(T)); if (IsDynAlloc) oldAlloc = mItems; mItems = newItems; mAllocSizeAndFlags = (.)(newSize | DynAllocFlag); } else { if (IsDynAlloc) oldAlloc = mItems; mItems = null; mAllocSizeAndFlags = 0; } if ((autoFree) && (oldAlloc != null)) { Free(oldAlloc); return null; } return oldAlloc; } protected void Free(T* val) { delete (void*)val; } /*protected T[] Alloc(int size) { return new:this T[size]; } protected void Free(Object obj) { delete:this obj; } protected virtual Object AllocObject(TypeInstance type, int size) { return Internal.ObjectAlloc(type, size); } protected virtual void FreeObject(Object obj) { delete obj; }*/ /// Adds an item to the back of the list. public void Add(T item) { if (mSize == AllocSize) { // We free after the insert to allow for inserting a ref to another list element let oldPtr = EnsureCapacity(mSize + 1, false); mItems[mSize++] = item; Free(oldPtr); return; } mItems[mSize++] = item; #if VERSION_LIST mVersion++; #endif } /// Returns a pointer to the start of the added uninitialized section public T* GrowUnitialized(int addSize) { if (mSize + addSize > AllocSize) EnsureCapacity(mSize + addSize, true); mSize += (int_cosize)addSize; #if VERSION_LIST mVersion++; #endif if (addSize == 0) return null; return &mItems[mSize - addSize]; } public void Clear() { if (mSize > 0) { mSize = 0; } #if VERSION_LIST mVersion++; #endif } /*public static void DeleteItemsAndClear(List list) where T : delete { foreach (var item in list) delete item; list.Clear(); }*/ public bool Contains(T item) { if (item == null) { for (int i = 0; i < mSize; i++) if (mItems[i] == null) return true; return false; } else { for (int i = 0; i < mSize; i++) if (mItems[i] == item) return true; return false; } } public void CopyTo(T[] array) { CopyTo(array, 0); } public void CopyTo(List destList) { destList.EnsureCapacity(mSize, true); destList.mSize = mSize; if (mSize > 0) Internal.MemCpy(destList.mItems, mItems, mSize * strideof(T), alignof(T)); } public void CopyTo(T[] array, int arrayIndex) { // Delegate rest of error checking to Array.Copy. for (int_cosize i = 0; i < mSize; i++) array[i + arrayIndex] = mItems[i]; } public T* EnsureCapacity(int min, bool autoFree) { int allocSize = AllocSize; if (allocSize >= min) return null; int_cosize newCapacity = (int_cosize)(allocSize == 0 ? cDefaultCapacity : allocSize * 2); // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. // Note that this check works even when mItems.Length overflowed thanks to the (uint) cast //if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength; if (newCapacity < min) newCapacity = (int_cosize)min; return Realloc(newCapacity, autoFree); } public void Reserve(int size) { EnsureCapacity(size, true); } public Enumerator GetEnumerator() { return Enumerator(this); } public int_cosize IndexOf(T item) { //return Array.IndexOf(mItems, item, 0, mSize); for (int i = 0; i < mSize; i++) if (mItems[i] == item) return (int_cosize)i; return -1; } public int_cosize IndexOf(T item, int index) { for (int i = index; i < mSize; i++) if (mItems[i] == item) return (int_cosize)i; return -1; } public int_cosize IndexOf(T item, int index, int count) { for (int i = index; i < index + count; i++) if (mItems[i] == item) return (int_cosize)i; return -1; } public void Insert(int index, T item) { if (mSize == AllocSize) { let oldPtr = EnsureCapacity(mSize + 1, false); Internal.MemCpy(mItems + index + 1, mItems + index, (mSize - index) * strideof(T), alignof(T)); Free(oldPtr); return; } if (index < mSize) { Internal.MemCpy(mItems + index + 1, mItems + index, (mSize - index) * strideof(T), alignof(T)); } mItems[index] = item; mSize++; #if VERSION_LIST mVersion++; #endif } public void Insert(int index, Span items) { if (items.Length == 0) return; } public void RemoveAt(int index) { if (((int)Internal.UnsafeCastToPtr(this) & 0xFFFF) == 0x4BA0) { NOP!(); } Debug.Assert((uint)index < (uint)mSize); if (index < mSize - 1) { Internal.MemCpy(mItems + index, mItems + index + 1, (mSize - index - 1) * strideof(T), alignof(T)); } mSize--; #if VERSION_LIST mVersion++; #endif } public void RemoveRange(int index, int count) { Debug.Assert((uint)index + (uint)count <= (uint)mSize); if (index + count < mSize - 1) { for (int i = index; i < mSize - count; i++) mItems[i] = mItems[i + count]; } mSize -= (.)count; #if VERSION_LIST mVersion++; #endif } public void RemoveAtFast(int index) { Debug.Assert((uint32)index < (uint32)mSize); mSize--; if (mSize > 0) mItems[index] = mItems[mSize]; #if VERSION_LIST mVersion++; #endif } public void Sort(Comparison comp) { var sorter = Sorter(mItems, null, mSize, comp); sorter.Sort(0, mSize); } public T PopBack() { T backVal = mItems[mSize - 1]; mSize--; return backVal; } public T PopFront() { T backVal = mItems[0]; RemoveAt(0); return backVal; } public bool Remove(T item) { int_cosize index = IndexOf(item); if (index >= 0) { RemoveAt(index); return true; } return false; } public static operator Span(List list) { return Span(list.mItems, list.mSize); } protected override void GCMarkMembers() { for (int i < mSize) { GC.Mark_Unbound(mItems[i]); } } public struct Enumerator : IRefEnumerator { private List mList; private int mIndex; #if VERSION_LIST private int32 mVersion; #endif private T* mCurrent; public this(List list) { mList = list; mIndex = 0; #if VERSION_LIST mVersion = list.mVersion; #endif mCurrent = null; } #if VERSION_LIST void CheckVersion() { if (mVersion != mList.mVersion) Runtime.FatalError(cVersionError); } #endif public void Dispose() { } public bool MoveNext() mut { List localList = mList; if ((uint(mIndex) < uint(localList.mSize))) { mCurrent = &localList.mItems[mIndex]; mIndex++; return true; } return MoveNextRare(); } private bool MoveNextRare() mut { #if VERSION_LIST CheckVersion(); #endif mIndex = mList.mSize + 1; mCurrent = null; return false; } public T Current { get { return *mCurrent; } } public ref T CurrentRef { get { return ref *mCurrent; } } public int Index { get { return mIndex - 1; } } public int Count { get { return mList.Count; } } public void Remove() mut { int curIdx = mIndex - 1; mList.RemoveAt(curIdx); #if VERSION_LIST mVersion = mList.mVersion; #endif mIndex = curIdx; } public void RemoveFast() mut { int curIdx = mIndex - 1; int lastIdx = mList.Count - 1; if (curIdx < lastIdx) mList[curIdx] = mList[lastIdx]; mList.RemoveAt(lastIdx); #if VERSION_LIST mVersion = mList.mVersion; #endif mIndex = curIdx; } public void Reset() mut { mIndex = 0; mCurrent = null; } public Result GetNext() mut { if (!MoveNext()) return .Err; return Current; } public Result GetNextRef() mut { if (!MoveNext()) return .Err; return &CurrentRef; } } } } namespace System.Collections.Generic { [Obsolete("The System.Collections.Generic containers have been moved into System.Collections", false)] typealias List = System.Collections.List; }