1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-18 16:10:26 +02:00

Improvements to Queue, to bring it more inline with List

This commit is contained in:
Brian Fiete 2020-03-20 09:26:16 -07:00
parent 69cb6d0208
commit 96c53d4a9e
5 changed files with 238 additions and 88 deletions

View file

@ -111,6 +111,12 @@ namespace System
arr[i] = default(T); arr[i] = default(T);
} }
public static void Clear<T>(T* arr, int length)
{
for (int i = 0; i < length; i++)
arr[i] = default(T);
}
public static void Copy<T, T2>(T[] arrayFrom, int srcOffset, T2[] arrayTo, int dstOffset, int length) where T : var where T2 : var public static void Copy<T, T2>(T[] arrayFrom, int srcOffset, T2[] arrayTo, int dstOffset, int length) where T : var where T2 : var
{ {
if (((Object)arrayTo == (Object)arrayFrom) && (dstOffset > srcOffset)) if (((Object)arrayTo == (Object)arrayFrom) && (dstOffset > srcOffset))

View file

@ -35,16 +35,6 @@ namespace System.Collections.Generic
const int_cosize DynAllocFlag = (int_cosize)0x80000000; const int_cosize DynAllocFlag = (int_cosize)0x80000000;
#endif #endif
#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 T* mItems;
private int_cosize mSize; private int_cosize mSize;
private int_cosize mAllocSizeAndFlags; private int_cosize mAllocSizeAndFlags;
@ -262,13 +252,7 @@ namespace System.Collections.Generic
protected T* Alloc(int size) protected T* Alloc(int size)
{ {
#if BF_ENABLE_REALTIME_LEAK_CHECK return Internal.AllocRawArrayUnmarked<T>(size);
// We don't want to use the default mark function because the GC 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
} }
protected void Free(T* val) protected void Free(T* val)

View file

@ -10,12 +10,22 @@ namespace System.Collections.Generic
{ {
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
/// A simple Queue of generic items. Internally it is implemented as a /// A simple Queue of generic items. Internally it is implemented as a
/// circular buffer, so Enqueue can be O(n). Dequeue is O(1). /// circular buffer, so Enqueue can be O(n). Dequeue is O(1).
public class Queue<T> : IEnumerable<T> //, System.Collections.ICollection, IReadOnlyCollection<T> public class Queue<T> : IEnumerable<T> //, System.Collections.ICollection, IReadOnlyCollection<T>
{ {
private T[] mArray; #if BF_LARGE_COLLECTIONS
const int_cosize SizeFlags = 0x7FFFFFFF'FFFFFFFF;
const int_cosize DynAllocFlag = (int_cosize)0x80000000'00000000;
#else
const int_cosize SizeFlags = 0x7FFFFFFF;
const int_cosize DynAllocFlag = (int_cosize)0x80000000;
#endif
private T* mItems;
private int_cosize mAllocSizeAndFlags;
private int_cosize mHead; // First valid element in the queue private int_cosize mHead; // First valid element in the queue
private int_cosize mTail; // Last valid element in the queue private int_cosize mTail; // Last valid element in the queue
private int_cosize mSize; // Number of elements. private int_cosize mSize; // Number of elements.
@ -29,24 +39,42 @@ namespace System.Collections.Generic
private const int32 cShrinkThreshold = 32; private const int32 cShrinkThreshold = 32;
private const int32 cGrowFactor = 200; // double each time private const int32 cGrowFactor = 200; // double each time
private const int32 cDefaultCapacity = 4; private const int32 cDefaultCapacity = 4;
static T[] sEmptyArray = new T[0] ~ delete _;
/// Creates a queue with room for capacity objects. The default initial /// Creates a queue with room for capacity objects. The default initial
/// capacity and grow factor are used. /// capacity and grow factor are used.
public this() public this()
{ {
mArray = sEmptyArray;
} }
/// Creates a queue with room for capacity objects. The default grow factor /// Creates a queue with room for capacity objects. The default grow factor
/// is used. /// is used.
[AllowAppend]
public this(int capacity) public this(int capacity)
{ {
T* items = append T[capacity]* (?);
Debug.Assert(capacity >= 0); Debug.Assert(capacity >= 0);
mArray = new T[capacity]; mItems = items;
mHead = 0; mHead = 0;
mTail = 0; mTail = 0;
mSize = 0; mSize = 0;
if (capacity > 0)
{
mItems = items;
mAllocSizeAndFlags = (int_cosize)(capacity & SizeFlags);
}
}
public ~this()
{
if (IsDynAlloc)
{
var items = mItems;
#if BF_ENABLE_REALTIME_LEAK_CHECK
mItems= null;
Interlocked.Fence();
#endif
Free(items);
}
} }
// Fills a Queue with the elements of an ICollection. Uses the enumerator // Fills a Queue with the elements of an ICollection. Uses the enumerator
@ -72,24 +100,42 @@ namespace System.Collections.Generic
} }
}*/ }*/
public int AllocSize
{
[Inline]
get
{
return mAllocSizeAndFlags & SizeFlags;
}
}
public bool IsDynAlloc
{
[Inline]
get
{
return (mAllocSizeAndFlags & DynAllocFlag) != 0;
}
}
public int Count public int Count
{ {
get { return mSize; } get { return mSize; }
} }
protected T* Alloc(int size)
{
return Internal.AllocRawArrayUnmarked<T>(size);
}
protected void Free(T* val)
{
delete (void*)val;
}
/// Removes all items from the queue. /// Removes all items from the queue.
public void Clear() public void Clear()
{ {
#if BF_ENABLE_REALTIME_LEAK_CHECK
if (mHead < mTail)
Array.Clear(mArray, mHead, mSize);
else
{
Array.Clear(mArray, mHead, mArray.Count - mHead);
Array.Clear(mArray, 0, mTail);
}
#endif
mHead = 0; mHead = 0;
mTail = 0; mTail = 0;
mSize = 0; mSize = 0;
@ -109,12 +155,15 @@ namespace System.Collections.Generic
int numToCopy = (arrayLen - arrayIndex < mSize) ? (arrayLen - arrayIndex) : mSize; int numToCopy = (arrayLen - arrayIndex < mSize) ? (arrayLen - arrayIndex) : mSize;
if (numToCopy == 0) return; if (numToCopy == 0) return;
int firstPart = (mArray.Count - mHead < numToCopy) ? mArray.Count - mHead : numToCopy; int firstPart = (AllocSize - mHead < numToCopy) ? AllocSize - mHead : numToCopy;
Array.Copy(mArray, mHead, array, arrayIndex, firstPart); //Array.Copy(mArray, mHead, array, arrayIndex, firstPart);
Internal.MemCpy(&array.[Friend]GetRef(arrayIndex), mItems + mHead, firstPart * strideof(T), alignof(T));
numToCopy -= firstPart; numToCopy -= firstPart;
if (numToCopy > 0) if (numToCopy > 0)
{ {
Array.Copy(mArray, 0, array, arrayIndex + mArray.Count - mHead, numToCopy); //Array.Copy(mArray, 0, array, arrayIndex + AllocSize - mHead, numToCopy);
Internal.MemCpy(&array.[Friend]GetRef(arrayIndex + AllocSize - mHead), mItems, numToCopy * strideof(T), alignof(T));
} }
} }
@ -122,18 +171,18 @@ namespace System.Collections.Generic
/// Adds item to the tail of the queue. /// Adds item to the tail of the queue.
public void Enqueue(T item) public void Enqueue(T item)
{ {
if (mSize == mArray.Count) if (mSize == AllocSize)
{ {
int newcapacity = (int)((int64)mArray.Count * (int64)cGrowFactor / 100); int newcapacity = (int)((int64)AllocSize * (int64)cGrowFactor / 100);
if (newcapacity < mArray.Count + cMinimumGrow) if (newcapacity < AllocSize + cMinimumGrow)
{ {
newcapacity = mArray.Count + cMinimumGrow; newcapacity = AllocSize + cMinimumGrow;
} }
SetCapacity(newcapacity); SetCapacity(newcapacity);
} }
mArray[mTail] = item; mItems[mTail] = item;
mTail = (mTail + 1) % (int_cosize)mArray.Count; mTail = (mTail + 1) % (int_cosize)AllocSize;
mSize++; mSize++;
#if VERSION_QUEUE #if VERSION_QUEUE
mVersion++; mVersion++;
@ -155,9 +204,8 @@ namespace System.Collections.Generic
//ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue); //ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue);
Runtime.FatalError(); Runtime.FatalError();
T removed = mArray[mHead]; T removed = mItems[mHead];
mArray[mHead] = default(T); mHead = (mHead + 1) % (int_cosize)AllocSize;
mHead = (mHead + 1) % (int_cosize)mArray.Count;
mSize--; mSize--;
#if VERSION_QUEUE #if VERSION_QUEUE
mVersion++; mVersion++;
@ -171,7 +219,7 @@ namespace System.Collections.Generic
public T Peek() public T Peek()
{ {
Debug.Assert(mSize != 0); Debug.Assert(mSize != 0);
return mArray[mHead]; return mItems[mHead];
} }
/// Returns true if the queue contains at least one object equal to item. /// Returns true if the queue contains at least one object equal to item.
@ -184,51 +232,33 @@ namespace System.Collections.Generic
{ {
if (((Object)item) == null) if (((Object)item) == null)
{ {
if (((Object)mArray[index]) == null) if (((Object)mItems[index]) == null)
return true; return true;
} }
else if (mArray[index] != null && mArray[index] == item) else if (mItems[index] != null && mItems[index] == item)
{ {
return true; return true;
} }
index = (index + 1) % mArray.Count; index = (index + 1) % AllocSize;
} }
return false; return false;
} }
T GetElement(int i) T GetElement(int i)
{ {
return mArray[(mHead + i) % mArray.Count]; return mItems[(mHead + i) % AllocSize];
} }
/// Iterates over the objects in the queue, returning an array of the ref T GetElementRef(int i)
/// objects in the Queue, or an empty array if the queue is empty.
/// The order of elements in the array is first in to last in, the same
/// order produced by successive calls to Dequeue.
public T[] ToArray()
{ {
T[] arr = new T[mSize]; return ref mItems[(mHead + i) % AllocSize];
if (mSize == 0)
return arr;
if (mHead < mTail)
{
Array.Copy(mArray, mHead, arr, 0, mSize);
}else
{
Array.Copy(mArray, mHead, arr, 0, mArray.Count - mHead);
Array.Copy(mArray, 0, arr, mArray.Count - mHead, mTail);
} }
return arr;
}
// PRIVATE Grows or shrinks the buffer to hold capacity objects. Capacity // PRIVATE Grows or shrinks the buffer to hold capacity objects. Capacity
// must be >= _size. // must be >= _size.
private void SetCapacity(int capacity) private void SetCapacity(int value)
{ {
T[] newarray = new T[capacity]; /*T* newarray = new T[capacity]*;
if (mSize > 0) if (mSize > 0)
{ {
if (mHead < mTail) if (mHead < mTail)
@ -236,22 +266,82 @@ namespace System.Collections.Generic
Array.Copy(mArray, mHead, newarray, 0, mSize); Array.Copy(mArray, mHead, newarray, 0, mSize);
}else }else
{ {
Array.Copy(mArray, mHead, newarray, 0, mArray.Count - mHead); Array.Copy(mArray, mHead, newarray, 0, AllocSize - mHead);
Array.Copy(mArray, 0, newarray, mArray.Count - mHead, mTail); Array.Copy(mArray, 0, newarray, AllocSize - mHead, mTail);
} }
}*/
if (value > 0)
{
T* newItems = Alloc(value);
#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)
{
if (mHead < mTail)
{
Internal.MemCpy(newItems, mItems + mHead, mSize * strideof(T), alignof(T));
}else
{
Internal.MemCpy(newItems, mItems + mHead, (AllocSize - mHead) * strideof(T), alignof(T));
Internal.MemCpy(newItems + (AllocSize - mHead), mItems, mTail * 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;
} }
mArray = newarray;
mHead = 0; mHead = 0;
mTail = (mSize == capacity) ? 0 : mSize; mTail = (mSize == value) ? 0 : mSize;
#if VERSION_QUEUE #if VERSION_QUEUE
mVersion++; mVersion++;
#endif #endif
} }
protected override void GCMarkMembers()
{
if (mItems == null)
return;
let type = typeof(T);
if ((type.[Friend]mTypeFlags & .WantsMark) == 0)
return;
int allocSize = AllocSize;
for (int i < mSize)
{
GC.Mark_Unbound(mItems[(i + mHead) % allocSize]);
}
}
public void TrimExcess() public void TrimExcess()
{ {
int32 threshold = (int32)(((double)mArray.Count) * 0.9); int32 threshold = (int32)(((double)AllocSize) * 0.9);
if (mSize < threshold) if (mSize < threshold)
{ {
SetCapacity(mSize); SetCapacity(mSize);
@ -261,14 +351,14 @@ namespace System.Collections.Generic
/// Implements an enumerator for a Queue. The enumerator uses the /// Implements an enumerator for a Queue. The enumerator uses the
/// internal version number of the list to ensure that no modifications are /// internal version number of the list to ensure that no modifications are
/// made to the list while an enumeration is in progress. /// made to the list while an enumeration is in progress.
public struct Enumerator : IEnumerator<T> public struct Enumerator : IRefEnumerator<T>
{ {
private Queue<T> mQueue; private Queue<T> mQueue;
private int32 mIndex; // -1 = not started, -2 = ended/disposed private int32 mIndex; // -1 = not started, -2 = ended/disposed
#if VERSION_QUEUE #if VERSION_QUEUE
private int32 mVersion; private int32 mVersion;
#endif #endif
private T mCurrentElement; private T* mCurrentElement;
public this(Queue<T> q) public this(Queue<T> q)
{ {
@ -277,7 +367,7 @@ namespace System.Collections.Generic
mVersion = mQueue.mVersion; mVersion = mQueue.mVersion;
#endif #endif
mIndex = -1; mIndex = -1;
mCurrentElement = default(T); mCurrentElement = null;
} }
#if VERSION_QUEUE #if VERSION_QUEUE
@ -291,7 +381,7 @@ namespace System.Collections.Generic
public void Dispose() mut public void Dispose() mut
{ {
mIndex = -2; mIndex = -2;
mCurrentElement = default(T); mCurrentElement = null;
} }
public bool MoveNext() mut public bool MoveNext() mut
@ -307,11 +397,11 @@ namespace System.Collections.Generic
if (mIndex == mQueue.mSize) if (mIndex == mQueue.mSize)
{ {
mIndex = -2; mIndex = -2;
mCurrentElement = default(T); mCurrentElement = null;
return false; return false;
} }
mCurrentElement = mQueue.GetElement(mIndex); mCurrentElement = &mQueue.GetElementRef(mIndex);
return true; return true;
} }
@ -326,7 +416,22 @@ namespace System.Collections.Generic
else else
Runtime.FatalError("Enumeration ended"); Runtime.FatalError("Enumeration ended");
} }
return mCurrentElement; return *mCurrentElement;
}
}
public ref T CurrentRef
{
get
{
if (mIndex < 0)
{
if (mIndex == -1)
Runtime.FatalError("Enumeration not started");
else
Runtime.FatalError("Enumeration ended");
}
return ref *mCurrentElement;
} }
} }
@ -345,6 +450,13 @@ namespace System.Collections.Generic
return .Err; return .Err;
return Current; return Current;
} }
public Result<T*> GetNextRef() mut
{
if (!MoveNext())
return .Err;
return &CurrentRef;
}
} }
} }
} }

