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);