diff --git a/std/experimental/allocator/building_blocks/affix_allocator.d b/std/experimental/allocator/building_blocks/affix_allocator.d index 0f2f20a4876..ddee165a1e5 100644 --- a/std/experimental/allocator/building_blocks/affix_allocator.d +++ b/std/experimental/allocator/building_blocks/affix_allocator.d @@ -124,17 +124,19 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) if (!bytes) return null; auto result = parent.allocate(actualAllocationSize(bytes)); if (result is null) return null; - static if (stateSize!Prefix) - { - assert(result.ptr.alignedAt(Prefix.alignof)); - emplace!Prefix(cast(Prefix*) result.ptr); - } - static if (stateSize!Suffix) - { - auto suffixP = result.ptr + result.length - Suffix.sizeof; - assert(suffixP.alignedAt(Suffix.alignof)); - emplace!Suffix(cast(Suffix*)(suffixP)); - } + () @trusted { + static if (stateSize!Prefix) + { + assert(result.ptr.alignedAt(Prefix.alignof)); + emplace!Prefix(cast(Prefix*) result.ptr); + } + static if (stateSize!Suffix) + { + auto suffixP = result.ptr + result.length - Suffix.sizeof; + assert(suffixP.alignedAt(Suffix.alignof)); + emplace!Suffix(cast(Suffix*)(suffixP)); + } + }(); return result[stateSize!Prefix .. stateSize!Prefix + bytes]; } @@ -411,18 +413,20 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) auto a = AffixAllocator!(BitmappedBlock!128, ulong, ulong) (BitmappedBlock!128(new ubyte[128 * 4096])); assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b = a.allocate(42); + auto b = (() pure nothrow @safe @nogc => a.allocate(42))(); assert(b.length == 42); assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no); } -@system unittest +nothrow @safe @nogc unittest { import std.experimental.allocator.mallocator : Mallocator; alias A = AffixAllocator!(Mallocator, size_t); auto b = A.instance.allocate(10); - A.instance.prefix(b) = 10; - assert(A.instance.prefix(b) == 10); + () @trusted { + A.instance.prefix(b) = 10; + assert(A.instance.prefix(b) == 10); + }(); import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; @@ -454,18 +458,19 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) assert(p.ptr is d.ptr && p.length >= d.length); } -@system unittest +//@system unittest +nothrow @safe unittest { import std.experimental.allocator.gc_allocator; alias a = AffixAllocator!(GCAllocator, uint).instance; // Check that goodAllocSize inherits from parent, i.e. GCAllocator - assert(__traits(compiles, (() nothrow @safe @nogc => a.goodAllocSize(1))())); + assert(__traits(compiles, (() @nogc => a.goodAllocSize(1))())); // Ensure deallocate inherits from parent auto b = a.allocate(42); assert(b.length == 42); - () nothrow @nogc { a.deallocate(b); }(); + () @trusted @nogc { a.deallocate(b); }(); } // Test that deallocateAll infers from parent @@ -474,7 +479,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) import std.experimental.allocator.building_blocks.region : Region; auto a = AffixAllocator!(Region!(), uint)(Region!()(new ubyte[1024 * 64])); - auto b = a.allocate(42); + auto b = (() pure nothrow @safe @nogc => a.allocate(42))(); assert(b.length == 42); assert((() nothrow @nogc => a.deallocateAll())()); } diff --git a/std/experimental/allocator/building_blocks/allocator_list.d b/std/experimental/allocator/building_blocks/allocator_list.d index 9e9dcda5125..382a3c0bbc5 100644 --- a/std/experimental/allocator/building_blocks/allocator_list.d +++ b/std/experimental/allocator/building_blocks/allocator_list.d @@ -89,8 +89,8 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) @disable this(this); // Is this node unused? - void setUnused() { next = &this; } - bool unused() const { return next is &this; } + void setUnused() @trusted { next = &this; } + bool unused() @safe const { return next is &this; } // Just forward everything to the allocator alias a this; @@ -196,9 +196,9 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) private void moveAllocators(void[] newPlace) { - assert(newPlace.ptr.alignedAt(Node.alignof)); + assert((() @trusted => newPlace.ptr.alignedAt(Node.alignof))()); assert(newPlace.length % Node.sizeof == 0); - auto newAllocators = cast(Node[]) newPlace; + auto newAllocators = (() @trusted => cast(Node[]) newPlace)(); assert(allocators.length <= newAllocators.length); // Move allocators @@ -210,11 +210,11 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) continue; } import core.stdc.string : memcpy; - memcpy(&newAllocators[i].a, &e.a, e.a.sizeof); + () @trusted { memcpy(&newAllocators[i].a, &e.a, e.a.sizeof); }(); if (e.next) { - newAllocators[i].next = newAllocators.ptr - + (e.next - allocators.ptr); + newAllocators[i].next = (() @trusted => newAllocators.ptr + + (e.next - allocators.ptr))(); } else { @@ -230,7 +230,7 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) auto toFree = allocators; // Change state - root = newAllocators.ptr + (root - allocators.ptr); + root = (() @trusted => newAllocators.ptr + (root - allocators.ptr))(); allocators = newAllocators; // Free the olden buffer @@ -238,11 +238,11 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) { static if (hasMember!(Allocator, "deallocate") && hasMember!(Allocator, "owns")) - deallocate(toFree); + () @trusted { deallocate(toFree); }(); } else { - bkalloc.deallocate(toFree); + () @trusted { bkalloc.deallocate(toFree); }(); } } @@ -253,7 +253,8 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) static if (hasMember!(Allocator, "expand") && hasMember!(Allocator, "owns")) { - immutable bool expanded = t && this.expand(t, Node.sizeof); + // TODO: remove trusted once expand is safe + immutable bool expanded = (() @trusted => t && this.expand(t, Node.sizeof))(); } else { @@ -263,26 +264,63 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) { import std.c.string : memcpy; assert(t.length % Node.sizeof == 0); - assert(t.ptr.alignedAt(Node.alignof)); - allocators = cast(Node[]) t; + assert((&t[0]).alignedAt(Node.alignof)); + allocators = (() @trusted => cast(Node[]) t)(); allocators[$ - 1].setUnused; - auto newAlloc = SAllocator(make(atLeastBytes)); - memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); - emplace(&newAlloc); + () @trusted { + // StatsCollector.~this isn't safe + auto newAlloc = SAllocator(make(atLeastBytes)); + memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); + emplace(&newAlloc); + }(); } else { immutable toAlloc = (allocators.length + 1) * Node.sizeof + atLeastBytes + 128; - auto newAlloc = SAllocator(make(toAlloc)); - auto newPlace = newAlloc.allocate( - (allocators.length + 1) * Node.sizeof); - if (!newPlace) return null; - moveAllocators(newPlace); - import core.stdc.string : memcpy; - memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); - emplace(&newAlloc); - assert(allocators[$ - 1].owns(allocators) == Ternary.yes); + + // TODO: Please double check below + // Because StatsCollector.~this isn't safe, we need to infer the + // safety of allocate + static if ( + is(typeof( + () { + // This mustn't compile if allocate is @system + auto newAlloc = SAllocator(make(toAlloc)); + () @safe { + // We need this inner lambda because SAllocator.~this + // is @system + auto newPlace = newAlloc.allocate( + (allocators.length + 1) * Node.sizeof); + }(); + }() + )) + ) + { + enum trusted_ = "@trusted"; + } + else + { + enum trusted_ = ""; + } + + bool failedNewAlloc = false; + mixin(q{() } ~ trusted_ ~ q{ { + auto newAlloc = SAllocator(make(toAlloc)); + auto newPlace = newAlloc.allocate( + (allocators.length + 1) * Node.sizeof); + if (!newPlace) + { + failedNewAlloc = true; + return; + } + moveAllocators(newPlace); + import core.stdc.string : memcpy; + memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); + emplace(&newAlloc); + assert(allocators[$ - 1].owns(allocators) == Ternary.yes); + }(); }); + if (failedNewAlloc) return null; } // Insert as new root if (root != &allocators[$ - 1]) @@ -304,14 +342,14 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) { void[] t = allocators; static if (hasMember!(BookkeepingAllocator, "expand")) - immutable bool expanded = bkalloc.expand(t, Node.sizeof); + immutable bool expanded = (() @trusted => bkalloc.expand(t, Node.sizeof))(); else immutable bool expanded = false; if (expanded) { assert(t.length % Node.sizeof == 0); - assert(t.ptr.alignedAt(Node.alignof)); - allocators = cast(Node[]) t; + assert((&t[0]).alignedAt(Node.alignof)); + allocators = (() @trusted => cast(Node[]) t)(); allocators[$ - 1].setUnused; } else @@ -319,14 +357,16 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) // Could not expand, create a new block t = bkalloc.allocate((allocators.length + 1) * Node.sizeof); assert(t.length % Node.sizeof == 0); - if (!t.ptr) return null; + if (!t) return null; moveAllocators(t); } assert(allocators[$ - 1].unused); - auto newAlloc = SAllocator(make(atLeastBytes)); - import core.stdc.string : memcpy; - memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); - emplace(&newAlloc); + () @trusted { + auto newAlloc = SAllocator(make(atLeastBytes)); + import core.stdc.string : memcpy; + memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); + emplace(&newAlloc); + }(); // Creation succeeded, insert as root if (allocators.length == 1) allocators[$ - 1].next = null; @@ -584,9 +624,9 @@ version(Posix) @system unittest import std.experimental.allocator.building_blocks.region : Region; AllocatorList!((n) => Region!GCAllocator(new ubyte[max(n, 1024 * 4096)]), NullAllocator) a; - const b1 = a.allocate(1024 * 8192); + const b1 = (() @safe => a.allocate(1024 * 8192))(); assert(b1 !is null); // still works due to overdimensioning - const b2 = a.allocate(1024 * 10); + const b2 = (() @safe => a.allocate(1024 * 10))(); assert(b2.length == 1024 * 10); a.deallocateAll(); } @@ -597,9 +637,9 @@ version(Posix) @system unittest import std.algorithm.comparison : max; import std.experimental.allocator.building_blocks.region : Region; AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a; - auto b1 = a.allocate(1024 * 8192); + auto b1 = (() @safe => a.allocate(1024 * 8192))(); assert(b1 !is null); // still works due to overdimensioning - b1 = a.allocate(1024 * 10); + b1 = (() @safe => a.allocate(1024 * 10))(); assert(b1.length == 1024 * 10); a.deallocateAll(); } @@ -611,9 +651,9 @@ version(Posix) @system unittest import std.experimental.allocator.mallocator : Mallocator; import std.typecons : Ternary; AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)]), Mallocator) a; - auto b1 = a.allocate(1024 * 8192); + auto b1 = (() @safe => a.allocate(1024 * 8192))(); assert(b1 !is null); - b1 = a.allocate(1024 * 10); + b1 = (() @safe => a.allocate(1024 * 10))(); assert(b1.length == 1024 * 10); a.allocate(1024 * 4095); assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no); @@ -627,18 +667,18 @@ version(Posix) @system unittest import std.experimental.allocator.building_blocks.region : Region; enum bs = GCAllocator.alignment; AllocatorList!((n) => Region!GCAllocator(256 * bs)) a; - auto b1 = a.allocate(192 * bs); + auto b1 = (() @safe => a.allocate(192 * bs))(); assert(b1.length == 192 * bs); assert(a.allocators.length == 1); - auto b2 = a.allocate(64 * bs); + auto b2 = (() @safe => a.allocate(64 * bs))(); assert(b2.length == 64 * bs); assert(a.allocators.length == 1); - auto b3 = a.allocate(192 * bs); + auto b3 = (() @safe => a.allocate(192 * bs))(); assert(b3.length == 192 * bs); assert(a.allocators.length == 2); // Ensure deallocate inherits from parent allocators () nothrow @nogc { a.deallocate(b1); }(); - b1 = a.allocate(64 * bs); + b1 = (() @safe => a.allocate(64 * bs))(); assert(b1.length == 64 * bs); assert(a.allocators.length == 2); a.deallocateAll(); diff --git a/std/experimental/allocator/building_blocks/bitmapped_block.d b/std/experimental/allocator/building_blocks/bitmapped_block.d index 553f4207606..e6de93e8985 100644 --- a/std/experimental/allocator/building_blocks/bitmapped_block.d +++ b/std/experimental/allocator/building_blocks/bitmapped_block.d @@ -284,7 +284,8 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment Allocations greater than 64 blocks require a multiword search through the metadata. */ - @trusted void[] allocate(const size_t s) + pure nothrow @trusted @nogc + void[] allocate(const size_t s) { const blocks = s.divideRoundUp(blockSize); void[] result = void; @@ -449,9 +450,10 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment return available; } + pure nothrow @safe @nogc private void[] smallAlloc(uint blocks) { - assert(blocks >= 2 && blocks <= 64, text(blocks)); + assert(blocks >= 2 && blocks <= 64); foreach (i; _startIdx .. _control.rep.length) { // Test within the current 64-bit word @@ -481,6 +483,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment return null; } + pure nothrow @safe @nogc private void[] hugeAlloc(size_t blocks) { assert(blocks > 64); @@ -553,8 +556,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment return false; } // Expansion successful - assert(p.ptr == b.ptr + blocksOld * blockSize, - text(p.ptr, " != ", b.ptr + blocksOld * blockSize)); + assert(p.ptr == b.ptr + blocksOld * blockSize); b = b.ptr[0 .. b.length + delta]; return true; } @@ -742,7 +744,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment uint blockSize = 64; auto a = BitmappedBlock!(chooseAtRuntime, 64)(cast(ubyte[])(r.allocateAll()), blockSize); static assert(hasMember!(InSituRegion!(10_240, 64), "allocateAll")); - const b = a.allocate(100); + const b = (() pure nothrow @safe @nogc => a.allocate(100))(); assert(b.length == 100); } @@ -752,7 +754,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment auto a = BitmappedBlock!(64, 64)(new ubyte[10_240]); assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - const b = a.allocate(100); + const b = (() pure nothrow @safe @nogc => a.allocate(100))(); assert(b.length == 100); assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); } @@ -787,10 +789,10 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment blocks = a._blocks; // test allocation of 0 bytes - auto x = a.allocate(0); + auto x = (() pure nothrow @safe @nogc => a.allocate(0))(); assert(x is null); // test allocation of 1 byte - x = a.allocate(1); + x = (() pure nothrow @safe @nogc => a.allocate(1))(); assert(x.length == 1 || blocks == 0, text(x.ptr, " ", x.length, " ", a)); assert((() nothrow @nogc => a.deallocateAll())()); @@ -800,11 +802,11 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment begin: foreach (i; 0 .. blocks / blocksAtATime) { - auto b = a.allocate(bs * blocksAtATime); + auto b = (() pure nothrow @safe @nogc => a.allocate(bs * blocksAtATime))(); assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); } - assert(a.allocate(bs * blocksAtATime) is null); - assert(a.allocate(1) is null); + assert((() pure nothrow @safe @nogc => a.allocate(bs * blocksAtATime))() is null); + assert((() pure nothrow @safe @nogc => a.allocate(1))() is null); // Now deallocate all and do it again! assert((() nothrow @nogc => a.deallocateAll())()); @@ -814,12 +816,12 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment auto v = new void[][blocks / blocksAtATime]; foreach (i; 0 .. blocks / blocksAtATime) { - auto b = a.allocate(bs * blocksAtATime); + auto b = (() pure nothrow @safe @nogc => a.allocate(bs * blocksAtATime))(); assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); v[i] = b; } - assert(a.allocate(bs * blocksAtATime) is null); - assert(a.allocate(1) is null); + assert((() pure nothrow @safe @nogc => a.allocate(bs * blocksAtATime))() is null); + assert((() pure nothrow @safe @nogc => a.allocate(1))() is null); foreach (i; 0 .. blocks / blocksAtATime) { @@ -828,7 +830,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment foreach (i; 0 .. blocks / blocksAtATime) { - auto b = a.allocate(bs * blocksAtATime); + auto b = (() pure nothrow @safe @nogc => a.allocate(bs * blocksAtATime))(); assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); v[i] = b; } @@ -851,7 +853,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment { foreach (i; 0 .. blocks / blocksAtATime - 1) { - auto b = a.allocate(bs * blocksAtATime); + auto b = (() pure nothrow @safe @nogc => a.allocate(bs * blocksAtATime))(); assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); (cast(ubyte[]) b)[] = 0xff; a.expand(b, blocksAtATime * bs) @@ -916,7 +918,7 @@ nothrow @safe @nogc unittest import std.typecons : Ternary; auto a = BitmappedBlock!(64, 8, GCAllocator)(1024 * 64); - const void[] buff = a.allocate(42); + const void[] buff = (() pure nothrow @safe @nogc => a.allocate(42))(); assert((() nothrow @safe @nogc => a.owns(buff))() == Ternary.yes); assert((() nothrow @safe @nogc => a.owns(null))() == Ternary.no); @@ -980,9 +982,9 @@ struct BitmappedBlockWithInternalPointers( immutable oldLength = _allocStart.rep.length; immutable bits = len.roundUpToMultipleOf(64); void[] b = _allocStart.rep; - if (!_heap.reallocate(b, bits / 8)) return false; - assert(b.length * 8 == bits, text(b.length * 8, " != ", bits)); - _allocStart = BitVector(cast(ulong[]) b); + if ((() @trusted => !_heap.reallocate(b, bits / 8))()) return false; + assert(b.length * 8 == bits); + _allocStart = BitVector((() @trusted => cast(ulong[]) b)()); assert(_allocStart.rep.length * 64 == bits); _allocStart.rep[oldLength .. $] = ulong.max; return true; @@ -1005,13 +1007,13 @@ struct BitmappedBlockWithInternalPointers( { auto r = _heap.allocate(bytes); if (!r.ptr) return r; - immutable block = (r.ptr - _heap._payload.ptr) / _heap.blockSize; + immutable block = (() @trusted => (r.ptr - _heap._payload.ptr) / _heap.blockSize)(); immutable blocks = (r.length + _heap.blockSize - 1) / _heap.blockSize; if (!ensureRoomForAllocStart(block + blocks)) { // Failed, free r and bailout - _heap.deallocate(r); + () @trusted { _heap.deallocate(r); }(); return null; } assert(block < _allocStart.length); @@ -1149,7 +1151,7 @@ struct BitmappedBlockWithInternalPointers( auto h = BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]); assert((() nothrow @safe @nogc => h.empty)() == Ternary.yes); - auto b = h.allocate(123); + auto b = (() pure nothrow @safe @nogc => h.allocate(123))(); assert(b.length == 123); assert((() nothrow @safe @nogc => h.empty)() == Ternary.no); @@ -1158,7 +1160,7 @@ struct BitmappedBlockWithInternalPointers( 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); + b = (() pure nothrow @safe @nogc => h.allocate(4096))(); offset = &b[0]; assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes); @@ -1296,13 +1298,13 @@ private struct BitVector auto rep() { return _rep; } - @safe @nogc + pure nothrow @safe @nogc this(ulong[] data) { _rep = data; } pure nothrow @safe @nogc void opSliceAssign(bool b) { _rep[] = b ? ulong.max : 0; } - pure @safe @nogc + pure nothrow @safe @nogc void opSliceAssign(bool b, ulong x, ulong y) { assert(x <= y && y <= _rep.length * 64); @@ -1334,7 +1336,7 @@ private struct BitVector } } - @safe @nogc + pure nothrow @safe @nogc bool opIndex(ulong x) { assert(x < length); @@ -1342,7 +1344,7 @@ private struct BitVector & (0x8000_0000_0000_0000UL >> (x % 64))) != 0; } - @safe @nogc + pure nothrow @safe @nogc void opIndexAssign(bool b, ulong x) { assert(x / 64 <= size_t.max); @@ -1352,7 +1354,7 @@ private struct BitVector else _rep[i] &= ~j; } - nothrow @safe @nogc + pure nothrow @safe @nogc ulong length() const { return _rep.length * 64; @@ -1361,7 +1363,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 + pure nothrow @safe @nogc ulong find1(ulong i) { assert(i < length); @@ -1389,7 +1391,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 + pure nothrow @safe @nogc ulong find1Backward(ulong i) { assert(i < length); @@ -1423,14 +1425,14 @@ private struct BitVector } /// Are all bits one? - nothrow @safe @nogc + pure nothrow @safe @nogc bool allAre1() const { foreach (w; _rep) if (w != ulong.max) return false; return true; } - nothrow @safe @nogc + pure nothrow @safe @nogc ulong findZeros(immutable size_t howMany, ulong start) { assert(start < length); diff --git a/std/experimental/allocator/building_blocks/bucketizer.d b/std/experimental/allocator/building_blocks/bucketizer.d index 03c2dbcb76a..7e454ad00a4 100644 --- a/std/experimental/allocator/building_blocks/bucketizer.d +++ b/std/experimental/allocator/building_blocks/bucketizer.d @@ -59,6 +59,7 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step) /** Directs the call to either one of the $(D buckets) allocators. */ + //pure nothrow @safe @nogc void[] allocate(size_t bytes) { if (!bytes) return null; @@ -66,7 +67,8 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step) { const actual = goodAllocSize(bytes); auto result = a.allocate(actual); - return result.ptr ? result.ptr[0 .. bytes] : null; + // TODO: Can't this just be 'result ? result[0 .. bytes] : null' ?; + return (() @trusted => result ? result.ptr[0 .. bytes] : null)(); } return null; } @@ -262,6 +264,7 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step) assert((() pure nothrow @safe @nogc => a.goodAllocSize(65))() == 128); + //auto b = (() pure nothrow @safe @nogc => a.allocate(100))(); auto b = a.allocate(100); assert(b.length == 100); // Make reallocate use extend diff --git a/std/experimental/allocator/building_blocks/fallback_allocator.d b/std/experimental/allocator/building_blocks/fallback_allocator.d index cf18a748bc1..b0239317cbd 100644 --- a/std/experimental/allocator/building_blocks/fallback_allocator.d +++ b/std/experimental/allocator/building_blocks/fallback_allocator.d @@ -258,7 +258,7 @@ struct FallbackAllocator(Primary, Fallback) } } -@system unittest +nothrow @safe unittest { import std.conv : text; import std.experimental.allocator.building_blocks.region : InSituRegion; @@ -268,13 +268,13 @@ struct FallbackAllocator(Primary, Fallback) // This allocation uses the stack auto b1 = a.allocate(1024); assert(b1.length == 1024, text(b1.length)); - assert((() pure nothrow @safe @nogc => a.primary.owns(b1))() == Ternary.yes); + assert((() pure @nogc => a.primary.owns(b1))() == Ternary.yes); // This large allocation will go to the GCAllocator auto b2 = a.allocate(1024 * 1024); - assert((() pure nothrow @safe @nogc => a.primary.owns(b2))() == Ternary.no); + assert((() pure @nogc => a.primary.owns(b2))() == Ternary.no); // Ensure deallocate inherits from parent allocators - () nothrow @nogc { a.deallocate(b1); }(); - () nothrow @nogc { a.deallocate(b2); }(); + () @trusted @nogc { a.deallocate(b1); }(); + () @trusted @nogc { a.deallocate(b2); }(); } @system unittest @@ -294,7 +294,7 @@ struct FallbackAllocator(Primary, Fallback) ); assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b = a.allocate(201); + auto b = (() pure nothrow @safe @nogc => a.allocate(201))(); assert(b.length == 201); assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); } @@ -409,17 +409,17 @@ fallbackAllocator(Primary, Fallback)(auto ref Primary p, auto ref Fallback f) } // Ensure `owns` inherits function attributes -@system unittest +nothrow @safe unittest { import std.experimental.allocator.building_blocks.region : InSituRegion; import std.typecons : Ternary; FallbackAllocator!(InSituRegion!16_384, InSituRegion!16_384) a; - auto buff = a.allocate(42); - assert((() pure nothrow @safe @nogc => a.owns(buff))() == Ternary.yes); + auto buff = (() pure @nogc => a.allocate(42))(); + assert((() pure @nogc => a.owns(buff))() == Ternary.yes); } -@system unittest +nothrow @safe unittest { import std.experimental.allocator.gc_allocator : GCAllocator; import std.typecons : Ternary; @@ -429,6 +429,6 @@ fallbackAllocator(Primary, Fallback)(auto ref Primary p, auto ref Fallback f) 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); + assert((() @nogc => a.resolveInternalPointer(null, p))() == Ternary.no); + assert((() @nogc => a.resolveInternalPointer(&b[0], p))() == Ternary.yes); } diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d index aeeae552e33..151e55feebd 100644 --- a/std/experimental/allocator/building_blocks/free_list.d +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -254,13 +254,14 @@ struct FreeList(ParentAllocator, return parent.goodAllocSize(bytes); } + //[>pure*/ nothrow @safe /*@nogc<] private void[] allocateEligible(size_t bytes) { assert(bytes); if (root) { // faster - auto result = (cast(ubyte*) root)[0 .. bytes]; + auto result = (() @trusted => (cast(ubyte*) root)[0 .. bytes])(); root = root.next; return result; } @@ -277,7 +278,7 @@ struct FreeList(ParentAllocator, auto result = parent.allocate(toAllocate); static if (hasTolerance) { - if (result) result = result.ptr[0 .. bytes]; + if (result) result = (() @trusted => result.ptr[0 .. bytes])(); } static if (adaptive == Yes.adaptive) { @@ -307,6 +308,7 @@ struct FreeList(ParentAllocator, Postcondition: $(D result.length == bytes || result is null) */ + //[>pure*/ nothrow @safe/* @nogc <] void[] allocate(size_t n) { static if (adaptive == Yes.adaptive) ++accumSamples; @@ -421,7 +423,7 @@ struct FreeList(ParentAllocator, } FreeList!(StatsCollectorWrapper, 16, 16) fl; - auto buf1 = fl.allocate(16); + auto buf1 = (() nothrow @safe @nogc => fl.allocate(16))(); auto buf2 = fl.allocate(16); assert(fl.parent.bytesUsed == 32); @@ -437,15 +439,18 @@ struct FreeList(ParentAllocator, { import std.experimental.allocator.gc_allocator : GCAllocator; FreeList!(GCAllocator, 0, 8) fl; - assert(fl.root is null); - auto b1 = fl.allocate(7); - fl.allocate(8); - assert(fl.root is null); - // Ensure deallocate inherits from parent - () nothrow @nogc { fl.deallocate(b1); }(); - assert(fl.root !is null); - fl.allocate(8); - assert(fl.root is null); + // Can't mark the unittest as @safe because of ~this + () nothrow @safe { + assert(fl.root is null); + auto b1 = fl.allocate(7); + fl.allocate(8); + assert(fl.root is null); + // Ensure deallocate inherits from parent + () @trusted @nogc { fl.deallocate(b1); }(); + assert(fl.root !is null); + fl.allocate(8); + assert(fl.root is null); + }(); } @system unittest @@ -462,9 +467,11 @@ struct FreeList(ParentAllocator, import std.experimental.allocator.building_blocks.region : Region; auto fl = FreeList!(Region!(), 0, 16)(Region!()(new ubyte[1024 * 64])); - auto b = fl.allocate(42); - assert(b.length == 42); - assert((() nothrow @nogc => fl.deallocateAll())()); + () nothrow @safe { + auto b = fl.allocate(42); + assert(b.length == 42); + assert((() @trusted @nogc => fl.deallocateAll())()); + }(); } /** @@ -759,23 +766,24 @@ struct ContiguousFreeList(ParentAllocator, alias A = ContiguousFreeList!(NullAllocator, 0, 64); auto a = A(new ubyte[1024]); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - - assert((() pure nothrow @safe @nogc => a.goodAllocSize(15))() == 64); - assert((() pure nothrow @safe @nogc => a.goodAllocSize(65))() - == (() nothrow @safe @nogc => NullAllocator.instance.goodAllocSize(65))()); - - auto b = a.allocate(100); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - assert(b.length == 0); - // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(b); }(); - b = a.allocate(64); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); - assert(b.length == 64); - assert((() nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() nothrow @safe @nogc => a.owns(null))() == Ternary.no); - () nothrow @nogc { a.deallocate(b); }(); + () nothrow @safe @nogc { + assert(a.empty == Ternary.yes); + assert((() pure => a.goodAllocSize(15))() == 64); + assert((() pure => a.goodAllocSize(65))() + == NullAllocator.instance.goodAllocSize(65)); + + auto b = a.allocate(100); + assert(a.empty == Ternary.yes); + assert(b.length == 0); + // Ensure deallocate inherits from parent + () @trusted { a.deallocate(b); }(); + b = a.allocate(64); + assert(a.empty == Ternary.no); + assert(b.length == 64); + assert(a.owns(b) == Ternary.yes); + assert(a.owns(null) == Ternary.no); + () @trusted { a.deallocate(b); }(); + }(); } @system unittest @@ -786,27 +794,28 @@ struct ContiguousFreeList(ParentAllocator, alias A = ContiguousFreeList!(Region!GCAllocator, 0, 64); auto a = A(Region!GCAllocator(1024 * 4), 1024); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - - assert((() pure nothrow @safe @nogc => a.goodAllocSize(15))() == 64); - assert((() pure nothrow @safe @nogc => a.goodAllocSize(65))() - == (() pure nothrow @safe @nogc => a.parent.goodAllocSize(65))()); - - auto b = a.allocate(100); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); - assert(a.allocated == 0); - assert(b.length == 100); - // Ensure deallocate inherits from parent - assert((() nothrow @nogc => a.deallocate(b))()); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - b = a.allocate(64); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); - assert(b.length == 64); - assert((() nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() nothrow @safe @nogc => a.owns(null))() == Ternary.no); - // Test deallocate infers from parent - assert((() nothrow @nogc => a.deallocate(b))()); - assert((() nothrow @nogc => a.deallocateAll())()); + () nothrow @safe @nogc { + assert(a.empty == Ternary.yes); + assert((() pure => a.goodAllocSize(15))() == 64); + assert((() pure => a.goodAllocSize(65))() + == (() pure => a.parent.goodAllocSize(65))()); + + auto b = a.allocate(100); + assert(a.empty == Ternary.no); + assert(a.allocated == 0); + assert(b.length == 100); + // Ensure deallocate inherits from parent + assert((() @trusted => a.deallocate(b))()); + assert(a.empty == Ternary.yes); + b = a.allocate(64); + assert(a.empty == Ternary.no); + assert(b.length == 64); + assert(a.owns(b) == Ternary.yes); + assert(a.owns(null) == Ternary.no); + // Test deallocate infers from parent + assert((() @trusted => a.deallocate(b))()); + assert((() @trusted => a.deallocateAll())()); + }(); } @system unittest @@ -814,7 +823,7 @@ struct ContiguousFreeList(ParentAllocator, import std.experimental.allocator.gc_allocator : GCAllocator; alias A = ContiguousFreeList!(GCAllocator, 64, 64); auto a = A(1024); - const b = a.allocate(100); + const b = (() nothrow @safe => a.allocate(100))(); assert(b.length == 100); } @@ -1042,7 +1051,7 @@ struct SharedFreeList(ParentAllocator, _root = _root.next; decNodes(); lock.unlock(); - return (cast(ubyte*) oldRoot)[0 .. bytes]; + return (() @trusted => (cast(ubyte*) oldRoot)[0 .. bytes])(); } } @@ -1157,15 +1166,16 @@ struct SharedFreeList(ParentAllocator, static shared SharedFreeList!(Mallocator, 64, 128, 10) a; - assert((() nothrow @safe @nogc => a.goodAllocSize(1))() == platformAlignment); - - auto b = a.allocate(96); - // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(b); }(); + () nothrow @safe { + assert((() @nogc => a.goodAllocSize(1))() == platformAlignment); + auto b = a.allocate(96); + // Ensure deallocate inherits from parent + () @trusted @nogc { a.deallocate(b); }(); + }(); void fun() { - auto b = cast(size_t[]) a.allocate(96); + auto b = cast(size_t[]) (() nothrow @safe => a.allocate(96))(); b[] = cast(size_t) &b; assert(b.equal(repeat(cast(size_t) &b, b.length))); @@ -1181,39 +1191,39 @@ struct SharedFreeList(ParentAllocator, tg.joinAll(); } -@system unittest +nothrow @safe @nogc unittest { import std.experimental.allocator.mallocator : Mallocator; static shared SharedFreeList!(Mallocator, 64, 128, 10) a; auto b = a.allocate(100); // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(b); }(); + () @trusted { a.deallocate(b); }(); assert(a.nodes == 1); b = []; - assert((() nothrow @nogc => a.deallocateAll())()); + assert((() @trusted => a.deallocateAll())()); assert(a.nodes == 0); } -@system unittest +nothrow @safe @nogc unittest { import std.experimental.allocator.mallocator : Mallocator; static shared SharedFreeList!(Mallocator, 64, 128, 10) a; auto b = a.allocate(100); auto c = a.allocate(100); // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(c); }(); + () @trusted { a.deallocate(c); }(); assert(a.nodes == 1); c = []; - a.minimize(); + () @trusted { a.minimize(); }(); assert(a.nodes == 0); - () nothrow @nogc { a.deallocate(b); }(); + () @trusted { a.deallocate(b); }(); assert(a.nodes == 1); b = []; - a.minimize(); + () @trusted { a.minimize(); }(); assert(a.nodes == 0); } -@system unittest +nothrow @safe @nogc unittest { import std.experimental.allocator.mallocator : Mallocator; static shared SharedFreeList!(Mallocator, 64, 128, 10) a; @@ -1221,12 +1231,12 @@ struct SharedFreeList(ParentAllocator, auto c = a.allocate(100); assert(a.nodes == 0); // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(b); }(); - () nothrow @nogc { a.deallocate(c); }(); + () @trusted { a.deallocate(b); }(); + () @trusted { a.deallocate(c); }(); assert(a.nodes == 2); b = []; c = []; - a.minimize(); + () @trusted { a.minimize(); }(); assert(a.nodes == 0); } @@ -1235,53 +1245,53 @@ struct SharedFreeList(ParentAllocator, import std.experimental.allocator.mallocator : Mallocator; shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a; scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); - auto c = a.allocate(64); + auto c = (() nothrow @safe @nogc => a.allocate(64))(); assert(a.reallocate(c, 96)); assert(c.length == 96); // Ensure deallocate inherits from parent () nothrow @nogc { a.deallocate(c); }(); } -@system unittest +nothrow @safe @nogc unittest { import std.experimental.allocator.mallocator : Mallocator; shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime, chooseAtRuntime) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); + scope(exit) assert((() @trusted => a.deallocateAll())()); a.allocate(64); } -@system unittest +nothrow @safe @nogc unittest { import std.experimental.allocator.mallocator : Mallocator; shared SharedFreeList!(Mallocator, 30, 40) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); + scope(exit) assert((() @trusted => a.deallocateAll())()); a.allocate(64); } -@system unittest +nothrow @safe @nogc unittest { import std.experimental.allocator.mallocator : Mallocator; shared SharedFreeList!(Mallocator, 30, 40, chooseAtRuntime) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); + scope(exit) assert((() @trusted => a.deallocateAll())()); a.allocate(64); } -@system unittest +@safe unittest { // Pull request #5556 import std.experimental.allocator.mallocator : Mallocator; shared SharedFreeList!(Mallocator, 0, chooseAtRuntime) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); + scope(exit) assert((() @trusted => a.deallocateAll())()); a.max = 64; - a.allocate(64); + () nothrow @nogc { a.allocate(64); }(); } -@system unittest +@safe unittest { // Pull request #5556 import std.experimental.allocator.mallocator : Mallocator; shared SharedFreeList!(Mallocator, chooseAtRuntime, 64) a; - scope(exit) assert((() nothrow @nogc => a.deallocateAll())()); + scope(exit) assert((() @trusted => a.deallocateAll())()); a.min = 32; - a.allocate(64); + () nothrow @nogc { a.allocate(64); }(); } diff --git a/std/experimental/allocator/building_blocks/free_tree.d b/std/experimental/allocator/building_blocks/free_tree.d index 896f102d688..cb8175f04ff 100644 --- a/std/experimental/allocator/building_blocks/free_tree.d +++ b/std/experimental/allocator/building_blocks/free_tree.d @@ -293,21 +293,20 @@ struct FreeTree(ParentAllocator) immutable s = goodAllocSize(n); // Consult the free tree. - auto result = findAndRemove(root, s); - if (result.ptr) return result.ptr[0 .. n]; + auto result = (() @trusted => findAndRemove(root, s))(); + if (result) return result[0 .. n]; // No block found, try the parent allocator. result = parent.allocate(s); - if (result.ptr) return result.ptr[0 .. n]; + if (result) return result[0 .. n]; // Parent ran out of juice, desperation mode on static if (hasMember!(ParentAllocator, "deallocate")) { - clear; + () @trusted { clear; }(); // Try parent allocator again. result = parent.allocate(s); - if (result.ptr) return result.ptr[0 .. n]; - return null; + return result ? result[0 .. n] : null; } else { @@ -336,21 +335,29 @@ struct FreeTree(ParentAllocator) { import std.experimental.allocator.gc_allocator; FreeTree!GCAllocator a; - auto b1 = a.allocate(10000); - auto b2 = a.allocate(20000); - auto b3 = a.allocate(30000); - assert(b1.ptr && b2.ptr && b3.ptr); - () nothrow @nogc { a.deallocate(b1); }(); - () nothrow @nogc { a.deallocate(b3); }(); - () nothrow @nogc { a.deallocate(b2); }(); - assert(a.formatSizes == "(20480 (12288 32768))", a.formatSizes); - - b1 = a.allocate(10000); - assert(a.formatSizes == "(20480 (_ 32768))", a.formatSizes); - b1 = a.allocate(30000); - assert(a.formatSizes == "(20480)", a.formatSizes); - b1 = a.allocate(20000); - assert(a.formatSizes == "(_)", a.formatSizes); + () nothrow @safe { + auto b1 = a.allocate(10000); + auto b2 = a.allocate(20000); + auto b3 = a.allocate(30000); + assert(b1 && b2 && b3); + assert(b1.length == 10000); + assert(b2.length == 20000); + assert(b3.length == 30000); + () @trusted @nogc { a.deallocate(b1); }(); + () @trusted @nogc { a.deallocate(b3); }(); + () @trusted @nogc { a.deallocate(b2); }(); + assert(a.formatSizes == "(20480 (12288 32768))", a.formatSizes); + + b1 = a.allocate(10000); + assert(b1.length == 10000); + assert(a.formatSizes == "(20480 (_ 32768))", a.formatSizes); + b1 = a.allocate(30000); + assert(b1.length == 30000); + assert(a.formatSizes == "(20480)", a.formatSizes); + b1 = a.allocate(20000); + assert(b1.length == 20000); + assert(a.formatSizes == "(_)", a.formatSizes); + }(); } @system unittest // build a complex free tree @@ -360,19 +367,26 @@ struct FreeTree(ParentAllocator) uint[] sizes = [3008,704,1856,576,1632,672,832,1856,1120,2656,1216,672, 448,992,2400,1376,2688,2656,736,1440]; void[][] allocs; - foreach (s; sizes) - allocs ~= a.allocate(s); - foreach_reverse (b; allocs) - { - assert(b.ptr); - () nothrow @nogc { a.deallocate(b); }(); - } - a.assertValid; - allocs = null; - foreach (s; sizes) - allocs ~= a.allocate(s); - assert(a.root is null); - a.assertValid; + () nothrow @safe { + foreach (s; sizes) + { + allocs ~= a.allocate(s); + assert(allocs[$ - 1].length == s); + } + foreach_reverse (b; allocs) + { + assert(b && (() @trusted @nogc => a.deallocate(b))()); + } + a.assertValid; + allocs = null; + foreach (s; sizes) + { + allocs ~= a.allocate(s); + assert(allocs[$ - 1].length == s); + } + assert(a.root is null); + a.assertValid; + }(); } /** Defined if $(D ParentAllocator.deallocate) exists, and returns to it @@ -422,7 +436,7 @@ struct FreeTree(ParentAllocator) static void f(ParentAllocator)(size_t sz) { static FreeTree!ParentAllocator myAlloc; - byte[] _payload = cast(byte[]) myAlloc.allocate(sz); + byte[] _payload = cast(byte[]) (() nothrow @safe => myAlloc.allocate(sz))(); assert(_payload, "_payload is null"); _payload[] = 0; () nothrow @nogc { myAlloc.deallocate(_payload); }(); @@ -446,8 +460,10 @@ struct FreeTree(ParentAllocator) FreeTree!MyAllocator ft; void[] x = ft.allocate(1); + assert(x.length == 1); () nothrow @nogc { ft.deallocate(x); }(); - ft.allocate(1000); + x = ft.allocate(1000); + assert(x.length == 1000); MyAllocator.alive = false; } @@ -475,15 +491,16 @@ struct FreeTree(ParentAllocator) FreeTree!MyAllocator ft; void[] x = ft.allocate(1); + assert(x.length == 1); () nothrow @nogc { ft.deallocate(x); }(); assert(myDeallocCounter == 0); x = ft.allocate(1000); // Triggers "desperation mode". assert(myDeallocCounter == 1); - assert(x.ptr); + assert(x && x.length == 1000); void[] y = ft.allocate(1000); /* Triggers "desperation mode" but there's nothing to deallocate so MyAllocator can't deliver. */ assert(myDeallocCounter == 1); - assert(y.ptr is null); + assert(y is null); } @system unittest @@ -501,7 +518,7 @@ struct FreeTree(ParentAllocator) import std.experimental.allocator.building_blocks.region : Region; auto a = FreeTree!(Region!())(Region!()(new ubyte[1024 * 64])); - auto b = a.allocate(42); + auto b = (() nothrow @safe @nogc => a.allocate(42))(); assert(b.length == 42); assert((() nothrow @nogc => a.deallocateAll())()); } diff --git a/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/std/experimental/allocator/building_blocks/kernighan_ritchie.d index 26fe6768f68..b2a2f876571 100644 --- a/std/experimental/allocator/building_blocks/kernighan_ritchie.d +++ b/std/experimental/allocator/building_blocks/kernighan_ritchie.d @@ -108,11 +108,13 @@ struct KRRegion(ParentAllocator = NullAllocator) this(this) @disable; + pure nothrow @trusted @nogc void[] payload() inout { return (cast(ubyte*) &this)[0 .. size]; } + pure nothrow @trusted @nogc bool adjacent(in Node* right) const { assert(right); @@ -120,6 +122,7 @@ struct KRRegion(ParentAllocator = NullAllocator) return p.ptr < right && right < p.ptr + p.length + Node.sizeof; } + pure nothrow @trusted @nogc bool coalesce(void* memoryEnd = null) { // Coalesce the last node before the memory end with any possible gap @@ -149,7 +152,7 @@ struct KRRegion(ParentAllocator = NullAllocator) if (leftover >= Node.sizeof) { // There's room for another node - auto newNode = cast(Node*) ((cast(ubyte*) &this) + bytes); + auto newNode = (() @trusted => cast(Node*) ((cast(ubyte*) &this) + bytes))(); newNode.size = leftover; newNode.next = next == &this ? newNode : next; assert(next); @@ -401,7 +404,7 @@ struct KRRegion(ParentAllocator = NullAllocator) immutable balance = root.size - actualBytes; if (balance >= Node.sizeof) { - auto newRoot = cast(Node*) (result + actualBytes); + auto newRoot = (() @trusted => cast(Node*) (result + actualBytes))(); newRoot.next = root.next; newRoot.size = balance; root = newRoot; @@ -411,7 +414,7 @@ struct KRRegion(ParentAllocator = NullAllocator) root = null; switchToFreeList; } - return result[0 .. n]; + return (() @trusted => result[0 .. n])(); } // Not enough memory, switch to freelist mode and fall through @@ -753,7 +756,7 @@ it actually returns memory to the operating system when possible. void[][] array; foreach (i; 1 .. 4) { - array ~= alloc.allocate(i); + array ~= (() nothrow @safe => alloc.allocate(i))(); assert(array[$ - 1].length == i); } () nothrow @nogc { alloc.deallocate(array[1]); }(); @@ -768,7 +771,7 @@ it actually returns memory to the operating system when possible. import std.typecons : Ternary; auto alloc = KRRegion!()( cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); - const store = alloc.allocate(KRRegion!().sizeof); + const store = (() pure nothrow @safe @nogc => alloc.allocate(KRRegion!().sizeof))(); auto p = cast(KRRegion!()* ) store.ptr; import core.stdc.string : memcpy; import std.algorithm.mutation : move; @@ -781,7 +784,7 @@ it actually returns memory to the operating system when possible. foreach (i; 0 .. array.length) { auto length = 100 * i + 1; - array[i] = p.allocate(length); + array[i] = (() pure nothrow @safe @nogc => p.allocate(length))(); assert(array[i].length == length, text(array[i].length)); assert((() pure nothrow @safe @nogc => p.owns(array[i]))() == Ternary.yes); } @@ -837,7 +840,7 @@ it actually returns memory to the operating system when possible. foreach (size; sizes) { - bufs ~= a.allocate(size); + bufs ~= (() pure nothrow @safe @nogc => a.allocate(size))(); } foreach (b; bufs.randomCover) @@ -879,11 +882,11 @@ it actually returns memory to the operating system when possible. foreach (size; sizes) { - bufs ~= a.allocate(size); + bufs ~= (() pure nothrow @safe @nogc => a.allocate(size))(); } () nothrow @nogc { a.deallocate(bufs[1]); }(); - bufs ~= a.allocate(sizes[1] - word); + bufs ~= (() pure nothrow @safe @nogc => a.allocate(sizes[1] - word))(); () nothrow @nogc { a.deallocate(bufs[0]); }(); foreach (i; 2 .. bufs.length) diff --git a/std/experimental/allocator/building_blocks/null_allocator.d b/std/experimental/allocator/building_blocks/null_allocator.d index 0b2dbb1ac52..04e1483b27a 100644 --- a/std/experimental/allocator/building_blocks/null_allocator.d +++ b/std/experimental/allocator/building_blocks/null_allocator.d @@ -20,6 +20,7 @@ struct NullAllocator //size_t goodAllocSize(size_t n) shared const //{ return .goodAllocSize(this, n); } /// Always returns $(D null). + pure nothrow @safe @nogc void[] allocate(size_t) shared { return null; } /// Always returns $(D null). void[] alignedAllocate(size_t, uint) shared { return null; } @@ -73,7 +74,7 @@ struct NullAllocator { assert(NullAllocator.instance.alignedAllocate(100, 0) is null); assert(NullAllocator.instance.allocateAll() is null); - auto b = NullAllocator.instance.allocate(100); + auto b = (() nothrow @safe @nogc => NullAllocator.instance.allocate(100))(); assert(b is null); assert(NullAllocator.instance.expand(b, 0)); assert(!NullAllocator.instance.expand(b, 42)); diff --git a/std/experimental/allocator/building_blocks/quantizer.d b/std/experimental/allocator/building_blocks/quantizer.d index 28afd93f9ab..91e2f179c0a 100644 --- a/std/experimental/allocator/building_blocks/quantizer.d +++ b/std/experimental/allocator/building_blocks/quantizer.d @@ -76,7 +76,7 @@ struct Quantizer(ParentAllocator, alias roundingFunction) void[] allocate(size_t n) { auto result = parent.allocate(goodAllocSize(n)); - return result.ptr ? result.ptr[0 .. n] : null; + return result ? result[0 .. n] : null; } /** @@ -230,20 +230,20 @@ struct Quantizer(ParentAllocator, alias roundingFunction) assert(buf.ptr); } -@system unittest +@safe unittest { import std.experimental.allocator.gc_allocator : GCAllocator; alias MyAlloc = Quantizer!(GCAllocator, (size_t n) => n.roundUpToMultipleOf(64)); - testAllocator!(() => MyAlloc()); - - assert((() pure nothrow @safe @nogc => MyAlloc().goodAllocSize(1))() == 64); + () @trusted { testAllocator!(() => MyAlloc()); }(); auto a = MyAlloc(); - auto b = a.allocate(42); + assert((() pure nothrow @nogc => a.goodAllocSize(1))() == 64); + + auto b = (() nothrow => a.allocate(42))(); assert(b.length == 42); // Ensure deallocate inherits from parent - () nothrow @nogc { a.deallocate(b); }(); + () @trusted nothrow @nogc { a.deallocate(b); }(); } // Check that owns inherits from parent, i.e. Region @@ -256,7 +256,7 @@ struct Quantizer(ParentAllocator, alias roundingFunction) alias Alloc = Quantizer!(Region!(Mallocator), (size_t n) => n.roundUpToMultipleOf(64)); auto a = Alloc(Region!Mallocator(1024 * 64)); - const b = a.allocate(42); + const b = (() pure nothrow @safe @nogc => a.allocate(42))(); assert(b.length == 42); assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes); assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no); @@ -290,7 +290,7 @@ struct Quantizer(ParentAllocator, alias roundingFunction) auto a = MyAlloc(Region!()(new ubyte[1024 * 64])); // Check that empty inherits from parent assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b = a.allocate(42); + auto b = (() pure nothrow @safe @nogc => a.allocate(42))(); assert(b.length == 42); assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no); // Check that deallocateAll inherits from parent diff --git a/std/experimental/allocator/building_blocks/region.d b/std/experimental/allocator/building_blocks/region.d index fb85109cff7..0cc56340bcc 100644 --- a/std/experimental/allocator/building_blocks/region.d +++ b/std/experimental/allocator/building_blocks/region.d @@ -121,6 +121,7 @@ struct Region(ParentAllocator = NullAllocator, A properly-aligned buffer of size $(D n) or $(D null) if request could not be satisfied. */ + pure nothrow @safe @nogc void[] allocate(size_t n) { if (n == 0) return null; @@ -132,23 +133,23 @@ struct Region(ParentAllocator = NullAllocator, else alias rounded = n; assert(available >= rounded); - auto result = (_current - rounded)[0 .. n]; - assert(result.ptr >= _begin); - _current = result.ptr; + auto result = (() @trusted => (_current - rounded)[0 .. n])(); + assert(&result[0] >= _begin); + _current = &result[0]; assert(owns(result) == Ternary.yes); return result; } else { - auto result = _current[0 .. n]; + auto result = (() @trusted => _current[0 .. n])(); static if (minAlign > 1) const rounded = n.roundUpToAlignment(alignment); else alias rounded = n; - _current += rounded; + () @trusted { _current += rounded; }(); if (_current <= _end) return result; // Slow path, backtrack - _current -= rounded; + () @trusted { _current -= rounded; }(); return null; } } @@ -368,12 +369,12 @@ struct Region(ParentAllocator = NullAllocator, Yes.growDownwards)(1024 * 64); assert((() pure nothrow @safe @nogc => reg.empty)() == Ternary.yes); - const b = reg.allocate(101); + const b = (() pure nothrow @safe @nogc => reg.allocate(101))(); assert(b.length == 101); assert((() nothrow @safe @nogc => reg.owns(b))() == Ternary.yes); // Ensure deallocate inherits from parent allocators - auto c = reg.allocate(42); + auto c = (() pure nothrow @safe @nogc => reg.allocate(42))(); assert(c.length == 42); assert((() nothrow @nogc => reg.deallocate(c))()); assert((() pure nothrow @safe @nogc => reg.empty)() == Ternary.no); @@ -392,7 +393,7 @@ struct Region(ParentAllocator = NullAllocator, import std.experimental.allocator.mallocator : Mallocator; auto reg = Region!(Mallocator)(1024 * 64); - auto b = reg.allocate(101); + auto b = (() pure nothrow @safe @nogc => reg.allocate(101))(); assert(b.length == 101); assert(reg.expand(b, 20)); assert(reg.expand(b, 73)); @@ -466,7 +467,8 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment) private void lazyInit() { assert(!_impl._current); - _impl = typeof(_impl)(_store); + // TODO: Remove once region ctor is made safe + _impl = (() @trusted => typeof(_impl)(_store))(); assert(_impl._current.alignedAt(alignment)); } @@ -611,7 +613,7 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment) import std.typecons : Ternary; InSituRegion!(4096, 1) r1; - auto a = r1.allocate(2001); + auto a = (() pure nothrow @safe @nogc => r1.allocate(2001))(); assert(a.length == 2001); import std.conv : text; assert(r1.available == 2095, text(r1.available)); @@ -621,9 +623,9 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment) InSituRegion!(65_536, 1024*4) r2; assert(r2.available <= 65_536); - a = r2.allocate(2001); + a = (() pure nothrow @safe @nogc => r2.allocate(2001))(); assert(a.length == 2001); - const void[] buff = r2.allocate(42); + const void[] buff = (() pure nothrow @safe @nogc => r2.allocate(42))(); assert((() nothrow @safe @nogc => r2.owns(buff))() == Ternary.yes); assert((() nothrow @nogc => r2.deallocateAll())()); } @@ -664,6 +666,7 @@ version(Posix) struct SbrkRegion(uint minAlign = platformAlignment) enum uint alignment = minAlign; /// Ditto + /*pure*/ nothrow @trusted @nogc void[] allocate(size_t bytes) shared { static if (minAlign > 1) @@ -830,7 +833,7 @@ version(Posix) @system unittest auto a = alloc.alignedAllocate(2001, 4096); assert(a.length == 2001); assert((() nothrow @safe @nogc => alloc.empty)() == Ternary.no); - auto b = alloc.allocate(2001); + auto b = (() nothrow @safe @nogc => alloc.allocate(2001))(); assert(b.length == 2001); assert((() nothrow @safe @nogc => alloc.owns(a))() == Ternary.yes); assert((() nothrow @safe @nogc => alloc.owns(b))() == Ternary.yes); @@ -840,7 +843,7 @@ version(Posix) @system unittest assert((() nothrow @nogc => alloc.deallocate(b))()); assert((() nothrow @nogc => alloc.deallocateAll())()); } - const void[] c = alloc.allocate(2001); + const void[] c = (() nothrow @safe @nogc => alloc.allocate(2001))(); assert(c.length == 2001); assert((() nothrow @safe @nogc => alloc.owns(c))() == Ternary.yes); assert((() nothrow @safe @nogc => alloc.owns(null))() == Ternary.no); diff --git a/std/experimental/allocator/building_blocks/scoped_allocator.d b/std/experimental/allocator/building_blocks/scoped_allocator.d index a5357272e94..77e79253198 100644 --- a/std/experimental/allocator/building_blocks/scoped_allocator.d +++ b/std/experimental/allocator/building_blocks/scoped_allocator.d @@ -88,8 +88,8 @@ struct ScopedAllocator(ParentAllocator) void[] allocate(size_t n) { auto b = parent.allocate(n); - if (!b.ptr) return b; - Node* toInsert = & parent.prefix(b); + if (!b) return b; + Node* toInsert = (() @trusted => & parent.prefix(b))(); toInsert.prev = null; toInsert.next = root; toInsert.length = n; @@ -235,7 +235,7 @@ struct ScopedAllocator(ParentAllocator) assert(!__traits(compiles, (() pure nothrow @safe @nogc => a.goodAllocSize(0))())); // Ensure deallocate inherits from parent allocators - auto b = a.allocate(42); + auto b = (() nothrow @safe => a.allocate(42))(); assert(b.length == 42); () nothrow @nogc { a.deallocate(b); }(); } @@ -247,9 +247,9 @@ struct ScopedAllocator(ParentAllocator) ScopedAllocator!(Region!()) a; a.parent.parent = Region!()(new ubyte[1024 * 64]); - auto b = a.allocate(42); + auto b = (() pure nothrow @safe @nogc => a.allocate(42))(); assert(b.length == 42); - assert((() nothrow @nogc => a.deallocateAll())()); + assert((() pure nothrow @nogc => a.deallocateAll())()); } @system unittest @@ -259,10 +259,12 @@ struct ScopedAllocator(ParentAllocator) import std.typecons : Ternary; auto a = Region!(Mallocator)(1024 * 64); - auto b = a.allocate(42); - assert(b.length == 42); - assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no); + () pure nothrow @safe @nogc { + auto b = a.allocate(42); + assert(b.length == 42); + assert(a.owns(b) == Ternary.yes); + assert(a.owns(null) == Ternary.no); + }(); } // Test empty @@ -272,7 +274,9 @@ struct ScopedAllocator(ParentAllocator) import std.typecons : Ternary; ScopedAllocator!Mallocator alloc; - assert((() pure nothrow @safe @nogc => alloc.empty)() == Ternary.yes); - const b = alloc.allocate(10); - assert((() pure nothrow @safe @nogc => alloc.empty)() == Ternary.no); + () nothrow @safe @nogc { + assert((() pure => alloc.empty)() == Ternary.yes); + const b = alloc.allocate(10); + assert((() pure => alloc.empty)() == Ternary.no); + }(); } diff --git a/std/experimental/allocator/building_blocks/segregator.d b/std/experimental/allocator/building_blocks/segregator.d index 78cc524a827..2e1ac592f3e 100644 --- a/std/experimental/allocator/building_blocks/segregator.d +++ b/std/experimental/allocator/building_blocks/segregator.d @@ -375,7 +375,7 @@ if (Args.length > 3) == GCAllocator.instance.goodAllocSize(1)); } -@system unittest +@safe unittest { import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; import std.typecons : Ternary; @@ -386,34 +386,37 @@ if (Args.length > 3) BitmappedBlock!(4096) ); - A a = A( + // TODO: remove trusted once BB.this is safe + A a = (() @trusted => A( BitmappedBlock!(4096)(new ubyte[4096 * 1024]), BitmappedBlock!(4096)(new ubyte[4096 * 1024]) - ); - - assert(a.empty == Ternary.yes); - auto b = a.allocate(42); - assert(b.length == 42); - assert(a.empty == Ternary.no); - assert(a.alignedReallocate(b, 256, 512)); - assert(b.length == 256); - assert(a.alignedReallocate(b, 42, 512)); - assert(b.length == 42); - assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes); - assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no); - // Ensure deallocate inherits from parent allocators - assert((() nothrow @nogc => a.deallocate(b))()); - assert(a.empty == Ternary.yes); - - // Test that deallocateAll inherits from parents - auto c = a.allocate(42); - assert(b.length == 42); - assert(a.empty == Ternary.no); - assert((() nothrow @nogc => a.deallocateAll())()); - assert(a.empty == Ternary.yes); + ))(); + + () pure nothrow @nogc { + assert(a.empty == Ternary.yes); + auto b = a.allocate(42); + assert(b.length == 42); + assert(a.empty == Ternary.no); + assert((() @trusted => a.alignedReallocate(b, 256, 512))()); + assert(b.length == 256); + assert((() @trusted => a.alignedReallocate(b, 42, 512))()); + assert(b.length == 42); + assert(a.owns(b) == Ternary.yes); + assert(a.owns(null) == Ternary.no); + // Ensure deallocate inherits from parent allocators + assert((() @trusted => a.deallocate(b))()); + assert(a.empty == Ternary.yes); + + // Test that deallocateAll inherits from parents + auto c = a.allocate(42); + assert(b.length == 42); + assert(a.empty == Ternary.no); + assert((() @trusted => a.deallocateAll())()); + assert(a.empty == Ternary.yes); + }(); } -@system unittest +nothrow @safe unittest { import std.experimental.allocator.gc_allocator : GCAllocator; import std.typecons : Ternary; @@ -424,14 +427,14 @@ if (Args.length > 3) assert(b.length == 201); void[] p; - assert((() nothrow @safe @nogc => a.resolveInternalPointer(&b[0], p))() == Ternary.yes); - assert((() nothrow @safe @nogc => a.resolveInternalPointer(null, p))() == Ternary.no); + assert((() @nogc => a.resolveInternalPointer(&b[0], p))() == Ternary.yes); + assert((() @nogc => a.resolveInternalPointer(null, p))() == Ternary.no); // Ensure deallocate inherits from parent allocators - assert((() nothrow @nogc => a.deallocate(b))()); + assert((() @trusted @nogc => a.deallocate(b))()); } -@system unittest +@safe unittest { import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlockWithInternalPointers; import std.typecons : Ternary; @@ -442,14 +445,17 @@ if (Args.length > 3) BitmappedBlockWithInternalPointers!(4096) ); - A a = A( + // TODO: remove trusted once BB.this is safe + A a = (() @trusted => A( BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]), BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]) - ); - - assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b = a.allocate(201); - assert(b.length == 201); - assert((() nothrow @safe @nogc => a.empty)() == Ternary.no); - assert((() nothrow @nogc => a.deallocate(b))()); + ))(); + + () nothrow @nogc { + assert(a.empty == Ternary.yes); + auto b = a.allocate(201); + assert(b.length == 201); + assert(a.empty == Ternary.no); + assert((() @trusted => a.deallocate(b))()); + }(); } diff --git a/std/experimental/allocator/building_blocks/stats_collector.d b/std/experimental/allocator/building_blocks/stats_collector.d index 18f9cdb549d..c0a942a7658 100644 --- a/std/experimental/allocator/building_blocks/stats_collector.d +++ b/std/experimental/allocator/building_blocks/stats_collector.d @@ -686,15 +686,15 @@ public: Allocator a; assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes); - auto b1 = a.allocate(100); + auto b1 = (() nothrow @safe => a.allocate(100))(); assert(a.numAllocate == 1); assert(a.expand(b1, 0)); assert(a.reallocate(b1, b1.length + 1)); - auto b2 = a.allocate(101); + auto b2 = (() nothrow @safe => a.allocate(101))(); assert(a.numAllocate == 2); assert(a.bytesAllocated == 202); assert(a.bytesUsed == 202); - auto b3 = a.allocate(202); + auto b3 = (() nothrow @safe => a.allocate(202))(); assert(a.numAllocate == 3); assert(a.bytesAllocated == 404); assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no); @@ -722,11 +722,11 @@ public: { import std.range : walkLength; Allocator a; - auto b1 = a.allocate(100); + auto b1 = (() nothrow @safe => a.allocate(100))(); assert(a.expand(b1, 0)); assert(a.reallocate(b1, b1.length + 1)); - auto b2 = a.allocate(101); - auto b3 = a.allocate(202); + auto b2 = (() nothrow @safe => a.allocate(101))(); + auto b3 = (() nothrow @safe => a.allocate(202))(); () nothrow @nogc { a.deallocate(b2); }(); () nothrow @nogc { a.deallocate(b1); }(); @@ -752,7 +752,7 @@ public: import std.experimental.allocator.building_blocks.region : Region; auto a = StatsCollector!(Region!(), Options.all, Options.all)(Region!()(new ubyte[1024 * 64])); - auto b = a.allocate(42); + auto b = (() nothrow @safe @nogc => a.allocate(42))(); assert(b.length == 42); assert((() nothrow @nogc => a.deallocateAll())()); } diff --git a/std/experimental/allocator/gc_allocator.d b/std/experimental/allocator/gc_allocator.d index 9e5ce79004b..3bc56038209 100644 --- a/std/experimental/allocator/gc_allocator.d +++ b/std/experimental/allocator/gc_allocator.d @@ -136,36 +136,36 @@ struct GCAllocator assert(GCAllocator.instance.expand(b, 1)); } -@system unittest +nothrow @safe unittest { import core.memory : GC; import std.typecons : Ternary; // test allocation sizes - assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(1))() == 16); + assert((() @nogc => GCAllocator.instance.goodAllocSize(1))() == 16); for (size_t s = 16; s <= 8192; s *= 2) { - assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(s))() == s); - assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(s - (s / 2) + 1))() == s); + assert((() @nogc => GCAllocator.instance.goodAllocSize(s))() == s); + assert((() @nogc => GCAllocator.instance.goodAllocSize(s - (s / 2) + 1))() == s); auto buffer = GCAllocator.instance.allocate(s); - scope(exit) () nothrow @nogc { GCAllocator.instance.deallocate(buffer); }(); + scope(exit) () @trusted @nogc { GCAllocator.instance.deallocate(buffer); }(); void[] 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(GCAllocator.instance.resolveInternalPointer(null, p) == Ternary.no); + assert(GCAllocator.instance.resolveInternalPointer(&buffer[0], p) == Ternary.yes); + assert(&p[0] == &buffer[0] && p.length >= buffer.length); - assert(GC.sizeOf(buffer.ptr) == s); + () @trusted { assert(GC.sizeOf(&buffer[0]) == s); }(); auto buffer2 = GCAllocator.instance.allocate(s - (s / 2) + 1); - scope(exit) () nothrow @nogc { GCAllocator.instance.deallocate(buffer2); }(); + scope(exit) () @trusted @nogc { GCAllocator.instance.deallocate(buffer2); }(); - assert(GC.sizeOf(buffer2.ptr) == s); + () @trusted { assert(GC.sizeOf(&buffer2[0]) == s); }(); } // anything above a page is simply rounded up to next page - assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(4096 * 4 + 1))() == 4096 * 5); + assert((() @nogc => GCAllocator.instance.goodAllocSize(4096 * 4 + 1))() == 4096 * 5); } nothrow @safe unittest diff --git a/std/experimental/allocator/mallocator.d b/std/experimental/allocator/mallocator.d index e62b9211e88..12834e27332 100644 --- a/std/experimental/allocator/mallocator.d +++ b/std/experimental/allocator/mallocator.d @@ -77,12 +77,11 @@ struct Mallocator @nogc @system nothrow unittest { - @nogc nothrow static void test(A)() { int* p = null; p = cast(int*) A.instance.allocate(int.sizeof); - scope(exit) () nothrow @nogc { A.instance.deallocate(p[0 .. int.sizeof]); }(); + scope(exit) A.instance.deallocate(p[0 .. int.sizeof]); *p = 42; assert(*p == 42); } diff --git a/std/experimental/allocator/mmap_allocator.d b/std/experimental/allocator/mmap_allocator.d index fc94749ef57..2fed659d3a6 100644 --- a/std/experimental/allocator/mmap_allocator.d +++ b/std/experimental/allocator/mmap_allocator.d @@ -28,6 +28,7 @@ struct MmapAllocator version(Posix) { /// Allocator API. + @trusted @nogc nothrow void[] allocate(size_t bytes) shared { import core.sys.posix.sys.mman : mmap, MAP_ANON, PROT_READ, @@ -54,6 +55,7 @@ struct MmapAllocator PAGE_READWRITE, MEM_RELEASE; /// Allocator API. + @trusted @nogc nothrow void[] allocate(size_t bytes) shared { if (!bytes) return null;