Skip to content

Instantly share code, notes, and snippets.

@kassane
Created November 24, 2024 15:17
Show Gist options
  • Select an option

  • Save kassane/60137547d5e3f8428ad157a6f8853c1d to your computer and use it in GitHub Desktop.

Select an option

Save kassane/60137547d5e3f8428ad157a6f8853c1d to your computer and use it in GitHub Desktop.

Revisions

  1. kassane created this gist Nov 24, 2024.
    233 changes: 233 additions & 0 deletions alloc_nogc.d
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,233 @@
    // Works in betterC mode (tested on: ldc2 & opend-ldc2)
    module arena;

    struct Arena
    {
    void* start;
    size_t used;
    size_t capacity;

    static scope Arena* create(size_t capacity)
    {
    auto arena = cast(Arena*) malloc(Arena.sizeof);
    if (!arena)
    return null;

    arena.start = malloc(capacity);
    if (!arena.start)
    {
    free(arena);
    return null;
    }

    arena.used = 0;
    arena.capacity = capacity;
    return arena;
    }

    scope void* alloc(size_t size)
    {
    if (used + size > capacity)
    return null;

    void* ptr = cast(void*)(cast(ubyte*) start + used);
    used += size;
    return ptr;
    }

    void reset()
    {
    used = 0;
    }

    void destroy()
    {
    if (start)
    free(start);
    free(&this);
    }

    ~this()
    {
    destroy();
    }

    import core.stdc.stdlib : malloc, free;
    }

    struct DynArray(T)
    {
    T* ptr;
    size_t length;
    size_t capacity;
    Arena* arena;

    static DynArray!T create(Arena* arena, size_t initialCapacity = 16)
    {
    DynArray!T arr;
    arr.arena = arena;
    arr.capacity = initialCapacity;
    arr.ptr = cast(T*) arena.alloc(T.sizeof * initialCapacity);
    arr.length = 0;
    return arr;
    }

    bool push(T value)
    {
    if (length >= capacity)
    {
    size_t newCapacity = capacity * 2;
    T* newPtr = cast(T*) arena.alloc(T.sizeof * newCapacity);
    if (!newPtr)
    return false;

    for (size_t i = 0; i < length; i++)
    newPtr[i] = ptr[i];

    ptr = newPtr;
    capacity = newCapacity;
    }

    ptr[length++] = value;
    return true;
    }

    T* opIndex(size_t index)
    {
    if (index >= length)
    return null;
    return &ptr[index];
    }
    }

    extern (C) void main()
    {
    auto arena = Arena.create(1024);
    scope (exit)
    arena.destroy();
    assert(arena !is null);

    auto arr = DynArray!int.create(arena);
    assert(arr.capacity == 16);
    assert(arr.length == 0);

    // Push test
    for (int i = 0; i < 10; i++)
    assert(arr.push(i));
    assert(arr.length == 10);

    // Access test
    for (size_t i = 0; i < arr.length; i++)
    {
    assert(*arr[i] == i);
    }

    for (int i = 0; i < 20; i++)
    assert(arr.push(i));
    assert(arr.length == 30);

    // final contents
    for (size_t i = 0; i < arr.length; i++)
    {
    printf("arr[%zu] = %d\n", i, *arr[i]);
    }

    {
    auto foo = ScopedRef!Foo.make();
    assert(foo.x == 1);
    assert(foo.y == 2);
    }
    }

    class Foo
    {
    int x;
    int y;
    this() @nogc nothrow @safe
    {
    x = 1;
    y = 2;
    printf("Foo ctor\n");
    }

    ~this() @nogc nothrow @safe
    {
    printf("Foo dtor\n");
    }

    }

    pragma(printf)
    extern (C) void printf(scope const(char)* fmt, scope...) @nogc nothrow;

    struct ScopedRef(T)
    {

    T value;
    alias value this;

    static ScopedRef!T make(Args...)(auto ref Args args) @nogc @safe nothrow
    {
    import core.exception : onOutOfMemoryError;

    enum tsize = __traits(classInstanceSize, T);
    T t = () @trusted {
    auto _t = cast(T) malloc(tsize);
    if (!_t)
    onOutOfMemoryError();
    import core.stdc.string : memcpy;

    memcpy(cast(void*) _t, __traits(initSymbol, T).ptr, tsize);
    return _t;
    }();
    if (!t)
    return ScopedRef!T(null);
    import core.lifetime : forward;

    t.__ctor(forward!args);

    return ScopedRef!T(t);
    }

    ~this() @nogc nothrow
    {
    if (value)
    {
    static if (__traits(hasMember, T, "__xdtor"))
    value.__xdtor();
    free(cast(void*) value);
    value = null;
    }
    }

    @disable this(this);
    import core.stdc.stdlib : malloc, free;
    }

    @("basic creation and destruction")
    @safe @nogc nothrow
    unittest
    {
    auto foo = ScopedRef!Foo.make();
    assert(foo.x == 1);
    assert(foo.y == 2);
    }

    @("null case")
    @safe @nogc nothrow
    unittest
    {
    auto foo = ScopedRef!Foo(null);
    assert(foo.value is null);

    }

    @("copy construction is disabled")
    @safe @nogc nothrow
    unittest
    {
    static assert(!__traits(compiles, {
    auto foo1 = ScopedRef!Foo.make();
    auto foo2 = foo1;
    }));
    }