Array support for refCounted factory function

This commit is contained in:
Eugen Wissner 2016-12-11 11:42:09 +01:00
parent a2dadda511
commit b20f367aa8
3 changed files with 138 additions and 109 deletions

View File

@ -29,7 +29,7 @@ struct Integer
pure nothrow @safe @nogc invariant pure nothrow @safe @nogc invariant
{ {
assert(rep.length || !sign, "0 should be positive."); assert(!rep.count || rep.length || !sign, "0 should be positive.");
} }
/** /**
@ -39,18 +39,37 @@ struct Integer
* T = Value type. * T = Value type.
* value = Initial value. * value = Initial value.
* allocator = Allocator. * allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null)
*/ */
this(T)(in T value, shared Allocator allocator = defaultAllocator) this(T)(in T value, shared Allocator allocator = defaultAllocator)
nothrow @safe @nogc nothrow @safe @nogc
if (isIntegral!T) if (isIntegral!T || is(T == Integer))
{
this(allocator);
static if (isIntegral!T)
{
assignInt(value);
}
else
{
rep = RefCounted!(ubyte[])(() @trusted {
return cast(ubyte[]) allocator.allocate(value.length);
}(), allocator);
value.rep.get.copy(rep.get);
sign = value.sign;
}
}
/// Ditto.
this(shared Allocator allocator) nothrow @safe @nogc
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body body
{ {
this(allocator); this.allocator = allocator;
assignInt(value);
} }
private @nogc unittest private @nogc unittest
@ -66,30 +85,6 @@ struct Integer
assert(h2.sign); assert(h2.sign);
} }
/// Ditto.
this(T)(in T value, shared Allocator allocator = defaultAllocator)
nothrow @safe @nogc
if (is(T == Integer))
in
{
assert(allocator !is null);
}
body
{
this(allocator);
allocator.resizeArray(rep, value.length);
value.rep.get.copy(rep.get);
sign = value.sign;
}
/// Ditto.
this(shared Allocator allocator) nothrow @safe @nogc
{
this.allocator = allocator;
rep = RefCounted!(ubyte[])(allocator);
}
/* /*
* Figures out the minimum amount of space this value will take * Figures out the minimum amount of space this value will take
* up in bytes and resizes the internal storage. Sets the sign. * up in bytes and resizes the internal storage. Sets the sign.
@ -99,6 +94,7 @@ struct Integer
in in
{ {
static assert(isIntegral!T); static assert(isIntegral!T);
assert(allocator !is null);
} }
body body
{ {
@ -123,8 +119,16 @@ struct Integer
} }
--size; --size;
} }
allocator.resizeArray(rep.get, size); if (rep.count)
{
allocator.resizeArray(rep.get, size);
}
else
{
rep = RefCounted!(ubyte[])(() @trusted {
return cast(ubyte[]) allocator.allocate(size);
}(), allocator);
}
/* Work backward through the int, masking off each byte (up to the /* Work backward through the int, masking off each byte (up to the
first 0 byte) and copy it into the internal representation in first 0 byte) and copy it into the internal representation in
big-endian format. */ big-endian format. */
@ -146,23 +150,31 @@ struct Integer
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref Integer opAssign(T)(in T value) nothrow @safe @nogc ref Integer opAssign(T)(in T value) nothrow @safe @nogc
if (isIntegral!T) if (isIntegral!T || is(T == Integer))
{ {
checkAllocator(); if (allocator is null)
assignInt(value); {
allocator = defaultAllocator;
return this; }
} static if (isIntegral!T)
{
/// Ditto. assignInt(value);
ref Integer opAssign(in Integer value) nothrow @safe @nogc }
{ else
checkAllocator(); {
if (rep.count)
allocator.resizeArray(rep, value.length); {
value.rep.get.copy(rep.get); allocator.resizeArray(rep, value.length);
}
sign = value.sign; else
{
rep = RefCounted!(ubyte[])(() @trusted {
return cast(ubyte[]) allocator.allocate(value.length);
}(), allocator);
}
value.rep.get.copy(rep.get);
sign = value.sign;
}
return this; return this;
} }
@ -368,7 +380,7 @@ struct Integer
} }
body body
{ {
checkAllocator(); initialize();
static if (op == "+") static if (op == "+")
{ {
if (h.sign == sign) if (h.sign == sign)
@ -522,7 +534,7 @@ struct Integer
} }
body body
{ {
checkAllocator(); initialize();
auto divisor = Integer(h, allocator); auto divisor = Integer(h, allocator);
size_t bitSize; size_t bitSize;
@ -654,7 +666,7 @@ struct Integer
Integer opUnary(string op)() nothrow @safe @nogc Integer opUnary(string op)() nothrow @safe @nogc
if ((op == "+") || (op == "-") || (op == "~")) if ((op == "+") || (op == "-") || (op == "~"))
{ {
checkAllocator(); initialize();
auto h = Integer(this, allocator); auto h = Integer(this, allocator);
static if (op == "-") static if (op == "-")
{ {
@ -748,7 +760,7 @@ struct Integer
} }
body body
{ {
checkAllocator(); initialize();
static if (op == "++") static if (op == "++")
{ {
@ -820,12 +832,16 @@ struct Integer
assert(h.rep[0] == 0x01); assert(h.rep[0] == 0x01);
} }
private void checkAllocator() nothrow @safe @nogc private void initialize() nothrow @safe @nogc
{ {
if (allocator is null) if (allocator is null)
{ {
allocator = defaultAllocator; allocator = defaultAllocator;
} }
if (!rep.count)
{
rep = allocator.refCounted!(ubyte[])(0);
}
} }
/** /**
@ -895,7 +911,7 @@ struct Integer
{ {
immutable step = n / 8; immutable step = n / 8;
checkAllocator(); initialize();
if (step >= rep.length) if (step >= rep.length)
{ {
allocator.resizeArray(rep, 0); allocator.resizeArray(rep, 0);
@ -976,7 +992,7 @@ struct Integer
immutable bit = n % 8; immutable bit = n % 8;
immutable delta = 8 - bit; immutable delta = 8 - bit;
checkAllocator(); initialize();
if (cast(ubyte) (rep[0] >> delta)) if (cast(ubyte) (rep[0] >> delta))
{ {
allocator.resizeArray(rep, i + n / 8 + 1); allocator.resizeArray(rep, i + n / 8 + 1);
@ -1015,7 +1031,7 @@ struct Integer
if (op == "<<" || op == ">>" || op == "+" || op == "-" || op == "/" if (op == "<<" || op == ">>" || op == "+" || op == "-" || op == "/"
|| op == "*" || op == "^^" || op == "%") || op == "*" || op == "^^" || op == "%")
{ {
checkAllocator(); initialize();
auto ret = Integer(this, allocator); auto ret = Integer(this, allocator);
mixin("ret " ~ op ~ "= n;"); mixin("ret " ~ op ~ "= n;");
return ret; return ret;
@ -1037,7 +1053,7 @@ struct Integer
if (op == "+" || op == "-" || op == "/" if (op == "+" || op == "-" || op == "/"
|| op == "*" || op == "^^" || op == "%") || op == "*" || op == "^^" || op == "%")
{ {
checkAllocator(); initialize();
auto ret = Integer(this, allocator); auto ret = Integer(this, allocator);
mixin("ret " ~ op ~ "= h;"); mixin("ret " ~ op ~ "= h;");
return ret; return ret;

View File

@ -11,7 +11,6 @@
module tanya.memory; module tanya.memory;
import core.exception; import core.exception;
import std.algorithm.mutation;
public import std.experimental.allocator : make, makeArray, expandArray, public import std.experimental.allocator : make, makeArray, expandArray,
stateSize, shrinkArray; stateSize, shrinkArray;
import std.traits; import std.traits;
@ -66,7 +65,7 @@ bool resizeArray(T)(shared Allocator allocator,
array = () @trusted { return cast(T[]) buf; }(); array = () @trusted { return cast(T[]) buf; }();
if (oldLength < length) if (oldLength < length)
{ {
array[oldLength .. $].uninitializedFill(init); array[oldLength .. $] = init;
} }
return true; return true;
} }

View File

@ -30,12 +30,13 @@ struct RefCounted(T)
{ {
static if (isReference!T) static if (isReference!T)
{ {
private T payload; private alias Payload = T;
} }
else else
{ {
private T* payload; private alias Payload = T*;
} }
private Payload payload_;
private uint counter; private uint counter;
@ -48,33 +49,20 @@ struct RefCounted(T)
/** /**
* Takes ownership over $(D_PARAM value), setting the counter to 1. * Takes ownership over $(D_PARAM value), setting the counter to 1.
* $(D_PARAM value) may be a pointer, an object or a dynamic array.
* *
* Params: * Params:
* value = Value whose ownership is taken over. * value = Value whose ownership is taken over.
* allocator = Allocator used to destroy the $(D_PARAM value) and to * allocator = Allocator used to destroy the $(D_PARAM value) and to
* allocate/deallocate internal storage. * allocate/deallocate internal storage.
*
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
*/ */
this(T value, shared Allocator allocator = defaultAllocator) this(Payload value, shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
body
{ {
this(allocator); this(allocator);
static if (!isReference!T) move(value, payload_);
{ counter = 1;
payload = cast(T*) allocator.allocate(stateSize!T).ptr;
move(value, *payload);
counter = 1;
}
else if (value !is null)
{
move(value, payload);
counter = 1;
}
} }
/// Ditto. /// Ditto.
@ -93,7 +81,7 @@ struct RefCounted(T)
*/ */
this(this) pure nothrow @safe @nogc this(this) pure nothrow @safe @nogc
{ {
if (isInitialized) if (count)
{ {
++counter; ++counter;
} }
@ -106,12 +94,12 @@ struct RefCounted(T)
*/ */
~this() ~this()
{ {
if (isInitialized && !--counter) if (count && !--counter)
{ {
static if (isReference!T) static if (isReference!T)
{ {
allocator.dispose(payload); allocator.dispose(payload_);
payload = null; payload_ = null;
} }
} }
} }
@ -138,23 +126,34 @@ struct RefCounted(T)
} }
static if (isReference!T) static if (isReference!T)
{ {
counter == 1 ? allocator.dispose(payload) : --counter; if (counter > 1)
{
--counter;
}
else if (counter == 1)
{
allocator.dispose(payload_);
}
else
{
counter = 1;
}
} }
else if (!isInitialized) else if (!count)
{ {
payload = cast(T*) allocator.allocate(stateSize!T).ptr; payload_ = cast(T*) allocator.allocate(stateSize!T).ptr;
counter = 1; counter = 1;
} }
move(rhs, get); move(rhs, payload);
return get; return payload;
} }
/// Ditto. /// Ditto.
ref typeof(this) opAssign(typeof(this) rhs) ref typeof(this) opAssign(typeof(this) rhs)
{ {
swap(counter, rhs.counter);
swap(get, rhs.get);
swap(allocator, rhs.allocator); swap(allocator, rhs.allocator);
swap(counter, rhs.counter);
swap(payload, rhs.payload);
return this; return this;
} }
@ -169,24 +168,27 @@ struct RefCounted(T)
*/ */
inout(T2) opCast(T2)() inout pure nothrow @safe @nogc inout(T2) opCast(T2)() inout pure nothrow @safe @nogc
if (is(T : T2)) if (is(T : T2))
in
{
assert(payload !is null, "Attempted to access an uninitialized reference.");
}
body
{ {
return get; return get;
} }
ref inout(T) get() inout return pure nothrow @safe @nogc /**
* Returns: Reference to the owned object.
*/
ref inout(T) get() inout pure nothrow @safe @nogc
in
{
assert(counter, "Attempted to access an uninitialized reference.");
}
body
{ {
static if (isReference!T) static if (isReference!T)
{ {
return payload; return payload_;
} }
else else
{ {
return *payload; return *payload_;
} }
} }
@ -200,12 +202,17 @@ struct RefCounted(T)
return counter; return counter;
} }
/** pragma(inline, true)
* Returns: Whether tihs $(D_PSYMBOL RefCounted) is initialized. private ref inout(T) payload() inout return pure nothrow @safe @nogc
*/
@property bool isInitialized() const pure nothrow @safe @nogc
{ {
return counter != 0; static if (isReference!T)
{
return payload_;
}
else
{
return *payload_;
}
} }
alias get this; alias get this;
@ -248,9 +255,9 @@ unittest
this(ref ubyte[] member) this(ref ubyte[] member)
{ {
assert(!this.member.isInitialized); assert(!this.member.count);
this.member = member; this.member = member;
assert(this.member.isInitialized); assert(this.member.count);
} }
} }
@ -291,17 +298,20 @@ private unittest
assert(destroyed == 1); assert(destroyed == 1);
RefCounted!int rc; RefCounted!int rc;
assert(rc.count == 0);
rc = 8; rc = 8;
assert(rc.count == 1);
} }
private unittest private unittest
{ {
auto rc = RefCounted!int(5); auto val = defaultAllocator.make!int(5);
auto rc = RefCounted!int(val);
static assert(is(typeof(rc.payload) == int*)); static assert(is(typeof(rc.payload_) == int*));
static assert(is(typeof(cast(int) rc) == int)); static assert(is(typeof(cast(int) rc) == int));
static assert(is(typeof(RefCounted!(int*).payload) == int*)); static assert(is(typeof(RefCounted!(int*).payload_) == int*));
static assert(is(typeof(cast(A) (RefCounted!A())) == A)); static assert(is(typeof(cast(A) (RefCounted!A())) == A));
static assert(is(typeof(cast(Object) (RefCounted!A())) == Object)); static assert(is(typeof(cast(Object) (RefCounted!A())) == Object));
@ -326,7 +336,11 @@ private unittest
RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args) RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
if (!is(T == interface) && !isAbstractClass!T) if (!is(T == interface) && !isAbstractClass!T)
{ {
static if (isReference!T) static if (isDynamicArray!T)
{
return typeof(return)(allocator.makeArray!(ForeachType!T)(args), allocator);
}
else static if (isReference!T)
{ {
return typeof(return)(allocator.make!T(args), allocator); return typeof(return)(allocator.make!T(args), allocator);
} }
@ -334,7 +348,7 @@ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
{ {
auto rc = typeof(return)(allocator); auto rc = typeof(return)(allocator);
rc.counter = 1; rc.counter = 1;
rc.payload = allocator.make!T(args); rc.payload_ = allocator.make!T(args);
return rc; return rc;
} }
} }
@ -378,6 +392,6 @@ private unittest
} }
{ {
auto rc = defaultAllocator.refCounted!E(); auto rc = defaultAllocator.refCounted!E();
assert(rc.isInitialized); assert(rc.count);
} }
} }