1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-18 16:10:26 +02:00
Beef/BeefLibs/corlib/src/Event.bf
2021-08-01 14:38:09 -03:00

329 lines
5.9 KiB
Beef

using System.Collections;
using System.Diagnostics;
namespace System
{
// Event owns the delegates it contains
// Event supports removals and additions while enumerating without invalidating the enumerator.
// Event can also safely be Disposed and even have its memory reclaimed during enumeration
// Event is not thread-safe: access must be protected during modification, and during enumeration
struct Event<T> where T : Delegate
{
// mData can be null with no listeners, T for one listener, or List<T> for multiple
// If we are enumerating then mData points to the enumerator.
int mData;
const int sIsEnumerating = 1;
const int sHadEnumRemoves = 2;
const int sFlagsMask = 3;
const int sDataMask = ~sFlagsMask;
public bool HasListeners
{
get
{
return Target != null;
}
}
public bool IsEmpty
{
get
{
Object data = Internal.UnsafeCastToObject((void*)(mData & sDataMask));
if (data == null)
return true;
if (var list = data as List<T>)
{
return list.Count == 0;
}
return false;
}
}
public int Count
{
get
{
Object data = Internal.UnsafeCastToObject((void*)(mData & sDataMask));
if (data == null)
return 0;
if (var list = data as List<T>)
{
return list.Count;
}
return 1;
}
}
public Object Target
{
get
{
if (mData & sIsEnumerating != 0)
{
Enumerator* enumerator = (Enumerator*)(void*)(mData & sDataMask);
return enumerator.[Friend]mTarget;
}
return Internal.UnsafeCastToObject((void*)mData);
}
set mut
{
if (mData & sIsEnumerating != 0)
{
Enumerator* enumerator = (Enumerator*)(void*)(mData & sDataMask);
enumerator.[Friend]mTarget = value;
}
else
{
mData = (int)Internal.UnsafeCastToPtr(value);
}
}
}
protected override void GCMarkMembers()
{
if (mData & sDataMask != 0)
GC.Mark(Internal.UnsafeCastToObject((void*)mData));
}
public void Add(T ownDelegate) mut
{
Object data = Target;
if (data == null)
{
Target = ownDelegate;
return;
}
if (var list = data as List<T>)
{
list.Add(ownDelegate);
}
else
{
var list = new List<T>();
list.Add((T)data);
list.Add(ownDelegate);
Target = list;
}
}
public void AddFront(T ownDelegate) mut
{
Object data = Target;
if (data == null)
{
Target = ownDelegate;
return;
}
if (var list = data as List<T>)
{
list.Insert(0, ownDelegate);
}
else
{
var list = new List<T>();
list.Add(ownDelegate);
list.Add((T)data);
Target = list;
}
}
public Result<void> Remove(T compareDelegate, bool deleteDelegate = false) mut
{
Object data = Target;
if (var list = data as List<T>)
{
int32 idx = -1;
for (int32 i = 0; i < list.Count; i++)
if (Delegate.Equals(list[i], compareDelegate))
{
idx = i;
break;
}
if (idx == -1)
return .Err;
if (deleteDelegate)
delete list[idx];
if (mData & sIsEnumerating != 0)
{
// In order to avoid invalidating the enumerator's index during enumeration, we
// just null this entry out and then we physically remove the entries upon
// Dispose of the enumerator
list[idx] = null;
mData |= sHadEnumRemoves;
}
else
list.RemoveAt(idx);
if (list.Count == 0)
{
delete list;
Target = null;
}
}
else
{
T dlgMember = (T)data;
if (!Delegate.Equals(compareDelegate, dlgMember))
return .Err;
if (deleteDelegate)
delete dlgMember;
Target = null;
}
return .Ok;
}
public rettype(T) Invoke(params T p) mut
{
var result = default(rettype(T));
for (var dlg in this)
{
result = dlg(params p);
}
return result;
}
public void Dispose() mut
{
if (mData == 0)
return;
var data = Target;
if (var list = data as List<T>)
{
for (var dlg in list)
delete dlg;
}
delete data;
Target = null;
}
public Enumerator GetEnumerator() mut
{
return Enumerator(ref this);
}
public struct Enumerator : IEnumerator<T>
{
Event<T>* mEvent;
Object mTarget;
int32 mIdx;
Enumerator* mRootEnumerator;
T mCurrent;
public this(ref Event<T> event)
{
mEvent = &event;
mIdx = -2;
mRootEnumerator = null;
mCurrent = null;
mTarget = null;
}
public void Dispose() mut
{
if (mRootEnumerator == &this)
{
if ((mEvent.mData & Event<T>.sHadEnumRemoves != 0) && (mTarget != null))
{
var list = (List<T>)mTarget;
// Remove nulls
for (int32 i = 0; i < list.Count; i++)
{
if (list[i] == null)
{
list.RemoveAt(i);
i--;
}
}
if (list.Count == 0)
{
delete list;
mEvent.mData = 0;
return;
}
}
mEvent.mData = (int)Internal.UnsafeCastToPtr(mTarget);
}
}
public void Reset() mut
{
mIdx = -1;
}
public bool MoveNext() mut
{
if (mIdx == -2)
{
// Attach
if (mEvent.mData & sIsEnumerating == 0)
{
mTarget = mEvent.Target;
mEvent.mData = (int)(void*)(&this) | sIsEnumerating;
mRootEnumerator = &this;
}
else
{
mRootEnumerator = (Enumerator*)(void*)(mEvent.mData & Event<T>.sDataMask);
}
mIdx = -1;
}
var data = mRootEnumerator.mTarget;
if (data == null)
return false;
if (var list = data as List<T>)
{
repeat
{
mIdx++;
if (mIdx >= list.Count)
return false;
mCurrent = list[mIdx];
}
while (mCurrent == null);
}
else
{
mIdx++;
if (mIdx > 0)
return false;
mCurrent = (T)data;
}
return true;
}
public T Current
{
get
{
return mCurrent;
}
}
public Result<T> GetNext() mut
{
if (!MoveNext())
return .Err;
return mCurrent;
}
}
}
}