This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.traits; | |
import core.memory; | |
struct Ptr(T) | |
{ | |
static if (is(T == class)) | |
alias T RefT; | |
else | |
alias T* RefT; | |
this(RefT p, void function(RefT) deleter) | |
{ | |
_ptr = p; | |
del = deleter; | |
} | |
private this(RefT ptr, ControlBlock *cb) | |
{ | |
_ptr = ptr; | |
this.cb = cb; | |
} | |
// copying requires explicit usage of .release or .dup | |
@disable this(this); | |
@property Ptr dup() | |
{ | |
ControlBlock* p = void; | |
if ((p = cb) !is null) | |
++p._count; | |
else | |
cb = p = alloc!ControlBlock(2, del); | |
return Ptr(_ptr, p); | |
} | |
@property Ptr release() | |
{ | |
import std.algorithm; | |
return move(this); | |
} | |
// rvalue assign | |
void opAssign(Ptr other) | |
{ | |
import std.algorithm; | |
move(other, this); | |
} | |
~this() | |
{ | |
if (auto p = cb) | |
{ | |
if (!--p._count) | |
{ | |
p._del(_ptr); | |
dealloc!ControlBlock(p); | |
} | |
} | |
else if (auto fn = del) | |
fn(_ptr); | |
else | |
assert(this is Ptr.init); | |
} | |
static if (is(T == class)) | |
{ | |
@property inout(T) get() inout | |
{ | |
return _ptr; | |
} | |
} | |
else | |
{ | |
@property ref inout(T) get() inout | |
{ | |
return *_ptr; | |
} | |
} | |
alias get this; | |
@property bool unique() const pure nothrow | |
{ | |
return count == 1; | |
} | |
@property size_t count() const pure nothrow | |
{ | |
if (auto p = cb) | |
return p._count; | |
else | |
return del is null ? 0 : 1; | |
} | |
private: | |
@property inout(void function(RefT)) del() inout pure nothrow | |
{ | |
return (cast(size_t)_pcb & 0x1) ? | |
cast(typeof(return))(cast(size_t)_pcb & ~cast(size_t)0x1) : | |
null; | |
} | |
@property void del(void function(RefT) val) pure nothrow | |
{ | |
_pcb = cast(void*)(cast(size_t)val | 0x1); | |
} | |
@property inout(ControlBlock)* cb() inout pure nothrow | |
{ | |
return (cast(size_t)_pcb & 0x1) ? null : cast(typeof(return))_pcb; | |
} | |
@property void cb(ControlBlock* val) pure nothrow | |
{ | |
assert(!(cast(size_t)val & 0x1)); | |
_pcb = val; | |
} | |
struct ControlBlock | |
{ | |
size_t _count; | |
void function(RefT) _del; | |
} | |
RefT _ptr; | |
void* _pcb; | |
} | |
/** | |
* | |
*/ | |
Ptr!(T) make(T, Args...)(Args args) | |
{ | |
return Ptr!T(alloc!T(args), &dealloc!T); | |
} | |
private: | |
auto alloc(T, Args...)(auto ref Args args) | |
{ | |
import core.stdc.stdlib, std.conv; | |
static if(is(T == class)) | |
enum sz = __traits(classInstanceSize, T); | |
else | |
enum sz = T.sizeof; | |
auto chunk = malloc(sz)[0 .. sz]; | |
static if (hasAliasing!T) | |
GC.addRange(chunk.ptr, sz); | |
return emplace!T(chunk, args); | |
} | |
void dealloc(T)(T p) if(is(T == class)) | |
{ | |
import core.stdc.stdlib; | |
if (p !is null) | |
{ | |
clear(p); | |
static if (hasAliasing!T) | |
GC.removeRange(cast(void*)p); | |
free(cast(void*)p); | |
} | |
} | |
void dealloc(T)(T* p) if(!is(T == class)) | |
{ | |
import core.stdc.stdlib; | |
if (p !is null) | |
{ | |
clear(*p); | |
static if (hasAliasing!T) | |
GC.removeRange(cast(void*)p); | |
free(cast(void*)p); | |
} | |
} | |
unittest | |
{ | |
Ptr!int p1; | |
assert(!p1.count); | |
assert(!p1.unique); | |
Ptr!int p2 = make!int(); | |
assert(p2.count == 1); | |
assert(p2.unique); | |
p1 = p2.dup; | |
assert(p1.get is p2.get); | |
assert(p1.count == 2); | |
assert(p2.count == 2); | |
auto p3 = p2.dup; | |
} | |
version (unittest) | |
{ | |
struct S | |
{ | |
this(int val=0) | |
{ | |
_val = val; | |
++_n; | |
} | |
~this() | |
{ | |
--_n; | |
} | |
int _val; | |
static size_t _n; | |
} | |
class C | |
{ | |
this(int val=0) | |
{ | |
_val = val; | |
++_n; | |
} | |
~this() | |
{ | |
--_n; | |
} | |
int _val; | |
static size_t _n; | |
} | |
} | |
unittest | |
{ | |
import std.typetuple; | |
static Ptr!T foo(T)(Ptr!T p) | |
{ | |
assert(p.unique); | |
return p.release; | |
} | |
static Ptr!T bar(T)(Ptr!T p) | |
{ | |
assert(!p.unique); | |
return p.dup; | |
} | |
foreach(T; TypeTuple!(S, C)) | |
{ | |
{ | |
scope (success) assert(T._n == 0); | |
auto ptr1 = make!T(0); | |
assert(ptr1.count == 1); | |
ptr1 = foo(ptr1.release); | |
assert(ptr1.count == 1); | |
auto ptr2 = foo(ptr1.release); | |
assert(ptr2.unique); | |
assert(ptr1.count == 0); | |
} | |
{ | |
scope (success) assert(T._n == 0); | |
auto ptr1 = make!T(0); | |
ptr1 = bar(ptr1.dup); | |
assert(ptr1.unique); | |
auto ptr2 = bar(ptr1.dup); | |
assert(ptr1.get == ptr2.get); | |
assert(ptr1.count == 2); | |
assert(ptr2.count == 2); | |
} | |
{ | |
scope (success) assert(T._n == 0); | |
Ptr!T p1, p2; | |
static assert(!__traits(compiles, p1 = p2)); | |
p1 = make!T(1); | |
p2 = make!T(2); | |
assert(p1._val == 1); | |
assert(p2._val == 2); | |
import std.algorithm; | |
swap(p1, p2); | |
assert(p1._val == 2); | |
assert(p2._val == 1); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment