wip/bindgen
copygirl 1 year ago
parent c6613679cc
commit 22dd934e95
  1. 3
      .gitmodules
  2. 17
      .vscode/launch.json
  3. 8
      .vscode/settings.json
  4. 14
      .vscode/tasks.json
  5. 14548
      flecs.h-ast
  6. 34
      gaemstone.ECS.sln
  7. 1
      src/flecs
  8. 421
      src/gaemstone.ECS.BindGen/Program.cs
  9. 21
      src/gaemstone.ECS.BindGen/gaemstone.ECS.BindGen.csproj
  10. 182
      src/gaemstone.ECS/flecs/flecs+Constants.g.cs
  11. 3773
      src/gaemstone.ECS/flecs/flecs+Functions.g.cs
  12. 1754
      src/gaemstone.ECS/flecs/flecs+Structs.g.cs
  13. 171
      src/gaemstone.Utility/Allocators.cs
  14. 34
      src/gaemstone.Utility/CStringExtensions.cs
  15. 30
      src/gaemstone.Utility/CallbackContextHelper.cs
  16. 26
      src/gaemstone.Utility/FlecsException.cs
  17. 41
      src/gaemstone.Utility/SpanExtensions.cs

3
.gitmodules vendored

@ -1,3 +1,6 @@
[submodule "src/flecs-cs"]
path = src/flecs-cs
url = https://github.com/flecs-hub/flecs-cs
[submodule "src/flecs"]
path = src/flecs
url = https://github.com/SanderMertens/flecs.git

@ -0,0 +1,17 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch BindGen",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"env": { "LIBCLANG_DISABLE_CRASH_RECOVERY": "true" },
"cwd": "${workspaceFolder}/src/gaemstone.ECS.BindGen",
"program": "${workspaceFolder}/src/gaemstone.ECS.BindGen/bin/Debug/net7.0/gaemstone.ECS.BindGen.dll",
"args": [],
"console": "internalConsole",
"stopAtEntry": false,
}
]
}

@ -0,0 +1,8 @@
{
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/bin": true,
"**/obj": true,
}
}

14
.vscode/tasks.json vendored

@ -0,0 +1,14 @@
{
"version": "2.0.0",
"tasks": [
{
"group": "build",
"label": "build",
"type": "shell", "command": "dotnet",
"options": { "cwd": "src/gaemstone.ECS.BindGen" },
"args": [ "build", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ],
"presentation": { "reveal": "silent" },
"problemMatcher": "$msCompile"
}
]
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0FC51081-529F-4DC2-91D0-18002C10B733}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaemstone.ECS", "src\gaemstone.ECS\gaemstone.ECS.csproj", "{7CDAB372-16EB-452C-B984-0BD5F1D0D411}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaemstone.ECS.BindGen", "src\gaemstone.ECS.BindGen\gaemstone.ECS.BindGen.csproj", "{4DA4F739-1C38-41D3-804B-B1114090FF53}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7CDAB372-16EB-452C-B984-0BD5F1D0D411}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CDAB372-16EB-452C-B984-0BD5F1D0D411}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CDAB372-16EB-452C-B984-0BD5F1D0D411}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CDAB372-16EB-452C-B984-0BD5F1D0D411}.Release|Any CPU.Build.0 = Release|Any CPU
{4DA4F739-1C38-41D3-804B-B1114090FF53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DA4F739-1C38-41D3-804B-B1114090FF53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DA4F739-1C38-41D3-804B-B1114090FF53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DA4F739-1C38-41D3-804B-B1114090FF53}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7CDAB372-16EB-452C-B984-0BD5F1D0D411} = {0FC51081-529F-4DC2-91D0-18002C10B733}
{4DA4F739-1C38-41D3-804B-B1114090FF53} = {0FC51081-529F-4DC2-91D0-18002C10B733}
EndGlobalSection
EndGlobal

@ -0,0 +1 @@
Subproject commit ddf4dfc8d0b09eec42876ea06f5f42849a0c23be

