DragonECS/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs

407 lines
14 KiB
C#
Raw Normal View History

2025-03-14 16:53:25 +08:00
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Core;
2025-05-18 00:49:12 +08:00
using DCFApixels.DragonECS.Core.Internal;
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))
{
2025-03-19 16:16:33 +08:00
EcsDebug.PrintError("The identifier cannot be empty or null");
id = string.Empty;
}
2025-03-19 16:16:33 +08:00
if (MetaID.IsValidID(id) == false)
{
2025-03-19 16:16:33 +08:00
EcsDebug.PrintError($"Identifier {id} contains invalid characters. Allowed charset: {MetaID.ALLOWED_CHARSET}");
id = string.Empty;
}
ID = id;
}
}
2024-09-16 19:29:48 +08:00
2025-03-19 16:16:33 +08:00
public static unsafe class MetaID
2024-09-16 19:29:48 +08:00
{
2025-03-19 16:16:33 +08:00
public const string ALLOWED_CHARSET = "_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
2025-03-19 16:51:08 +08:00
//public static bool IsFixedNameType(Type type)
//{
// if (type.IsPrimitive)
// {
// return true;
// }
// if(type == typeof(string))
// {
// return true;
// }
// return false;
//}
2024-10-12 20:06:21 +08:00
public static bool IsGenericID(string id)
{
2025-03-20 10:55:43 +08:00
return id[id.Length - 1] == '>' || Regex.IsMatch(id, @"^[^,<>\s]*$");
2024-10-12 20:06:21 +08:00
}
2025-03-20 10:55:43 +08:00
public static bool IsValidID(string id)
2025-03-19 16:16:33 +08:00
{
2025-03-20 10:55:43 +08:00
return Regex.IsMatch(id, @"^[a-zA-Z0-9_]+$");
2025-03-19 16:16:33 +08:00
}
2024-10-12 20:06:21 +08:00
2025-03-19 16:16:33 +08:00
[ThreadStatic]
private static uint _randonState;
public static string GenerateNewUniqueID()
2024-09-16 19:29:48 +08:00
{
2025-03-19 16:16:33 +08:00
const int BYTES = 16;
const int CHARS = BYTES * 2;
const string CHARSET = "0123456789ABCDEF";
if (_randonState == 0)
{
IntPtr prt = Marshal.AllocHGlobal(1);
long alloc = (long)prt;
2025-03-19 16:16:33 +08:00
Marshal.FreeHGlobal(prt);
_randonState = (uint)alloc ^ (uint)DateTime.Now.Millisecond;
}
2024-09-16 19:29:48 +08:00
2025-03-19 16:16:33 +08:00
byte* bytes = stackalloc byte[BYTES];
Span<byte> x = new Span<byte>(bytes, BYTES);
long* bytesLong = (long*)bytes;
uint* bytesUInt = (uint*)bytes;
bytesLong[0] = DateTime.Now.Ticks;
_randonState = BitsUtility.NextXorShiftState(_randonState);
bytesUInt[2] = _randonState;
_randonState = BitsUtility.NextXorShiftState(_randonState);
bytesUInt[3] = _randonState;
2024-09-16 19:29:48 +08:00
2025-03-19 16:16:33 +08:00
char* str = stackalloc char[CHARS];
for (int i = 0, j = 0; i < BYTES; i++)
2024-09-16 19:29:48 +08:00
{
2025-03-19 16:16:33 +08:00
byte b = bytes[i];
str[j++] = CHARSET[b & 0x0000_000F];
str[j++] = CHARSET[(b >> 4) & 0x0000_000F];
2024-09-16 19:29:48 +08:00
}
2025-03-19 16:16:33 +08:00
return new string(str, 0, CHARS);
2024-09-16 19:29:48 +08:00
}
2024-10-12 20:06:21 +08:00
public static string IDToAttribute(string id)
{
2025-03-19 16:16:33 +08:00
return $"[MetaID(\"{id}\")]";
}
public static string ConvertIDToTypeName(string id)
{
id = id.Replace("_1", "__");
id = id.Replace("_2", "__");
id = id.Replace("_3", "__");
id = id.Replace("<", "_1");
id = id.Replace(">", "_2");
id = id.Replace(",", "_3");
return "_" + id;
}
public static string ParseIDFromTypeName(string name)
{
2025-05-18 00:49:12 +08:00
using (StackAllocator.Alloc(name.Length, out char* buffer))
2025-03-19 16:16:33 +08:00
{
2025-05-18 00:49:12 +08:00
int count = 0;
//skip name[0] char
for (int i = 1, iMax = name.Length; i < iMax; i++)
2025-03-19 16:16:33 +08:00
{
2025-05-18 00:49:12 +08:00
char current = name[i];
if (current == '_')
2025-03-19 16:16:33 +08:00
{
2025-05-18 00:49:12 +08:00
if (++i >= iMax) { break; }
current = name[i];
switch (current)
{
case '1': current = '<'; break;
case '2': current = '>'; break;
case '3': current = ','; break;
}
2025-03-19 16:16:33 +08:00
}
2025-05-18 00:49:12 +08:00
buffer[count++] = current;
2025-03-19 16:16:33 +08:00
}
2025-05-18 00:49:12 +08:00
return new string(buffer, 0, count);
2025-03-19 16:16:33 +08:00
}
2024-10-12 20:06:21 +08:00
}
2025-03-19 16:16:33 +08:00
2024-10-12 20:06:21 +08:00
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
2025-03-19 16:16:33 +08:00
public static bool TryFindMetaIDCollisions(IEnumerable<TypeMeta> metas, out CollisionList collisions)
{
collisions = new CollisionList(metas);
return collisions.IsHasAnyCollision;
}
public static CollisionList FindMetaIDCollisions(IEnumerable<TypeMeta> metas)
{
return new CollisionList(metas);
}
2025-03-17 20:00:40 +08:00
#region CollisionList
[DebuggerTypeProxy(typeof(DebuggerProxy))]
2025-03-20 10:55:43 +08:00
[DebuggerDisplay("HasAnyCollision: {IsHasAnyCollision} ListsCount: {Count}")]
2025-03-17 21:46:51 +08:00
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;
2025-03-20 13:36:37 +08:00
private HashSet<string> _collidingIDs;
2025-03-17 20:00:40 +08:00
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
2025-03-20 13:36:37 +08:00
public bool IsCollidingID(string id)
{
2025-04-19 10:53:52 +08:00
if (_collidingIDs == null)
2025-03-20 13:36:37 +08:00
{
return false;
}
return _collidingIDs.Contains(id);
}
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)
{
2025-03-20 10:55:43 +08:00
if (meta.IsHasMetaID() == false) { continue; }
2025-03-17 20:00:40 +08:00
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)
{
2025-03-20 13:36:37 +08:00
_collidingIDs = new HashSet<string>();
2025-03-17 20:00:40 +08:00
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
}
}
2025-03-20 13:36:37 +08:00
for (int i = 0; i < _listsCount; i++)
{
_collidingIDs.Add(this[i].MetaID);
}
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}")]
2025-03-20 10:55:43 +08:00
[DebuggerTypeProxy(typeof(DebuggerProxy))]
2025-03-17 21:46:51 +08:00
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-20 10:55:43 +08:00
if (count == 0)
2025-03-17 21:46:51 +08:00
{
_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
2025-03-20 10:55:43 +08:00
#region DebuggerProxy
private class DebuggerProxy
{
public Type[] Types;
public DebuggerProxy(Collision collision)
{
Types = collision.Select(o => o.Type).ToArray();
}
}
#endregion
}
2025-03-17 20:00:40 +08:00
#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
}