View file

@ -8,6 +8,17 @@ namespace System
public Type mType; public Type mType;
public void* mMarkFunc; public void* mMarkFunc;
public int32 mMaxStackTrace; public int32 mMaxStackTrace;
public struct Unmarked<T>
{
public static DbgRawAllocData sRawAllocData;
public static this()
{
sRawAllocData.mMarkFunc = null;
sRawAllocData.mMaxStackTrace = 1;
sRawAllocData.mType = typeof(T);
}
}
} }
[AlwaysInclude] [AlwaysInclude]
@ -81,6 +92,17 @@ namespace System
sModuleHandle = handle; sModuleHandle = handle;
} }
public static T* AllocRawArrayUnmarked<T>(int size)
{
#if BF_ENABLE_REALTIME_LEAK_CHECK
// We don't want to use the default mark function because the GC 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), &DbgRawAllocData.Unmarked<T>.sRawAllocData);
#else
return new T[size]*(?);
#endif
}
public static Object ObjectAlloc(TypeInstance typeInst, int size) public static Object ObjectAlloc(TypeInstance typeInst, int size)
{ {
#if BF_ENABLE_OBJECT_DEBUG_FLAGS #if BF_ENABLE_OBJECT_DEBUG_FLAGS

View file

@ -219,10 +219,36 @@ DisplayString = "{{ count={mSize} }}"
[[Type.Expand.Item]] [[Type.Expand.Item]]
Name = "[Count]" Name = "[Count]"
Value = "mSize" Value = "mSize"
[[Type.Expand.Item]]
Condition = "__getHighBits(mAllocSizeAndFlags, 1) == 1"
Name = "[AllocSize]"
Value = "__clearHighBits(mAllocSizeAndFlags, 1)"
[[Type.Expand.Item]]
Condition = "__getHighBits(mAllocSizeAndFlags, 1) == 0"
Name = "[InternalSize]"
Value = "__clearHighBits(mAllocSizeAndFlags, 1)"
[Type.Expand.ArrayItems] [Type.Expand.ArrayItems]
Size = "mSize" Size = "mSize"
ValuePointer = "mItems" ValuePointer = "mItems"
[[Type]]
Name = "System.Collections.Generic.Queue<*>"
DisplayString = "{{ count={mSize} }}"
[[Type.Expand.Item]]
Name = "[Count]"
Value = "mSize"
[[Type.Expand.Item]]
Condition = "__getHighBits(mAllocSizeAndFlags, 1) == 1"
Name = "[AllocSize]"
Value = "__clearHighBits(mAllocSizeAndFlags, 1)"
[[Type.Expand.Item]]
Condition = "__getHighBits(mAllocSizeAndFlags, 1) == 0"
Name = "[InternalSize]"
Value = "__clearHighBits(mAllocSizeAndFlags, 1)"
[Type.Expand.IndexListItems]
Size = "mSize"
ValueNode = "mItems[($i + mHead) % __clearHighBits(mAllocSizeAndFlags, 2)]"
[[Type]] [[Type]]
Name = "System.Collections.Generic.Dictionary<*, *>" Name = "System.Collections.Generic.Dictionary<*, *>"
DisplayString = "{{ count={mCount - mFreeCount} }}" DisplayString = "{{ count={mCount - mFreeCount} }}"