DragonECS/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs

315 lines
11 KiB
C#
Raw Normal View History

2025-03-14 16:53:25 +08:00
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
2024-11-01 20:43:15 +08:00
using DCFApixels.DragonECS.Internal;
using System;
2025-03-17 20:00:40 +08:00
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
2024-10-12 20:06:21 +08:00
using System.Text.RegularExpressions;
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class MetaIDAttribute : EcsMetaAttribute
{
public readonly string ID;
public MetaIDAttribute(string id)
{
if (string.IsNullOrEmpty(id))
{
Throw.ArgumentNull(nameof(id));
}
2024-10-12 20:06:21 +08:00
if (MetaID.IsGenericID(id) == false)
{
2024-10-13 15:21:49 +08:00
Throw.ArgumentException($"Identifier {id} contains invalid characters: ,<>");
}
2024-09-27 21:46:16 +08:00
id = string.Intern(id);
ID = id;
}
}
2024-09-16 19:29:48 +08:00
2024-10-12 20:06:21 +08:00
public static class MetaID
2024-09-16 19:29:48 +08:00
{
[ThreadStatic]
2024-09-27 21:46:16 +08:00
private static Random _randon;
[ThreadStatic]
private static byte[] _buffer;
[ThreadStatic]
private static bool _isInit;
2025-03-17 21:46:51 +08:00
public static bool TryFindMetaIDCollisions(IEnumerable<TypeMeta> metas, out CollisionList collisions)
{
collisions = new CollisionList(metas);
return collisions.IsHasAnyCollision;
}
2025-03-17 20:00:40 +08:00
public static CollisionList FindMetaIDCollisions(IEnumerable<TypeMeta> metas)
{
return new CollisionList(metas);
}
2024-10-12 20:06:21 +08:00
public static bool IsGenericID(string id)
{
return Regex.IsMatch(id, @"^[^,<>\s]*$");
}
2024-09-16 19:29:48 +08:00
public static unsafe string GenerateNewUniqueID()
{
if (_isInit == false)
{
IntPtr prt = Marshal.AllocHGlobal(1);
long alloc = (long)prt;
Marshal.Release(prt);
2024-09-27 21:46:16 +08:00
_randon = new Random((int)alloc);
_buffer = new byte[8];
_isInit = true;
}
2024-09-16 19:29:48 +08:00
byte* hibits = stackalloc byte[8];
long* hibitsL = (long*)hibits;
hibitsL[0] = DateTime.Now.Ticks;
hibitsL[1] = _randon.Next();
2024-09-16 19:29:48 +08:00
for (int i = 0; i < 8; i++)
2024-09-16 19:29:48 +08:00
{
_buffer[i] = hibits[i];
2024-09-16 19:29:48 +08:00
}
return BitConverter.ToString(_buffer).Replace("-", "");
2024-09-16 19:29:48 +08:00
}
2024-10-12 20:06:21 +08:00
public static string IDToAttribute(string id)
{
return $"[MetaID(\"id\")]";
}
public static string GenerateNewUniqueIDWithAttribute()
2024-09-16 19:29:48 +08:00
{
2024-10-12 20:06:21 +08:00
return IDToAttribute(GenerateNewUniqueID());
2024-09-16 19:29:48 +08:00
}
2025-03-17 20:00:40 +08:00
#region CollisionList
[DebuggerTypeProxy(typeof(DebuggerProxy))]
2025-03-17 21:46:51 +08:00
[DebuggerDisplay("HasAnyCollision: {IsHasAnyCollision} ListsCount: {ListsCount}")]
public class CollisionList : IEnumerable<CollisionList.Collision>
2025-03-17 20:00:40 +08:00
{
2025-03-17 21:46:51 +08:00
private LinkedList[] _linkedLists;
private Entry[] _entries;
2025-03-17 20:00:40 +08:00
private int _collisionsCount;
private int _listsCount;
public int CollisionsCount
{
get { return _collisionsCount; }
}
2025-03-17 21:46:51 +08:00
public int Count
2025-03-17 20:00:40 +08:00
{
get { return _listsCount; }
}
public bool IsHasAnyCollision
{
get { return _listsCount > 0; }
}
2025-03-17 21:46:51 +08:00
public Collision this[int index]
{
get
{
var list = _linkedLists[index];
return new Collision(this, list.headNode, list.count);
}
}
2025-03-17 20:00:40 +08:00
public CollisionList(IEnumerable<TypeMeta> metas)
{
var metasCount = metas.Count();
Dictionary<string, int> listIndexes = new Dictionary<string, int>(metasCount);
2025-03-17 21:46:51 +08:00
_linkedLists = new LinkedList[metasCount];
_entries = new Entry[metasCount];
2025-03-17 20:00:40 +08:00
bool hasCollision = false;
_listsCount = 0;
foreach (var meta in metas)
{
if (listIndexes.TryGetValue(meta.MetaID, out int headIndex))
{
hasCollision = true;
}
else
{
headIndex = _listsCount++;
listIndexes.Add(meta.MetaID, headIndex);
}
int nodeIndex = _collisionsCount++;
2025-03-17 21:46:51 +08:00
ref var list = ref _linkedLists[headIndex];
ref Entry entry = ref _entries[nodeIndex];
2025-03-17 20:00:40 +08:00
if (list.count == 0)
{
entry.next = -1;
}
2025-03-17 21:46:51 +08:00
else
{
entry.next = list.headNode;
}
2025-03-17 20:00:40 +08:00
entry.meta = meta;
2025-03-17 21:46:51 +08:00
list.headNode = nodeIndex;
listIndexes[meta.MetaID] = headIndex;
2025-03-17 20:00:40 +08:00
list.count++;
}
if (hasCollision)
{
for (int i = 0; i < _listsCount; i++)
{
ref var list = ref _linkedLists[i];
if (list.count <= 1)
{
2025-03-17 21:46:51 +08:00
_linkedLists[i--] = _linkedLists[--_listsCount];
2025-03-17 20:00:40 +08:00
}
}
}
else
{
_listsCount = 0;
}
}
2025-03-17 21:46:51 +08:00
[DebuggerDisplay("Count: {count}")]
private struct LinkedList
2025-03-17 20:00:40 +08:00
{
public int count;
2025-03-17 21:46:51 +08:00
public int headNode;
2025-03-17 20:00:40 +08:00
}
2025-03-17 21:46:51 +08:00
[DebuggerDisplay("ID: {meta.MetaID} next: {next}")]
2025-03-17 20:00:40 +08:00
public struct Entry
{
public TypeMeta meta;
public int next;
}
#region Enumerator
2025-03-17 21:46:51 +08:00
public Enumerator GetEnumerator() { return new Enumerator(this, _listsCount); }
2025-03-17 20:00:40 +08:00
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
2025-03-17 21:46:51 +08:00
IEnumerator<Collision> IEnumerable<Collision>.GetEnumerator() { return GetEnumerator(); }
public struct Enumerator : IEnumerator<Collision>
2025-03-17 20:00:40 +08:00
{
private readonly CollisionList _collisions;
private readonly int _count;
private int _index;
2025-03-17 21:46:51 +08:00
public Collision Current
2025-03-17 20:00:40 +08:00
{
get
{
var list = _collisions._linkedLists[_index];
2025-03-17 21:46:51 +08:00
return new Collision(_collisions, list.headNode, list.count);
2025-03-17 20:00:40 +08:00
}
}
object IEnumerator.Current { get { return Current; } }
public Enumerator(CollisionList collisions, int count)
{
_collisions = collisions;
_count = count;
_index = -1;
}
2025-03-17 21:46:51 +08:00
public bool MoveNext() { return ++_index < _count; }
2025-03-17 20:00:40 +08:00
public void Dispose() { }
public void Reset() { _index = -1; }
}
#endregion
2025-03-17 21:46:51 +08:00
[DebuggerDisplay("Count: {Count}")]
public readonly struct Collision : IEnumerable<TypeMeta>
2025-03-17 20:00:40 +08:00
{
private readonly CollisionList _collisions;
2025-03-17 21:46:51 +08:00
private readonly string _metaID;
2025-03-17 20:00:40 +08:00
private readonly int _head;
private readonly int _count;
public int Count
{
get { return _count; }
}
2025-03-17 21:46:51 +08:00
public string MetaID
{
get { return _metaID; }
}
internal Collision(CollisionList collisions, int head, int count)
2025-03-17 20:00:40 +08:00
{
_collisions = collisions;
2025-03-17 21:46:51 +08:00
if(count == 0)
{
_head = 0;
_metaID = string.Empty;
}
else
{
_head = head;
_metaID = collisions._entries[_head].meta.MetaID;
}
2025-03-17 20:00:40 +08:00
_head = head;
_count = count;
}
#region Enumerator
2025-03-17 21:46:51 +08:00
public Enumerator GetEnumerator() { return new Enumerator(_collisions._entries, _head); }
2025-03-17 20:00:40 +08:00
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
IEnumerator<TypeMeta> IEnumerable<TypeMeta>.GetEnumerator() { return GetEnumerator(); }
public struct Enumerator : IEnumerator<TypeMeta>
{
private readonly Entry[] _linkedEntries;
private readonly int _head;
private int _nextIndex;
private int _index;
public TypeMeta Current { get { return _linkedEntries[_index].meta; } }
object IEnumerator.Current { get { return Current; } }
public Enumerator(Entry[] linkedEntries, int head)
{
_linkedEntries = linkedEntries;
_head = head;
_nextIndex = _head;
_index = -1;
}
public bool MoveNext()
{
if (_nextIndex < 0) { return false; }
_index = _nextIndex;
_nextIndex = _linkedEntries[_index].next;
return true;
}
public void Dispose() { }
public void Reset()
{
_nextIndex = _head;
_index = -1;
}
}
#endregion
}
#region DebuggerProxy
private class DebuggerProxy
{
2025-03-17 21:46:51 +08:00
public string[][] Lists;
2025-03-17 20:00:40 +08:00
public DebuggerProxy(CollisionList collisions)
{
2025-03-17 21:46:51 +08:00
Lists = new string[collisions.Count][];
2025-03-17 20:00:40 +08:00
int i = 0;
foreach (var list in collisions)
{
int j = 0;
2025-03-17 21:46:51 +08:00
Lists[i] = new string[list.Count];
2025-03-17 20:00:40 +08:00
foreach (var typeMeta in list)
{
2025-03-17 21:46:51 +08:00
Lists[i][j] = typeMeta.MetaID;
2025-03-17 20:00:40 +08:00
j++;
}
i++;
}
}
}
#endregion
}
#endregion
2024-09-16 19:29:48 +08:00
}
2024-10-12 20:06:21 +08:00
}