@ -0,0 +1,421 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using ClangSharp.Interop;
namespace gaemstone.ECS.BindGen;
public unsafe static class Program
{
// TODO: Handle being called from other working directories gracefully.
private const string FlecsRepositoryLocation = "../flecs";
private const string OutputFolderLocation = "../gaemstone.ECS/flecs";
private static readonly Dictionary<string, string> TypeAliases = new() {
{ "int8_t" , "sbyte" },
{ "int16_t", "short" },
{ "int32_t", "int" },
{ "int64_t", "long" },
{ "uint8_t" , "byte" },
{ "uint16_t", "ushort" },
{ "uint32_t", "uint" },
{ "uint64_t", "ulong" },
{ "ecs_flags8_t" , "byte" },
{ "ecs_flags16_t", "ushort" },
{ "ecs_flags32_t", "uint" },
{ "ecs_flags64_t", "ulong" },
{ "ecs_size_t", "int" },
};
public static void Main()
{
using var fileConstants = File.CreateText(Path.Combine(OutputFolderLocation, "flecs+Constants.g.cs"));
using var fileStructs = File.CreateText(Path.Combine(OutputFolderLocation, "flecs+Structs.g.cs"));
using var fileFunctions = File.CreateText(Path.Combine(OutputFolderLocation, "flecs+Functions.g.cs"));
static string StartProcessAndReturnOutput(string program, string arguments, string workDir = "")
{
var gitProcess = new Process { StartInfo = new() {
FileName = "git", Arguments = "describe --tags --always", WorkingDirectory = workDir,
RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true,
} };
gitProcess.Start();
var result = gitProcess.StandardOutput.ReadToEnd().Trim();
gitProcess.WaitForExit();
return result;
}
var bindgenVersion = StartProcessAndReturnOutput("git", "describe --tags --always");
var flecsVersion = StartProcessAndReturnOutput("git", "describe --tags --always", FlecsRepositoryLocation);
var autoGeneratedHeader = $$"""
// <auto-generated>
// Generated by gaemstone.ECS.BindGen {{ bindgenVersion }}
// Time: {{ DateTime.UtcNow :u}}
// Flecs version: {{ flecsVersion }}
// </auto-generated>
""";
fileConstants.WriteLine($$"""
{{ autoGeneratedHeader }}
#pragma warning disable CS8981
public static partial class flecs
{
""");
fileStructs.Write($$"""
{{ autoGeneratedHeader }}
using System.Runtime.InteropServices;
#pragma warning disable CS8981
public static unsafe partial class flecs
{
""");
fileFunctions.WriteLine($$"""
{{ autoGeneratedHeader }}
using System;
using System.Runtime.InteropServices;
#pragma warning disable CS8981
public static unsafe partial class flecs
{
private const string LibraryName = "flecs";
/// <summary> Indicates types are marked as "const" on C's end. </summary>
[AttributeUsage(AttributeTargets.Parameter, AttributeTargets.ReturnValue)]
public class ConstAttribute : Attribute { }
""");
using var unit = CXTranslationUnit.CreateFromSourceFile(
CXIndex.Create(), Path.Combine(FlecsRepositoryLocation, "flecs.h"),
default, default);
unit.Cursor.VisitChildren((cursor, _, _) => {
switch (cursor) {
case { Location.IsFromMainFile: false }: // Not from the file we're trying to parse.
case { Kind: CXCursorKind.CXCursor_MacroInstantiation
or CXCursorKind.CXCursor_InclusionDirective }: // Ignore these altogether.
break;
case { Kind: CXCursorKind.CXCursor_MacroDefinition }:
WriteMacro(fileConstants, 1, cursor);
break;
// case { Kind: CXCursorKind.CXCursor_TypedefDecl }:
// WriteTypedef(cursor, output);
// break;
case { Kind: CXCursorKind.CXCursor_EnumDecl }:
fileStructs.WriteLine();
WriteEnum(fileStructs, 1, cursor);
break;
case { Kind: CXCursorKind.CXCursor_StructDecl }:
// Skip forward declarations, unless they're not defined at all.
if (!cursor.IsDefinition && !cursor.Definition.IsNull) break;
fileStructs.WriteLine();
WriteStruct(fileStructs, 1, cursor);
break;
case { Kind: CXCursorKind.CXCursor_FunctionDecl }:
fileFunctions.WriteLine();
WriteFunction(fileFunctions, 1, cursor);
break;
default:
Console.WriteLine($"{cursor.Kind} {cursor}");
Console.WriteLine(GetSource(unit, cursor.Extent));
break;
}
return CXChildVisitResult.CXChildVisit_Continue;
}, default);
fileConstants.WriteLine("}");
fileStructs .WriteLine("}");
fileFunctions.WriteLine("}");
}
private static void WriteMacro(StreamWriter writer, int indent, CXCursor cursor)
{
if (cursor.IsMacroFunctionLike) return;
var unit = cursor.TranslationUnit;
var tokens = unit.Tokenize(cursor.Extent).ToArray();
if (tokens.Length < 2) return; // No value.
var type = "uint";
var name = cursor.Spelling.ToString();
if (name is "NULL" or "ECS_VECTOR_T_SIZE"
or "EcsLastInternalComponentId"
or "ECS_FUNC_NAME_BACK") return;
var value = GetSource(unit, Range(unit, tokens[1], tokens[^1]));
if (value is not [ '(', .., ')']) return;
if (value.Contains('\\')) value = value.Replace("\\\n", "").Replace(" ", "");
if (value.Contains("ull")) { value = value.Replace("ull", "ul"); type = "ulong"; }
if (name == "ECS_MAX_COMPONENT_ID") value = value.Replace("(uint32_t)", "(uint)");
WriteComment(writer, indent, cursor);
WriteIndent(writer, indent).WriteLine($"public const {type} {name} = {value};");
}
private static void WriteEnum(StreamWriter writer, int indent, CXCursor cursor)
{
WriteComment(writer, indent, cursor);
WriteIndent(writer, indent).WriteLine($"public {cursor.Type}");
WriteIndent(writer, indent).WriteLine("{");
cursor.VisitChildren((value, _, _) => {
WriteComment(writer, indent + 1, value);
WriteIndent(writer, indent + 1).WriteLine($"{value},");
return CXChildVisitResult.CXChildVisit_Continue;
}, default);
WriteIndent(writer, indent).WriteLine("}");
}
private static void WriteStruct(StreamWriter writer, int indent, CXCursor cursor,
string? name = null, bool noComment = false)
{
if (!noComment) WriteComment(writer, indent, cursor);
if (cursor.Kind == CXCursorKind.CXCursor_UnionDecl)
WriteIndent(writer, indent).WriteLine("[StructLayout(LayoutKind.Explicit)]");
name ??= cursor.Type.ToString();
if (name.StartsWith("struct ")) name = name[("struct ".Length)..];
WriteIndent(writer, indent).Write($"public struct {name}");
var hasFields = false; // For creating a shorthand struct definition that doesn't take up 3 lines.
cursor.VisitChildren((field, _, _) => {
// Nested struct and union declarations will be handled when field is written.
if (field.Kind is CXCursorKind.CXCursor_StructDecl
or CXCursorKind.CXCursor_UnionDecl)
return CXChildVisitResult.CXChildVisit_Continue;
if (field.Kind is not CXCursorKind.CXCursor_FieldDecl)
throw new NotSupportedException();
if (!hasFields) {
writer.WriteLine();
WriteIndent(writer, indent).WriteLine("{");
hasFields = true;
}
if (cursor.Kind == CXCursorKind.CXCursor_UnionDecl)
WriteIndent(writer, indent).WriteLine("[FieldOffset(0)]");
WriteComment(writer, indent + 1, field);
WriteIndent(writer, indent + 1);
var name = field.DisplayName.ToString();
if (IsReservedKeyword(name)) name = $"@{name}";
if (field.Type.Declaration.IsAnonymous) {
writer.WriteLine($"public _{field.DisplayName} {name};");
WriteStruct(writer, indent + 1, field.Type.Declaration,
name: $"_{field.DisplayName}", noComment: true);
} else if (field.Type.kind == CXTypeKind.CXType_ConstantArray) {
writer.WriteLine($"public fixed {field.Type.ArrayElementType} {name}[{field.Type.ArraySize}];");
} else {
var type = GetTypeString(field.Type, out var isConst);
if (isConst) writer.Write($"[Const] ");
writer.WriteLine($"public {type} {name};");
}
return CXChildVisitResult.CXChildVisit_Continue;
}, default);
if (!hasFields) writer.WriteLine(" { }");
else WriteIndent(writer, indent).WriteLine("}");
}
private static void WriteFunction(StreamWriter writer, int indent, CXCursor cursor)
{
WriteComment(writer, indent, cursor);
var returnType = GetTypeString(cursor.ReturnType, out var returnIsConst);
if (returnIsConst) WriteIndent(writer, indent).WriteLine("[return: Const]");
WriteIndent(writer, indent).WriteLine("[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]");
WriteIndent(writer, indent).Write($"public static extern {returnType} {cursor.Spelling}(");
for (var i = 0; i < cursor.NumArguments; i++) {
var arg = cursor.GetArgument((uint)i);
if (i > 0) writer.Write(", ");
var argType = GetTypeString(arg.Type, out var argIsConst);
if (argIsConst) writer.Write("[Const] ");
var name = arg.DisplayName.ToString();
if (IsReservedKeyword(name)) name = $"@{name}";
writer.Write($"{argType} {name}");
}
writer.WriteLine(");");
}
private static void WriteComment(StreamWriter writer, int indent, CXCursor cursor)
{
var comment = cursor.ParsedComment;
if (comment.Kind == CXCommentKind.CXComment_Null) return;
if (comment.Kind != CXCommentKind.CXComment_FullComment) throw new NotSupportedException();
var children = Children(comment).ToArray();
var paragraphs = children
.TakeWhile(c => c.Kind == CXCommentKind.CXComment_Paragraph)
.Select(GetCommentText).Where(p => p != null).ToArray();
var @params = children
.Where (c => c.Kind == CXCommentKind.CXComment_ParamCommand)
.Select(c => (c.ParamCommandComment_ParamName, GetCommentText(Children(c).Single()))).ToArray();
var @return = children
.Where (c => c.Kind == CXCommentKind.CXComment_BlockCommand)
.Where (c => c.BlockCommandComment_CommandName.ToString() == "return")
.Select(c => GetCommentText(Children(c).Single())).FirstOrDefault();
if (paragraphs.Length > 1) {
WriteIndent(writer, indent).WriteLine($"/// <summary>");
foreach (var paragraph in paragraphs)
WriteIndent(writer, indent).WriteLine($"/// <p> {paragraph} </p>");
WriteIndent(writer, indent).WriteLine($"/// </summary>");
} else if (paragraphs.Length == 1)
WriteIndent(writer, indent).WriteLine($"/// <summary> {paragraphs[0]} </summary>");
foreach (var (name, text) in @params)
WriteIndent(writer, indent).WriteLine($$"""/// <param name="{{ name }}"> {{ text }} </param>""");
if (@return != null)
WriteIndent(writer, indent).WriteLine($$"""/// <returns> {{ @return }} </returns>""");
foreach (var child in Children(comment))
switch (child.Kind) {
case CXCommentKind.CXComment_Paragraph:
case CXCommentKind.CXComment_ParamCommand:
case CXCommentKind.CXComment_BlockCommand when child.BlockCommandComment_CommandName.ToString() == "return":
break;
default:
WriteIndent(writer, indent);
writer.WriteLine($"//// {child.Kind}");
break;
}
static IEnumerable<CXComment> Children(CXComment parent)
=> Enumerable.Range(0, (int)parent.NumChildren).Select(i => parent.GetChild((uint)i));
static string? GetCommentText(CXComment paragraph)
{
if (paragraph.Kind != CXCommentKind.CXComment_Paragraph) throw new NotSupportedException();
var sb = new StringBuilder();
foreach (var part in Children(paragraph)) {
if (part.Kind != CXCommentKind.CXComment_Text) throw new NotSupportedException();
var str = part.TextComment_Text.ToString().Trim();
if (str == "") continue;
if (sb.Length > 0) sb.Append(' ');
sb.Append(str);
}
if (sb.Length == 0) return null;
sb.Replace("<", "&lt;").Replace(">", "&gt;");
if (sb[^1] != '.') sb.Append('.');
return sb.ToString();
}
}
private static StreamWriter WriteIndent(StreamWriter writer, int indent)
{
for (var i = 0; i < indent; i++)
writer.Write('\t');
return writer;
}
private static CXSourceRange Range(CXTranslationUnit unit, CXToken start, CXToken end)
=> CXSourceRange.Create(start.GetExtent(unit).Start, end.GetExtent(unit).End);
private static string GetTypeString(CXType type, out bool isConst)
{
isConst = false;
switch (type) {
case { kind: CXTypeKind.CXType_Pointer,
PointeeType: { kind: CXTypeKind.CXType_FunctionProto } funcType }:
var resultType = GetTypeString(funcType.ResultType, out _);
var argTypes = Enumerable.Range(0, funcType.NumArgTypes)
.Select(i => funcType.GetArgType((uint)i))
.Select(a => GetTypeString(a, out _))
.ToArray();
return $"delegate* unmanaged<{string.Join(", ", argTypes)}, {resultType}>";
case { kind: CXTypeKind.CXType_Pointer,
PointeeType: { kind: CXTypeKind.CXType_Elaborated,
Declaration.IsDefined: false} }:
return "void*";
case { kind: CXTypeKind.CXType_Pointer }:
return GetTypeString(type.PointeeType, out isConst) + "*";
case { kind: CXTypeKind.CXType_VariableArray
or CXTypeKind.CXType_IncompleteArray }:
return GetTypeString(type.ArrayElementType, out isConst) + "[]";
case { kind: CXTypeKind.CXType_Record }:
throw new NotSupportedException();
case { kind: CXTypeKind.CXType_Elaborated }:
var name = type.Declaration.ToString();
if (name == "") throw new Exception();
return name;
case { kind: CXTypeKind.CXType_FunctionProto }:
throw new NotSupportedException();
default:
name = type.ToString();
if (name == "") throw new Exception();
if (type.IsConstQualified) {
if (!name.StartsWith("const ")) throw new Exception();
name = name[("const ".Length)..];
isConst = true;
}
if (name.Contains(' ')) throw new Exception();
if (TypeAliases.TryGetValue(name, out var alias)) name = alias;
return name;
}
}
private static string GetSource(CXTranslationUnit unit, CXSourceRange range)
{
range.Start.GetFileLocation(out var startFile, out _, out _, out var start);
range.End .GetFileLocation(out var endFile , out _, out _, out var end);
if (startFile != endFile) return string.Empty;
return Encoding.UTF8.GetString(
unit.GetFileContents(startFile, out _)
.Slice((int)start, (int)(end - start)));
}
// See https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/
// Reserved keywords are unlikely to change in future C# versions, as they'd break
// existing programs. New ones are instead added as contextual keywords.
private static bool IsReservedKeyword(string str)
=> str is "abstract" or "as" or "base" or "bool" or "break" or "byte"
or "case" or "catch" or "char" or "checked" or "class"
or "const" or "continue" or "decimal" or "default"
or "delegate" or "do" or "double" or "else" or "enum"
or "event" or "explicit" or "extern" or "false" or "finally"
or "fixed" or "float" or "for" or "foreach" or "goto" or "if"
or "implicit" or "in" or "int" or "interface" or "internal"
or "is" or "lock" or "long" or "namespace" or "new" or "null"
or "object" or "operator" or "out" or "override" or "params"
or "private" or "protected" or "public" or "readonly" or "ref"
or "return" or "sbyte" or "sealed" or "short" or"sizeof"
or "stackalloc" or "static" or "string" or "struct" or "switch"
or "this" or "throw" or "true" or "try" or "typeof" or "uint"
or "ulong" or "unchecked" or "unsafe" or "ushort" or "using"
or "virtual" or "void" or "volatile" or "while";
}

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<LangVersion>preview</LangVersion>
<TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<RuntimeIdentifier>ubuntu.22.04-x64</RuntimeIdentifier>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ClangSharp" Version="15.0.2" />
</ItemGroup>
</Project>

@ -0,0 +1,182 @@
// <auto-generated>
// Generated by gaemstone.ECS.BindGen c661367
// Time: 2023-01-09 18:48:29Z
// Flecs version: v3.1.3
// </auto-generated>
#pragma warning disable CS8981
public static partial class flecs
{
public const uint ECS_ID_CACHE_SIZE = (32);
public const uint ECS_TERM_DESC_CACHE_SIZE = (16);
public const uint ECS_OBSERVER_DESC_EVENT_COUNT_MAX = (8);
public const uint ECS_VARIABLE_COUNT_MAX = (64);
public const uint EcsWorldQuitWorkers = (1u << 0);
public const uint EcsWorldReadonly = (1u << 1);
public const uint EcsWorldQuit = (1u << 2);
public const uint EcsWorldFini = (1u << 3);
public const uint EcsWorldMeasureFrameTime = (1u << 4);
public const uint EcsWorldMeasureSystemTime = (1u << 5);
public const uint EcsWorldMultiThreaded = (1u << 6);
public const uint EcsOsApiHighResolutionTimer = (1u << 0);
public const uint EcsOsApiLogWithColors = (1u << 1);
public const uint EcsOsApiLogWithTimeStamp = (1u << 2);
public const uint EcsOsApiLogWithTimeDelta = (1u << 3);
public const uint EcsEntityObserved = (1u << 31);
public const uint EcsEntityObservedId = (1u << 30);
public const uint EcsEntityObservedTarget = (1u << 29);
public const uint EcsEntityObservedAcyclic = (1u << 28);
public const uint EcsIdOnDeleteRemove = (1u << 0);
public const uint EcsIdOnDeleteDelete = (1u << 1);
public const uint EcsIdOnDeletePanic = (1u << 2);
public const uint EcsIdOnDeleteMask = (EcsIdOnDeletePanic|EcsIdOnDeleteRemove|EcsIdOnDeleteDelete);
public const uint EcsIdOnDeleteObjectRemove = (1u << 3);
public const uint EcsIdOnDeleteObjectDelete = (1u << 4);
public const uint EcsIdOnDeleteObjectPanic = (1u << 5);
public const uint EcsIdOnDeleteObjectMask = (EcsIdOnDeleteObjectPanic|EcsIdOnDeleteObjectRemove|EcsIdOnDeleteObjectDelete);
public const uint EcsIdExclusive = (1u << 6);
public const uint EcsIdDontInherit = (1u << 7);
public const uint EcsIdAcyclic = (1u << 8);
public const uint EcsIdTag = (1u << 9);
public const uint EcsIdWith = (1u << 10);
public const uint EcsIdUnion = (1u << 11);
public const uint EcsIdHasOnAdd = (1u << 15);
public const uint EcsIdHasOnRemove = (1u << 16);
public const uint EcsIdHasOnSet = (1u << 17);
public const uint EcsIdHasUnSet = (1u << 18);
public const uint EcsIdEventMask = (EcsIdHasOnAdd|EcsIdHasOnRemove|EcsIdHasOnSet|EcsIdHasUnSet);
public const uint EcsIdMarkedForDelete = (1u << 30);
public const uint EcsIterIsValid = (1u << 0u);
public const uint EcsIterIsFilter = (1u << 1u);
public const uint EcsIterIsInstanced = (1u << 2u);
public const uint EcsIterHasShared = (1u << 3u);
public const uint EcsIterTableOnly = (1u << 4u);
public const uint EcsIterEntityOptional = (1u << 5u);
public const uint EcsIterNoResults = (1u << 6u);
public const uint EcsIterIgnoreThis = (1u << 7u);
public const uint EcsIterMatchVar = (1u << 8u);
public const uint EcsEventTableOnly = (1u << 8u);
public const uint EcsEventNoOnSet = (1u << 16u);
public const uint EcsFilterMatchThis = (1u << 1u);
public const uint EcsFilterMatchOnlyThis = (1u << 2u);
public const uint EcsFilterMatchPrefab = (1u << 3u);
public const uint EcsFilterMatchDisabled = (1u << 4u);
public const uint EcsFilterMatchEmptyTables = (1u << 5u);
public const uint EcsFilterMatchAnything = (1u << 6u);
public const uint EcsFilterIsFilter = (1u << 7u);
public const uint EcsFilterIsInstanced = (1u << 8u);
public const uint EcsFilterPopulate = (1u << 9u);
public const uint EcsTableHasBuiltins = (1u << 1u);
public const uint EcsTableIsPrefab = (1u << 2u);
public const uint EcsTableHasIsA = (1u << 3u);
public const uint EcsTableHasChildOf = (1u << 4u);
public const uint EcsTableHasPairs = (1u << 5u);
public const uint EcsTableHasModule = (1u << 6u);
public const uint EcsTableIsDisabled = (1u << 7u);
public const uint EcsTableHasCtors = (1u << 8u);
public const uint EcsTableHasDtors = (1u << 9u);
public const uint EcsTableHasCopy = (1u << 10u);
public const uint EcsTableHasMove = (1u << 11u);
public const uint EcsTableHasUnion = (1u << 12u);
public const uint EcsTableHasToggle = (1u << 13u);
public const uint EcsTableHasOverrides = (1u << 14u);
public const uint EcsTableHasOnAdd = (1u << 15u);
public const uint EcsTableHasOnRemove = (1u << 16u);
public const uint EcsTableHasOnSet = (1u << 17u);
public const uint EcsTableHasUnSet = (1u << 18u);
public const uint EcsTableHasObserved = (1u << 20u);
public const uint EcsTableMarkedForDelete = (1u << 30u);
public const uint EcsTableHasLifecycle = (EcsTableHasCtors | EcsTableHasDtors);
public const uint EcsTableIsComplex = (EcsTableHasLifecycle | EcsTableHasUnion | EcsTableHasToggle);
public const uint EcsTableHasAddActions = (EcsTableHasIsA | EcsTableHasUnion | EcsTableHasCtors | EcsTableHasOnAdd | EcsTableHasOnSet);
public const uint EcsTableHasRemoveActions = (EcsTableHasIsA | EcsTableHasDtors | EcsTableHasOnRemove | EcsTableHasUnSet);
public const uint EcsQueryHasRefs = (1u << 1u);
public const uint EcsQueryIsSubquery = (1u << 2u);
public const uint EcsQueryIsOrphaned = (1u << 3u);
public const uint EcsQueryHasOutColumns = (1u << 4u);
public const uint EcsQueryHasMonitor = (1u << 5u);
public const uint EcsAperiodicEmptyTables = (1u << 1u);
public const uint EcsAperiodicComponentMonitors = (1u << 2u);
public const uint EcsAperiodicEmptyQueries = (1u << 4u);
public const uint ecs_world_t_magic = (0x65637377);
public const uint ecs_stage_t_magic = (0x65637373);
public const uint ecs_query_t_magic = (0x65637371);
public const uint ecs_rule_t_magic = (0x65637375);
public const uint ecs_table_t_magic = (0x65637374);
public const uint ecs_filter_t_magic = (0x65637366);
public const uint ecs_trigger_t_magic = (0x65637372);
public const uint ecs_observer_t_magic = (0x65637362);
public const uint ECS_ROW_MASK = (0x0FFFFFFFu);
public const uint ECS_ROW_FLAGS_MASK = (~ECS_ROW_MASK);
public const ulong ECS_ID_FLAGS_MASK = (0xFFul << 60);
public const ulong ECS_ENTITY_MASK = (0xFFFFFFFFul);
public const ulong ECS_GENERATION_MASK = (0xFFFFul << 32);
public const uint ECS_COMPONENT_MASK = (~ECS_ID_FLAGS_MASK);
public const uint FLECS_SPARSE_CHUNK_SIZE = (4096);
public const uint ECS_STRBUF_ELEMENT_SIZE = (511);
public const uint ECS_STRBUF_MAX_LIST_DEPTH = (32);
public const uint EcsSelf = (1u << 1);
public const uint EcsUp = (1u << 2);
public const uint EcsDown = (1u << 3);
public const uint EcsTraverseAll = (1u << 4);
public const uint EcsCascade = (1u << 5);
public const uint EcsParent = (1u << 6);
public const uint EcsIsVariable = (1u << 7);
public const uint EcsIsEntity = (1u << 8);
public const uint EcsFilter = (1u << 9);
public const uint EcsTraverseFlags = (EcsUp|EcsDown|EcsTraverseAll|EcsSelf|EcsCascade|EcsParent);
public const uint flecs_iter_cache_ids = (1u << 0u);
public const uint flecs_iter_cache_columns = (1u << 1u);
public const uint flecs_iter_cache_sources = (1u << 2u);
public const uint flecs_iter_cache_sizes = (1u << 3u);
public const uint flecs_iter_cache_ptrs = (1u << 4u);
public const uint flecs_iter_cache_match_indices = (1u << 5u);
public const uint flecs_iter_cache_variables = (1u << 6u);
public const uint flecs_iter_cache_all = (255);
public const uint ECS_HI_COMPONENT_ID = (256);
public const uint ECS_MAX_COMPONENT_ID = (~((uint)(ECS_ID_FLAGS_MASK >> 32)));
public const uint ECS_MAX_RECURSION = (512);
public const uint ECS_MAX_TOKEN_SIZE = (256);
public const ulong ECS_ID_FLAG_BIT = (1ul << 63);
public const uint EcsFirstUserComponentId = (32);
public const uint EcsFirstUserEntityId = (ECS_HI_COMPONENT_ID + 128);
public const uint ECS_INVALID_OPERATION = (1);
public const uint ECS_INVALID_PARAMETER = (2);
public const uint ECS_CONSTRAINT_VIOLATED = (3);
public const uint ECS_OUT_OF_MEMORY = (4);
public const uint ECS_OUT_OF_RANGE = (5);
public const uint ECS_UNSUPPORTED = (6);
public const uint ECS_INTERNAL_ERROR = (7);
public const uint ECS_ALREADY_DEFINED = (8);
public const uint ECS_MISSING_OS_API = (9);
public const uint ECS_OPERATION_FAILED = (10);
public const uint ECS_INVALID_CONVERSION = (11);
public const uint ECS_ID_IN_USE = (12);
public const uint ECS_CYCLE_DETECTED = (13);
public const uint ECS_LEAK_DETECTED = (14);
public const uint ECS_INCONSISTENT_NAME = (20);
public const uint ECS_NAME_IN_USE = (21);
public const uint ECS_NOT_A_COMPONENT = (22);
public const uint ECS_INVALID_COMPONENT_SIZE = (23);
public const uint ECS_INVALID_COMPONENT_ALIGNMENT = (24);
public const uint ECS_COMPONENT_NOT_REGISTERED = (25);
public const uint ECS_INCONSISTENT_COMPONENT_ID = (26);
public const uint ECS_INCONSISTENT_COMPONENT_ACTION = (27);
public const uint ECS_MODULE_UNDEFINED = (28);
public const uint ECS_MISSING_SYMBOL = (29);
public const uint ECS_ALREADY_IN_USE = (30);
public const uint ECS_ACCESS_VIOLATION = (40);
public const uint ECS_COLUMN_INDEX_OUT_OF_RANGE = (41);
public const uint ECS_COLUMN_IS_NOT_SHARED = (42);
public const uint ECS_COLUMN_IS_SHARED = (43);
public const uint ECS_COLUMN_TYPE_MISMATCH = (45);
public const uint ECS_INVALID_WHILE_READONLY = (70);
public const uint ECS_LOCKED_STORAGE = (71);
public const uint ECS_INVALID_FROM_WORKER = (72);
public const uint ECS_REST_DEFAULT_PORT = (27750);
public const uint ECS_STAT_WINDOW = (60);
public const uint ECS_MEMBER_DESC_CACHE_SIZE = (32);
public const uint ECS_META_MAX_SCOPE_DEPTH = (32);
public const uint ECS_HTTP_HEADER_COUNT_MAX = (32);
public const uint ECS_HTTP_QUERY_PARAM_COUNT_MAX = (32);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,171 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using static flecs_hub.flecs.Runtime;
namespace gaemstone.Utility;
public interface IAllocator
{
nint Allocate(int byteCount);
void Free(nint pointer);
}
public unsafe static class AllocatorExtensions
{
public static Span<T> Allocate<T>(this IAllocator allocator, int count) where T : unmanaged
=> new((void*)allocator.Allocate(sizeof(T) * count), count);
public static void Free<T>(this IAllocator allocator, Span<T> span) where T : unmanaged
=> allocator.Free((nint)Unsafe.AsPointer(ref span[0]));
public static Span<T> AllocateCopy<T>(this IAllocator allocator, ReadOnlySpan<T> orig) where T : unmanaged
{ var copy = allocator.Allocate<T>(orig.Length); orig.CopyTo(copy); return copy; }
public static ref T Allocate<T>(this IAllocator allocator) where T : unmanaged
=> ref Unsafe.AsRef<T>((void*)allocator.Allocate(sizeof(T)));
public static void Free<T>(this IAllocator allocator, ref T value) where T : unmanaged
=> allocator.Free((nint)Unsafe.AsPointer(ref value));
public static CString AllocateCString(this IAllocator allocator, string? value)
{
if (value == null) return default;
var bytes = Encoding.UTF8.GetByteCount(value);
var span = allocator.Allocate<byte>(bytes + 1);
Encoding.UTF8.GetBytes(value, span);
span[^1] = 0; // In case the allocated span is not cleared.
return new((nint)Unsafe.AsPointer(ref span[0]));
}
public static CString AllocateCString(this IAllocator allocator, ReadOnlySpan<byte> utf8)
{
var copy = allocator.Allocate<byte>(utf8.Length + 1);
utf8.CopyTo(copy);
copy[^1] = 0; // In case the allocated span is not cleared.
return new((nint)Unsafe.AsPointer(ref copy[0]));
}
}
public static class TempAllocator
{
public const int Capacity = 1024 * 1024; // 1 MB
private static readonly ThreadLocal<ArenaAllocator> _allocator
= new(() => new(Capacity));
public static ResetOnDispose Use()
{
var allocator = _allocator.Value!;
return new(allocator, allocator.Used);
}
public sealed class ResetOnDispose
: IAllocator
, IDisposable
{
private readonly ArenaAllocator _allocator;
private readonly int _start;
public ResetOnDispose(ArenaAllocator allocator, int start)
{ _allocator = allocator; _start = start; }
// TODO: Print warning in finalizer if Dispose wasn't called manually.
// IAllocator implementation
public nint Allocate(int byteCount) => _allocator.Allocate(byteCount);
public void Free(nint pointer) { /* Do nothing. */ }
// IDisposable implementation
public void Dispose() => _allocator.Reset(_start);
}
}
public class GlobalHeapAllocator
: IAllocator
{
public static GlobalHeapAllocator Instance { get; } = new();
public nint Allocate(int byteCount)
=> Marshal.AllocHGlobal(byteCount);
public void Free(nint pointer)
=> Marshal.FreeHGlobal(pointer);
}
public class ArenaAllocator
: IAllocator
, IDisposable
{
private nint _buffer;
public int Capacity { get; private set; }
public int Used { get; private set; } = 0;
public ArenaAllocator(int capacity)
{
if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity));
_buffer = Marshal.AllocHGlobal(capacity);
Capacity = capacity;
}
public void Dispose()
{
Marshal.FreeHGlobal(_buffer);
_buffer = default;
Capacity = 0;
}
public nint Allocate(int byteCount)
{
if (_buffer == default) throw new ObjectDisposedException(nameof(ArenaAllocator));
if (Used + byteCount > Capacity) throw new InvalidOperationException(
$"Cannot allocate more than {Capacity} bytes with this {nameof(ArenaAllocator)}");
var ptr = _buffer + Used;
Used += byteCount;
return ptr;
}
public void Free(nint pointer)
{ /* Do nothing. */ }
public unsafe void Reset(int start = 0)
{
if (start > Used) throw new ArgumentOutOfRangeException(nameof(start));
new Span<byte>((void*)(_buffer + start), Used - start).Clear();
Used = start;
}
}
public class RingAllocator
: IAllocator
, IDisposable
{
private nint _buffer;
private int _current = 0;
public int Capacity { get; private set; }
public RingAllocator(int capacity)
{
if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity));
_buffer = Marshal.AllocHGlobal(capacity);
Capacity = capacity;
}
public void Dispose()
{
Marshal.FreeHGlobal(_buffer);
_buffer = default;
Capacity = 0;
}
public nint Allocate(int byteCount)
{
if (_buffer == default) throw new ObjectDisposedException(nameof(RingAllocator));
if (byteCount > Capacity) throw new ArgumentOutOfRangeException(nameof(byteCount));
if (_current + byteCount > Capacity) _current = 0;
var ptr = _buffer + _current;
_current += byteCount;
return ptr;
}
public void Free(nint pointer)
{ /* Do nothing. */ }
}

@ -1,34 +0,0 @@
using System;
using System.Runtime.InteropServices;
using static flecs_hub.flecs;
using static flecs_hub.flecs.Runtime;
namespace gaemstone.Utility;
public unsafe static class CStringExtensions
{
public static CString Empty { get; } = (CString)"";
public static CString ETX { get; } = (CString)"\x3"; // TODO: Temporary, until flecs supports Empty.
public static unsafe byte[]? FlecsToBytes(this CString str)
{
if (str.IsNull) return null;
var pointer = (byte*)(nint)str;
// Find length of the string by locating the NUL character.
var length = 0; while (true) if (pointer[length++] == 0) break;
// Create span over the region, NUL included, copy it into an array.
return new Span<byte>(pointer, length).ToArray();
}
public static byte[]? FlecsToBytesAndFree(this CString str)
{ var result = str.FlecsToBytes(); str.FlecsFree(); return result; }
public static string? FlecsToString(this CString str)
=> Marshal.PtrToStringUTF8(str);
public static string? FlecsToStringAndFree(this CString str)
{ var result = str.FlecsToString(); str.FlecsFree(); return result; }
public static void FlecsFree(this CString str)
{ if (!str.IsNull) ecs_os_get_api().free_.Data.Pointer((void*)(nint)str); }
}

@ -1,30 +0,0 @@
using System.Collections.Generic;
namespace gaemstone.Utility;
public static class CallbackContextHelper
{
private static readonly Dictionary<nint, object> _contexts = new();
private static nint _counter = 0;
public static nint Create<T>(T context) where T : notnull
{
lock (_contexts) {
var id = _counter++;
_contexts.Add(id, context);
return id;
}
}
public static T Get<T>(nint id)
{
lock (_contexts)
return (T)_contexts[id];
}
public static void Free(nint id)
{
lock (_contexts)
_contexts.Remove(id);
}
}

@ -1,26 +0,0 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace gaemstone.Utility;
public class FlecsException
: Exception
{
public FlecsException() : base() { }
public FlecsException(string message) : base(message) { }
}
public class FlecsAbortException
: FlecsException
{
private readonly string _stackTrace = new StackTrace(2, true).ToString();
public override string? StackTrace => _stackTrace;
private FlecsAbortException()
: base("Abort was called by flecs") { }
[UnmanagedCallersOnly]
internal static void Callback()
=> throw new FlecsAbortException();
}

@ -1,41 +0,0 @@
using System;
namespace gaemstone.Utility;
public static class SpanExtensions
{
public static T? GetOrNull<T>(this Span<T> span, int index)
where T : struct => GetOrNull((ReadOnlySpan<T>)span, index);
public static T? GetOrNull<T>(this ReadOnlySpan<T> span, int index)
where T : struct => (index >= 0 && index < span.Length) ? span[index] : null;
public static ReadOnlySpanSplitEnumerator<T> Split<T>(this ReadOnlySpan<T> span, T splitOn)
where T : IEquatable<T> => new(span, splitOn);
public ref struct ReadOnlySpanSplitEnumerator<T>
where T : IEquatable<T>
{
private readonly ReadOnlySpan<T> _span;
private readonly T _splitOn;
private int _index = -1;
private int _end = -1;
public ReadOnlySpanSplitEnumerator(ReadOnlySpan<T> span, T splitOn)
{ _span = span; _splitOn = splitOn; }
public ReadOnlySpanSplitEnumerator<T> GetEnumerator() => this;
public ReadOnlySpan<T> Current
=> (_end >= 0) && (_end <= _span.Length)
? _span[_index.._end] : throw new InvalidOperationException();
public bool MoveNext()
{
if (_end == _span.Length) return false;
_end++; _index = _end;
while (_end < _span.Length && !_span[_end].Equals(_splitOn)) _end++;
return true;
}
}
}
Loading…
Cancel
Save