diff --git a/Qrakhen.Qamp.Core/Collections/Addreg.cs b/Qrakhen.Qamp.Core/Collections/Addreg.cs
deleted file mode 100644
index 45e3d67..0000000
--- a/Qrakhen.Qamp.Core/Collections/Addreg.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-using Qrakhen.Qamp.Core.Collections.Abstractions;
-using Qrakhen.Qamp.Core.Values;
-using System.Collections;
-
-namespace Qrakhen.Qamp.Core.Collections;
-
-///
-/// Similar to a register with the caveat that the memory can free up or reserve blocks within itself, returning the next writeable address when storing data.
-///
-public class Addreg :
- IGetSet,
- IToArray,
- IEnumerable>
-{
- private const int BLOCK_SIZE = sizeof(int);
- private const uint BLOCK_FULL = (uint)((1UL << 32) - 1);
-
- private readonly TValue[] _data;
-
- private readonly uint[] _alloc;
-
- public int Size { get; }
-
- public TValue this[Address index]
- {
- get => _data[index];
- set => _data[index] = value;
- }
-
- public Addreg(int size = 4096)
- {
- Size = size;
- _data = new TValue[size];
- }
-
- private int GetBlockIndex(Address address)
- {
- return (int)(address / BLOCK_SIZE);
- }
-
- private int GetBitIndex(Address address)
- {
- return (int)(address % BLOCK_SIZE);
- }
-
- private Address SeekFree(Address from = default)
- {
- Address cur = from - (from % BLOCK_SIZE);
- while (cur < Size)
- {
- int block = GetBlockIndex(cur);
- uint mask = _alloc[block];
- if (mask < BLOCK_FULL)
- {
- int index = 0;
- while ((mask & (1 << index)) > 0)
- index++;
-
- return (Address)((long)block * BLOCK_SIZE + index);
- }
- cur += BLOCK_SIZE;
- }
- return Address.Void;
- }
-
- public void Free(Address address, int size = 1)
- {
- ArgumentOutOfRangeException.ThrowIfNotEqual(1, size); // only support single address freeing atm
- int block = GetBlockIndex(address);
- int index = GetBitIndex(address);
- uint flags = _alloc[block];
- _alloc[block] &= ~(1U << index);
- }
-
- public void Allocate(int size = 1)
- {
- ArgumentOutOfRangeException.ThrowIfNotEqual(1, size); // only support single address allocation atm
- Address address = SeekFree();
- int block = GetBlockIndex(address);
- int index = GetBitIndex(address);
- _alloc[block] |= (1U << index);
- }
-
- public TValue[] ToArray()
- {
- return _data.ToArray();
- }
-
- public TValue Get(Address index)
- {
- return _data[index];
- }
-
- public void Set(Address index, TValue value)
- {
- _data[index] = value;
- }
-
- public Address Add(TValue value)
- {
- Address free = SeekFree();
- if (free == Address.Void)
- throw new QampException($"Tried to add {value} to a memory block, but no free memory could be allocated.");
- Set(free, value);
- return free;
- }
-
- public IEnumerator> GetEnumerator()
- {
- for (Address addr = 0; addr < Size; addr++)
- yield return new KeyValuePair(addr, Get(addr));
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-}
diff --git a/Qrakhen.Qamp.Core/Collections/Table.cs b/Qrakhen.Qamp.Core/Collections/Table.cs
new file mode 100644
index 0000000..b18b371
--- /dev/null
+++ b/Qrakhen.Qamp.Core/Collections/Table.cs
@@ -0,0 +1,184 @@
+using Qrakhen.Qamp.Core.Collections.Abstractions;
+using Qrakhen.Qamp.Core.Values;
+using System;
+using System.Collections;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Qrakhen.Qamp.Core.Collections;
+
+///
+/// Similar to a register with as key;
+/// But with the caveat that the memory slots can be allocated or freed up,
+/// automatically seeking for free addresses when adding data,
+/// making it much easier to deal with addresses or pointers.
+///
+public class Table :
+ IGetSet,
+ IToArray,
+ IEnumerable>
+{
+ public const int BLOCK_SIZE = sizeof(int);
+ private const uint BLOCK_FULL = (uint)((1UL << 32) - 1);
+
+ private readonly TValue[] _data;
+ private readonly uint[] _alloc;
+
+ public int Size { get; }
+ public int Blocks => Size / BLOCK_SIZE;
+
+ public TValue this[Address index]
+ {
+ get => Get(index);
+ set => Set(index, value);
+ }
+
+ public Table(int size = 4096)
+ {
+ Size = size;
+ if (size % BLOCK_SIZE != 0)
+ throw new ArgumentException($"{nameof(Table)} size parameter must be product (multiple) of its block size {BLOCK_SIZE}.");
+ _data = new TValue[size];
+ _alloc = new uint[size / BLOCK_SIZE];
+ }
+
+ private int GetBlockIndex(Address address)
+ {
+ return (int)(address / BLOCK_SIZE);
+ }
+
+ private int GetBitIndex(Address address)
+ {
+ return (int)(address % BLOCK_SIZE);
+ }
+
+ public bool IsAllocated(Address address)
+ {
+ return (_alloc[GetBlockIndex(address)] & (1 << GetBitIndex(address))) > 0;
+ }
+
+ private void SetAllocBit(Address address, bool value)
+ {
+ int block = GetBlockIndex(address);
+ int index = GetBitIndex(address);
+ if (value)
+ _alloc[block] |= 1U << index;
+ else
+ _alloc[block] &= ~(1U << index);
+ }
+
+ private Address SeekFree(Address from = default)
+ {
+ Address cur = from - (from % BLOCK_SIZE);
+ while (cur < Size)
+ {
+ int block = GetBlockIndex(cur);
+ uint mask = _alloc[block];
+ if (mask < BLOCK_FULL)
+ {
+ int index = 0;
+ while ((mask & (1 << index)) > 0)
+ index++;
+
+ return (Address)((long)block * BLOCK_SIZE + index);
+ }
+ cur += BLOCK_SIZE;
+ }
+ return Address.Void;
+ }
+
+ private bool Validate(Address address)
+ {
+ if (address < 0 || address >= Size)
+ throw new RuntimeException($"Tried to access address outside memory {address}");
+ return true;
+ }
+
+ public void Free(Address address, int size = 1)
+ {
+ ArgumentOutOfRangeException.ThrowIfNotEqual(1, size); // only support single address freeing atm
+ SetAllocBit(address, false);
+ }
+
+ public Address Allocate(int size = 1)
+ {
+ ArgumentOutOfRangeException.ThrowIfNotEqual(1, size); // only support single address allocation atm
+ Address address = SeekFree();
+ SetAllocBit(address, true);
+ return address;
+ }
+
+ public TValue[] ToArray()
+ {
+ return [.. _data];
+ }
+
+ public TValue Get(Address index)
+ {
+ Validate(index);
+ if (!IsAllocated(index))
+ return default;
+ return _data[index];
+ }
+
+ public bool TryGet(Address index, [MaybeNullWhen(false)] out TValue? value)
+ {
+ Validate(index);
+ value = default;
+ if (!IsAllocated(index))
+ return false;
+ value = _data[index];
+ return true;
+ }
+
+ public void Set(Address index, TValue value)
+ {
+ Validate(index);
+ SetAllocBit(index, true);
+ _data[index] = value;
+ }
+
+ ///
+ /// Calls and then ,
+ /// returning the address at which was stored at.
+ ///
+ public Address Add(TValue value)
+ {
+ Address free = SeekFree();
+ if (free == Address.Void)
+ throw new QampException($"Tried to add {value} to a memory block, but no free memory could be allocated.");
+ Set(free, value);
+ return free;
+ }
+
+ public string Print()
+ {
+ string r = $"Table [{nameof(TValue)}]:\n";
+ int c = 0;
+ for (int b = 0; b < Blocks; b++)
+ {
+ uint block = _alloc[b];
+ if (block == 0)
+ continue;
+ for (int i = 0; i < BLOCK_SIZE; i++)
+ {
+ Address address = MakeAddress(b, i);
+ if (!IsAllocated(address))
+ continue;
+ c++;
+ r += $" {address}: {Get(address)}\n";
+ }
+ }
+ r += $"{c}/{Size - c} slots allocated.";
+ return r;
+ }
+
+ public static Address MakeAddress(int block, int index) => new((long)block * BLOCK_SIZE + index);
+
+ public IEnumerator> GetEnumerator()
+ {
+ for (Address addr = 0; addr < Size; addr++)
+ yield return new KeyValuePair(addr, Get(addr));
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+}
diff --git a/Qrakhen.Qamp.Core/Values/Address.cs b/Qrakhen.Qamp.Core/Values/Address.cs
index a3640bc..edd4ea4 100644
--- a/Qrakhen.Qamp.Core/Values/Address.cs
+++ b/Qrakhen.Qamp.Core/Values/Address.cs
@@ -7,7 +7,7 @@ public readonly record struct Address(long Value)
{
public static readonly Address Void = new Address(-1);
- public override string ToString() => $"&{Value:x8}";
+ public override string ToString() => Value < 0 ? "&void" : $"&{Value:x8}";
public static implicit operator long(Address address) => address.Value;
public static implicit operator Address(long value) => new(value);
diff --git a/Qrakhen.Qamp.Core/Values/Objects/Obj.cs b/Qrakhen.Qamp.Core/Values/Objects/Obj.cs
index bf76bcf..8ad8d7b 100644
--- a/Qrakhen.Qamp.Core/Values/Objects/Obj.cs
+++ b/Qrakhen.Qamp.Core/Values/Objects/Obj.cs
@@ -2,29 +2,39 @@
namespace Qrakhen.Qamp.Core.Values.Objects;
-public static class ObjRegister
+public class ObjTable(int size = 4096)
{
- private static readonly Addreg _register = [];
+ public static readonly ObjTable Shared = new ObjTable(4096);
+
+ private readonly Table _table = new Table(size);
+
+ public Obj? Get(Address address) => _table[address];
+ public T? Get(Address address) where T : Obj => _table[address] as T;
+ public bool TryGet(Address address, out Obj? obj) => _table.TryGet(address, out obj);
+ public void Free(Address address) => _table.Free(address);
+ public Address Register(Obj obj) => _table.Add(obj);
}
-public class Obj(ValueType type) : ITypedValue
+public class Obj : ITypedValue
{
- public bool __gcMarked = false;
- public int __gcCount = 1;
+ internal bool __GC_Marked = false;
+ internal int __GC_Count = 1;
- public readonly ValueType Type = type;
+ internal readonly Address __Address; // unsure lol
+ public readonly ValueType Type;
public ValueType ValueType => Type;
+ public Obj(ValueType type)
+ {
+ Type = type;
+ __Address = ObjTable.Shared.Register(this);
+ }
+
public static Value Create(Obj obj)
{
return new Value(Ptr.Create(obj));
}
public override string ToString() => "Obj";
-
- static Obj()
- {
-
- }
}
diff --git a/Qrakhen.Qamp.Core/Values/Objects/String.cs b/Qrakhen.Qamp.Core/Values/Objects/String.cs
index a37015d..5f56d9c 100644
--- a/Qrakhen.Qamp.Core/Values/Objects/String.cs
+++ b/Qrakhen.Qamp.Core/Values/Objects/String.cs
@@ -36,7 +36,7 @@ public class String(string? value) : Obj(ValueType.String)
List result = new();
foreach (var pair in _strings) {
String str = pair.Value;
- if (str == null || str.__gcMarked)
+ if (str == null || str.__GC_Marked)
continue;
byte[] bytes;
bytes = Encoding.ASCII.GetBytes(str.Value!);
diff --git a/Qrakhen.Qamp.Core/Values/Ptr.cs b/Qrakhen.Qamp.Core/Values/Ptr.cs
index 430029d..820df21 100644
--- a/Qrakhen.Qamp.Core/Values/Ptr.cs
+++ b/Qrakhen.Qamp.Core/Values/Ptr.cs
@@ -4,33 +4,25 @@ using Qrakhen.Qamp.Core.Collections;
namespace Qrakhen.Qamp.Core.Values;
+// generally interesting: GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Normal));
+
[StructLayout(LayoutKind.Sequential, Size = sizeof(ulong))]
public readonly struct Ptr
{
- private static readonly PushStack _register = new();
-
- private static readonly Collections.Addreg _memory = [];
+ private static readonly Table _register = []; // should not be needed that anymore tbh.
[Serialized] public readonly Address Address;
- //private readonly IntPtr _pointer;
- //public IntPtr Pointer => _pointer;
-
public Obj? Value {
get {
- if (_register.TryGet(Address, out Obj obj))
+ if (ObjTable.Shared.TryGet(Address, out Obj? obj))
return obj;
return null;
- //if (_pointer == IntPtr.Zero)
- // throw new ObjectDisposedException(nameof(Ptr));
- //return (Obj)GCHandle.FromIntPtr(_pointer).Target!;
}
}
private Ptr(Address address)
- {
-
- //_pointer = GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Normal));
+ {
Address = address;
}
@@ -61,7 +53,7 @@ public readonly struct Ptr
{
await Task.Run(() => {
foreach (var obj in _register) {
- obj.__gcCount = 0;
+ obj.Value.__GC_Count = 0;
}
});
}
@@ -69,9 +61,9 @@ public readonly struct Ptr
public static byte[] SerializeFunctions()
{
List result = new();
- for (int i = 0; i < _register.Count; i++) {
- Obj obj = _register.Get(i);
- if (obj == null || obj.__gcMarked)
+ foreach (var element in _register) {
+ Obj obj = element.Value;
+ if (obj == null || obj.__GC_Marked)
continue;
byte[] bytes;
if (obj is Function function) {
@@ -82,7 +74,7 @@ public readonly struct Ptr
continue;
}
- result.AddRange(i.GetBytes());
+ result.AddRange(((long)element.Key).GetBytes());
result.AddRange(bytes.Length.GetBytes());
result.AddRange(bytes);
result.Add(0);