Skip to content

Commit

Permalink
Merge pull request #3991 from andralex/AffixAllocator
Browse files Browse the repository at this point in the history
Allow affix access from qualified data
  • Loading branch information
WalterBright committed Apr 26, 2016
2 parents edbb7a1 + 249f10a commit 5b2d6f5
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 22 deletions.
8 changes: 7 additions & 1 deletion std/conv.d
Original file line number Diff line number Diff line change
Expand Up @@ -3934,7 +3934,6 @@ the type we want to build. This helps to build qualified objects on mutable
buffer, without breaking the type system with unsafe casts.
+/
package void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args)
if (is(UT == Unqual!T))
{
static if (args.length == 0)
{
Expand Down Expand Up @@ -4087,6 +4086,13 @@ unittest
assert(*emplace!int(&a, b) == 42);
}

unittest
{
shared int i;
emplace(&i, 42);
assert(i == 42);
}

private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) @nogc pure nothrow
{
assert(chunk.length >= typeSize, "emplace: Chunk size too small.");
Expand Down
95 changes: 86 additions & 9 deletions std/experimental/allocator/building_blocks/affix_allocator.d
Original file line number Diff line number Diff line change
Expand Up @@ -184,21 +184,40 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
mixin(forwardToMember("parent",
"deallocateAll", "empty"));

// Computes suffix type given buffer type
private template Payload2Affix(Payload, Affix)
{
static if (is(Payload[] : void[]))
alias Payload2Affix = Affix;
else static if (is(Payload[] : shared(void)[]))
alias Payload2Affix = shared Affix;
else static if (is(Payload[] : immutable(void)[]))
alias Payload2Affix = shared Affix;
else static if (is(Payload[] : const(shared(void))[]))
alias Payload2Affix = shared Affix;
else static if (is(Payload[] : const(void)[]))
alias Payload2Affix = const Affix;
else
static assert(0, "Internal error for type " ~ Payload.stringof);
}

// Extra functions
static if (stateSize!Prefix)
static ref Prefix prefix(void[] b)
{
static auto ref prefix(T)(T[] b)
{
assert(b.ptr && b.ptr.alignedAt(Prefix.alignof));
return (cast(Prefix*)b.ptr)[-1];
return (cast(Payload2Affix!(T, Prefix)*) b.ptr)[-1];
}
}
static if (stateSize!Suffix)
ref Suffix suffix(void[] b)
auto ref suffix(T)(T[] b)
{
assert(b.ptr);
auto p = b.ptr - stateSize!Prefix
+ actualAllocationSize(b.length);
assert(p && p.alignedAt(Suffix.alignof));
return (cast(Suffix*) p)[-1];
return (cast(Payload2Affix!(T, Suffix)*) p)[-1];
}
}

Expand Down Expand Up @@ -227,18 +246,59 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
Ternary empty();

/**
The `instance` singleton is defined if and only if the parent allocator has no state and defines its own `it` object.
The `instance` singleton is defined if and only if the parent allocator
has no state and defines its own `it` object.
*/
static AffixAllocator instance;

/**
Affix access functions offering mutable references to the affixes of a block previously allocated with this allocator. $(D b) may not be null. They are defined if and only if the corresponding affix is not $(D void).
Affix access functions offering references to the affixes of a
block `b` previously allocated with this allocator. `b` may not be null.
They are defined if and only if the corresponding affix is not `void`.
The qualifiers of the affix are not always the same as the qualifiers
of the argument. This is because the affixes are not part of the data
itself, but instead are just $(I associated) with the data and known
to the allocator. The table below documents the type of `preffix(b)` and
`affix(b)` depending on the type of `b`.
$(BOOKTABLE Result of `prefix`/`suffix` depending on argument (`U` is
any unqualified type, `Affix` is `Prefix` or `Suffix`),
$(TR $(TH Argument$(NBSP)Type) $(TH Return) $(TH Comments))
$(TR $(TD `shared(U)[]`) $(TD `ref shared Affix`)
$(TD Data is shared across threads and the affix follows suit.))
Precondition: $(D b !is null)
$(TR $(TD `immutable(U)[]`) $(TD `ref shared Affix`)
$(TD Although the data is immutable, the allocator "knows" the
underlying memory is mutable, so `immutable` is elided for the affix
which is independent from the data itself. However, the result is
`shared` because `immutable` is implicitly shareable so multiple
threads may access and manipulate the affix for the same data.))
$(TR $(TD `const(shared(U))[]`) $(TD `ref shared Affix`)
$(TD The data is always shareable across threads. Even if the data
is `const`, the affix is modifiable by the same reasoning as for
`immutable`.))
$(TR $(TD `const(U)[]`) $(TD `ref const Affix`)
$(TD The input may have originated from `U[]` or `immutable(U)[]`,
so it may be actually shared or not. Returning an unqualified affix
may result in race conditions, whereas returning a `shared` affix
may result in inadvertent sharing of mutable thread-local data
across multiple threads. So the returned type is conservatively
`ref const`.))
$(TR $(TD `U[]`) $(TD `ref Affix`)
$(TD Unqualified data has unqualified affixes.))
)
Precondition: `b !is null` and `b` must have been allocated with
this allocator.
*/
static ref Prefix prefix(void[] b);
static ref auto prefix(T)(T[] b);
/// Ditto
static ref Suffix suffix(void[] b);
ref auto suffix(T)(T[] b);
}
else static if (is(typeof(Allocator.instance) == shared))
{
Expand Down Expand Up @@ -292,3 +352,20 @@ unittest
b = B.instance.allocate(100);
assert(b is null);
}

