diff --git a/dub.sdl b/dub.sdl index 06e4283..66590d2 100644 --- a/dub.sdl +++ b/dub.sdl @@ -5,8 +5,6 @@ copyright "Copyright © 2023, Inochi2D Project" license "BSD 2-clause" targetPath "out/" -dependency "tinyd-rt" version=">=0.0.0" optional=true - buildOptions "debugInfoC" platform="windows" configuration "main" { diff --git a/source/numem/all.d b/source/numem/all.d index c462a57..79726e9 100644 --- a/source/numem/all.d +++ b/source/numem/all.d @@ -6,16 +6,10 @@ */ /** - Automatically imports all of the numem types. + Automatically imports all of the base numem functionality. + Some extra functionality has to be */ +deprecated("To import core numem functionality, just import numem.") module numem.all; -public import numem.core; -public import numem.core.memory; -public import numem.collections; -public import numem.core.exception; -public import numem.io; -public import numem.string; -public import numem.conv; -public import numem.events; -public import numem.format; +public import numem; \ No newline at end of file diff --git a/source/numem/collections/map.d b/source/numem/collections/map.d index e500b53..d047d7b 100644 --- a/source/numem/collections/map.d +++ b/source/numem/collections/map.d @@ -258,7 +258,7 @@ unittest { // Associative array of ints that are // indexed by string keys. // The KeyType is string. - map!(string, int) aa = nogc_construct!(map!(string, int))(); + map!(string, int) aa; aa["hello"] = 3; // set value associated with key "hello" to 3 int value = aa["hello"]; // lookup value from a key assert(value == 3); diff --git a/source/numem/collections/set.d b/source/numem/collections/set.d index ed52474..12a5531 100644 --- a/source/numem/collections/set.d +++ b/source/numem/collections/set.d @@ -162,7 +162,7 @@ unittest { @("set: insertion, deletion and testing") unittest { - set!(string) keywords = nogc_construct!(set!string)(); + set!(string) keywords; assert(keywords.insert("public")); assert(keywords.insert("private")); diff --git a/source/numem/collections/vector.d b/source/numem/collections/vector.d index 414c737..1861e8e 100644 --- a/source/numem/collections/vector.d +++ b/source/numem/collections/vector.d @@ -687,8 +687,7 @@ alias weak_vector(T) = VectorImpl!(T, false); @("vector: Issue #2") unittest { - class A { - } + class A { } shared_ptr!A a = shared_new!A(); vector!(shared_ptr!A) v; v ~= a; // Used to crash, see Issue #2 diff --git a/source/numem/conv.d b/source/numem/conv.d index adb5128..7eae453 100644 --- a/source/numem/conv.d +++ b/source/numem/conv.d @@ -9,9 +9,15 @@ Utilities for converting between some basic types */ module numem.conv; +import numem.string; +import numem.format; import core.stdc.stdlib; import std.traits; -import numem.all; + + +// +// TODO: REIMPLEMENT ALL OF THIS IN PURE D. +// @nogc: diff --git a/source/numem/core/env.d b/source/numem/core/env.d deleted file mode 100644 index c12fbc5..0000000 --- a/source/numem/core/env.d +++ /dev/null @@ -1,96 +0,0 @@ -/* - Copyright © 2024, Inochi2D Project - Distributed under the 2-Clause BSD License, see LICENSE file. - - Authors: Luna the Foxgirl -*/ - -/** - Numem environment managment support -*/ -module numem.core.env; -import numem.string; -import numem.text.unicode; -import numem.core.memory; - -version(Windows) import core.sys.windows.winbase : GetEnvironmentVariableW, SetEnvironmentVariableW; -else import core.sys.posix.stdlib : setenv, getenv; -import core.sys.windows.raserror; - -@nogc: - -/** - Interface to system environment. -*/ -struct Environment { -@nogc: -private: - static nstring get(const(char)* key) { - version(Windows) { - auto utf16k = key.fromStringz.toUTF16; - - // Try getting the size of the env var. - // if this fails, the env var is probably empty. - uint bufSize = GetEnvironmentVariableW(utf16k.ptr, null, 0); - if (bufSize == 0) - return nstring.init; - - // Windows includes the null terminator, but n*string does too - // so to not have 2 null terminators, subtract 1. - nwstring envstr = nwstring(bufSize-1); - bufSize = GetEnvironmentVariableW(utf16k.ptr, cast(wchar*)envstr.ptr, cast(uint)(envstr.length+1)); - - nogc_delete(utf16k); - return envstr.toUTF8; - } else { - return nstring(getenv(key)); - } - } - - static bool set(const(char)* key, nstring value) { - version(Windows) { - auto utf16k = key.fromStringz.toUTF16(); - auto utf16v = value.toUTF16(); - return cast(bool)SetEnvironmentVariableW(utf16k.ptr, utf16v.ptr); - } else { - return setenv(key, value.ptr, 1) == 0; - } - } - -public: - - /** - Returns the value at the given key. - - Returns an empty nstring if key was not found. - */ - static ref auto opIndex(const(char)* key) { - return get(key); - } - - /** - Sets the value at the given key. - */ - static void opIndexAssign(string value, const(char)* key) { - set(key, nstring(value)); - } - - /** - Appends to the value at the given key. - */ - static void opIndexOpAssign(string op = "~")(string value, const(char)* key) { - auto tmp = get(key); - tmp ~= value; - set(key, tmp); - } -} - -@("Environment: Get and Set") -unittest { - auto envA = Environment["A"]; - assert(envA.empty()); - - Environment["A"] = "Hello, world!"; - envA = Environment["A"]; - assert(envA == "Hello, world!"); -} \ No newline at end of file diff --git a/source/numem/core/hooks.d b/source/numem/core/hooks.d new file mode 100644 index 0000000..3c5aaef --- /dev/null +++ b/source/numem/core/hooks.d @@ -0,0 +1,129 @@ +/* + Copyright © 2023, Inochi2D Project + Distributed under the 2-Clause BSD License, see LICENSE file. + + Authors: Luna Nielsen +*/ + +/** + Numem Hooks. + + This file contains all the core hooks numem calls internally to handle memory. + Given that some platforms may not have a C standard library, these hooks allow you + to override how numem handles memory for such platforms from an external library. + + In this case, all of the hooks presented here will need to be implemented to cover + all of the used internal hooks within numem. + + Various extra hooks are provided in other files throughout numem, but are optional. +*/ +module numem.core.hooks; +public import core.attribute : weak; + +@nogc nothrow: + +/** + Allocates `bytes` worth of memory. + + NOTE: External libraries may override this + implementation. + + By default calls C stdlib alloc. +*/ +@weak +extern(C) +void* nuAlloc(size_t bytes) { + import core.stdc.stdlib : malloc; + return malloc(bytes); +} + + +/** + Reallocates memory at `data` to be `bytes` worth of memory. + + NOTE: External libraries may override this + implementation. + + By default calls C stdlib realloc. +*/ +@weak +extern(C) +void* nuRealloc(void* data, size_t newSize) { + import core.stdc.stdlib : realloc; + return realloc(data, newSize); +} + +/** + Frees the memory at `data`. + + NOTE: External libraries may override this + implementation. + + By default calls C stdlib alloc. +*/ +@weak +extern(C) +void nuFree(void* data) { + import core.stdc.stdlib : free; + free(data); +} + +/** + Copies `bytes` worth of data from `src` into `dst`. + Memory needs to be allocated and within range. + + NOTE: External libraries may override this + implementation. + + By default calls C stdlib memcpy. +*/ +@weak +extern(C) +void* nuMemcpy(inout(void)* dst, inout(void)* src, size_t bytes) { + import core.stdc.string : memcpy; + return memcpy(cast(void*)dst, cast(void*)src, bytes); +} + +/** + Moves `bytes` worth of data from `src` into `dst`. + Memory needs to be allocated and within range. + + NOTE: External libraries may override this + implementation. + + By default calls C stdlib memmove. +*/ +@weak +extern(C) +void* nuMemmove(void* dst, void* src, size_t bytes) { + import core.stdc.string : memmove; + return memmove(dst, src, bytes); +} + +/** + Fills `dst` with `value` for `bytes` bytes. + + NOTE: External libraries may override this + implementation. + + By default calls C stdlib memset. +*/ +void* nuMemset(void* dst, ubyte value, size_t bytes) { + import core.stdc.string : memset; + return memset(dst, value, bytes); +} + +/** + Hook which forcefully quits or crashes the application due to an invalid state. + + NOTE: External libraries may override this + implementation. + + By default calls C stdlib abort. +*/ +@weak +extern(C) +void nuAbort() { + import core.stdc.stdlib : abort; + abort(); +} \ No newline at end of file diff --git a/source/numem/core/memory/alloc.d b/source/numem/core/memory/alloc.d deleted file mode 100644 index 4c099f1..0000000 --- a/source/numem/core/memory/alloc.d +++ /dev/null @@ -1,73 +0,0 @@ -module numem.core.memory.alloc; -public import core.stdc.stdlib : free, malloc, exit; -import std.traits; - -@nogc nothrow: - -// Deletion function signature. -private extern (D) alias fp_t = void function (Object); - -/** - Destroy element with a destructor. -*/ -@trusted -void destruct(T, bool doFree=true)(ref T obj_) { - - static if (isPointer!T || is(T == class)) { - if (obj_ !is null) { - auto cInfo = cast(ClassInfo)typeid(obj_); - if (cInfo) { - auto c = cInfo; - - // Call destructors in order of most specific - // to least-specific - do { - if (c.destructor) - (cast(fp_t)c.destructor)(cast(Object)obj_); - } while((c = c.base) !is null); - - } else { - - // Item is a struct, we can destruct it directly. - static if (__traits(hasMember, T, "__dtor")) { - assumeNothrowNoGC!(typeof(&obj_.__dtor))(&obj_.__dtor)(); - } else static if (__traits(hasMember, T, "__xdtor")) { - assumeNothrowNoGC!(typeof(&obj_.__xdtor))(&obj_.__xdtor)(); - } - } - - static if (doFree) { - free(cast(void*)obj_); - obj_ = null; - } - } - } else { - - // Item is a struct, we can destruct it. - static if (__traits(hasMember, T, "__dtor")) { - assumeNothrowNoGC!(typeof(&obj_.__dtor))(&obj_.__dtor)(); - } else static if (__traits(hasMember, T, "__xdtor")) { - assumeNothrowNoGC!(typeof(&obj_.__xdtor))(&obj_.__xdtor)(); - } - } -} - -/** - Forces a function to assume that it's nogc compatible. -*/ -auto assumeNoGC(T) (T t) { - static if (isFunctionPointer!T || isDelegate!T) { - enum attrs = functionAttributes!T | FunctionAttribute.nogc; - return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; - } else static assert(false); -} - -/** - Forces a function to assume that it's nothrow nogc compatible. -*/ -auto assumeNothrowNoGC(T) (T t) { - static if (isFunctionPointer!T || isDelegate!T) { - enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_; - return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; - } else static assert(false); -} \ No newline at end of file diff --git a/source/numem/core/memory/lifetime.d b/source/numem/core/memory/lifetime.d new file mode 100644 index 0000000..1543d47 --- /dev/null +++ b/source/numem/core/memory/lifetime.d @@ -0,0 +1,233 @@ +/* + Copyright © 2023, Inochi2D Project + Distributed under the 2-Clause BSD License, see LICENSE file. + + Authors: Luna Nielsen +*/ + +/** + Lifetime handling in numem. +*/ +module numem.core.memory.lifetime; +import numem.core.utils; +import numem.core.hooks; +import numem.core.trace; +import std.traits; +import core.lifetime : forward; +import core.internal.traits : isInnerClass; + +// Deletion function signature. +private extern (D) alias fp_t = void function (Object) @nogc nothrow; + +/** + Destroy element with a destructor. +*/ +@trusted +void destruct(T, bool doFree=true)(ref T obj_) @nogc nothrow { + + static if (isPointer!T || is(T == class)) { + if (obj_ !is null) { + auto cInfo = cast(ClassInfo)typeid(obj_); + if (cInfo) { + auto c = cInfo; + + // Call destructors in order of most specific + // to least-specific + do { + if (c.destructor) + (cast(fp_t)c.destructor)(cast(Object)obj_); + } while((c = c.base) !is null); + + } else { + + // Item is a struct, we can destruct it directly. + static if (__traits(hasMember, T, "__dtor")) { + assumeNothrowNoGC!(typeof(&obj_.__dtor))(&obj_.__dtor)(); + } else static if (__traits(hasMember, T, "__xdtor")) { + assumeNothrowNoGC!(typeof(&obj_.__xdtor))(&obj_.__xdtor)(); + } + } + + static if (doFree) { + nuFree(cast(void*)obj_); + obj_ = null; + } + } + } else { + + // Item is a struct, we can destruct it. + static if (__traits(hasMember, T, "__dtor")) { + assumeNothrowNoGC!(typeof(&obj_.__dtor))(&obj_.__dtor)(); + } else static if (__traits(hasMember, T, "__xdtor")) { + assumeNothrowNoGC!(typeof(&obj_.__xdtor))(&obj_.__xdtor)(); + } + } +} + +/** + Runs copy postblit operations for `dst`. + + If `dst` has a copy constructor it will be run, + otherwise if it has a `this(this)` postblit that will be run. + + If no form of postblit is available, this function will be NO-OP. +*/ +pragma(inline, true) +void postblit(T)(ref T dst, ref T src) @nogc nothrow { + static if (__traits(hasCopyConstructor, T)) { + dst.__ctor(src); + } else static if(__traits(hasPostblit, T)) { + dst.__xpostblit(); + } +} + +/** + Runs move postblit operation for `dst`. +*/ +pragma(inline, true) +void move_postblit(T)(ref T dst, ref T src) @nogc nothrow { + static if (hasElaborateMove!T) + assumeNothrowNoGC!(typeof(&__move_post_blt))(&__move_post_blt)(dst, src); +} + +/** + Gets the amount of bytes needed to allocate an instance of type `T`. +*/ +template nuAllocSize(T) { + static if (is(T == class)) + enum nuAllocSize = __traits(classInstanceSize, T); + else + enum nuAllocSize = T.sizeof; +} + +/** + Initializes the memory at the specified chunk. +*/ +void initializeAt(T)(scope ref T chunk) @nogc nothrow @trusted { + static if (is(T == class)) { + + // NOTE: class counts as a pointer, so its normal init symbol + // in general circumstances is null, we don't want this, so class check + // should be first! Otherwise the chunk = T.init will mess us up. + const void[] initSym = __traits(initSymbol, T); + nuMemcpy(cast(void*)chunk, initSym.ptr, initSym.length); + } else static if (__traits(isZeroInit, T)) { + nuMemset(cast(void*)&chunk, 0, T.sizeof); + } else static if (__traits(isScalar, T) || + (T.sizeof <= 16 && !hasElaborateAssign!T && __traits(compiles, () { T chunk; chunk = T.init; }))) { + chunk = T.init; + } else static if (__traits(isStaticArray, T)) { + foreach(i; 0..T.length) + initializeAt(chunk[i]); + } else { + const void[] initSym = __traits(initSymbol, T); + nuMemcpy(cast(void*)&chunk, initSym.ptr, initSym.length); + } +} + +/** + Runs constructor for the memory at dst +*/ +void emplace(T, UT, Args...)(ref UT dst, auto ref Args args) @nogc nothrow { + enum isConstructibleOther = + (!is(T == struct) && Args.length == 1) || // Primitives, enums, arrays. + (Args.length == 1 && is(typeof({T t = forward!(args[0]); }))) || // Conversions + is(typeof(T(forward!args))); // General constructors. + + static if (is(T == class)) { + + static assert(!__traits(isAbstractClass, T), + T.stringof ~ " is abstract and can't be emplaced."); + + // NOTE: Since we need to handle inner-classes + // we need to initialize here instead of next to the ctor. + initializeAt(dst); + + static if (isInnerClass!T) { + static assert(Args.length > 0, + "Initializing an inner class requires a pointer to the outer class"); + + static assert(is(Args[0] : typeof(T.outer)), + "The first argument must be a pointer to the outer class"); + + chunk.outer = args[0]; + alias fargs = args[1..$]; + alias fargsT = Args[1..$]; + + } else { + alias fargs = args; + alias fargsT = Args; + } + import core.stdc.stdio : printf; + + static if (is(typeof(dst.__ctor(forward!fargs)))) { + assumeNothrowNoGC((T chunk, fargsT args) { + chunk.__ctor(forward!args); + })(dst, fargs); + } else { + static assert(fargs.length == 0 && !is(typeof(&T.__ctor)), + "No constructor for " ~ T.stringof ~ " found matching arguments "~fargsT.stringof~"!"); + } + + } else static if (args.length == 0) { + + static assert(is(typeof({static T i;})), + "Cannot emplace a " ~ T.stringof ~ ", its constructor is marked with @disable."); + initializeAt(dst); + } else static if (isConstructibleOther) { + + // Handler struct which forwards construction + // to the payload. + static struct S { + T payload; + this()(auto ref Args args) { + static if (__traits(compiles, payload = forward!args)) + payload = forward!args; + else + payload = T(forward!args); + } + } + + if (__ctfe) { + static if (__traits(compiles, dst = T(forward!args))) + dst = T(forward!args); + else static if(args.length == 1 && __traits(compiles, dst = forward!(args[0]))) + dst = forward!(args[0]); + else static assert(0, + "Can't emplace " ~ T.stringof ~ " at compile-time using " ~ Args.stringof ~ "."); + } else { + S* p = cast(S*)cast(void*)&dst; + static if (UT.sizeof > 0) + initializeAt(*p); + + p.__ctor(forward!args); + } + } else static if (is(typeof(dst.__ctor(forward!args)))) { + + initializeAt(dst); + assumeNothrowNoGC((T chunk, Args args) { + chunk.__ctor(forward!args); + })(dst, args); + } else { + static assert(!(Args.length == 1 && is(Args[0] : T)), + "Can't emplace a " ~ T.stringof ~ " because the postblit is disabled."); + + static assert(0, + "No constructor for " ~ T.stringof ~ " found matching arguments "~fargs.stringof~"!"); + } +} + +/// +void emplace(UT, Args...)(auto ref UT dst, auto ref Args args) @nogc nothrow { + emplace!(UT, UT, Args)(dst, forward!args); +} + +/** + Gets the reference type version of type T. +*/ +template RefT(T) { + static if (is(T == class) || isPointer!T) + alias RefT = T; + else + alias RefT = T*; +} \ No newline at end of file diff --git a/source/numem/core/memory/package.d b/source/numem/core/memory/package.d index 4a25c85..81f8c53 100644 --- a/source/numem/core/memory/package.d +++ b/source/numem/core/memory/package.d @@ -6,95 +6,12 @@ */ module numem.core.memory; -import numem.core.memory.alloc; -import numem.core.trace; +import numem.core.memory.lifetime; +import numem.core.hooks; import std.traits; -import core.stdc.string : memset; public import numem.core.memory.smartptr; - -version(Have_tinyd_rt) { - private __gshared - auto __gc_new(T, Args...)(Args args) { - return new T(args); - } -} else { - import core.lifetime : copyEmplace, emplace; -} - -private { - // NOTE: D's implementation of emplace makes it ambiguous what emplace to call in certain instances - // These functions forward to the correct D core functions for emplacing. - - // Case: Is a class - T __impl_nogc_emplace(T, Args...)(void[] chunk, Args args) if(is(T == class)) { - return emplace!(T, Args)(chunk, args); - } - - // Case: Is struct or basic pointer. - T* __impl_nogc_emplace(T, Args...)(T* chunk, Args args) if(!is(T == class)) { - return emplace!(T, Args)(chunk, args); - } - - enum isAggregateStackType(T) = - is(T == struct) || is(T == union); -} - -nothrow @nogc: - -// -// MANUAL MEMORY MANAGMENT -// -private { - - version(minimal_rt) { - - // The type of the destructor caller - alias _impl_mrt_dfunctype = nothrow @nogc @system void function(void*); - - // Generic destructor call - void _impl_destructorCall(T)(void* instance) nothrow @nogc { - import std.traits : BaseTypeTuple; - - // Scope for base class destructor - static foreach(item; BaseClassesTuple!T) { - { - static if (!is(item == Object) && !is(item == T)) { - static if (__traits(hasMember, item, "__xdtor")) { - auto dtorptr = &item.init.__xdtor; - dtorptr.ptr = instance; - dtorptr(); - } else static if (__traits(hasMember, item, "__dtor")) { - auto dtorptr = &item.init.__dtor; - dtorptr.ptr = instance; - dtorptr(); - } - } - } - } - - // Scope for self destructor - { - static if (__traits(hasMember, T, "__xdtor")) { - auto dtorptr = &T.init.__xdtor; - dtorptr.ptr = instance; - dtorptr(); - } else static if (__traits(hasMember, T, "__dtor")) { - auto dtorptr = &T.init.__dtor; - dtorptr.ptr = instance; - dtorptr(); - } - } - } - - // Structure for storing the destructor reference. - struct _impl_destructorStruct { - _impl_mrt_dfunctype destruct; - } - } -} - -extern(C): +debug(trace) import numem.core.trace; /** UDA which allows initializing an empty struct, even when copying is disabled. @@ -102,106 +19,24 @@ extern(C): struct AllowInitEmpty; /** - Constructs a type, this allows initializing types on the stack instead of heap. -*/ -T nogc_construct(T, Args...)(Args args) if (is(T == struct) || is(T == class) || is (T == union)) { - static if (is(T == class)) { - return (assumeNothrowNoGC(&__gc_new!(T, Args)))(args); - } else { - static if (hasUDA!(T, AllowInitEmpty) && args.length == 0) { - return T.init; - } else { - return T(args); - } - } -} - -/** - Allocates a new struct on the heap. - Immediately exits the application if out of memory. -*/ -T* nogc_new(T, Args...)(Args args) if (is(T == struct) || is(T == union)) { - - version(Have_tinyd_rt) { - return (assumeNothrowNoGC(&__gc_new!(T, Args)))(args); - } else { - void* rawMemory = malloc(T.sizeof); - if (!rawMemory) { - exit(-1); - } - - T* obj = cast(T*)rawMemory; - static if (hasUDA!(T, AllowInitEmpty) && args.length == 0) { - nogc_emplace!T(obj); - } static if (args.length == 1 && is(typeof(args[0]) == T)) { - nogc_copyemplace(obj, args[0]); - } else { - nogc_emplace!T(obj, args); - } - - // Tracing - debug(trace) dbg_alloc(obj); - - return obj; - } -} - -/** - Allocates a new class on the heap. - Immediately exits the application if out of memory. + Allocates a new instance of type T. */ -T nogc_new(T, Args...)(Args args) if (is(T == class)) { - - alias emplaceFunc = typeof(&emplace!T); - - version(Have_tinyd_rt) { - return (assumeNothrowNoGC(&__gc_new!(T, Args)))(args); - } else version(minimal_rt) { - immutable(size_t) destructorObjSize = _impl_destructorStruct.sizeof; - immutable(size_t) classObjSize = __traits(classInstanceSize, T); - immutable size_t allocSize = classObjSize + destructorObjSize; - - void* rawMemory = malloc(allocSize); - if (!rawMemory) { - exit(-1); - } - - // Allocate class destructor list - nogc_emplace!classDestructorList(rawMemory[0..destructorObjSize], &_impl_destructorCall!T); - - // Allocate class - T obj = nogc_emplace!T(rawMemory[destructorObjSize .. allocSize], args); - - // Tracing - debug(trace) dbg_alloc(obj); - - return obj; - } else { - immutable size_t allocSize = __traits(classInstanceSize, T); - void* rawMemory = malloc(allocSize); - if (!rawMemory) { - exit(-1); - } - - return nogc_emplace!T(rawMemory[0 .. allocSize], args); - } -} - -/** - Allocates a new basic type on the heap. - Immediately exits the application if out of memory. -*/ -T* nogc_new(T)(T value = T.init) if (isBasicType!T) { - T* rawMemory = cast(T*)malloc(T.sizeof); - if (!rawMemory) { - exit(-1); - } +RefT!T nogc_new(T, Args...)(Args args) { + RefT!T newobject = cast(RefT!T)nuAlloc(nuAllocSize!T); + if (!newobject) + nuAbort(); + + static if (is(T == class) || isPointer!T) + emplace(newobject, args); + else + emplace(*newobject, args); + // Tracing - debug(trace) dbg_alloc(rawMemory); - - *rawMemory = value; - return rawMemory; + debug(trace) + dbg_alloc(newobject); + + return newobject; } /** @@ -210,18 +45,27 @@ T* nogc_new(T)(T value = T.init) if (isBasicType!T) { For structs this will call the struct's destructor if it has any. */ void nogc_delete(T, bool doFree=true)(ref T obj_) { - - // Tracing - debug(trace) dbg_dealloc(obj_); destruct!(T, doFree)(obj_); + + // Tracing + debug(trace) + dbg_dealloc(obj_); +} + +/** + Initializes the object at `element`, filling it out with + its default state. +*/ +void nogc_initialize(T)(ref T element) { + initializeAt(element); } /** Zero-fills an object */ void nogc_zeroinit(T)(ref T element) { - memset(&element, 0, element.sizeof); + nuMemset(&element, 0, element.sizeof); } /** @@ -229,27 +73,14 @@ void nogc_zeroinit(T)(ref T element) { */ T nogc_zeroinit(T)() { T element; - memset(&element, 0, element.sizeof); + nuMemset(&element, 0, element.sizeof); return element; } -auto nogc_copyemplace(T)(T* target, ref T source) { - alias t = typeof(©Emplace!(T, T)); - return assumeNothrowNoGC!t(©Emplace!(T, T))(source, *target); -} - /** - nogc emplace function -*/ -auto nogc_emplace(T, Args...)(T* chunk, Args args) { - alias t = typeof(&__impl_nogc_emplace!(T, Args)); - return assumeNothrowNoGC!t(&__impl_nogc_emplace!(T, Args))(chunk, args); -} - -/** - nogc emplace function + Allocates a new class on the heap. + Immediately exits the application if out of memory. */ -auto nogc_emplace(T, Args...)(void[] chunk, Args args) if (is(T == class)) { - alias t = typeof(&__impl_nogc_emplace!(T, Args)); - return assumeNothrowNoGC!t(&__impl_nogc_emplace!(T, Args))(chunk, args); +void nogc_emplace(T, Args...)(ref auto T dest, Args args) { + emplace!(T, T, Args)(dest, args); } \ No newline at end of file diff --git a/source/numem/core/package.d b/source/numem/core/package.d index 7d6798d..510c6a0 100644 --- a/source/numem/core/package.d +++ b/source/numem/core/package.d @@ -1,3 +1,4 @@ module numem.core; -public import numem.core.memory; \ No newline at end of file +public import numem.core.memory; +public import numem.core.exception; \ No newline at end of file diff --git a/source/numem/core/utils.d b/source/numem/core/utils.d new file mode 100644 index 0000000..d2dd6ee --- /dev/null +++ b/source/numem/core/utils.d @@ -0,0 +1,34 @@ +/* + Copyright © 2023, Inochi2D Project + Distributed under the 2-Clause BSD License, see LICENSE file. + + Authors: Luna Nielsen +*/ + +/** + Utility functions which are used to break the D type system. + + These should only be used when absolutely neccesary. +*/ +module numem.core.utils; +import std.traits; + +/** + Forces a function to assume that it's nogc compatible. +*/ +auto assumeNoGC(T) (T t) { + static if (isFunctionPointer!T || isDelegate!T) { + enum attrs = functionAttributes!T | FunctionAttribute.nogc; + return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; + } else static assert(false); +} + +/** + Forces a function to assume that it's nothrow nogc compatible. +*/ +auto assumeNothrowNoGC(T) (T t) { + static if (isFunctionPointer!T || isDelegate!T) { + enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_; + return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; + } else static assert(false); +} \ No newline at end of file diff --git a/source/numem/env.d b/source/numem/env.d new file mode 100644 index 0000000..6a018dd --- /dev/null +++ b/source/numem/env.d @@ -0,0 +1,113 @@ +/* + Copyright © 2024, Inochi2D Project + Distributed under the 2-Clause BSD License, see LICENSE file. + + Authors: Luna the Foxgirl +*/ + +/** + Numem Environment handling +*/ +module numem.env; +import numem.string; +import numem.text.unicode; +import numem.core.memory; +import numem.core.hooks; + +@nogc: + +/** + Interface to system environment. +*/ +struct Environment { +@nogc: +public: + + /** + Returns the value at the given key. + + Returns an empty nstring if key was not found. + */ + static ref auto opIndex(const(char)* key) { + return nuGetEnvironmentVariable(nstring(key)); + } + + /** + Sets the value at the given key. + */ + static bool opIndexAssign(string value, const(char)* key) { + return nuSetEnvironmentVariable(nstring(key), nstring(value)); + } + + /** + Appends to the value at the given key. + */ + static bool opIndexOpAssign(string op = "~")(string value, const(char)* key) { + auto tmp = get(key); + tmp ~= value; + return nuSetEnvironmentVariable(nstring(key), tmp); + } +} + +@("Environment: Get and Set") +unittest { + auto envA = Environment["A"]; + assert(envA.empty()); + + assert(Environment["A"] = "Hello, world!"); // We return whether setting succeeded. + envA = Environment["A"]; + assert(envA == "Hello, world!"); +} + +/** + Hook which fetches the specified environment variable. +*/ +@weak +extern(C) +nstring nuGetEnvironmentVariable(nstring key) @nogc { + version(Windows) { + import core.sys.windows.winbase : GetEnvironmentVariableW; + auto utf16k = key.toUTF16; + + // Try getting the size of the env var. + // if this fails, the env var is probably empty. + uint bufSize = GetEnvironmentVariableW(utf16k.ptr, null, 0); + if (bufSize == 0) + return nstring.init; + + // Windows includes the null terminator, but n*string does too + // so to not have 2 null terminators, subtract 1. + nwstring envstr = nwstring(bufSize-1); + bufSize = GetEnvironmentVariableW(utf16k.ptr, envstr.ptr, envstr.length+1); + + nogc_delete(utf16k); + return envstr.toUTF8; + } else version(Posix) { + + import core.sys.posix.stdlib : getenv; + return nstring(getenv(key.ptr)); + } else { + return nstring(null); + } +} + +/** + Hook which sets the specified environment variable. +*/ +@weak +extern(C) +bool nuSetEnvironmentVariable(nstring key, nstring val) @nogc { + version(Windows) { + + import core.sys.windows.winbase : SetEnvironmentVariableW; + auto utf16k = key.toUTF16(); + auto utf16v = value.toUTF16(); + return SetEnvironmentVariableW(utf16k.ptr, utf16v.ptr); + } else version(Posix) { + + import core.sys.posix.stdlib : setenv; + return setenv(key.ptr, val.ptr, 1) == 0; + } else { + return false; + } +} \ No newline at end of file diff --git a/source/numem/mem.d b/source/numem/mem.d deleted file mode 100644 index c79df8b..0000000 --- a/source/numem/mem.d +++ /dev/null @@ -1,11 +0,0 @@ -/* - Copyright © 2024, Inochi2D Project - Distributed under the 2-Clause BSD License, see LICENSE file. - - Authors: Luna the Foxgirl -*/ - -deprecated("This module has been moved to numem.core") -module numem.mem; - -public import numem.core; \ No newline at end of file diff --git a/source/numem/package.d b/source/numem/package.d new file mode 100644 index 0000000..d4c15a2 --- /dev/null +++ b/source/numem/package.d @@ -0,0 +1,21 @@ +/* + Copyright © 2024, Inochi2D Project + Distributed under the 2-Clause BSD License, see LICENSE file. + + Authors: Luna the Foxgirl +*/ + +/** + Automatically imports all of the base numem functionality. + Some extra functionality has to be +*/ +module numem; + + +public import numem.core; +public import numem.collections; +public import numem.io; +public import numem.string; +public import numem.conv; +public import numem.events; +public import numem.format; diff --git a/source/numem/core/random.d b/source/numem/random.d similarity index 99% rename from source/numem/core/random.d rename to source/numem/random.d index 0e3d586..72e639b 100644 --- a/source/numem/core/random.d +++ b/source/numem/random.d @@ -1,4 +1,4 @@ -module numem.core.random; +module numem.random; import numem.core; import std.traits; diff --git a/source/numem/core/uuid.d b/source/numem/uuid.d similarity index 99% rename from source/numem/core/uuid.d rename to source/numem/uuid.d index ce9e189..3b5099d 100644 --- a/source/numem/core/uuid.d +++ b/source/numem/uuid.d @@ -8,8 +8,8 @@ /** RFC4122 compliant UUIDs */ -module numem.core.uuid; -import numem.core.random; +module numem.uuid; +import numem.random; import numem.string; import numem.io.endian; import numem.conv;