User:Schuetzm/RC, Owned and allocators

From D Wiki
Jump to: navigation, search
// defined in std.allocator
interface Allocator;

// owning (unique) wrapper
// for simplicity only implemented for classes, but can of course be
// specialized for structs and arrays, too
struct Owned(T) if(is(T == class)) {
private:
    T payload;
    Allocator allocator;

public:
    this(Owned!T other) {
        this.payload = other.payload;
        other.payload = null;
    }

    this(Args...)(Allocator allocator, auto ref Args args) {
        this.allocator = allocator;
        // in a real implementation, exception safety is needed here, of course
        auto buffer = allocator.allocate(__traits(classInstanceSize, T));
        payload = buffer.emplace!T(args);
    }

    ~this() {
        if(payload !is null) {
            destroy(payload);
            auto buffer = cast(void*)(payload)[0 .. __traits(classInstanceSize, T)];
            allocator.deallocate(buffer);
        }
    }

    @disable this(this);
    @disable opAssign();

    Owned!T release() {
        return Owned!T(this);
    }

    RC!T toRC() {
        return RC!T(this);
    }

    scope!this(T) borrow() {
        return payload;
    }

    alias borrow this;
}

// reference counting wrapper
struct RC(T) if(is(T == class)) {
private:
    T payload;

    @property ref Allocator allocator() {
        return cast(Allocator)(cast(void*)(payload) + __traits(classInstanceSize, T));
    }

    @property ref size_t refcount() {
        return cast(size_t)(cast(void*)(payload) + __traits(classInstanceSize, T) + Allocator.sizeof);
    }

    enum bufferSize =
            __traits(classInstanceSize, T) +        // space for the payload
            Allocator.sizeof +                      // ... the allocator reference
            size_t.sizeof;                          // ... and the reference count

    private void destroy__() {
        destroy(payload);
        allocator.deallocate(cast(void*)(payload)[0 .. bufferSize]);
        payload = null;
    }

public:
    this(Owned!T other) {
        auto buffer = other.allocator.reallocate(
            other.payload[0 .. __traits(classInstanceSize, T)],
            bufferSize
        );

        if(buffer is null) {
            // out of memory
        }

        allocator = other.allocator;
        refcount = 1;
    }

    this(Allocator, Args...)(Allocator alloc, auto ref Args args) {
        auto buffer = other.allocator.allocate(bufferSize);

        if(buffer is null) {
            // out of memory
        }

        payload = buffer[0 .. __traits(classInstanceSize, T)].emplace!T(args);

        allocator = other.allocator;
        refcount = 1;
    }

    ~this() {
        if(--refcount == 0)
            destroy__();
    }

    this(scope this) {
        ++refcount;
    }

    ~his() scope {
        // don't decrement
    }

    this(scope this) scope {
        // don't increment
    }

    void opAssign(RC!T other) {
        if(--refcount == 0)
            destroy__();
        payload = other.payload;
        ++refcount;
    }

    scope!this(T) borrow() {
        return payload;
    }

    alias borrow this;
}