diff --git a/std/experimental/allocator/building_blocks/affix_allocator.d b/std/experimental/allocator/building_blocks/affix_allocator.d index af352a82f00..ed44bebc7d6 100644 --- a/std/experimental/allocator/building_blocks/affix_allocator.d +++ b/std/experimental/allocator/building_blocks/affix_allocator.d @@ -182,9 +182,9 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) if (r != Ternary.yes || p1 is null) return r; p1 = p1[stateSize!Prefix .. $]; - auto p2 = (p1.ptr + p1.length - stateSize!Suffix) - .alignDownTo(Suffix.alignof); - result = p1[0 .. p2 - p1.ptr]; + auto p2 = (() @trusted => (&p1[0] + p1.length - stateSize!Suffix) + .alignDownTo(Suffix.alignof))(); + result = p1[0 .. p2 - &p1[0]]; return Ternary.yes; } @@ -435,8 +435,8 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) static assert(is(typeof(&MyAllocator.instance.prefix(e)) == const(uint)*)); void[] p; - assert(MyAllocator.instance.resolveInternalPointer(null, p) == Ternary.no); - Ternary r = MyAllocator.instance.resolveInternalPointer(d.ptr, p); + assert((() nothrow @safe @nogc => MyAllocator.instance.resolveInternalPointer(null, p))() == Ternary.no); + assert((() nothrow @safe => MyAllocator.instance.resolveInternalPointer(&d[0], p))() == Ternary.yes); assert(p.ptr is d.ptr && p.length >= d.length); } diff --git a/std/experimental/allocator/building_blocks/bitmapped_block.d b/std/experimental/allocator/building_blocks/bitmapped_block.d index fdcce538376..4c13fd44143 100644 --- a/std/experimental/allocator/building_blocks/bitmapped_block.d +++ b/std/experimental/allocator/building_blocks/bitmapped_block.d @@ -1016,22 +1016,24 @@ struct BitmappedBlockWithInternalPointers( } /// Ditto + nothrow @safe @nogc Ternary resolveInternalPointer(const void* p, ref void[] result) { - if (p < _heap._payload.ptr - || p >= _heap._payload.ptr + _heap._payload.length) + if ((() @trusted => _heap._payload + && (p < &_heap._payload[0] + || p >= &_heap._payload[0] + _heap._payload.length))()) { return Ternary.no; } // Find block start - auto block = (p - _heap._payload.ptr) / _heap.blockSize; + auto block = (() @trusted => (p - &_heap._payload[0]) / _heap.blockSize)(); if (block >= _allocStart.length) return Ternary.no; // Within an allocation, must find the 1 just to the left of it auto i = _allocStart.find1Backward(block); if (i == i.max) return Ternary.no; auto j = _allocStart.find1(i + 1); - result = _heap._payload.ptr[cast(size_t) (_heap.blockSize * i) - .. cast(size_t) (_heap.blockSize * j)]; + result = (() @trusted => _heap._payload.ptr[cast(size_t) (_heap.blockSize * i) + .. cast(size_t) (_heap.blockSize * j)])(); return Ternary.yes; } @@ -1089,24 +1091,29 @@ struct BitmappedBlockWithInternalPointers( assert(b.length == 123); void[] p; - Ternary r = h.resolveInternalPointer(b.ptr + 17, p); + void* offset = &b[0] + 17; + assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes); assert(p.ptr is b.ptr); assert(p.length >= b.length); b = h.allocate(4096); - h.resolveInternalPointer(b.ptr, p); + offset = &b[0]; + assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes); assert(p is b); - h.resolveInternalPointer(b.ptr + 11, p); + offset = &b[0] + 11; + assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes); assert(p is b); void[] unchanged = p; - h.resolveInternalPointer(b.ptr - 40_970, p); + offset = &b[0] - 40_970; + assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.no); assert(p is unchanged); assert(h.expand(b, 1)); assert(b.length == 4097); - h.resolveInternalPointer(b.ptr + 4096, p); + offset = &b[0] + 4096; + assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes); assert(p.ptr is b.ptr); } @@ -1120,6 +1127,7 @@ struct BitmappedBlockWithInternalPointers( Returns the number of most significant ones before a zero can be found in $(D x). If $(D x) contains no zeros (i.e. is equal to $(D ulong.max)), returns 64. */ +pure nothrow @safe @nogc private uint leadingOnes(ulong x) { uint result = 0; @@ -1131,7 +1139,7 @@ private uint leadingOnes(ulong x) return result; } -@system unittest +@safe unittest { assert(leadingOnes(0) == 0); assert(leadingOnes(~0UL) == 64); @@ -1145,6 +1153,7 @@ private uint leadingOnes(ulong x) /** Finds a run of contiguous ones in $(D x) of length at least $(D n). */ +pure nothrow @safe @nogc private uint findContigOnes(ulong x, uint n) { while (n > 1) @@ -1156,7 +1165,7 @@ private uint findContigOnes(ulong x, uint n) return leadingOnes(~x); } -@system unittest +@safe unittest { assert(findContigOnes(0x0000_0000_0000_0300, 2) == 54); @@ -1173,6 +1182,7 @@ private uint findContigOnes(ulong x, uint n) /* Unconditionally sets the bits from lsb through msb in w to zero. */ +pure nothrow @safe @nogc private void setBits(ref ulong w, uint lsb, uint msb) { assert(lsb <= msb && msb < 64); @@ -1180,7 +1190,7 @@ private void setBits(ref ulong w, uint lsb, uint msb) w |= mask; } -@system unittest +@safe unittest { ulong w; w = 0; setBits(w, 0, 63); assert(w == ulong.max); @@ -1192,6 +1202,7 @@ private void setBits(ref ulong w, uint lsb, uint msb) /* Are bits from lsb through msb in w zero? If so, make then 1 and return the resulting w. Otherwise, just return 0. */ +pure nothrow @safe @nogc private bool setBitsIfZero(ref ulong w, uint lsb, uint msb) { assert(lsb <= msb && msb < 64); @@ -1202,6 +1213,7 @@ private bool setBitsIfZero(ref ulong w, uint lsb, uint msb) } // Assigns bits in w from lsb through msb to zero. +pure nothrow @safe @nogc private void resetBits(ref ulong w, uint lsb, uint msb) { assert(lsb <= msb && msb < 64); @@ -1218,10 +1230,13 @@ private struct BitVector auto rep() { return _rep; } + @safe @nogc this(ulong[] data) { _rep = data; } + pure nothrow @safe @nogc void opSliceAssign(bool b) { _rep[] = b ? ulong.max : 0; } + pure @safe @nogc void opSliceAssign(bool b, ulong x, ulong y) { assert(x <= y && y <= _rep.length * 64); @@ -1253,6 +1268,7 @@ private struct BitVector } } + @safe @nogc bool opIndex(ulong x) { assert(x < length); @@ -1260,6 +1276,7 @@ private struct BitVector & (0x8000_0000_0000_0000UL >> (x % 64))) != 0; } + @safe @nogc void opIndexAssign(bool b, ulong x) { assert(x / 64 <= size_t.max); @@ -1269,6 +1286,7 @@ private struct BitVector else _rep[i] &= ~j; } + nothrow @safe @nogc ulong length() const { return _rep.length * 64; @@ -1277,6 +1295,7 @@ private struct BitVector /* Returns the index of the first 1 to the right of i (including i itself), or length if not found. */ + nothrow @safe @nogc ulong find1(ulong i) { assert(i < length); @@ -1304,6 +1323,7 @@ private struct BitVector /* Returns the index of the first 1 to the left of i (including i itself), or ulong.max if not found. */ + nothrow @safe @nogc ulong find1Backward(ulong i) { assert(i < length); @@ -1329,6 +1349,7 @@ private struct BitVector } /// Are all bits zero? + nothrow @safe @nogc bool allAre0() const { foreach (w; _rep) if (w) return false; @@ -1336,12 +1357,14 @@ private struct BitVector } /// Are all bits one? + nothrow @safe @nogc bool allAre1() const { foreach (w; _rep) if (w != ulong.max) return false; return true; } + nothrow @safe @nogc ulong findZeros(immutable size_t howMany, ulong start) { assert(start < length); @@ -1378,7 +1401,7 @@ private struct BitVector } } -@system unittest +@safe unittest { auto v = BitVector(new ulong[10]); assert(v.length == 640); diff --git a/std/experimental/allocator/building_blocks/bucketizer.d b/std/experimental/allocator/building_blocks/bucketizer.d index ddfc24c8bb7..bd8ed8ffc9c 100644 --- a/std/experimental/allocator/building_blocks/bucketizer.d +++ b/std/experimental/allocator/building_blocks/bucketizer.d @@ -238,7 +238,6 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step) auto b = a.allocate(400); assert(b.length == 400); assert(a.owns(b) == Ternary.yes); - void[] p; a.deallocate(b); } diff --git a/std/experimental/allocator/building_blocks/fallback_allocator.d b/std/experimental/allocator/building_blocks/fallback_allocator.d index a478fd7e211..5d63f964dc3 100644 --- a/std/experimental/allocator/building_blocks/fallback_allocator.d +++ b/std/experimental/allocator/building_blocks/fallback_allocator.d @@ -364,3 +364,17 @@ fallbackAllocator(Primary, Fallback)(auto ref Primary p, auto ref Fallback f) auto buff = a.allocate(42); assert((() pure nothrow @safe @nogc => a.owns(buff))() == Ternary.yes); } + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.typecons : Ternary; + + auto a = fallbackAllocator(GCAllocator.instance, GCAllocator.instance); + auto b = a.allocate(1020); + assert(b.length == 1020); + + void[] p; + assert((() nothrow @safe @nogc => a.resolveInternalPointer(null, p))() == Ternary.no); + assert((() nothrow @safe @nogc => a.resolveInternalPointer(&b[0], p))() == Ternary.yes); +} diff --git a/std/experimental/allocator/building_blocks/null_allocator.d b/std/experimental/allocator/building_blocks/null_allocator.d index c37ec2232cc..f80446b7a4d 100644 --- a/std/experimental/allocator/building_blocks/null_allocator.d +++ b/std/experimental/allocator/building_blocks/null_allocator.d @@ -44,6 +44,7 @@ struct NullAllocator /** Returns $(D Ternary.no). */ + pure nothrow @safe @nogc Ternary resolveInternalPointer(const void*, ref void[]) shared const { return Ternary.no; } /** @@ -81,6 +82,7 @@ struct NullAllocator import std.typecons : Ternary; assert(NullAllocator.instance.empty() == Ternary.yes); assert((() nothrow @safe @nogc => NullAllocator.instance.owns(null))() == Ternary.no); + void[] p; - assert(NullAllocator.instance.resolveInternalPointer(null, p) == Ternary.no); + assert((() nothrow @safe @nogc => NullAllocator.instance.resolveInternalPointer(null, p))() == Ternary.no); } diff --git a/std/experimental/allocator/building_blocks/segregator.d b/std/experimental/allocator/building_blocks/segregator.d index c556e2b4d8b..3ef862b1851 100644 --- a/std/experimental/allocator/building_blocks/segregator.d +++ b/std/experimental/allocator/building_blocks/segregator.d @@ -401,3 +401,20 @@ if (Args.length > 3) assert(b.length == 42); a.deallocate(b); } + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.typecons : Ternary; + + shared Segregator!(1024 * 4, GCAllocator, GCAllocator) a; + + auto b = a.allocate(201); + assert(b.length == 201); + + void[] p; + assert((() nothrow @safe @nogc => a.resolveInternalPointer(null, p))() == Ternary.no); + assert((() nothrow @safe @nogc => a.resolveInternalPointer(&b[0], p))( ) == Ternary.yes); + + a.deallocate(b); +} diff --git a/std/experimental/allocator/gc_allocator.d b/std/experimental/allocator/gc_allocator.d index 348312c53a4..bfc6305aa60 100644 --- a/std/experimental/allocator/gc_allocator.d +++ b/std/experimental/allocator/gc_allocator.d @@ -70,7 +70,7 @@ struct GCAllocator } /// Ditto - pure nothrow + pure nothrow @trusted @nogc Ternary resolveInternalPointer(const void* p, ref void[] result) shared { auto r = GC.addrOf(cast(void*) p); @@ -151,8 +151,8 @@ struct GCAllocator scope(exit) GCAllocator.instance.deallocate(buffer); void[] p; - assert(GCAllocator.instance.resolveInternalPointer(null, p) == Ternary.no); - Ternary r = GCAllocator.instance.resolveInternalPointer(buffer.ptr, p); + assert((() nothrow @safe => GCAllocator.instance.resolveInternalPointer(null, p))() == Ternary.no); + assert((() nothrow @safe => GCAllocator.instance.resolveInternalPointer(&buffer[0], p))() == Ternary.yes); assert(p.ptr is buffer.ptr && p.length >= buffer.length); assert(GC.sizeOf(buffer.ptr) == s); @@ -166,3 +166,17 @@ struct GCAllocator // anything above a page is simply rounded up to next page assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(4096 * 4 + 1))() == 4096 * 5); } + +nothrow @safe unittest +{ + import std.typecons : Ternary; + + void[] buffer = GCAllocator.instance.allocate(42); + void[] result; + Ternary found = GCAllocator.instance.resolveInternalPointer(&buffer[0], result); + + assert(found == Ternary.yes && &result[0] == &buffer[0] && result.length >= buffer.length); + assert(GCAllocator.instance.resolveInternalPointer(null, result) == Ternary.no); + void *badPtr = (() @trusted => cast(void*)(0xdeadbeef))(); + assert(GCAllocator.instance.resolveInternalPointer(badPtr, result) == Ternary.no); +} diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 9113b278301..83eea5ba26a 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -2585,6 +2585,10 @@ struct ThreadLocal(A) /// unittest { + import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mallocator : Mallocator; + static assert(!is(ThreadLocal!Mallocator)); static assert(!is(ThreadLocal!GCAllocator)); alias ThreadLocal!(FreeList!(GCAllocator, 0, 8)) Allocator; @@ -2745,12 +2749,17 @@ private struct EmbeddedTree(T, alias less) void dump() { + import std.stdio; writeln(typeid(this), " @ ", cast(void*) &this); dump(root, 3); } void dump(Node* r, uint indent) { + import std.stdio; + import std.range : repeat; + import std.array : array; + write(repeat(' ', indent).array); if (!r) { @@ -2780,10 +2789,12 @@ private struct EmbeddedTree(T, alias less) unittest { + import std.experimental.allocator.gc_allocator : GCAllocator; + alias a = GCAllocator.instance; alias Tree = EmbeddedTree!(int, (a, b) => a.payload < b.payload); Tree t; - assert(t.empty); + assert(t.empty == Ternary.yes); int[] vals = [ 6, 3, 9, 1, 0, 2, 8, 11 ]; foreach (v; vals) { @@ -2792,14 +2803,14 @@ unittest assert(n); t.assertSane; } - assert(!t.empty); + assert(t.empty != Ternary.yes); foreach (v; vals) { Tree.Node n = { v }; assert(t.remove(&n)); t.assertSane; } - assert(t.empty); + assert(t.empty == Ternary.yes); } /* @@ -2815,6 +2826,8 @@ the block size and two for search management). */ private struct InternalPointersTree(Allocator) { + import std.experimental.allocator.building_blocks.affix_allocator : AffixAllocator; + alias Tree = EmbeddedTree!(size_t, (a, b) => cast(void*) a + a.payload < cast(void*) b); alias Parent = AffixAllocator!(Allocator, Tree.Node); @@ -2844,7 +2857,7 @@ private struct InternalPointersTree(Allocator) /// Ditto bool deallocate(void[] b) { - if (!b.ptr) return; + if (!b.ptr) return true; Tree.Node* n = &parent.prefix(b); blockMap.remove(n) || assert(false); parent.deallocate(b); @@ -2888,6 +2901,7 @@ private struct InternalPointersTree(Allocator) /** Returns the block inside which $(D p) resides, or $(D null) if the pointer does not belong. */ + pure nothrow @safe @nogc Ternary resolveInternalPointer(const void* p, ref void[] result) { // Must define a custom find @@ -2899,7 +2913,7 @@ private struct InternalPointersTree(Allocator) { n = n.left; } - else if (p > (cast(void*) (n + 1)) + n.payload) + else if ((() @trusted => p > (cast(void*) (n + 1)) + n.payload)()) { n = n.right; } @@ -2913,13 +2927,16 @@ private struct InternalPointersTree(Allocator) auto n = find(); if (!n) return Ternary.no; - result = (cast(void*) (n + 1))[0 .. n.payload]; + result = (() @trusted => (cast(void*) (n + 1))[0 .. n.payload])(); return Ternary.yes; } } unittest { + import std.experimental.allocator.mallocator : Mallocator; + import std.random : randomCover; + InternalPointersTree!(Mallocator) a; int[] vals = [ 6, 3, 9, 1, 2, 8, 11 ]; void[][] allox; @@ -2931,15 +2948,17 @@ unittest foreach (b; allox) { - void[] p; - Ternary r = a.resolveInternalPointer(b.ptr, p); - assert(p.ptr is b.ptr && p.length >= b.length); - r = a.resolveInternalPointer(b.ptr + b.length, p); - assert(p.ptr is b.ptr && p.length >= b.length); - r = a.resolveInternalPointer(b.ptr + b.length / 2, p); - assert(p.ptr is b.ptr && p.length >= b.length); - auto bogus = new void[b.length]; - assert(a.resolveInternalPointer(bogus.ptr, p) == Ternary.no); + () pure nothrow @safe { + void[] p; + Ternary r = (() @nogc => a.resolveInternalPointer(&b[0], p))(); + assert(&p[0] == &b[0] && p.length >= b.length); + r = a.resolveInternalPointer((() @trusted => &b[0] + b.length)(), p); + assert(&p[0] == &b[0] && p.length >= b.length); + r = a.resolveInternalPointer((() @trusted => &b[0] + b.length / 2)(), p); + assert(&p[0] == &b[0] && p.length >= b.length); + auto bogus = new void[b.length]; + assert(a.resolveInternalPointer(&bogus[0], p) == Ternary.no); + }(); } foreach (b; allox.randomCover) @@ -2947,12 +2966,21 @@ unittest a.deallocate(b); } - assert(a.empty); + assert(a.empty == Ternary.yes); } //version (std_allocator_benchmark) unittest { + import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; + import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; + import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; + import std.experimental.allocator.building_blocks.segregator : Segregator; + import std.experimental.allocator.building_blocks.bucketizer : Bucketizer; + import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mallocator : Mallocator; + static void testSpeed(A)() { static if (stateSize!A) A a; @@ -2980,6 +3008,8 @@ unittest } } + import std.algorithm.comparison : max; + alias FList = FreeList!(GCAllocator, 0, unbounded); alias A = Segregator!( 8, FreeList!(GCAllocator, 0, 8), @@ -2990,23 +3020,33 @@ unittest 2048, Bucketizer!(FList, 1025, 2048, 256), 3584, Bucketizer!(FList, 2049, 3584, 512), 4072 * 1024, AllocatorList!( - (size_t n) => BitmappedBlock!(4096)(GCAllocator.instance.allocate( + (size_t n) => BitmappedBlock!(4096)(cast(ubyte[]) GCAllocator.instance.allocate( max(n, 4072 * 1024)))), GCAllocator ); - import std.datetime, std.experimental.allocator.null_allocator; + import std.stdio; + import std.conv : to; + import std.datetime.stopwatch; + import std.algorithm.iteration : map; + if (false) writeln(benchmark!( testSpeed!NullAllocator, testSpeed!Mallocator, testSpeed!GCAllocator, testSpeed!(ThreadLocal!A), testSpeed!(A), - )(20)[].map!(t => t.to!("seconds", double))); + )(20)[].map!(t => t.to!Duration)); } unittest { + import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.building_blocks.region : InSituRegion; + import std.experimental.allocator.building_blocks.fallback_allocator : FallbackAllocator; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mallocator : Mallocator; + auto a = allocatorObject(Mallocator.instance); auto b = a.allocate(100); assert(b.length == 100); @@ -3026,14 +3066,21 @@ unittest /// unittest { + import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; + import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; + import std.experimental.allocator.building_blocks.segregator : Segregator; + import std.experimental.allocator.building_blocks.bucketizer : Bucketizer; + import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.gc_allocator : GCAllocator; + /// Define an allocator bound to the built-in GC. IAllocator alloc = allocatorObject(GCAllocator.instance); auto b = alloc.allocate(42); assert(b.length == 42); - assert(alloc.deallocate(b) == Ternary.yes); + assert(alloc.deallocate(b)); + import std.algorithm.comparison : max; // Define an elaborate allocator and bind it to the class API. - // Note that the same variable "alloc" is used. alias FList = FreeList!(GCAllocator, 0, unbounded); alias A = ThreadLocal!( Segregator!( @@ -3045,13 +3092,13 @@ unittest 2048, Bucketizer!(FList, 1025, 2048, 256), 3584, Bucketizer!(FList, 2049, 3584, 512), 4072 * 1024, AllocatorList!( - (n) => BitmappedBlock!(4096)(GCAllocator.instance.allocate( + (n) => BitmappedBlock!(4096)(cast(ubyte[]) GCAllocator.instance.allocate( max(n, 4072 * 1024)))), GCAllocator ) ); auto alloc2 = allocatorObject(A.instance); - b = alloc.allocate(101); - assert(alloc.deallocate(b) == Ternary.yes); + b = alloc2.allocate(101); + assert(alloc2.deallocate(b)); }