Skip to content

IDisposable in ZeroLib #217

@ScottKane

Description

@ScottKane

Could you add IDisposable to ZeroLib please? I'm testing out building an allocator with bflat but because Zero doesn't come with IDisposable I can't use the using keyword meaning it will always need to be done manually.

using System.Runtime.InteropServices;

Console.Write("Free bytes: ");
Console.WriteInt(Allocator.Available);
Console.WriteLine("");

var array = new int[1000];
var objects = new object[10];
for (var i = 0; i < 10; i++)
    objects[i] = new object();

object boxed = 42;

var box = new Box<int>(100);
Console.Write("Box value: ");
Console.WriteInt(box.Value);
Console.WriteLine("");

var vec = new Vec<int>();

for (var i = 0; i < 5; i++)
{
    vec.Push(i * i);
}

Console.Write("Vec contents: ");
for (var i = 0; i < vec.Length; i++)
{
    Console.WriteInt(vec[i]);
    Console.Write(" ");
}
Console.WriteLine("");

Console.Write("Free bytes: ");
Console.WriteInt(Allocator.Available);
Console.WriteLine("");

box.Dispose();
vec.Dispose();

public unsafe struct Console
{
    [DllImport("kernel32.dll")]
    private static extern void* GetStdHandle(uint nStdHandle);
    
    [DllImport("kernel32.dll")]
    private static extern bool WriteFile(void* hFile, void* lpBuffer, uint nNumberOfBytesToWrite, uint* lpNumberOfBytesWritten, void* lpOverlapped);
    
    private static void* _stdoutHandle;
    
    private static void EnsureHandle()
    {
        if (_stdoutHandle == null)
            _stdoutHandle = GetStdHandle(0xFFFFFFF5);
    }
    
    private static void WriteString(string message)
    {
        EnsureHandle();
        
        var bytes = stackalloc byte[message.Length];
        for (var i = 0; i < message.Length; i++)
            bytes[i] = (byte)message[i];
        
        uint written;
        WriteFile(_stdoutHandle, bytes, (uint)message.Length, &written, null);
    }
    
    public static void Write(string message) => WriteString(message);
    
    public static void WriteLine(string message)
    {
        WriteString(message);
        WriteString("\n");
    }
    
    public static void WriteInt(int value)
    {
        if (value == 0)
        {
            WriteString("0");
            return;
        }
        
        var isNegative = value < 0;
        if (isNegative) value = -value;
        
        var temp = value;
        var digitCount = 0;
        while (temp > 0)
        {
            temp /= 10;
            digitCount++;
        }
        
        var totalLen = digitCount + (isNegative ? 1 : 0);
        var buffer = stackalloc byte[totalLen];
        
        var pos = totalLen - 1;
        temp = value;
        while (temp > 0)
        {
            buffer[pos--] = (byte)('0' + (temp % 10));
            temp /= 10;
        }
        
        if (isNegative)
            buffer[0] = (byte)'-';
        
        EnsureHandle();
        uint written;
        WriteFile(_stdoutHandle, buffer, (uint)totalLen, &written, null);
    }
}

public unsafe struct Allocator
{
    [StructLayout(LayoutKind.Sequential, Size = 16 * 1024 * 1024)]
    private struct Heap;
    
    private struct Block
    {
        public int Size;
        public bool Used;
    }
    
    private static readonly Heap _heap;
    private static readonly byte* _start;
    private static readonly byte* _end;
    
    static Allocator()
    {
        fixed (Heap* pointer = &_heap)
        {
            _start = (byte*)pointer;
            _end = _start + sizeof(Heap);
        }
        
        var block = (Block*)_start;
        block->Size = sizeof(Heap) - sizeof(Block);
        block->Used = false;
    }

#if WINDOWS
    [UnmanagedCallersOnly(EntryPoint = "LocalAlloc")]
    public static void* LocalAlloc(uint flags, uint size)
    {
        var result = Allocate((int)size);
        if (result != null && (flags & 0x40) != 0)
            Set(result, 0, (int)size);
        
        return result;
    }
    
    [UnmanagedCallersOnly(EntryPoint = "LocalFree")]
    public static void* LocalFree(void* ptr)
    {
        Free(ptr);
        return null;
    }
#else
    [UnmanagedCallersOnly(EntryPoint = "SystemNative_Malloc")]
    public static void* SystemNative_Malloc(nuint size) => Allocate((int)size);

    [UnmanagedCallersOnly(EntryPoint = "SystemNative_Free")]
    public static void SystemNative_Free(void* pointer) => Free(pointer);
#endif
    
    public static void* Allocate(int size)
    {
        if (size <= 0) return null;
        
        size = (size + 7) & ~7;
        
        var current = (Block*)_start;
        
        while ((byte*)current < _end)
        {
            if (!current->Used && current->Size >= size)
            {
                if (current->Size > size + sizeof(Block) + 16)
                {
                    var next = (Block*)((byte*)current + sizeof(Block) + size);
                    next->Size = current->Size - size - sizeof(Block);
                    next->Used = false;
                    current->Size = size;
                }
                
                current->Used = true;
                
                return (byte*)current + sizeof(Block);
            }
            
            current = (Block*)((byte*)current + sizeof(Block) + current->Size);
        }
        
        return null;
    }
    
    public static void Free(void* pointer)
    {
        if (pointer == null) return;
        
        var block = (Block*)((byte*)pointer - sizeof(Block));
        block->Used = false;
        
        var next = (Block*)((byte*)block + sizeof(Block) + block->Size);
        
        if ((byte*)next < _end && !next->Used)
            block->Size += sizeof(Block) + next->Size;
        
        CoalescePrevious(block);
    }
    
    private static void CoalescePrevious(Block* target)
    {
        var current = (Block*)_start;
        
        while (current < target)
        {
            var next = (Block*)((byte*)current + sizeof(Block) + current->Size);
            if (next == target && !current->Used)
            {
                current->Size += sizeof(Block) + target->Size;
                return;
            }
            current = next;
        }
    }
    
    public static void* Set(void* pointer, int value, int count)
    {
        var bytes = (byte*)pointer;
        for (var i = 0; i < count; i++)
            bytes[i] = (byte)value;
        
        return pointer;
    }
    
    public static void* Copy(void* destination, void* source, int count)
    {
        for (var i = 0; i < count; i++)
            ((byte*)destination)[i] = ((byte*)source)[i];
        
        return destination;
    }

    public static int Available
    {
        get
        {
            var free = 0;
            var current = (Block*)_start;

            while ((byte*)current < _end)
            {
                if (!current->Used)
                    free += current->Size;
                current = (Block*)((byte*)current + sizeof(Block) + current->Size);
            }

            return free;
        }
    }
}

public unsafe struct Box<T> : IDisposable where T : unmanaged
{
    private T* _pointer;
    
    public Box(T value)
    {
        _pointer = (T*)Allocator.Allocate(sizeof(T));
        if (_pointer != null) *_pointer = value;
    }
    
    public ref T Value => ref *_pointer;

    public void Dispose()
    {
        if (_pointer == null) return;
        Allocator.Free(_pointer);
        _pointer = null;
    }
}
    
public unsafe struct Vec<T> : IDisposable where T : unmanaged
{
    private T* _data;
    private int _capacity;
    
    public Vec()
    {
        _capacity = 4;
        Length = 0;
        _data = (T*)Allocator.Allocate(sizeof(T) * 4);
        
        if (_data == null)
            _capacity = 0;
    }
    
    public Vec(int capacity)
    {
        _capacity = capacity;
        Length = 0;
        _data = (T*)Allocator.Allocate(sizeof(T) * capacity);
        
        if (_data == null)
            _capacity = 0;
    }
    
    public int Length { get; private set; }

    public ref T this[int index] => ref _data[index];
    
    public void Push(T item)
    {
        if (_data == null)
            return;
            
        if (Length >= _capacity)
        {
            var capacity = _capacity * 2;
            var data = (T*)Allocator.Allocate(sizeof(T) * capacity);
            if (data != null)
            {
                Allocator.Copy(data, _data, sizeof(T) * Length);
                Allocator.Free(_data);
                _data = data;
                _capacity = capacity;
            }
            else
            {
                return;
            }
        }
        
        if (Length < _capacity)
        {
            _data[Length++] = item;
        }
    }

    public void Dispose()
    {
        if (_data == null) return;
        Allocator.Free(_data);
        _data = null;
    }
}

public interface IDisposable
{
    void Dispose();
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions