mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 11:38:21 +02:00
When resizing, insured we didn't release old memory until after insert
This commit is contained in:
parent
40fe76d903
commit
a30ad3d9d2
4 changed files with 212 additions and 139 deletions
|
@ -12,6 +12,7 @@ namespace System.Collections
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
public class Dictionary<TKey, TValue> :
|
public class Dictionary<TKey, TValue> :
|
||||||
ICollection<(TKey key, TValue value)>,
|
ICollection<(TKey key, TValue value)>,
|
||||||
|
@ -29,8 +30,8 @@ namespace System.Collections
|
||||||
public int_cosize mNext; // Index of next entry, -1 if last
|
public int_cosize mNext; // Index of next entry, -1 if last
|
||||||
}
|
}
|
||||||
|
|
||||||
int_cosize* mBuckets ~ delete _;
|
int_cosize* mBuckets;
|
||||||
Entry* mEntries ~ delete _;
|
Entry* mEntries;
|
||||||
int_cosize mAllocSize;
|
int_cosize mAllocSize;
|
||||||
int_cosize mCount;
|
int_cosize mCount;
|
||||||
int_cosize mFreeList;
|
int_cosize mFreeList;
|
||||||
|
@ -49,6 +50,20 @@ namespace System.Collections
|
||||||
//TODO: this.comparer = comparer ?? EqualityComparer<TKey>.Default;
|
//TODO: this.comparer = comparer ?? EqualityComparer<TKey>.Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ~this()
|
||||||
|
{
|
||||||
|
if (mEntries != null)
|
||||||
|
{
|
||||||
|
var entries = mEntries;
|
||||||
|
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||||
|
// To avoid scanning items being deleted
|
||||||
|
mEntries = null;
|
||||||
|
Interlocked.Fence();
|
||||||
|
#endif
|
||||||
|
Free(entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
{
|
{
|
||||||
get { return mCount - mFreeCount; }
|
get { return mCount - mFreeCount; }
|
||||||
|
@ -123,7 +138,7 @@ namespace System.Collections
|
||||||
{
|
{
|
||||||
TKey* keyPtr;
|
TKey* keyPtr;
|
||||||
TValue* valuePtr;
|
TValue* valuePtr;
|
||||||
bool inserted = Insert(key, false, out keyPtr, out valuePtr);
|
bool inserted = Insert(key, false, out keyPtr, out valuePtr, null);
|
||||||
if (!inserted)
|
if (!inserted)
|
||||||
return false;
|
return false;
|
||||||
*keyPtr = key;
|
*keyPtr = key;
|
||||||
|
@ -133,7 +148,32 @@ namespace System.Collections
|
||||||
|
|
||||||
public bool TryAdd(TKey key, out TKey* keyPtr, out TValue* valuePtr)
|
public bool TryAdd(TKey key, out TKey* keyPtr, out TValue* valuePtr)
|
||||||
{
|
{
|
||||||
return Insert(key, false, out keyPtr, out valuePtr);
|
return Insert(key, false, out keyPtr, out valuePtr, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual (Entry*, int_cosize*) Alloc(int size)
|
||||||
|
{
|
||||||
|
int byteSize = size * (strideof(Entry) + sizeof(int_cosize));
|
||||||
|
uint8* allocPtr = new uint8[byteSize]*;
|
||||||
|
return ((Entry*)allocPtr, (int_cosize*)(allocPtr + size * strideof(Entry)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Free(Entry* entryPtr)
|
||||||
|
{
|
||||||
|
delete (void*)entryPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void GCMarkMembers()
|
||||||
|
{
|
||||||
|
if (mEntries == null)
|
||||||
|
return;
|
||||||
|
let type = typeof(Entry);
|
||||||
|
if ((type.[Friend]mTypeFlags & .WantsMark) == 0)
|
||||||
|
return;
|
||||||
|
for (int i < mCount)
|
||||||
|
{
|
||||||
|
GC.Mark_Unbound(mEntries[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AddResult
|
public enum AddResult
|
||||||
|
@ -146,7 +186,7 @@ namespace System.Collections
|
||||||
{
|
{
|
||||||
TKey* keyPtr;
|
TKey* keyPtr;
|
||||||
TValue* valuePtr;
|
TValue* valuePtr;
|
||||||
if (Insert(key, false, out keyPtr, out valuePtr))
|
if (Insert(key, false, out keyPtr, out valuePtr, null))
|
||||||
return .Added(keyPtr, valuePtr);
|
return .Added(keyPtr, valuePtr);
|
||||||
return .Exists(keyPtr, valuePtr);
|
return .Exists(keyPtr, valuePtr);
|
||||||
}
|
}
|
||||||
|
@ -299,9 +339,8 @@ namespace System.Collections
|
||||||
private void Initialize(int capacity)
|
private void Initialize(int capacity)
|
||||||
{
|
{
|
||||||
int_cosize size = GetPrimeish((int_cosize)capacity);
|
int_cosize size = GetPrimeish((int_cosize)capacity);
|
||||||
mBuckets = new int_cosize[size]*;
|
(mEntries, mBuckets) = Alloc(size);
|
||||||
for (int_cosize i < (int_cosize)size) mBuckets[i] = -1;
|
for (int_cosize i < (int_cosize)size) mBuckets[i] = -1;
|
||||||
mEntries = new Entry[size]*;
|
|
||||||
mAllocSize = size;
|
mAllocSize = size;
|
||||||
mFreeList = -1;
|
mFreeList = -1;
|
||||||
}
|
}
|
||||||
|
@ -328,6 +367,7 @@ namespace System.Collections
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int_cosize index;
|
int_cosize index;
|
||||||
|
Entry* oldData = null;
|
||||||
if (mFreeCount > 0)
|
if (mFreeCount > 0)
|
||||||
{
|
{
|
||||||
index = mFreeList;
|
index = mFreeList;
|
||||||
|
@ -338,7 +378,7 @@ namespace System.Collections
|
||||||
{
|
{
|
||||||
if (mCount == mAllocSize)
|
if (mCount == mAllocSize)
|
||||||
{
|
{
|
||||||
Resize();
|
oldData = Resize(false);
|
||||||
targetBucket = hashCode % (int_cosize)mAllocSize;
|
targetBucket = hashCode % (int_cosize)mAllocSize;
|
||||||
}
|
}
|
||||||
index = mCount;
|
index = mCount;
|
||||||
|
@ -350,17 +390,18 @@ namespace System.Collections
|
||||||
mEntries[index].mKey = key;
|
mEntries[index].mKey = key;
|
||||||
mEntries[index].mValue = value;
|
mEntries[index].mValue = value;
|
||||||
mBuckets[targetBucket] = index;
|
mBuckets[targetBucket] = index;
|
||||||
|
if (oldData != null)
|
||||||
|
Free(oldData);
|
||||||
#if VERSION_DICTIONARY
|
#if VERSION_DICTIONARY
|
||||||
mVersion++;
|
mVersion++;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Insert(TKey key, bool add, out TKey* keyPtr, out TValue* valuePtr)
|
private bool Insert(TKey key, bool add, out TKey* keyPtr, out TValue* valuePtr, Entry** outOldData)
|
||||||
{
|
{
|
||||||
if (mBuckets == null) Initialize(0);
|
if (mBuckets == null) Initialize(0);
|
||||||
int32 hashCode = (int32)key.GetHashCode() & 0x7FFFFFFF;
|
int32 hashCode = (int32)key.GetHashCode() & 0x7FFFFFFF;
|
||||||
int_cosize targetBucket = hashCode % (int_cosize)mAllocSize;
|
int_cosize targetBucket = hashCode % (int_cosize)mAllocSize;
|
||||||
|
|
||||||
for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
|
for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
|
||||||
{
|
{
|
||||||
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key))
|
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key))
|
||||||
|
@ -371,10 +412,13 @@ namespace System.Collections
|
||||||
}
|
}
|
||||||
keyPtr = &mEntries[i].mKey;
|
keyPtr = &mEntries[i].mKey;
|
||||||
valuePtr = &mEntries[i].mValue;
|
valuePtr = &mEntries[i].mValue;
|
||||||
|
if (outOldData != null)
|
||||||
|
*outOldData = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int_cosize index;
|
int_cosize index;
|
||||||
|
Entry* oldData = null;
|
||||||
if (mFreeCount > 0)
|
if (mFreeCount > 0)
|
||||||
{
|
{
|
||||||
index = mFreeList;
|
index = mFreeList;
|
||||||
|
@ -385,7 +429,7 @@ namespace System.Collections
|
||||||
{
|
{
|
||||||
if (mCount == mAllocSize)
|
if (mCount == mAllocSize)
|
||||||
{
|
{
|
||||||
Resize();
|
oldData = Resize(false);
|
||||||
targetBucket = hashCode % (int_cosize)mAllocSize;
|
targetBucket = hashCode % (int_cosize)mAllocSize;
|
||||||
}
|
}
|
||||||
index = mCount;
|
index = mCount;
|
||||||
|
@ -402,6 +446,10 @@ namespace System.Collections
|
||||||
|
|
||||||
keyPtr = &mEntries[index].mKey;
|
keyPtr = &mEntries[index].mKey;
|
||||||
valuePtr = &mEntries[index].mValue;
|
valuePtr = &mEntries[index].mValue;
|
||||||
|
if (outOldData != null)
|
||||||
|
*outOldData = oldData;
|
||||||
|
else
|
||||||
|
Free(oldData);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,17 +466,16 @@ namespace System.Collections
|
||||||
return GetPrimeish(newSize);
|
return GetPrimeish(newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Resize()
|
private Entry* Resize(bool autoFree)
|
||||||
{
|
{
|
||||||
Resize(ExpandSize(mCount), false);
|
return Resize(ExpandSize(mCount), false, autoFree);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Resize(int newSize, bool forceNewHashCodes)
|
private Entry* Resize(int newSize, bool forceNewHashCodes, bool autoFree)
|
||||||
{
|
{
|
||||||
Contract.Assert(newSize >= mAllocSize);
|
Contract.Assert(newSize >= mAllocSize);
|
||||||
int_cosize* newBuckets = new int_cosize[newSize]*;
|
(var newEntries, var newBuckets) = Alloc(newSize);
|
||||||
for (int_cosize i = 0; i < newSize; i++) newBuckets[i] = -1;
|
for (int_cosize i = 0; i < newSize; i++) newBuckets[i] = -1;
|
||||||
Entry* newEntries = new Entry[newSize]*;
|
|
||||||
Internal.MemCpy(newEntries, mEntries, mCount * strideof(Entry), alignof(Entry));
|
Internal.MemCpy(newEntries, mEntries, mCount * strideof(Entry), alignof(Entry));
|
||||||
|
|
||||||
if (forceNewHashCodes)
|
if (forceNewHashCodes)
|
||||||
|
@ -451,12 +498,17 @@ namespace System.Collections
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete mBuckets;
|
let oldPtr = mEntries;
|
||||||
delete mEntries;
|
|
||||||
|
|
||||||
mBuckets = newBuckets;
|
mBuckets = newBuckets;
|
||||||
mEntries = newEntries;
|
mEntries = newEntries;
|
||||||
mAllocSize = (int_cosize)newSize;
|
mAllocSize = (int_cosize)newSize;
|
||||||
|
|
||||||
|
if (autoFree)
|
||||||
|
{
|
||||||
|
Free(oldPtr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return oldPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Remove(TKey key)
|
public bool Remove(TKey key)
|
||||||
|
@ -875,7 +927,7 @@ namespace System.Collections
|
||||||
public struct KeyEnumerator : IEnumerator<TKey>, IResettable
|
public struct KeyEnumerator : IEnumerator<TKey>, IResettable
|
||||||
{
|
{
|
||||||
private Dictionary<TKey, TValue> mDictionary;
|
private Dictionary<TKey, TValue> mDictionary;
|
||||||
# if VERSION_DICTIONARY
|
#if VERSION_DICTIONARY
|
||||||
private int32 mVersion;
|
private int32 mVersion;
|
||||||
#endif
|
#endif
|
||||||
private int_cosize mIndex;
|
private int_cosize mIndex;
|
||||||
|
|
|
@ -106,6 +106,7 @@ namespace System.Collections
|
||||||
{
|
{
|
||||||
var items = mItems;
|
var items = mItems;
|
||||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||||
|
// To avoid scanning items being deleted
|
||||||
mItems = null;
|
mItems = null;
|
||||||
Interlocked.Fence();
|
Interlocked.Fence();
|
||||||
#endif
|
#endif
|
||||||
|
@ -138,41 +139,9 @@ namespace System.Collections
|
||||||
Debug.Assert((uint)value <= (uint)SizeFlags);
|
Debug.Assert((uint)value <= (uint)SizeFlags);
|
||||||
if (value != AllocSize)
|
if (value != AllocSize)
|
||||||
{
|
{
|
||||||
if (value > 0)
|
T* oldAlloc = Realloc(value, true);
|
||||||
{
|
if (oldAlloc != null)
|
||||||
T* newItems = Alloc(value);
|
Free(oldAlloc);
|
||||||
|
|
||||||
#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));
|
|
||||||
|
|
||||||
var oldItems = mItems;
|
|
||||||
mItems = newItems;
|
|
||||||
if (IsDynAlloc)
|
|
||||||
{
|
|
||||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
|
||||||
// We need to avoid scanning a deleted mItems
|
|
||||||
Interlocked.Fence();
|
|
||||||
#endif
|
|
||||||
Free(oldItems);
|
|
||||||
}
|
|
||||||
mAllocSizeAndFlags = (.)(value | DynAllocFlag);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (IsDynAlloc)
|
|
||||||
Free(mItems);
|
|
||||||
mItems = null;
|
|
||||||
mAllocSizeAndFlags = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,39 +228,58 @@ namespace System.Collections
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected T* Alloc(int size)
|
protected virtual T* Alloc(int size)
|
||||||
{
|
{
|
||||||
return Internal.AllocRawArrayUnmarked<T>(size);
|
return Internal.AllocRawArrayUnmarked<T>(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Free(T* val)
|
protected virtual void Free(T* val)
|
||||||
{
|
{
|
||||||
delete (void*)val;
|
delete (void*)val;
|
||||||
}
|
}
|
||||||
/*protected T[] Alloc(int size)
|
|
||||||
|
T* Realloc(int newSize, bool autoFree)
|
||||||
{
|
{
|
||||||
return new:this T[size];
|
T* oldAlloc = null;
|
||||||
|
if (newSize > 0)
|
||||||
|
{
|
||||||
|
T* newItems = Alloc(newSize);
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Free(Object obj)
|
if ((autoFree) && (oldAlloc != null))
|
||||||
{
|
{
|
||||||
delete:this obj;
|
Free(oldAlloc);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Object AllocObject(TypeInstance type, int size)
|
return oldAlloc;
|
||||||
{
|
|
||||||
return Internal.ObjectAlloc(type, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void FreeObject(Object obj)
|
|
||||||
{
|
|
||||||
delete obj;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/// Adds an item to the back of the list.
|
/// Adds an item to the back of the list.
|
||||||
public void Add(T item)
|
public void Add(T item)
|
||||||
{
|
{
|
||||||
if (mSize == AllocSize) EnsureCapacity(mSize + 1);
|
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;
|
mItems[mSize++] = item;
|
||||||
#if VERSION_LIST
|
#if VERSION_LIST
|
||||||
mVersion++;
|
mVersion++;
|
||||||
|
@ -301,7 +289,14 @@ namespace System.Collections
|
||||||
/// Adds an item to the back of the list.
|
/// Adds an item to the back of the list.
|
||||||
public void Add(Span<T> addSpan)
|
public void Add(Span<T> addSpan)
|
||||||
{
|
{
|
||||||
if (mSize == AllocSize) EnsureCapacity(mSize + addSpan.Length);
|
if (mSize == AllocSize)
|
||||||
|
{
|
||||||
|
let oldPtr = EnsureCapacity(mSize + addSpan.Length, false);
|
||||||
|
for (var val in ref addSpan)
|
||||||
|
mItems[mSize++] = val;
|
||||||
|
Free(oldPtr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (var val in ref addSpan)
|
for (var val in ref addSpan)
|
||||||
mItems[mSize++] = val;
|
mItems[mSize++] = val;
|
||||||
#if VERSION_LIST
|
#if VERSION_LIST
|
||||||
|
@ -312,7 +307,7 @@ namespace System.Collections
|
||||||
/// Returns a pointer to the start of the added uninitialized section
|
/// Returns a pointer to the start of the added uninitialized section
|
||||||
public T* GrowUnitialized(int addSize)
|
public T* GrowUnitialized(int addSize)
|
||||||
{
|
{
|
||||||
if (mSize + addSize > AllocSize) EnsureCapacity(mSize + addSize);
|
if (mSize + addSize > AllocSize) EnsureCapacity(mSize + addSize, true);
|
||||||
mSize += (int_cosize)addSize;
|
mSize += (int_cosize)addSize;
|
||||||
#if VERSION_LIST
|
#if VERSION_LIST
|
||||||
mVersion++;
|
mVersion++;
|
||||||
|
@ -365,7 +360,7 @@ namespace System.Collections
|
||||||
|
|
||||||
public void CopyTo(List<T> destList)
|
public void CopyTo(List<T> destList)
|
||||||
{
|
{
|
||||||
destList.EnsureCapacity(mSize);
|
destList.EnsureCapacity(mSize, true);
|
||||||
destList.mSize = mSize;
|
destList.mSize = mSize;
|
||||||
if (mSize > 0)
|
if (mSize > 0)
|
||||||
Internal.MemCpy(destList.mItems, mItems, mSize * strideof(T), alignof(T));
|
Internal.MemCpy(destList.mItems, mItems, mSize * strideof(T), alignof(T));
|
||||||
|
@ -385,25 +380,23 @@ namespace System.Collections
|
||||||
array[i + arrayIndex] = mItems[i + index];
|
array[i + arrayIndex] = mItems[i + index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnsureCapacity(int min)
|
public T* EnsureCapacity(int min, bool autoFree)
|
||||||
{
|
{
|
||||||
int allocSize = AllocSize;
|
int allocSize = AllocSize;
|
||||||
if (allocSize < min)
|
if (allocSize >= min)
|
||||||
{
|
return null;
|
||||||
int newCapacity = allocSize == 0 ? cDefaultCapacity : allocSize + allocSize / 2;
|
|
||||||
// If we overflow, try to set to max. The "< min" check after still still bump us up
|
int_cosize newCapacity = (int_cosize)(allocSize == 0 ? cDefaultCapacity : allocSize * 2);
|
||||||
// if necessary
|
// Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
|
||||||
if (newCapacity > SizeFlags)
|
// Note that this check works even when mItems.Length overflowed thanks to the (uint) cast
|
||||||
newCapacity = SizeFlags;
|
//if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
|
||||||
if (newCapacity < min)
|
if (newCapacity < min) newCapacity = (int_cosize)min;
|
||||||
newCapacity = min;
|
return Realloc(newCapacity, autoFree);
|
||||||
Capacity = newCapacity;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reserve(int size)
|
public void Reserve(int size)
|
||||||
{
|
{
|
||||||
EnsureCapacity(size);
|
EnsureCapacity(size, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Enumerator GetEnumerator()
|
public Enumerator GetEnumerator()
|
||||||
|
@ -453,7 +446,8 @@ namespace System.Collections
|
||||||
|
|
||||||
public void Insert(int index, T item)
|
public void Insert(int index, T item)
|
||||||
{
|
{
|
||||||
if (mSize == AllocSize) EnsureCapacity(mSize + 1);
|
var item; // This creates a copy - required if item is a ref to an element
|
||||||
|
if (mSize == AllocSize) EnsureCapacity(mSize + 1, true);
|
||||||
if (index < mSize)
|
if (index < mSize)
|
||||||
{
|
{
|
||||||
Internal.MemCpy(mItems + index + 1, mItems + index, (mSize - index) * strideof(T), alignof(T));
|
Internal.MemCpy(mItems + index + 1, mItems + index, (mSize - index) * strideof(T), alignof(T));
|
||||||
|
@ -467,10 +461,12 @@ namespace System.Collections
|
||||||
|
|
||||||
public void Insert(int index, Span<T> items)
|
public void Insert(int index, Span<T> items)
|
||||||
{
|
{
|
||||||
|
//TODO: Handle case where Span is a reference to ourselves
|
||||||
|
|
||||||
if (items.Length == 0)
|
if (items.Length == 0)
|
||||||
return;
|
return;
|
||||||
int addCount = items.Length;
|
int addCount = items.Length;
|
||||||
if (mSize + addCount > AllocSize) EnsureCapacity(mSize + addCount);
|
if (mSize + addCount > AllocSize) EnsureCapacity(mSize + addCount, true);
|
||||||
if (index < mSize)
|
if (index < mSize)
|
||||||
{
|
{
|
||||||
Internal.MemCpy(mItems + index + addCount, mItems + index, (mSize - index) * strideof(T), alignof(T));
|
Internal.MemCpy(mItems + index + addCount, mItems + index, (mSize - index) * strideof(T), alignof(T));
|
||||||
|
|
|
@ -183,7 +183,7 @@ namespace BeefPerf
|
||||||
if (mStreamDataList.Capacity == 0)
|
if (mStreamDataList.Capacity == 0)
|
||||||
mStreamDataList.Capacity = srcStreamDataList.Count;
|
mStreamDataList.Capacity = srcStreamDataList.Count;
|
||||||
else
|
else
|
||||||
mStreamDataList.EnsureCapacity(srcStreamDataList.Count);
|
mStreamDataList.EnsureCapacity(srcStreamDataList.Count, true);
|
||||||
mStreamDataList.GrowUnitialized(idx - (int32)mStreamDataList.Count + 1);
|
mStreamDataList.GrowUnitialized(idx - (int32)mStreamDataList.Count + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,33 +140,9 @@ namespace System.Collections
|
||||||
Debug.Assert((uint)value <= (uint)SizeFlags);
|
Debug.Assert((uint)value <= (uint)SizeFlags);
|
||||||
if (value != AllocSize)
|
if (value != AllocSize)
|
||||||
{
|
{
|
||||||
if (value > 0)
|
T* oldAlloc = Realloc(value, true);
|
||||||
{
|
if (oldAlloc != null)
|
||||||
T* newItems = Alloc(value);
|
Free(oldAlloc);
|
||||||
|
|
||||||
#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)
|
|
||||||
Free(mItems);
|
|
||||||
mItems = newItems;
|
|
||||||
mAllocSizeAndFlags = (.)(value | DynAllocFlag);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (IsDynAlloc)
|
|
||||||
Free(mItems);
|
|
||||||
mItems = null;
|
|
||||||
mAllocSizeAndFlags = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,6 +234,46 @@ namespace System.Collections
|
||||||
#endif
|
#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)
|
protected void Free(T* val)
|
||||||
{
|
{
|
||||||
delete (void*)val;
|
delete (void*)val;
|
||||||
|
@ -285,12 +301,14 @@ namespace System.Collections
|
||||||
/// Adds an item to the back of the list.
|
/// Adds an item to the back of the list.
|
||||||
public void Add(T item)
|
public void Add(T item)
|
||||||
{
|
{
|
||||||
if (((int)Internal.UnsafeCastToPtr(this) & 0xFFFF) == 0x4BA0)
|
if (mSize == AllocSize)
|
||||||
{
|
{
|
||||||
NOP!();
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mSize == AllocSize) EnsureCapacity(mSize + 1);
|
|
||||||
mItems[mSize++] = item;
|
mItems[mSize++] = item;
|
||||||
#if VERSION_LIST
|
#if VERSION_LIST
|
||||||
mVersion++;
|
mVersion++;
|
||||||
|
@ -300,7 +318,7 @@ namespace System.Collections
|
||||||
/// Returns a pointer to the start of the added uninitialized section
|
/// Returns a pointer to the start of the added uninitialized section
|
||||||
public T* GrowUnitialized(int addSize)
|
public T* GrowUnitialized(int addSize)
|
||||||
{
|
{
|
||||||
if (mSize + addSize > AllocSize) EnsureCapacity(mSize + addSize);
|
if (mSize + addSize > AllocSize) EnsureCapacity(mSize + addSize, true);
|
||||||
mSize += (int_cosize)addSize;
|
mSize += (int_cosize)addSize;
|
||||||
#if VERSION_LIST
|
#if VERSION_LIST
|
||||||
mVersion++;
|
mVersion++;
|
||||||
|
@ -353,7 +371,7 @@ namespace System.Collections
|
||||||
|
|
||||||
public void CopyTo(List<T> destList)
|
public void CopyTo(List<T> destList)
|
||||||
{
|
{
|
||||||
destList.EnsureCapacity(mSize);
|
destList.EnsureCapacity(mSize, true);
|
||||||
destList.mSize = mSize;
|
destList.mSize = mSize;
|
||||||
if (mSize > 0)
|
if (mSize > 0)
|
||||||
Internal.MemCpy(destList.mItems, mItems, mSize * strideof(T), alignof(T));
|
Internal.MemCpy(destList.mItems, mItems, mSize * strideof(T), alignof(T));
|
||||||
|
@ -366,23 +384,23 @@ namespace System.Collections
|
||||||
array[i + arrayIndex] = mItems[i];
|
array[i + arrayIndex] = mItems[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnsureCapacity(int min)
|
public T* EnsureCapacity(int min, bool autoFree)
|
||||||
{
|
{
|
||||||
int allocSize = AllocSize;
|
int allocSize = AllocSize;
|
||||||
if (allocSize < min)
|
if (allocSize >= min)
|
||||||
{
|
return null;
|
||||||
|
|
||||||
int_cosize newCapacity = (int_cosize)(allocSize == 0 ? cDefaultCapacity : allocSize * 2);
|
int_cosize newCapacity = (int_cosize)(allocSize == 0 ? cDefaultCapacity : allocSize * 2);
|
||||||
// Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
|
// 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
|
// Note that this check works even when mItems.Length overflowed thanks to the (uint) cast
|
||||||
//if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
|
//if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
|
||||||
if (newCapacity < min) newCapacity = (int_cosize)min;
|
if (newCapacity < min) newCapacity = (int_cosize)min;
|
||||||
Capacity = newCapacity;
|
return Realloc(newCapacity, autoFree);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reserve(int size)
|
public void Reserve(int size)
|
||||||
{
|
{
|
||||||
EnsureCapacity(size);
|
EnsureCapacity(size, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Enumerator GetEnumerator()
|
public Enumerator GetEnumerator()
|
||||||
|
@ -417,7 +435,14 @@ namespace System.Collections
|
||||||
|
|
||||||
public void Insert(int index, T item)
|
public void Insert(int index, T item)
|
||||||
{
|
{
|
||||||
if (mSize == AllocSize) EnsureCapacity(mSize + 1);
|
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)
|
if (index < mSize)
|
||||||
{
|
{
|
||||||
Internal.MemCpy(mItems + index + 1, mItems + index, (mSize - index) * strideof(T), alignof(T));
|
Internal.MemCpy(mItems + index + 1, mItems + index, (mSize - index) * strideof(T), alignof(T));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue