diff --git a/source/numem/collections/vector.d b/source/numem/collections/vector.d index 9ccadca..c2ffb7c 100644 --- a/source/numem/collections/vector.d +++ b/source/numem/collections/vector.d @@ -12,7 +12,7 @@ import core.stdc.stdlib : malloc, realloc, free; import core.stdc.string : memcpy, memmove; import core.atomic : atomicFetchAdd, atomicFetchSub, atomicStore, atomicLoad; import std.math.rounding : quantize, ceil; -import std.traits : isCopyable; +import std.traits; /// Gets whether the specified type is some variety of the vector type enum isSomeVector(T) = is(T : VectorImpl!U, U...); @@ -62,17 +62,40 @@ private: // a opOpAssign operation. // But, we need to do this without triggering the postblit or move-constructor here, // or the same problem happen! - for (size_t n = before; n < capacity_; ++n) - { + for (size_t n = before; n < capacity_; ++n) { + T tmp = T.init; - _memcpy(memory + n, &tmp, 1); + memcpy(memory + n, &tmp, T.sizeof); } } } pragma(inline, true) void _memcpy(T* dst, T* src, size_t length) { - memcpy(dst, src, T.sizeof*length); + + static if(__traits(hasPostblit, T)) { + + memcpy(dst, src, T.sizeof*length); + foreach(i; 0..length) { + dst[i].__xpostblit(); + } + } else static if (__traits(hasCopyConstructor, T)) { + + // Initializer + T tmp = T.init; + foreach(i; 0..length) { + + // Copy over initializer + memcpy(dst + i, &tmp, T.sizeof); + + // Call copy constructor + dst[i].__ctor(src[i]); + } + + } else { + + memcpy(dst, src, T.sizeof*length); + } } public: @@ -87,13 +110,15 @@ public: static if (ownsMemory) { // Delete elements in the array. - foreach_reverse(item; 0..size_) { - nogc_delete(this.memory[item]); + static if (!isBasicType!T) { + foreach_reverse(item; 0..size_) { + nogc_delete(this.memory[item]); + } } - } - // Free the pointer - free(cast(void*)this.memory); + // Free the pointer + free(cast(void*)this.memory); + } } this.memory = null; @@ -114,6 +139,13 @@ public: this._memcpy(this.memory, data.ptr, data.length); } + /// Constructor + @trusted + this(ref T[] data) { + this.resize_(data.length); + this._memcpy(this.memory, data.ptr, data.length); + } + /** Makes a copy of a vector @@ -400,93 +432,42 @@ public: return this; } - static if (is(T : unique_ptr!U, U)) { - - /** - Add value to vector - */ - @trusted - ref auto opOpAssign(string op = "~")(T value) { - size_t cSize = size_; - - // Very hacky move operation - this.resize_(size_+1); - memcpy(&memory[cSize], &value, T.sizeof); - value.clear(); - - return this; - } - - /** - Add vector items to vector - */ - @trusted - ref auto opOpAssign(string op = "~")(vector!T other) { - size_t cSize = size_; - - // Very hacky move operation - this.resize_(size_ + other.size_); - memcpy(&memory[cSize], other.memory, other.size_*T.sizeof); - foreach(i; 0..other.size) other[i].clear(); - - return this; - } - - /** - Add slice to vector - */ - @trusted - ref auto opOpAssign(string op = "~")(T[] other) { - size_t cSize = size_; - - // Very hacky move operation - this.resize_(size_ + other.length); - memcpy(&memory[cSize], other.ptr, other.length*T.sizeof); - foreach(i; 0..other.length) other[i].clear(); - - return this; - } - - } else { + /** + Add value to vector + */ + @trusted + ref auto opOpAssign(string op = "~")(T value) { + size_t cSize = size_; - /** - Add value to vector - */ - @trusted - ref auto opOpAssign(string op = "~")(T value) { - size_t cSize = size_; + this.resize_(size_+1); + this._memcpy(this.memory+cSize, &value, 1); - this.resize_(size_+1); - memory[cSize] = value; + return this; + } - return this; - } + /** + Add vector items to vector + */ + @trusted + ref auto opOpAssign(string op = "~")(vector!T other) { + size_t cSize = size_; + + this.resize_(size_ + other.size_); + this._memcpy(this.memory+cSize, other.ptr, other.size_); - /** - Add vector items to vector - */ - @trusted - ref auto opOpAssign(string op = "~")(vector!T other) { - size_t cSize = size_; - - this.resize_(size_ + other.size_); - this.memory[cSize..cSize+other.size_] = other.memory[0..other.size_]; - - return this; - } + return this; + } - /** - Add slice to vector - */ - @trusted - ref auto opOpAssign(string op = "~")(T[] other) { - size_t cSize = size_; + /** + Add slice to vector + */ + @trusted + ref auto opOpAssign(string op = "~")(T[] other) { + size_t cSize = size_; - this.resize_(size_ + other.length); - this.memory[cSize..cSize+other.length] = other[0..$]; - - return this; - } + this.resize_(size_ + other.length); + this._memcpy(this.memory+cSize, other.ptr, other.length); + return this; } /** @@ -503,7 +484,7 @@ public: D slices are short lived and may end up pointing to invalid memory if their string is modified. */ @trusted - T[] opSlice(size_t start, size_t end) { + T[] opSlice(size_t dim = 0)(size_t start, size_t end) { return memory[start..end]; } @@ -530,6 +511,36 @@ public: ref T opIndex(size_t index) { return memory[index]; } + + /** + Allows assigning a range of values to the vector. + */ + @trusted + void opAssign(T)(in inout(T)[] values) { + this.resize(values.length); + this._memcpy(memory, cast(T*)values.ptr, values.length); + } + + /** + Allows assigning a range of values to the vector. + */ + @trusted + void opIndexAssign(in T value, size_t index) { + this._memcpy(cast(T*)&memory[index], cast(T*)&value, 1); + } + + /** + Allows assigning a range of values to the vector. + */ + @trusted + void opIndexAssign(in T[] values, T[] slice) { + size_t offset = slice.ptr-this.memory; + if (offset+slice.length > size_) { + this.resize_(offset+slice.length); + } + + this._memcpy(cast(T*)slice.ptr, cast(T*)values.ptr, slice.length); + } } /** diff --git a/source/numem/core/memory/alloc.d b/source/numem/core/memory/alloc.d new file mode 100644 index 0000000..e4f1e4e --- /dev/null +++ b/source/numem/core/memory/alloc.d @@ -0,0 +1,55 @@ +module numem.core.memory.alloc; +public import core.stdc.stdlib : free, malloc, exit; +import std.traits; + +@nogc nothrow: + +/** + Destroy element with a destructor. +*/ +@trusted +void destruct(T)(ref T obj_) { + + static if(isPointer!T || is(T == class)) { + if (cast(void*)obj_) { + + // Don't try to run dtor on null object. + 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)(); + } + + 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/package.d b/source/numem/core/memory/package.d index 7232850..17be6d0 100644 --- a/source/numem/core/memory/package.d +++ b/source/numem/core/memory/package.d @@ -6,10 +6,9 @@ */ module numem.core.memory; -import core.stdc.stdlib : free, exit, malloc; -import std.traits; -import numem.core.utils; +import numem.core.memory.alloc; import numem.core.trace; +import std.traits; public import numem.core.memory.smartptr; @@ -35,6 +34,9 @@ private { 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: @@ -211,89 +213,7 @@ void nogc_delete(T)(ref T obj_) { // Tracing debug(trace) dbg_dealloc(obj_); - version(minimal_rt) { - static if (isPointer!T || is(T == class)) { - if (obj_) { - - static if (is(T == class)) { - - // Do pointer arithmetic to fetch the class destructor list and call it. - void* chunkStart = (cast(void*)obj_)-_impl_destructorStruct.sizeof; - auto store = cast(_impl_destructorStruct*)chunkStart; - store.destruct(cast(void*)obj_); - free(chunkStart); - - } else static if (is(T == struct) || is(T == union)) { - - // Try to call elaborate destructor first before attempting __dtor - static if (__traits(hasMember, T, "__xdtor")) { - assumeNothrowNoGC(&obj_.__xdtor)(); - } else static if (__traits(hasMember, T, "__dtor")) { - assumeNothrowNoGC(&obj_.__dtor)(); - } - free(cast(void*)obj_); - } else { - free(cast(void*)obj_); - } - } - } else static if (is(T == struct) || is(T == union)) { - - // Try to call elaborate destructor first before attempting __dtor - static if (__traits(hasMember, T, "__xdtor")) { - assumeNothrowNoGC(&obj_.__xdtor)(); - } else static if (__traits(hasMember, T, "__dtor")) { - assumeNothrowNoGC(&obj_.__dtor)(); - } - } - - } else { - - - // With a normal runtime we can use destroy - static if (is(PointerTarget!T == struct) || is(PointerTarget!T == union)) { - auto objptr_ = &obj_; - - if (objptr_) { - - // First create an internal function that calls with the correct parameters - alias destroyFuncRef = void function(typeof(objptr_)); - destroyFuncRef dfunc = (typeof(objptr_) objptr_) { destroy!false(objptr_); }; - - // Then assume it's nothrow nogc - assumeNothrowNoGC!destroyFuncRef(dfunc)(objptr_); - - // Free memory - static if(isPointer!T) { - free(cast(void*)obj_); - obj_ = null; - } - } - } else static if (is(T == class)) { - if (obj_) { - - // First create an internal function that calls with the correct parameters - alias destroyFuncRef = void function(typeof(obj_)); - destroyFuncRef dfunc = (typeof(obj_) objptr_) { destroy!false(objptr_); }; - - // Then assume it's nothrow nogc - assumeNothrowNoGC!destroyFuncRef(dfunc)(obj_); - - // NOTE: We already know it's heap allocated. - free(cast(void*)obj_); - obj_ = null; - } - - } else { - - // Free memory - static if(isPointer!T) { - if (cast(void*)obj_) { - free(cast(void*)obj_); - obj_ = null; - } - } - } - } + destruct(obj_); } auto nogc_copyemplace(T)(T* target, ref T source) { diff --git a/source/numem/core/memory/smartptr.d b/source/numem/core/memory/smartptr.d index 8f13c25..d8925a7 100644 --- a/source/numem/core/memory/smartptr.d +++ b/source/numem/core/memory/smartptr.d @@ -53,15 +53,18 @@ private { void free() { - // Free and atomically store null in the pointer. - static if (is(T == class)) { + if (ref_) { - // Store reference in variable as casting makes it an rvalue - // rvalues can't be sent to ref parameters. - T refT = cast(T)ref_; - nogc_delete!T(refT); - } else { - nogc_delete!(T*)(ref_); + // Free and atomically store null in the pointer. + static if (is(T == class)) { + + // Store reference in variable as casting makes it an rvalue + // rvalues can't be sent to ref parameters. + T refT = cast(T)ref_; + nogc_delete!T(refT); + } else { + nogc_delete!(T*)(ref_); + } } // Enforce that strongRefs is set to 0. diff --git a/source/numem/core/utils.d b/source/numem/core/utils.d deleted file mode 100644 index de498c5..0000000 --- a/source/numem/core/utils.d +++ /dev/null @@ -1,29 +0,0 @@ -/* - Copyright © 2024, Inochi2D Project - Distributed under the 2-Clause BSD License, see LICENSE file. - - Authors: Luna the Foxgirl -*/ - -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/string.d b/source/numem/string.d index 9832e46..2bc17b3 100644 --- a/source/numem/string.d +++ b/source/numem/string.d @@ -34,7 +34,8 @@ enum isSomeChar(T) = struct basic_string(T) if (is(T == char) || is(T == dchar) || is(T == wchar)) { nothrow @nogc: private: - vector!(T) vec_; + alias selfType = basic_string!T; + vector!T vec_; void append_(const(T)[] span) { if (span.length == 0) return; @@ -52,8 +53,8 @@ private: void set_(const(T)[] span) { vec_.resize(span.length+1); - (cast(T*)vec_.data)[0..span.length] = span[0..$]; - (cast(T*)vec_.data)[this.size()] = '\0'; + vec_[0..span.length] = span[0..span.length]; + vec_[this.size()] = '\0'; } public: @@ -101,16 +102,22 @@ public: Makes a copy of a string */ @trusted - this(ref return scope basic_string!T rhs) { - this.vec_ = rhs.vec_; + this(ref return scope selfType rhs) { + selfType* self = &this; + selfType* other = &rhs; + (*self).set_((*other)[]); } /** Makes a copy of a string */ @trusted - this(ref return scope inout(basic_string!T) rhs) inout { - this.vec_ = rhs.vec_; + this(ref return scope inout(selfType) rhs) inout { + if (rhs.size > 0) { + selfType* self = (cast(selfType*)&this); + selfType* other = (cast(selfType*)&rhs); + (*self).set_((*other)[]); + } } @@ -217,22 +224,6 @@ public: return cast(immutable(T)[])(this.vec_.data()[0..this.size()]); } - /** - Casts nstring to C string - */ - @trusted - const(T)* opCast()() { - return toCString(); - } - - /** - Casts nstring to D string - */ - @trusted - immutable(T)[] opCast()() { - return toDString(); - } - /** Set content of string */ @@ -472,4 +463,62 @@ unittest { const(char)* cstr2 = "ABCD"; assert(cstrlen(cstr1) == 1); assert(cstrlen(cstr2) == 4); +} + +@("string: vector-of-strings") +unittest { + vector!nstring strings; + strings ~= nstring("a"); + strings ~= nstring("b"); + + vector!nstring copy = strings; + nogc_delete(copy); + + assert(strings[0] == "a"); + assert(strings[1] == "b"); + + assert(copy.size() == 0); +} + +@("string: map-of-strings") +unittest { + import numem.collections.map; + map!(uint, nstring) strings; + strings[0] = nstring("a"); + strings[1] = nstring("b"); + + assert(strings[0] == "a"); + assert(strings[1] == "b"); +} + +version(unittest) { + struct MyStruct { + @nogc: + this(ref return scope inout(MyStruct) src) inout + { + foreach (i, ref inout field; src.tupleof) + this.tupleof[i] = field; + } + nstring str; + } +} + +@("string: struct-with-strings") +unittest { + import std.stdio : writeln; + + vector!MyStruct struct_; + struct_ ~= MyStruct(nstring("a")); + struct_ ~= MyStruct(nstring("b")); + + writeln(struct_[0..$]); + writeln(struct_[0].str); + + vector!MyStruct copy = struct_; + nogc_delete(copy); + + assert(struct_[0].str == "a"); + assert(struct_[1].str == "b"); + + assert(copy.size() == 0); } \ No newline at end of file