unittest
{
import std.experimental.allocator.gc_allocator;
import std.experimental.allocator;
alias AffixAllocator!(GCAllocator, uint) MyAllocator;
auto a = MyAllocator.instance.makeArray!(shared int)(100);
static assert(is(typeof(&MyAllocator.instance.prefix(a)) == shared(uint)*));
auto b = MyAllocator.instance.makeArray!(shared const int)(100);
static assert(is(typeof(&MyAllocator.instance.prefix(b)) == shared(uint)*));
auto c = MyAllocator.instance.makeArray!(immutable int)(100);
static assert(is(typeof(&MyAllocator.instance.prefix(c)) == shared(uint)*));
auto d = MyAllocator.instance.makeArray!(int)(100);
static assert(is(typeof(&MyAllocator.instance.prefix(d)) == uint*));
auto e = MyAllocator.instance.makeArray!(const int)(100);
static assert(is(typeof(&MyAllocator.instance.prefix(e)) == const(uint)*));
}
2 changes: 1 addition & 1 deletion std/experimental/allocator/common.d
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ unittest
Returns `true` if `ptr` is aligned at `alignment`.
*/
@nogc nothrow pure
package bool alignedAt(void* ptr, uint alignment)
package bool alignedAt(T)(T* ptr, uint alignment)
{
return cast(size_t) ptr % alignment == 0;
}
Expand Down
49 changes: 38 additions & 11 deletions std/experimental/allocator/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,8 @@ T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length)
if (!length) return null;
auto m = alloc.allocate(T.sizeof * length);
if (!m.ptr) return null;
return uninitializedFillDefault(cast(T[]) m);
alias U = Unqual!T;
return cast(T[]) uninitializedFillDefault(cast(U[]) m);
}

unittest
Expand Down Expand Up @@ -658,6 +659,24 @@ unittest
test2(theAllocator);
}

unittest
{
auto a = theAllocator.makeArray!(shared int)(5);
static assert(is(typeof(a) == shared(int)[]));
assert(a.length == 5);
assert(a.equal([0, 0, 0, 0, 0]));

auto b = theAllocator.makeArray!(const int)(5);
static assert(is(typeof(b) == const(int)[]));
assert(b.length == 5);
assert(b.equal([0, 0, 0, 0, 0]));

auto c = theAllocator.makeArray!(immutable int)(5);
static assert(is(typeof(c) == immutable(int)[]));
assert(c.length == 5);
assert(c.equal([0, 0, 0, 0, 0]));
}

/// Ditto
T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length,
auto ref T init)
Expand Down Expand Up @@ -688,21 +707,29 @@ T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length,
}
else
{
fillWithMemcpy(result, init);
alias U = Unqual!T;
fillWithMemcpy(cast(U[]) result, *(cast(U*) &init));
}
return result;
}

///
unittest
{
int[] a = theAllocator.makeArray!int(2);
assert(a == [0, 0]);
a = theAllocator.makeArray!int(3, 42);
assert(a == [42, 42, 42]);
import std.range : only;
a = theAllocator.makeArray!int(only(42, 43, 44));
assert(a == [42, 43, 44]);
static void test(T)()
{
T[] a = theAllocator.makeArray!T(2);
assert(a.equal([0, 0]));
a = theAllocator.makeArray!T(3, 42);
assert(a.equal([42, 42, 42]));
import std.range : only;
a = theAllocator.makeArray!T(only(42, 43, 44));
assert(a.equal([42, 43, 44]));
}
test!int();
test!(shared int)();
test!(const int)();
test!(immutable int)();
}

unittest
Expand Down Expand Up @@ -737,15 +764,15 @@ if (isInputRange!R)
{
foreach (j; 0 .. i)
{
destroy(result[j]);
destroy(*cast(Unqual!T*) (result.ptr + j));
}
alloc.deallocate(m);
}

for (; !range.empty; range.popFront, ++i)
{
import std.conv : emplace;
emplace!T(result.ptr + i, range.front);
cast(void) emplace!T(result.ptr + i, range.front);
}

return result;
Expand Down

0 comments on commit 5b2d6f5

Please sign in to comment.