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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions