diff --git a/.gitignore b/.gitignore index 13cd417..4ecb6d8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ stdx-allocator-test-* *.o *.obj *.lst +dub.selections.json diff --git a/dub.sdl b/dub.sdl index 6705476..19ed148 100644 --- a/dub.sdl +++ b/dub.sdl @@ -2,5 +2,5 @@ name "stdx-allocator" description "Extracted std.experimental.allocator" authors "Team Phobos" copyright "Copyright © 2017, Team Phobos" -license "Boost" -dependency "mir-core" version="~>0.0.0" +license "BSL-1.0" +dependency "mir-core" version="~>0.0.5" diff --git a/source/stdx/allocator/building_blocks/affix_allocator.d b/source/stdx/allocator/building_blocks/affix_allocator.d index 747088f..a84da70 100644 --- a/source/stdx/allocator/building_blocks/affix_allocator.d +++ b/source/stdx/allocator/building_blocks/affix_allocator.d @@ -20,7 +20,7 @@ The following methods are defined if $(D Allocator) defines them, and forward to struct AffixAllocator(Allocator, Prefix, Suffix = void) { import mir.utility : min; - import stdx.allocator.internal : emplace; + import mir.conv : emplace; import stdx.allocator : IAllocator, theAllocator; import stdx.allocator.common : stateSize, forwardToMember, roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf, diff --git a/source/stdx/allocator/building_blocks/allocator_list.d b/source/stdx/allocator/building_blocks/allocator_list.d index 209d048..b62b73f 100644 --- a/source/stdx/allocator/building_blocks/allocator_list.d +++ b/source/stdx/allocator/building_blocks/allocator_list.d @@ -64,7 +64,7 @@ called `factory`. */ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) { - import stdx.allocator.internal : emplace; + import mir.conv : emplace; import stdx.allocator.building_blocks.stats_collector : StatsCollector, Options; import stdx.allocator.internal : Ternary; diff --git a/source/stdx/allocator/building_blocks/bitmapped_block.d b/source/stdx/allocator/building_blocks/bitmapped_block.d index 4239477..81d8841 100644 --- a/source/stdx/allocator/building_blocks/bitmapped_block.d +++ b/source/stdx/allocator/building_blocks/bitmapped_block.d @@ -46,7 +46,9 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment ParentAllocator = NullAllocator) { import stdx.allocator.internal : Ternary; - import std.typecons : tuple, Tuple; + import mir.functional : RefTuple; + // for internal API only + private alias Tuple = RefTuple!(size_t, uint); @system unittest { @@ -361,7 +363,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment it succeeds, fills "result" with the result and returns tuple(size_t.max, 0). Otherwise, returns a tuple with the next position to search. */ - private Tuple!(size_t, uint) allocateAt(size_t wordIdx, uint msbIdx, + private Tuple allocateAt(size_t wordIdx, uint msbIdx, size_t blocks, ref void[] result) { assert(blocks > 0); @@ -375,12 +377,12 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment { // Success result = blocksFor(wordIdx, msbIdx, blocks); - return tuple(size_t.max, 0u); + return Tuple(size_t.max, 0u); } // Can't allocate, make a suggestion return msbIdx + blocks == 64 - ? tuple(wordIdx + 1, 0u) - : tuple(wordIdx, cast(uint) (msbIdx + blocks)); + ? Tuple(wordIdx + 1, 0u) + : Tuple(wordIdx, cast(uint) (msbIdx + blocks)); } // Allocation spans two control words or more immutable mask = ulong.max >> msbIdx; @@ -388,14 +390,14 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment { // We can't allocate the rest of this control word, // return a suggestion. - return tuple(wordIdx + 1, 0u); + return Tuple(wordIdx + 1, 0u); } // We can allocate the rest of this control word, but we first need to // make sure we can allocate the tail. if (wordIdx + 1 == _control.rep.length) { // No more memory - return tuple(_control.rep.length, 0u); + return Tuple(_control.rep.length, 0u); } auto hint = allocateAt(wordIdx + 1, 0, blocks - 64 + msbIdx, result); if (hint[0] == size_t.max) @@ -403,7 +405,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment // We did it! _control.rep[wordIdx] |= mask; result = blocksFor(wordIdx, msbIdx, blocks); - return tuple(size_t.max, 0u); + return Tuple(size_t.max, 0u); } // Failed, return a suggestion that skips this whole run. return hint; @@ -649,7 +651,8 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment return Ternary(_control.allAre0()); } - void dump() + debug(std_experimental_allocator_bitmapped_block) + void dump()() { import std.stdio : writefln, writeln; writefln("%s @ %s {", typeid(this), cast(void*) _control._rep.ptr); @@ -1091,13 +1094,11 @@ x). If $(D x) contains no zeros (i.e. is equal to $(D ulong.max)), returns 64. */ private uint leadingOnes()(ulong x) { - uint result = 0; - while (cast(long) x < 0) - { - ++result; - x <<= 1; - } - return result; + import mir.bitop: ctlz; + x = ~x; + if (x) + return cast(uint) x.ctlz; + return 64; } @system unittest diff --git a/source/stdx/allocator/building_blocks/fallback_allocator.d b/source/stdx/allocator/building_blocks/fallback_allocator.d index ffd4457..e0f8e61 100644 --- a/source/stdx/allocator/building_blocks/fallback_allocator.d +++ b/source/stdx/allocator/building_blocks/fallback_allocator.d @@ -271,51 +271,6 @@ struct FallbackAllocator(Primary, Fallback) a.deallocate(b2); } -/* -Forwards an argument from one function to another -*/ -private auto ref forward(alias arg)() -{ - static if (__traits(isRef, arg)) - { - return arg; - } - else - { - import std.algorithm.mutation : move; - return move(arg); - } -} - -@safe unittest -{ - void fun(T)(auto ref T, string) { /* ... */ } - void gun(T...)(auto ref T args) - { - fun(forward!(args[0]), forward!(args[1])); - } - gun(42, "hello"); - int x; - gun(x, "hello"); -} - -@safe unittest -{ - static void checkByRef(T)(auto ref T value) - { - static assert(__traits(isRef, value)); - } - - static void checkByVal(T)(auto ref T value) - { - static assert(!__traits(isRef, value)); - } - - static void test1(ref int a) { checkByRef(forward!a); } - static void test2(int a) { checkByVal(forward!a); } - static void test3() { int a; checkByVal(forward!a); } -} - /** Convenience function that uses type deduction to return the appropriate $(D FallbackAllocator) instance. To initialize with allocators that don't have @@ -324,6 +279,8 @@ state, use their $(D it) static member. FallbackAllocator!(Primary, Fallback) fallbackAllocator(Primary, Fallback)(auto ref Primary p, auto ref Fallback f) { + import mir.functional: forward; + alias R = FallbackAllocator!(Primary, Fallback); static if (stateSize!Primary) diff --git a/source/stdx/allocator/building_blocks/free_tree.d b/source/stdx/allocator/building_blocks/free_tree.d index 68b30ee..f95e66c 100644 --- a/source/stdx/allocator/building_blocks/free_tree.d +++ b/source/stdx/allocator/building_blocks/free_tree.d @@ -111,7 +111,7 @@ struct FreeTree(ParentAllocator) } debug(std_experimental_allocator_free_tree) - private void dump() + private void dump()() { import std.stdio : writef, writefln, writeln; writeln(typeof(this).stringof, "@", &this, " {"); diff --git a/source/stdx/allocator/building_blocks/kernighan_ritchie.d b/source/stdx/allocator/building_blocks/kernighan_ritchie.d index 225bed5..30b9a1e 100644 --- a/source/stdx/allocator/building_blocks/kernighan_ritchie.d +++ b/source/stdx/allocator/building_blocks/kernighan_ritchie.d @@ -3,7 +3,6 @@ module stdx.allocator.building_blocks.kernighan_ritchie; import stdx.allocator.building_blocks.null_allocator; //debug = KRRegion; -version(unittest) import std.conv : text; debug(KRRegion) import std.stdio; // KRRegion @@ -100,7 +99,9 @@ struct KRRegion(ParentAllocator = NullAllocator) private static struct Node { - import std.typecons : tuple, Tuple; + import mir.functional : RefTuple; + + alias Tuple = RefTuple!(void[], Node*); Node* next; size_t size; @@ -135,7 +136,7 @@ struct KRRegion(ParentAllocator = NullAllocator) return true; } - Tuple!(void[], Node*) allocateHere(size_t bytes) + Tuple allocateHere(size_t bytes) { assert(bytes >= Node.sizeof); assert(bytes % Node.alignof == 0); @@ -152,11 +153,11 @@ struct KRRegion(ParentAllocator = NullAllocator) newNode.size = leftover; newNode.next = next == &this ? newNode : next; assert(next); - return tuple(payload, newNode); + return Tuple(payload, newNode); } // No slack space, just return next node - return tuple(payload, next == &this ? null : next); + return Tuple(payload, next == &this ? null : next); } } @@ -192,7 +193,7 @@ struct KRRegion(ParentAllocator = NullAllocator) return Range(root, root); } - string toString() + string toString()() { import std.format : format; string s = "KRRegion@"; @@ -748,6 +749,7 @@ it actually returns memory to the operating system when possible. @system unittest { + import std.conv : text; import stdx.allocator.gc_allocator : GCAllocator; import stdx.allocator.internal : Ternary; auto alloc = KRRegion!()( @@ -756,7 +758,7 @@ it actually returns memory to the operating system when possible. auto p = cast(KRRegion!()* ) store.ptr; import core.stdc.string : memcpy; import std.algorithm.mutation : move; - import stdx.allocator.internal : emplace; + import mir.conv : emplace; memcpy(p, &alloc, alloc.sizeof); emplace(&alloc); diff --git a/source/stdx/allocator/internal.d b/source/stdx/allocator/internal.d index b94bf25..80f4b5a 100644 --- a/source/stdx/allocator/internal.d +++ b/source/stdx/allocator/internal.d @@ -3,1307 +3,6 @@ module stdx.allocator.internal; import std.traits; -/+ -emplaceRef is a package function for phobos internal use. It works like -emplace, but takes its argument by ref (as opposed to "by pointer"). - -This makes it easier to use, easier to be safe, and faster in a non-inline -build. - -Furthermore, emplaceRef optionally takes a type paremeter, which specifies -the type we want to build. This helps to build qualified objects on mutable -buffer, without breaking the type system with unsafe casts. -+/ -package void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) -{ - static if (args.length == 0) - { - static assert(is(typeof({static T i;})), - convFormat("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof)); - static if (is(T == class)) static assert(!isAbstractClass!T, - T.stringof ~ " is abstract and it can't be emplaced"); - emplaceInitializer(chunk); - } - else static if ( - !is(T == struct) && Args.length == 1 /* primitives, enums, arrays */ - || - Args.length == 1 && is(typeof({T t = args[0];})) /* conversions */ - || - is(typeof(T(args))) /* general constructors */) - { - static struct S - { - T payload; - this(ref Args x) - { - static if (Args.length == 1) - static if (is(typeof(payload = x[0]))) - payload = x[0]; - else - payload = T(x[0]); - else - payload = T(x); - } - } - if (__ctfe) - { - static if (is(typeof(chunk = T(args)))) - chunk = T(args); - else static if (args.length == 1 && is(typeof(chunk = args[0]))) - chunk = args[0]; - else assert(0, "CTFE emplace doesn't support " - ~ T.stringof ~ " from " ~ Args.stringof); - } - else - { - S* p = () @trusted { return cast(S*) &chunk; }(); - emplaceInitializer(*p); - p.__ctor(args); - } - } - else static if (is(typeof(chunk.__ctor(args)))) - { - // This catches the rare case of local types that keep a frame pointer - emplaceInitializer(chunk); - chunk.__ctor(args); - } - else - { - //We can't emplace. Try to diagnose a disabled postblit. - static assert(!(Args.length == 1 && is(Args[0] : T)), - convFormat("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof)); - - //We can't emplace. - static assert(false, - convFormat("%s cannot be emplaced from %s.", T.stringof, Args[].stringof)); - } -} -// ditto -package void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) -if (is(UT == Unqual!UT)) -{ - emplaceRef!(UT, UT)(chunk, args); -} - -//emplace helper functions -private void emplaceInitializer(T)(ref T chunk) @trusted pure nothrow -{ - static if (!hasElaborateAssign!T && isAssignable!T) - chunk = T.init; - else - { - import core.stdc.string : memcpy; - static immutable T init = T.init; - memcpy(&chunk, &init, T.sizeof); - } -} - -// emplace -/** -Given a pointer $(D chunk) to uninitialized memory (but already typed -as $(D T)), constructs an object of non-$(D class) type $(D T) at that -address. If `T` is a class, initializes the class reference to null. - -Returns: A pointer to the newly constructed object (which is the same -as $(D chunk)). - */ -T* emplace(T)(T* chunk) @safe pure nothrow -{ - emplaceRef!T(*chunk); - return chunk; -} - -/// -@system unittest -{ - static struct S - { - int i = 42; - } - S[2] s2 = void; - emplace(&s2); - assert(s2[0].i == 42 && s2[1].i == 42); -} - -/// -@system unittest -{ - interface I {} - class K : I {} - - K k = void; - emplace(&k); - assert(k is null); - - I i = void; - emplace(&i); - assert(i is null); -} - -/** -Given a pointer $(D chunk) to uninitialized memory (but already typed -as a non-class type $(D T)), constructs an object of type $(D T) at -that address from arguments $(D args). If `T` is a class, initializes -the class reference to `args[0]`. - -This function can be $(D @trusted) if the corresponding constructor of -$(D T) is $(D @safe). - -Returns: A pointer to the newly constructed object (which is the same -as $(D chunk)). - */ -T* emplace(T, Args...)(T* chunk, auto ref Args args) -if (is(T == struct) || Args.length == 1) -{ - emplaceRef!T(*chunk, args); - return chunk; -} - -/// -@system unittest -{ - int a; - int b = 42; - assert(*emplace!int(&a, b) == 42); -} - -@system unittest -{ - shared int i; - emplace(&i, 42); - assert(i == 42); -} - -private @nogc pure nothrow @safe -void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) -{ - assert(chunk.length >= typeSize, "emplace: Chunk size too small."); - assert((cast(size_t) chunk.ptr) % typeAlignment == 0, "emplace: Chunk is not aligned."); -} - -/** -Given a raw memory area `chunk` (but already typed as a class type `T`), -constructs an object of `class` type `T` at that address. The constructor -is passed the arguments `Args`. - -If `T` is an inner class whose `outer` field can be used to access an instance -of the enclosing class, then `Args` must not be empty, and the first member of it -must be a valid initializer for that `outer` field. Correct initialization of -this field is essential to access members of the outer class inside `T` methods. - -Note: -This function is `@safe` if the corresponding constructor of `T` is `@safe`. - -Returns: The newly constructed object. - */ -T emplace(T, Args...)(T chunk, auto ref Args args) -if (is(T == class)) -{ - static assert(!isAbstractClass!T, T.stringof ~ - " is abstract and it can't be emplaced"); - - // Initialize the object in its pre-ctor state - enum classSize = __traits(classInstanceSize, T); - (() @trusted => (cast(void*) chunk)[0 .. classSize] = typeid(T).initializer[])(); - - 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 args1 = args[1..$]; - } - else alias args1 = args; - - // Call the ctor if any - static if (is(typeof(chunk.__ctor(args1)))) - { - // T defines a genuine constructor accepting args - // Go the classic route: write .init first, then call ctor - chunk.__ctor(args1); - } - else - { - static assert(args1.length == 0 && !is(typeof(&T.__ctor)), - "Don't know how to initialize an object of type " - ~ T.stringof ~ " with arguments " ~ typeof(args1).stringof); - } - return chunk; -} - -/// -@safe unittest -{ - () @safe { - class SafeClass - { - int x; - @safe this(int x) { this.x = x; } - } - - auto buf = new void[__traits(classInstanceSize, SafeClass)]; - auto support = (() @trusted => cast(SafeClass)(buf.ptr))(); - auto safeClass = emplace!SafeClass(support, 5); - assert(safeClass.x == 5); - - class UnsafeClass - { - int x; - @system this(int x) { this.x = x; } - } - - auto buf2 = new void[__traits(classInstanceSize, UnsafeClass)]; - auto support2 = (() @trusted => cast(UnsafeClass)(buf2.ptr))(); - static assert(!__traits(compiles, emplace!UnsafeClass(support2, 5))); - static assert(!__traits(compiles, emplace!UnsafeClass(buf2, 5))); - }(); -} - -@safe unittest -{ - class Outer - { - int i = 3; - class Inner - { - @safe auto getI() { return i; } - } - } - auto outerBuf = new void[__traits(classInstanceSize, Outer)]; - auto outerSupport = (() @trusted => cast(Outer)(outerBuf.ptr))(); - - auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)]; - auto innerSupport = (() @trusted => cast(Outer.Inner)(innerBuf.ptr))(); - - auto inner = innerSupport.emplace!(Outer.Inner)(outerSupport.emplace!Outer); - assert(inner.getI == 3); -} - -/** -Given a raw memory area `chunk`, constructs an object of `class` type `T` at -that address. The constructor is passed the arguments `Args`. - -If `T` is an inner class whose `outer` field can be used to access an instance -of the enclosing class, then `Args` must not be empty, and the first member of it -must be a valid initializer for that `outer` field. Correct initialization of -this field is essential to access members of the outer class inside `T` methods. - -Preconditions: -`chunk` must be at least as large as `T` needs and should have an alignment -multiple of `T`'s alignment. (The size of a `class` instance is obtained by using -$(D __traits(classInstanceSize, T))). - -Note: -This function can be `@trusted` if the corresponding constructor of `T` is `@safe`. - -Returns: The newly constructed object. - */ -T emplace(T, Args...)(void[] chunk, auto ref Args args) -if (is(T == class)) -{ - enum classSize = __traits(classInstanceSize, T); - testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof); - return emplace!T(cast(T)(chunk.ptr), args); -} - -/// -@system unittest -{ - static class C - { - int i; - this(int i){this.i = i;} - } - auto buf = new void[__traits(classInstanceSize, C)]; - auto c = emplace!C(buf, 5); - assert(c.i == 5); -} - -@system unittest -{ - class Outer - { - int i = 3; - class Inner - { - auto getI() { return i; } - } - } - auto outerBuf = new void[__traits(classInstanceSize, Outer)]; - auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)]; - auto inner = innerBuf.emplace!(Outer.Inner)(outerBuf.emplace!Outer); - assert(inner.getI == 3); -} - -@nogc pure nothrow @safe unittest -{ - static if (__VERSION__ >= 2072) - mixin(` - int var = 6; - align(__conv_EmplaceTestClass.alignof) ubyte[__traits(classInstanceSize, __conv_EmplaceTestClass)] buf; - auto support = (() @trusted => cast(__conv_EmplaceTestClass)(buf.ptr))(); - auto k = emplace!__conv_EmplaceTestClass(support, 5, var); - assert(k.i == 5); - assert(var == 7); - `); -} - -/** -Given a raw memory area $(D chunk), constructs an object of non-$(D -class) type $(D T) at that address. The constructor is passed the -arguments $(D args), if any. - -Preconditions: -$(D chunk) must be at least as large -as $(D T) needs and should have an alignment multiple of $(D T)'s -alignment. - -Note: -This function can be $(D @trusted) if the corresponding constructor of -$(D T) is $(D @safe). - -Returns: A pointer to the newly constructed object. - */ -T* emplace(T, Args...)(void[] chunk, auto ref Args args) -if (!is(T == class)) -{ - testEmplaceChunk(chunk, T.sizeof, T.alignof, T.stringof); - emplaceRef!(T, Unqual!T)(*cast(Unqual!T*) chunk.ptr, args); - return cast(T*) chunk.ptr; -} - -/// -@system unittest -{ - struct S - { - int a, b; - } - auto buf = new void[S.sizeof]; - S s; - s.a = 42; - s.b = 43; - auto s1 = emplace!S(buf, s); - assert(s1.a == 42 && s1.b == 43); -} - -// Bulk of emplace unittests starts here - -@system unittest /* unions */ -{ - static union U - { - string a; - int b; - struct - { - long c; - int[] d; - } - } - U u1 = void; - U u2 = { "hello" }; - emplace(&u1, u2); - assert(u1.a == "hello"); -} - -version(unittest) private struct __conv_EmplaceTest -{ - int i = 3; - this(int i) - { - assert(this.i == 3 && i == 5); - this.i = i; - } - this(int i, ref int j) - { - assert(i == 5 && j == 6); - this.i = i; - ++j; - } - -@disable: - this(); - this(this); - void opAssign(); -} - -version(unittest) private class __conv_EmplaceTestClass -{ - int i = 3; - this(int i) @nogc @safe pure nothrow - { - assert(this.i == 3 && i == 5); - this.i = i; - } - this(int i, ref int j) @nogc @safe pure nothrow - { - assert(i == 5 && j == 6); - this.i = i; - ++j; - } -} - -@system unittest // bugzilla 15772 -{ - abstract class Foo {} - class Bar: Foo {} - void[] memory; - // test in emplaceInitializer - static assert(!is(typeof(emplace!Foo(cast(Foo*) memory.ptr)))); - static assert( is(typeof(emplace!Bar(cast(Bar*) memory.ptr)))); - // test in the emplace overload that takes void[] - static assert(!is(typeof(emplace!Foo(memory)))); - static assert( is(typeof(emplace!Bar(memory)))); -} - -@system unittest -{ - struct S { @disable this(); } - S s = void; - static assert(!__traits(compiles, emplace(&s))); - emplace(&s, S.init); -} - -@system unittest -{ - struct S1 - {} - - struct S2 - { - void opAssign(S2); - } - - S1 s1 = void; - S2 s2 = void; - S1[2] as1 = void; - S2[2] as2 = void; - emplace(&s1); - emplace(&s2); - emplace(&as1); - emplace(&as2); -} - -@system unittest -{ - static struct S1 - { - this(this) @disable; - } - static struct S2 - { - this() @disable; - } - S1[2] ss1 = void; - S2[2] ss2 = void; - emplace(&ss1); - static assert(!__traits(compiles, emplace(&ss2))); - S1 s1 = S1.init; - S2 s2 = S2.init; - static assert(!__traits(compiles, emplace(&ss1, s1))); - emplace(&ss2, s2); -} - -@system unittest -{ - struct S - { - immutable int i; - } - S s = void; - S[2] ss1 = void; - S[2] ss2 = void; - emplace(&s, 5); - assert(s.i == 5); - emplace(&ss1, s); - assert(ss1[0].i == 5 && ss1[1].i == 5); - emplace(&ss2, ss1); - assert(ss2 == ss1); -} - -//Start testing emplace-args here - -@system unittest -{ - interface I {} - class K : I {} - - K k = null, k2 = new K; - assert(k !is k2); - emplace!K(&k, k2); - assert(k is k2); - - I i = null; - assert(i !is k); - emplace!I(&i, k); - assert(i is k); -} - -@system unittest -{ - static struct S - { - int i = 5; - void opAssign(S){assert(0);} - } - S[2] sa = void; - S[2] sb; - emplace(&sa, sb); - assert(sa[0].i == 5 && sa[1].i == 5); -} - -//Start testing emplace-struct here - -// Test constructor branch -@system unittest -{ - struct S - { - double x = 5, y = 6; - this(int a, int b) - { - assert(x == 5 && y == 6); - x = a; - y = b; - } - } - - auto s1 = new void[S.sizeof]; - auto s2 = S(42, 43); - assert(*emplace!S(cast(S*) s1.ptr, s2) == s2); - assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45)); -} - -@system unittest -{ - __conv_EmplaceTest k = void; - emplace(&k, 5); - assert(k.i == 5); -} - -@system unittest -{ - int var = 6; - __conv_EmplaceTest k = void; - emplace(&k, 5, var); - assert(k.i == 5); - assert(var == 7); -} - -// Test matching fields branch -@system unittest -{ - struct S { uint n; } - S s; - emplace!S(&s, 2U); - assert(s.n == 2); -} - -@safe unittest -{ - struct S { int a, b; this(int){} } - S s; - static assert(!__traits(compiles, emplace!S(&s, 2, 3))); -} - -@system unittest -{ - struct S { int a, b = 7; } - S s1 = void, s2 = void; - - emplace!S(&s1, 2); - assert(s1.a == 2 && s1.b == 7); - - emplace!S(&s2, 2, 3); - assert(s2.a == 2 && s2.b == 3); -} - -//opAssign -@system unittest -{ - static struct S - { - int i = 5; - void opAssign(int){assert(0);} - void opAssign(S){assert(0);} - } - S sa1 = void; - S sa2 = void; - S sb1 = S(1); - emplace(&sa1, sb1); - emplace(&sa2, 2); - assert(sa1.i == 1); - assert(sa2.i == 2); -} - -//postblit precedence -@system unittest -{ - //Works, but breaks in "-w -O" because of @@@9332@@@. - //Uncomment test when 9332 is fixed. - static struct S - { - int i; - - this(S other){assert(false);} - this(int i){this.i = i;} - this(this){} - } - S a = void; - assert(is(typeof({S b = a;}))); //Postblit - assert(is(typeof({S b = S(a);}))); //Constructor - auto b = S(5); - emplace(&a, b); - assert(a.i == 5); - - static struct S2 - { - int* p; - this(const S2){} - } - static assert(!is(immutable S2 : S2)); - S2 s2 = void; - immutable is2 = (immutable S2).init; - emplace(&s2, is2); -} - -//nested structs and postblit -@system unittest -{ - static struct S - { - int* p; - this(int i){p = [i].ptr;} - this(this) - { - if (p) - p = [*p].ptr; - } - } - static struct SS - { - S s; - void opAssign(const SS) - { - assert(0); - } - } - SS ssa = void; - SS ssb = SS(S(5)); - emplace(&ssa, ssb); - assert(*ssa.s.p == 5); - assert(ssa.s.p != ssb.s.p); -} - -//disabled postblit -@system unittest -{ - static struct S1 - { - int i; - @disable this(this); - } - S1 s1 = void; - emplace(&s1, 1); - assert(s1.i == 1); - static assert(!__traits(compiles, emplace(&s1, S1.init))); - - static struct S2 - { - int i; - @disable this(this); - this(ref S2){} - } - S2 s2 = void; - static assert(!__traits(compiles, emplace(&s2, 1))); - emplace(&s2, S2.init); - - static struct SS1 - { - S1 s; - } - SS1 ss1 = void; - emplace(&ss1); - static assert(!__traits(compiles, emplace(&ss1, SS1.init))); - - static struct SS2 - { - S2 s; - } - SS2 ss2 = void; - emplace(&ss2); - static assert(!__traits(compiles, emplace(&ss2, SS2.init))); - - - // SS1 sss1 = s1; //This doesn't compile - // SS1 sss1 = SS1(s1); //This doesn't compile - // So emplace shouldn't compile either - static assert(!__traits(compiles, emplace(&sss1, s1))); - static assert(!__traits(compiles, emplace(&sss2, s2))); -} - -//Imutability -@system unittest -{ - //Castable immutability - { - static struct S1 - { - int i; - } - static assert(is( immutable(S1) : S1)); - S1 sa = void; - auto sb = immutable(S1)(5); - emplace(&sa, sb); - assert(sa.i == 5); - } - //Un-castable immutability - { - static struct S2 - { - int* p; - } - static assert(!is(immutable(S2) : S2)); - S2 sa = void; - auto sb = immutable(S2)(null); - assert(!__traits(compiles, emplace(&sa, sb))); - } -} - -@system unittest -{ - static struct S - { - immutable int i; - immutable(int)* j; - } - S s = void; - emplace(&s, 1, null); - emplace(&s, 2, &s.i); - assert(s is S(2, &s.i)); -} - -//Context pointer -@system unittest -{ - int i = 0; - { - struct S1 - { - void foo(){++i;} - } - S1 sa = void; - S1 sb; - emplace(&sa, sb); - sa.foo(); - assert(i == 1); - } - { - struct S2 - { - void foo(){++i;} - this(this){} - } - S2 sa = void; - S2 sb; - emplace(&sa, sb); - sa.foo(); - assert(i == 2); - } -} - -//Alias this -@system unittest -{ - static struct S - { - int i; - } - //By Ref - { - static struct SS1 - { - int j; - S s; - alias s this; - } - S s = void; - SS1 ss = SS1(1, S(2)); - emplace(&s, ss); - assert(s.i == 2); - } - //By Value - { - static struct SS2 - { - int j; - S s; - S foo() @property{return s;} - alias foo this; - } - S s = void; - SS2 ss = SS2(1, S(2)); - emplace(&s, ss); - assert(s.i == 2); - } -} -version(unittest) -{ - //Ambiguity - struct __std_conv_S - { - int i; - this(__std_conv_SS ss) {assert(0);} - static opCall(__std_conv_SS ss) - { - __std_conv_S s; s.i = ss.j; - return s; - } - } - struct __std_conv_SS - { - int j; - __std_conv_S s; - ref __std_conv_S foo() return @property {s.i = j; return s;} - alias foo this; - } - static assert(is(__std_conv_SS : __std_conv_S)); - @system unittest - { - __std_conv_S s = void; - __std_conv_SS ss = __std_conv_SS(1); - - __std_conv_S sTest1 = ss; //this calls "SS alias this" (and not "S.this(SS)") - emplace(&s, ss); //"alias this" should take precedence in emplace over "opCall" - assert(s.i == 1); - } -} - -//Nested classes -@system unittest -{ - class A{} - static struct S - { - A a; - } - S s1 = void; - S s2 = S(new A); - emplace(&s1, s2); - assert(s1.a is s2.a); -} - -//safety & nothrow & CTFE -@system unittest -{ - //emplace should be safe for anything with no elaborate opassign - static struct S1 - { - int i; - } - static struct S2 - { - int i; - this(int j)@safe nothrow{i = j;} - } - - int i; - S1 s1 = void; - S2 s2 = void; - - auto pi = &i; - auto ps1 = &s1; - auto ps2 = &s2; - - void foo() @safe nothrow - { - emplace(pi); - emplace(pi, 5); - emplace(ps1); - emplace(ps1, 5); - emplace(ps1, S1.init); - emplace(ps2); - emplace(ps2, 5); - emplace(ps2, S2.init); - } - foo(); - - T bar(T)() @property - { - T t/+ = void+/; //CTFE void illegal - emplace(&t, 5); - return t; - } - // CTFE - enum a = bar!int; - static assert(a == 5); - enum b = bar!S1; - static assert(b.i == 5); - enum c = bar!S2; - static assert(c.i == 5); - // runtime - auto aa = bar!int; - assert(aa == 5); - auto bb = bar!S1; - assert(bb.i == 5); - auto cc = bar!S2; - assert(cc.i == 5); -} - - -@system unittest -{ - struct S - { - int[2] get(){return [1, 2];} - alias get this; - } - struct SS - { - int[2] ii; - } - struct ISS - { - int[2] ii; - } - S s; - SS ss = void; - ISS iss = void; - emplace(&ss, s); - emplace(&iss, s); - assert(ss.ii == [1, 2]); - assert(iss.ii == [1, 2]); -} - -//disable opAssign -@system unittest -{ - static struct S - { - @disable void opAssign(S); - } - S s; - emplace(&s, S.init); -} - -//opCall -@system unittest -{ - int i; - //Without constructor - { - static struct S1 - { - int i; - static S1 opCall(int*){assert(0);} - } - S1 s = void; - static assert(!__traits(compiles, emplace(&s, 1))); - } - //With constructor - { - static struct S2 - { - int i = 0; - static S2 opCall(int*){assert(0);} - static S2 opCall(int){assert(0);} - this(int i){this.i = i;} - } - S2 s = void; - emplace(&s, 1); - assert(s.i == 1); - } - //With postblit ambiguity - { - static struct S3 - { - int i = 0; - static S3 opCall(ref S3){assert(0);} - } - S3 s = void; - emplace(&s, S3.init); - } -} - -@safe unittest //@@@9559@@@ -{ - import std.algorithm.iteration : map; - import std.array : array; - import std.typecons : Nullable; - alias I = Nullable!int; - auto ints = [0, 1, 2].map!(i => i & 1 ? I.init : I(i))(); - auto asArray = array(ints); -} - -@system unittest //http://forum.dlang.org/post/nxbdgtdlmwscocbiypjs@forum.dlang.org -{ - import std.array : array; - import std.datetime : SysTime, UTC; - import std.math : isNaN; - - static struct A - { - double i; - } - - static struct B - { - invariant() - { - if (j == 0) - assert(a.i.isNaN(), "why is 'j' zero?? and i is not NaN?"); - else - assert(!a.i.isNaN()); - } - SysTime when; // comment this line avoid the breakage - int j; - A a; - } - - B b1 = B.init; - assert(&b1); // verify that default eyes invariants are ok; - - auto b2 = B(SysTime(0, UTC()), 1, A(1)); - assert(&b2); - auto b3 = B(SysTime(0, UTC()), 1, A(1)); - assert(&b3); - - auto arr = [b2, b3]; - - assert(arr[0].j == 1); - assert(arr[1].j == 1); - auto a2 = arr.array(); // << bang, invariant is raised, also if b2 and b3 are good -} - -//static arrays -@system unittest -{ - static struct S - { - int[2] ii; - } - static struct IS - { - immutable int[2] ii; - } - int[2] ii; - S s = void; - IS ims = void; - ubyte ub = 2; - emplace(&s, ub); - emplace(&s, ii); - emplace(&ims, ub); - emplace(&ims, ii); - uint[2] uu; - static assert(!__traits(compiles, {S ss = S(uu);})); - static assert(!__traits(compiles, emplace(&s, uu))); -} - -@system unittest -{ - int[2] sii; - int[2] sii2; - uint[2] uii; - uint[2] uii2; - emplace(&sii, 1); - emplace(&sii, 1U); - emplace(&uii, 1); - emplace(&uii, 1U); - emplace(&sii, sii2); - //emplace(&sii, uii2); //Sorry, this implementation doesn't know how to... - //emplace(&uii, sii2); //Sorry, this implementation doesn't know how to... - emplace(&uii, uii2); - emplace(&sii, sii2[]); - //emplace(&sii, uii2[]); //Sorry, this implementation doesn't know how to... - //emplace(&uii, sii2[]); //Sorry, this implementation doesn't know how to... - emplace(&uii, uii2[]); -} - -@system unittest -{ - bool allowDestruction = false; - struct S - { - int i; - this(this){} - ~this(){assert(allowDestruction);} - } - S s = S(1); - S[2] ss1 = void; - S[2] ss2 = void; - S[2] ss3 = void; - emplace(&ss1, s); - emplace(&ss2, ss1); - emplace(&ss3, ss2[]); - assert(ss1[1] == s); - assert(ss2[1] == s); - assert(ss3[1] == s); - allowDestruction = true; -} - -@system unittest -{ - //Checks postblit, construction, and context pointer - int count = 0; - struct S - { - this(this) - { - ++count; - } - ~this() - { - --count; - } - } - - S s; - { - S[4] ss = void; - emplace(&ss, s); - assert(count == 4); - } - assert(count == 0); -} - -@system unittest -{ - struct S - { - int i; - } - S s; - S[2][2][2] sss = void; - emplace(&sss, s); -} - -@system unittest //Constness -{ - import std.stdio; - - int a = void; - emplaceRef!(const int)(a, 5); - - immutable i = 5; - const(int)* p = void; - emplaceRef!(const int*)(p, &i); - - struct S - { - int* p; - } - alias IS = immutable(S); - S s = void; - emplaceRef!IS(s, IS()); - S[2] ss = void; - emplaceRef!(IS[2])(ss, IS()); - - IS[2] iss = IS.init; - emplaceRef!(IS[2])(ss, iss); - emplaceRef!(IS[2])(ss, iss[]); -} - -pure nothrow @safe @nogc unittest -{ - int i; - emplaceRef(i); - emplaceRef!int(i); - emplaceRef(i, 5); - emplaceRef!int(i, 5); -} - -// Test attribute propagation for UDTs -pure nothrow @safe /* @nogc */ unittest -{ - static struct Safe - { - this(this) pure nothrow @safe @nogc {} - } - - Safe safe = void; - emplaceRef(safe, Safe()); - - Safe[1] safeArr = [Safe()]; - Safe[1] uninitializedSafeArr = void; - emplaceRef(uninitializedSafeArr, safe); - emplaceRef(uninitializedSafeArr, safeArr); - - static struct Unsafe - { - this(this) @system {} - } - - Unsafe unsafe = void; - static assert(!__traits(compiles, emplaceRef(unsafe, Unsafe()))); - - Unsafe[1] unsafeArr = [Unsafe()]; - Unsafe[1] uninitializedUnsafeArr = void; - static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafe))); - static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafeArr))); -} - -@system unittest -{ - // Issue 15313 - static struct Node - { - int payload; - Node* next; - uint refs; - } - - import core.stdc.stdlib : malloc; - void[] buf = malloc(Node.sizeof)[0 .. Node.sizeof]; - - import stdx.allocator.internal : emplace; - const Node* n = emplace!(const Node)(buf, 42, null, 10); - assert(n.payload == 42); - assert(n.next == null); - assert(n.refs == 10); -} - -@system unittest -{ - int var = 6; - auto k = emplace!__conv_EmplaceTest(new void[__conv_EmplaceTest.sizeof], 5, var); - assert(k.i == 5); - assert(var == 7); -} - -@system unittest -{ - class A - { - int x = 5; - int y = 42; - this(int z) - { - assert(x == 5 && y == 42); - x = y = z; - } - } - void[] buf; - - static if (__VERSION__ >= 2072) - mixin(` - static align(A.alignof) byte[__traits(classInstanceSize, A)] sbuf; - buf = sbuf[]; - auto a = emplace!A(buf, 55); - assert(a.x == 55 && a.y == 55); - - // emplace in bigger buffer - buf = new byte[](__traits(classInstanceSize, A) + 10); - a = emplace!A(buf, 55); - assert(a.x == 55 && a.y == 55); - - // need ctor args - static assert(!is(typeof(emplace!A(buf)))); - `); -} // Bulk of emplace unittests ends here static if (is(typeof({ import std.typecons : Ternary; }))) @@ -1426,57 +125,3 @@ if (isNumeric!X) assert(!isPowerOf2(cast(X) x)); } } - -/** -Determines whether `T` is a class nested inside another class -and that `T.outer` is the implicit reference to the outer class -(i.e. `outer` has not been used as a field or method name) - -Params: - T = type to test - -Returns: -`true` if `T` is a class nested inside another, with the conditions described above; -`false` otherwise -*/ -template isInnerClass(T) - if (is(T == class)) -{ - import std.meta : staticIndexOf; - - static if (is(typeof(T.outer))) - enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) - && (staticIndexOf!(__traits(allMembers, T), "outer") == -1); - else - enum isInnerClass = false; -} - -/// -@safe unittest -{ - class C - { - int outer; - } - static assert(!isInnerClass!C); - - class Outer1 - { - class Inner1 { } - class Inner2 - { - int outer; - } - } - static assert(isInnerClass!(Outer1.Inner1)); - static assert(!isInnerClass!(Outer1.Inner2)); - - static class Outer2 - { - static class Inner - { - int outer; - } - } - static assert(!isInnerClass!(Outer2.Inner)); -} diff --git a/source/stdx/allocator/package.d b/source/stdx/allocator/package.d index 31c342e..0cd28a8 100644 --- a/source/stdx/allocator/package.d +++ b/source/stdx/allocator/package.d @@ -537,7 +537,7 @@ private IAllocator setupThreadAllocator()() nothrow @nogc @safe } assert(!_threadAllocator); - import stdx.allocator.internal : emplace; + import mir.conv : emplace; static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState; _threadAllocator = () @trusted { return emplace!(ThreadAllocator)(_threadAllocatorState[]); } (); return _threadAllocator; @@ -663,14 +663,17 @@ propagates the exception. auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args) { import mir.utility : max; - import stdx.allocator.internal : emplace, emplaceRef; + import mir.conv : emplace, emplaceRef; auto m = alloc.allocate(max(stateSize!T, size_t(1))); if (!m.ptr) return null; // make can only be @safe if emplace or emplaceRef is `pure` auto construct() { - static if (is(T == class)) return emplace!T(m, args); + static if (is(T == class)) + { + return emplace!T(m, args); + } else { // Assume cast is safe as allocation succeeded for `stateSize!T` @@ -896,38 +899,56 @@ nothrow @safe @nogc unittest assertThrown(make!InvalidImpureStruct(Mallocator.instance, 42)); } -private void fillWithMemcpy(T)(void[] array, auto ref T filler) nothrow +/++ ++/ +T[] uninitializedFillDefault(T)(T[] array) nothrow @nogc { - import core.stdc.string : memcpy; - import mir.utility : min; - if (!array.length) return; - memcpy(array.ptr, &filler, T.sizeof); - // Fill the array from the initialized portion of itself exponentially. - for (size_t offset = T.sizeof; offset < array.length; ) + static if (__VERSION__ < 2083) { - size_t extent = min(offset, array.length - offset); - memcpy(array.ptr + offset, array.ptr, extent); - offset += extent; + static if (is(Unqual!T == char) || is(Unqual!T == wchar)) + { + import core.stdc.string : memset; + if (array !is null) + memset(array.ptr, 0xff, T.sizeof * array.length); + return array; + } + else + { + pragma(inline, false); + import mir.conv : emplaceInitializer; + foreach(ref e; array) + emplaceInitializer(e); + return array; + } + } + else + { + static if (__traits(isZeroInit, T)) + { + import core.stdc.string : memset; + if (array !is null) + memset(array.ptr, 0, T.sizeof * array.length); + return array; + } + else static if (is(Unqual!T == char) || is(Unqual!T == wchar)) + { + import core.stdc.string : memset; + if (array !is null) + memset(array.ptr, 0xff, T.sizeof * array.length); + return array; + } + else + { + pragma(inline, false); + import mir.conv : emplaceInitializer; + foreach(ref e; array) + emplaceInitializer(e); + return array; + } } } -@system unittest -{ - int[] a; - fillWithMemcpy(a, 42); - assert(a.length == 0); - a = [ 1, 2, 3, 4, 5 ]; - fillWithMemcpy(a, 42); - assert(a == [ 42, 42, 42, 42, 42]); -} - -private T[] uninitializedFillDefault(T)(T[] array) nothrow -{ - T t = T.init; - fillWithMemcpy(array, t); - return array; -} - +/// pure nothrow @nogc @system unittest { @@ -1039,6 +1060,7 @@ T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length, if (!m.ptr) return null; auto result = () @trusted { return cast(T[]) m; } (); import std.traits : hasElaborateCopyConstructor; + size_t i = 0; static if (hasElaborateCopyConstructor!T) { scope(failure) @@ -1049,7 +1071,6 @@ T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length, alloc.deallocate(m); } - size_t i = 0; static if (hasElaborateDestructor!T) { scope (failure) @@ -1060,16 +1081,11 @@ T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length, } } } - import stdx.allocator.internal : emplace; - for (; i < length; ++i) - { - emplace!T(&result[i], init); - } } - else + import mir.conv: emplaceRef; + for (; i < length; ++i) { - alias U = Unqual!T; - () @trusted { fillWithMemcpy(cast(U[]) result, *(cast(U*) &init)); }(); + emplaceRef!T(result[i], init); } return result; } @@ -1195,7 +1211,7 @@ if (isInputRange!R && !isInfinite!R) alloc.deallocate(m); } - import stdx.allocator.internal : emplaceRef; + import mir.conv : emplaceRef; static if (isNarrowString!R || isRandomAccessRange!R) { foreach (j; 0 .. range.length) @@ -1252,7 +1268,7 @@ if (isInputRange!R && !isInfinite!R) } result = () @trusted { return cast(T[]) m; } (); } - import stdx.allocator.internal : emplaceRef; + import mir.conv : emplaceRef; emplaceRef(result[initialized], range.front); } @@ -1583,7 +1599,7 @@ if (isInputRange!R) for (; !range.empty; range.popFront, toFill.popFront) { assert(!toFill.empty); - import stdx.allocator.internal : emplace; + import mir.conv : emplace; emplace!T(&toFill.front, range.front); } assert(toFill.empty); @@ -1603,7 +1619,7 @@ if (isInputRange!R) array = cast(T[]) buf; return false; } - import stdx.allocator.internal : emplace; + import mir.conv : emplace; emplace!T(buf[$ - T.sizeof .. $], range.front); } @@ -2001,7 +2017,7 @@ statically-typed allocator.) CAllocatorImpl!A allocatorObject(A)(auto ref A a) if (!isPointer!A) { - import stdx.allocator.internal : emplace; + import mir.conv : emplace; static if (stateSize!A == 0) { enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof); @@ -2042,7 +2058,7 @@ if (!isPointer!A) CAllocatorImpl!(A, Yes.indirect) allocatorObject(A)(A* pa) { assert(pa); - import stdx.allocator.internal : emplace; + import mir.conv : emplace; auto state = pa.allocate(stateSize!(CAllocatorImpl!(A, Yes.indirect))); static if (__traits(hasMember, A, "deallocate")) { @@ -2093,7 +2109,7 @@ statically-typed allocator.) shared(CSharedAllocatorImpl!A) sharedAllocatorObject(A)(auto ref A a) if (!isPointer!A) { - import stdx.allocator.internal : emplace; + import mir.conv : emplace; static if (stateSize!A == 0) { enum s = stateSize!(CSharedAllocatorImpl!A).divideRoundUp(ulong.sizeof); @@ -2127,7 +2143,7 @@ if (!isPointer!A) shared(CSharedAllocatorImpl!(A, Yes.indirect)) sharedAllocatorObject(A)(A* pa) { assert(pa); - import stdx.allocator.internal : emplace; + import mir.conv : emplace; auto state = pa.allocate(stateSize!(CSharedAllocatorImpl!(A, Yes.indirect))); static if (__traits(hasMember, A, "deallocate")) {