summaryrefslogtreecommitdiff
path: root/libphobos/src/std/experimental/allocator/common.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/src/std/experimental/allocator/common.d')
-rw-r--r--libphobos/src/std/experimental/allocator/common.d192
1 files changed, 134 insertions, 58 deletions
diff --git a/libphobos/src/std/experimental/allocator/common.d b/libphobos/src/std/experimental/allocator/common.d
index 0eec0d3cf85..8acd763b97c 100644
--- a/libphobos/src/std/experimental/allocator/common.d
+++ b/libphobos/src/std/experimental/allocator/common.d
@@ -1,16 +1,19 @@
+// Written in the D programming language.
/**
Utility and ancillary artifacts of `std.experimental.allocator`. This module
shouldn't be used directly; its functionality will be migrated into more
appropriate parts of `std`.
Authors: $(HTTP erdani.com, Andrei Alexandrescu), Timon Gehr (`Ternary`)
+
+Source: $(PHOBOSSRC std/experimental/allocator/common.d)
*/
module std.experimental.allocator.common;
import std.algorithm.comparison, std.traits;
/**
Returns the size in bytes of the state that needs to be allocated to hold an
-object of type $(D T). $(D stateSize!T) is zero for $(D struct)s that are not
+object of type `T`. `stateSize!T` is zero for `struct`s that are not
nested and have no nonstatic member variables.
*/
template stateSize(T)
@@ -54,7 +57,7 @@ template hasStaticallyKnownAlignment(Allocator)
}
/**
-$(D chooseAtRuntime) is a compile-time constant of type $(D size_t) that several
+`chooseAtRuntime` is a compile-time constant of type `size_t` that several
parameterized structures in this module recognize to mean deferral to runtime of
the exact value. For example, $(D BitmappedBlock!(Allocator, 4096)) (described in
detail below) defines a block allocator with block size of 4096 bytes, whereas
@@ -64,11 +67,11 @@ field storing the block size, initialized by the user.
enum chooseAtRuntime = size_t.max - 1;
/**
-$(D unbounded) is a compile-time constant of type $(D size_t) that several
+`unbounded` is a compile-time constant of type `size_t` that several
parameterized structures in this module recognize to mean "infinite" bounds for
-the parameter. For example, $(D Freelist) (described in detail below) accepts a
-$(D maxNodes) parameter limiting the number of freelist items. If $(D unbounded)
-is passed for $(D maxNodes), then there is no limit and no checking for the
+the parameter. For example, `Freelist` (described in detail below) accepts a
+`maxNodes` parameter limiting the number of freelist items. If `unbounded`
+is passed for `maxNodes`, then there is no limit and no checking for the
number of nodes.
*/
enum unbounded = size_t.max;
@@ -80,7 +83,7 @@ current platform.
enum uint platformAlignment = std.algorithm.comparison.max(double.alignof, real.alignof);
/**
-The default good size allocation is deduced as $(D n) rounded up to the
+The default good size allocation is deduced as `n` rounded up to the
allocator's alignment.
*/
size_t goodAllocSize(A)(auto ref A a, size_t n)
@@ -88,7 +91,7 @@ size_t goodAllocSize(A)(auto ref A a, size_t n)
return n.roundUpToMultipleOf(a.alignment);
}
-/**
+/*
Returns s rounded up to a multiple of base.
*/
@safe @nogc nothrow pure
@@ -108,13 +111,13 @@ unittest
assert(118.roundUpToMultipleOf(11) == 121);
}
-/**
+/*
Returns `n` rounded up to a multiple of alignment, which must be a power of 2.
*/
@safe @nogc nothrow pure
package size_t roundUpToAlignment(size_t n, uint alignment)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
assert(alignment.isPowerOf2);
immutable uint slack = cast(uint) n & (alignment - 1);
const result = slack
@@ -133,13 +136,13 @@ unittest
assert(118.roundUpToAlignment(64) == 128);
}
-/**
+/*
Returns `n` rounded down to a multiple of alignment, which must be a power of 2.
*/
@safe @nogc nothrow pure
package size_t roundDownToAlignment(size_t n, uint alignment)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
assert(alignment.isPowerOf2);
return n & ~size_t(alignment - 1);
}
@@ -153,7 +156,7 @@ unittest
assert(63.roundDownToAlignment(64) == 0);
}
-/**
+/*
Advances the beginning of `b` to start at alignment `a`. The resulting buffer
may therefore be shorter. Returns the adjusted buffer, or null if obtaining a
non-empty buffer is impossible.
@@ -177,7 +180,7 @@ package void[] roundUpToAlignment(void[] b, uint a)
assert(roundUpToAlignment(buf, 128) !is null);
}
-/**
+/*
Like `a / b` but rounds the result up, not down.
*/
@safe @nogc nothrow pure
@@ -187,7 +190,7 @@ package size_t divideRoundUp(size_t a, size_t b)
return (a + b - 1) / b;
}
-/**
+/*
Returns `s` rounded up to a multiple of `base`.
*/
@nogc nothrow pure
@@ -209,8 +212,8 @@ nothrow pure
assert(roundStartToMultipleOf(p, 16) is p);
}
-/**
-Returns $(D s) rounded up to the nearest power of 2.
+/*
+Returns `s` rounded up to the nearest power of 2.
*/
@safe @nogc nothrow pure
package size_t roundUpToPowerOf2(size_t s)
@@ -246,18 +249,14 @@ unittest
assert(((size_t.max >> 1) + 1).roundUpToPowerOf2 == (size_t.max >> 1) + 1);
}
-/**
-Returns the number of trailing zeros of $(D x).
+/*
+Returns the number of trailing zeros of `x`.
*/
@safe @nogc nothrow pure
package uint trailingZeros(ulong x)
{
- uint result;
- while (result < 64 && !(x & (1UL << result)))
- {
- ++result;
- }
- return result;
+ import core.bitop : bsf;
+ return x == 0 ? 64 : bsf(x);
}
@safe @nogc nothrow pure
@@ -270,7 +269,7 @@ unittest
assert(trailingZeros(4) == 2);
}
-/**
+/*
Returns `true` if `ptr` is aligned at `alignment`.
*/
@nogc nothrow pure
@@ -279,14 +278,14 @@ package bool alignedAt(T)(T* ptr, uint alignment)
return cast(size_t) ptr % alignment == 0;
}
-/**
+/*
Returns the effective alignment of `ptr`, i.e. the largest power of two that is
a divisor of `ptr`.
*/
@nogc nothrow pure
-package uint effectiveAlignment(void* ptr)
+package size_t effectiveAlignment(void* ptr)
{
- return 1U << trailingZeros(cast(size_t) ptr);
+ return (cast(size_t) 1) << trailingZeros(cast(size_t) ptr);
}
@nogc nothrow pure
@@ -294,28 +293,31 @@ package uint effectiveAlignment(void* ptr)
{
int x;
assert(effectiveAlignment(&x) >= int.alignof);
+
+ const max = (cast(size_t) 1) << (size_t.sizeof * 8 - 1);
+ assert(effectiveAlignment(cast(void*) max) == max);
}
-/**
+/*
Aligns a pointer down to a specified alignment. The resulting pointer is less
than or equal to the given pointer.
*/
@nogc nothrow pure
-package void* alignDownTo(void* ptr, uint alignment)
+package void* alignDownTo(return scope void* ptr, uint alignment)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
assert(alignment.isPowerOf2);
return cast(void*) (cast(size_t) ptr & ~(alignment - 1UL));
}
-/**
+/*
Aligns a pointer up to a specified alignment. The resulting pointer is greater
than or equal to the given pointer.
*/
@nogc nothrow pure
-package void* alignUpTo(void* ptr, uint alignment)
+package void* alignUpTo(return scope void* ptr, uint alignment)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
assert(alignment.isPowerOf2);
immutable uint slack = cast(size_t) ptr & (alignment - 1U);
return slack ? ptr + alignment - slack : ptr;
@@ -324,27 +326,27 @@ package void* alignUpTo(void* ptr, uint alignment)
@safe @nogc nothrow pure
package bool isGoodStaticAlignment(uint x)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
return x.isPowerOf2;
}
@safe @nogc nothrow pure
package bool isGoodDynamicAlignment(uint x)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
return x.isPowerOf2 && x >= (void*).sizeof;
}
/**
-The default $(D reallocate) function first attempts to use $(D expand). If $(D
-Allocator.expand) is not defined or returns $(D false), $(D reallocate)
+The default `reallocate` function first attempts to use `expand`. If $(D
+Allocator.expand) is not defined or returns `false`, `reallocate`
allocates a new block of memory of appropriate size and copies data from the old
-block to the new block. Finally, if $(D Allocator) defines $(D deallocate), $(D
+block to the new block. Finally, if `Allocator` defines `deallocate`, $(D
reallocate) uses it to free the old memory block.
-$(D reallocate) does not attempt to use $(D Allocator.reallocate) even if
+`reallocate` does not attempt to use `Allocator.reallocate` even if
defined. This is deliberate so allocators may use it internally within their own
-implementation of $(D reallocate).
+implementation of `reallocate`.
*/
bool reallocate(Allocator)(ref Allocator a, ref void[] b, size_t s)
@@ -366,20 +368,21 @@ bool reallocate(Allocator)(ref Allocator a, ref void[] b, size_t s)
/**
-The default $(D alignedReallocate) function first attempts to use $(D expand).
-If $(D Allocator.expand) is not defined or returns $(D false), $(D
+The default `alignedReallocate` function first attempts to use `expand`.
+If `Allocator.expand` is not defined or returns `false`, $(D
alignedReallocate) allocates a new block of memory of appropriate size and
-copies data from the old block to the new block. Finally, if $(D Allocator)
-defines $(D deallocate), $(D alignedReallocate) uses it to free the old memory
+copies data from the old block to the new block. Finally, if `Allocator`
+defines `deallocate`, `alignedReallocate` uses it to free the old memory
block.
-$(D alignedReallocate) does not attempt to use $(D Allocator.reallocate) even if
+`alignedReallocate` does not attempt to use `Allocator.reallocate` even if
defined. This is deliberate so allocators may use it internally within their own
-implementation of $(D reallocate).
+implementation of `reallocate`.
*/
bool alignedReallocate(Allocator)(ref Allocator alloc,
ref void[] b, size_t s, uint a)
+if (hasMember!(Allocator, "alignedAllocate"))
{
static if (hasMember!(Allocator, "expand"))
{
@@ -388,9 +391,10 @@ bool alignedReallocate(Allocator)(ref Allocator alloc,
}
else
{
- if (b.length == s) return true;
+ if (b.length == s && b.ptr.alignedAt(a)) return true;
}
auto newB = alloc.alignedAllocate(s, a);
+ if (newB.length != s) return false;
if (newB.length <= b.length) newB[] = b[0 .. newB.length];
else newB[0 .. b.length] = b[];
static if (hasMember!(Allocator, "deallocate"))
@@ -399,6 +403,63 @@ bool alignedReallocate(Allocator)(ref Allocator alloc,
return true;
}
+@system unittest
+{
+ bool called = false;
+ struct DummyAllocator
+ {
+ void[] alignedAllocate(size_t size, uint alignment)
+ {
+ called = true;
+ return null;
+ }
+ }
+
+ struct DummyAllocatorExpand
+ {
+ void[] alignedAllocate(size_t size, uint alignment)
+ {
+ return null;
+ }
+
+ bool expand(ref void[] b, size_t length)
+ {
+ called = true;
+ return true;
+ }
+ }
+
+ char[128] buf;
+ uint alignment = 32;
+ auto alignedPtr = roundUpToMultipleOf(cast(size_t) buf.ptr, alignment);
+ auto diff = alignedPtr - cast(size_t) buf.ptr;
+
+ // Align the buffer to 'alignment'
+ void[] b = cast(void[]) (buf.ptr + diff)[0 .. buf.length - diff];
+
+ DummyAllocator a1;
+ // Ask for same length and alignment, should not call 'alignedAllocate'
+ assert(alignedReallocate(a1, b, b.length, alignment));
+ assert(!called);
+
+ // Ask for same length, different alignment
+ // should call 'alignedAllocate' if not aligned to new value
+ alignedReallocate(a1, b, b.length, alignment + 1);
+ assert(b.ptr.alignedAt(alignment + 1) || called);
+ called = false;
+
+ DummyAllocatorExpand a2;
+ // Ask for bigger length, same alignment, should call 'expand'
+ assert(alignedReallocate(a2, b, b.length + 1, alignment));
+ assert(called);
+ called = false;
+
+ // Ask for bigger length, different alignment
+ // should call 'alignedAllocate' if not aligned to new value
+ alignedReallocate(a2, b, b.length + 1, alignment + 1);
+ assert(b.ptr.alignedAt(alignment + 1) || !called);
+}
+
/**
Forwards each of the methods in `funs` (if defined) to `member`.
*/
@@ -417,14 +478,13 @@ Forwards each of the methods in `funs` (if defined) to `member`.
return result;
}
-version (unittest)
+version (StdUnittest)
{
- import std.experimental.allocator : IAllocator, ISharedAllocator;
package void testAllocator(alias make)()
{
import std.conv : text;
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
import std.stdio : writeln, stderr;
import std.typecons : Ternary;
alias A = typeof(make());
@@ -450,6 +510,18 @@ version (unittest)
assert(b2.length == 2);
assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr);
+ // Test allocateZeroed
+ static if (hasMember!(A, "allocateZeroed"))
+ {{
+ auto b3 = a.allocateZeroed(8);
+ if (b3 !is null)
+ {
+ assert(b3.length == 8);
+ foreach (e; cast(ubyte[]) b3)
+ assert(e == 0);
+ }
+ }}
+
// Test alignedAllocate
static if (hasMember!(A, "alignedAllocate"))
{{
@@ -545,18 +617,22 @@ version (unittest)
}}
}
- package void testAllocatorObject(AllocInterface)(AllocInterface a)
- if (is(AllocInterface : IAllocator)
- || is (AllocInterface : shared ISharedAllocator))
+ package void testAllocatorObject(RCAllocInterface)(RCAllocInterface a)
{
+ // this used to be a template constraint, but moving it inside prevents
+ // unnecessary import of std.experimental.allocator
+ import std.experimental.allocator : RCIAllocator, RCISharedAllocator;
+ static assert(is(RCAllocInterface == RCIAllocator)
+ || is (RCAllocInterface == RCISharedAllocator));
+
import std.conv : text;
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
import std.stdio : writeln, stderr;
import std.typecons : Ternary;
scope(failure) stderr.writeln("testAllocatorObject failed for ",
- AllocInterface.stringof);
+ RCAllocInterface.stringof);
- assert(a);
+ assert(!a.isNull);
// Test alignment
assert(a.alignment.isPowerOf2);