Skip to content

Instantly share code, notes, and snippets.

@kassane
Created November 24, 2024 15:17
Show Gist options
  • Save kassane/60137547d5e3f8428ad157a6f8853c1d to your computer and use it in GitHub Desktop.
Save kassane/60137547d5e3f8428ad157a6f8853c1d to your computer and use it in GitHub Desktop.
Dlang betterC mode test - DynArray w/ Arena Allocator (like zig) & ScopedRef D class instanciation
// 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;
}));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment