Skip to content

Commit

Permalink
add mir.variant and mir.rc (#224)
Browse files Browse the repository at this point in the history
  • Loading branch information
9il authored Dec 26, 2019
1 parent 2a337ae commit 65e0563
Show file tree
Hide file tree
Showing 8 changed files with 404 additions and 46 deletions.
9 changes: 5 additions & 4 deletions doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ PACKAGE_mir = \
small_string \
small_array \
polynomial \
format\
parse\
appender\
exception\
format \
parse \
appender \
exception \
variant \

PACKAGE_mir_algorithm = iteration setops
PACKAGE_mir_array = allocation
Expand Down
2 changes: 2 additions & 0 deletions index.d
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ $(BOOKTABLE ,
$(TR $(TDNW $(MREF mir,math,constant)) $(TD Math constants))
$(TR $(TDNW $(MREF mir,polynomial)) $(TD Polynomial ref-counted structure))
$(LEADINGROW Reference counting)
$(TR $(TDNW $(MREF mir,rc)) $(TD Reference counting package and RC conversion utilities.))
$(TR $(TDNW $(MREF mir,rc,array)) $(TD Thread safe reference count array and the iterator to adopt it to ndslice.))
$(TR $(TDNW $(MREF mir,rc,ptr)) $(TD Thread safe reference count pointer with polymorphism support for strucs and objects.))
$(TR $(TDNW $(MREF mir,rc,slim_ptr)) $(TD Thread safe reference count pointer for strucs and objects.))
Expand All @@ -40,6 +41,7 @@ $(BOOKTABLE ,
$(LEADINGROW Interconnection with other languages)
$(TR $(TDNW $(MREF mir,ndslice,connect,cpython)) $(TD Utilities for $(HTTPS docs.python.org/3/c-api/buffer.html, Python Buffer Protocol)))
$(LEADINGROW Accessories)
$(TR $(TDNW $(MREF mir,variant)) $(TD Variant Type (aka Algebraic Type) with clever member access))
$(TR $(TDNW $(MREF mir,exception)) $(TD @nogc MirException with formatting))
$(TR $(TDNW $(MREF mir,format)) $(TD @nogc Formatting Utilities))
$(TR $(TDNW $(MREF mir,parse)) $(TD @nogc Parsing Utilities))
Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ mir_algorithm_src = [
'source/mir/small_array.d',
'source/mir/small_string.d',
'source/mir/type_info.d',
'source/mir/variant.d',
]

mir_algorithm_lib = library(meson.project_name(),
Expand Down
22 changes: 11 additions & 11 deletions source/mir/rc/array.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import mir.rc.context;
import mir.type_info;
import std.traits;

private static immutable allocationExcMsg = "mir_rcarray: out of memory error.";
package static immutable allocationExcMsg = "mir_rcarray: out of memory error.";

version (D_Exceptions)
{
import core.exception: OutOfMemoryError;
private static immutable allocationError = new OutOfMemoryError(allocationExcMsg);
package static immutable allocationError = new OutOfMemoryError(allocationExcMsg);
}

/++
Expand All @@ -27,16 +27,16 @@ The implementation never adds roots into the GC.
struct mir_rcarray(T)
{
///
private T* _payload;
private ref inout(mir_rc_context) context() inout scope return pure nothrow @nogc @trusted @property
package T* _payload;
package ref inout(mir_rc_context) context() inout scope return pure nothrow @nogc @trusted @property
{
assert(_payload);
return (cast(inout(mir_rc_context)*)_payload)[-1];
}
private void _reset() { _payload = null; }
package void _reset() { _payload = null; }

private alias ThisTemplate = .mir_rcarray;
private alias _thisPtr = _payload;
package alias ThisTemplate = .mir_rcarray;
package alias _thisPtr = _payload;

///
void proxySwap(ref typeof(this) rhs) pure nothrow @nogc @safe
Expand Down Expand Up @@ -176,11 +176,11 @@ struct mir_rcarray(T)

static if (isImplicitlyConvertible!(const T, T))
static if (isImplicitlyConvertible!(const Unqual!T, T))
private alias V = const Unqual!T;
package alias V = const Unqual!T;
else
private alias V = const T;
package alias V = const T;
else
private alias V = T;
package alias V = T;

}

Expand Down Expand Up @@ -215,7 +215,7 @@ unittest
static assert(is(typeof(fs) == Slice!(double*)));
}

private template LikeArray(Range)
package template LikeArray(Range)
{
static if (__traits(identifier, Range) == "mir_slice")
{
Expand Down
145 changes: 144 additions & 1 deletion source/mir/rc/package.d
Original file line number Diff line number Diff line change
@@ -1,10 +1,153 @@
/++
$(H1 Thread-safe reference-counted arrays and pointers)
Publicly imports $(MREF mir,rc,array) and $(MREF mir,rc,ptr).
Mir provides two kinds of ref-counting pointers and two kinds of ref-counted arrays.
The first kind pointer is `RCPtr`, which consists of a pointer to the context and pointer to the value.`RCPtr` supports structural and object polymorphism. It allows getting members with the same context as the root.
The second kind is `SlimRCPtr`, which consist only from a pointer to the value. The context for `SlimRCPtr`is computed using a fixed-length memory shift from the pointer to the value.
`SlimRCPtr` can be converted to an `RCPtr` and to an `RCArray` of the one element.
`RCArray` is an array type without range primitives. It's length can't be reduced after construction.In the other hand, `Slice!(RCI!(T))` is an ndslice with all random-access range primitives.`RCI` is an iterator, which consists of `RCArray` and the pointer to the current element.
`RCArray!T` can be converted or moved to `Slice!(RCI!(T))` using `.asSlice` or `.moveToSlice` methods respectively.
$(RED `RCArray!T` aliases itself to a common D array slice. This feature may cause a segmentation fault in safe code if used without DIP1000.)
`RCPtr!T` can be constructed from an element index and `RCArray!T` / `Slice!(RCI!(T))`.
The package publicly imports $(MREF mir,rc,array), $(MREF mir,rc,ptr), and $(MREF mir,rc,slim_ptr).
See_also: $(MREF mir,ndslice).
+/
module mir.rc;

public import mir.rc.array;
public import mir.rc.ptr;
public import mir.rc.slim_ptr;

import mir.ndslice.slice;

/++
Returns: shared pointer constructed from the slim shared pointer.
The function has zero computation cost.
+/
RCPtr!F toRCPtr(F)(return SlimRCPtr!F contextAndValue) @trusted
{
typeof(return) ret;
ret._value = contextAndValue._value;
ret._context = &contextAndValue.context();
contextAndValue._value = null;
return ret;
}

///
version(mir_test)
@safe pure @nogc nothrow
unittest
{
import core.lifetime: move;
struct S
{
double e;
}
struct C
{
int i;
S s;
}

auto a = createSlimRC!C(10, S(3));
auto s = a.move.toRCPtr.shareMember!"s";
assert(s._counter == 1);
assert(s.e == 3);
}

/++
Returns: shared pointer constructed with the `array`'s context and the value points to `array[index]`.
The function has zero computation cost.
+/
RCPtr!F toRCPtrAt(F)(return RCArray!F array, size_t index) @trusted
if (!is(R == class) && !is(R == interface))
in {
assert(index < array.length, "toRCPtrAt: index should be less then array.length");
}
body {
typeof(return) ret;
ret._value = array._payload + index;
ret._context = &array.context();
array._payload = null;
return ret;
}

///
version(mir_test)
@safe pure @nogc nothrow
unittest
{
struct S { double e; }

auto a = RCArray!S(10);
a[3].e = 4;

auto s = a.toRCPtrAt(3);

assert(s._counter == 2);
assert(s.e == 4);
}

/// ditto
RCPtr!F toRCPtrAt(F)(return Slice!(RCI!F) array, size_t index) @trusted
if (!is(R == class) && !is(R == interface))
in {
assert(index < array.length, "toRCPtrAt: index should be less then array.length");
}
body {
typeof(return) ret;
ret._value = array._iterator._iterator + index;
ret._context = &array._iterator._array.context();
array._iterator._array._payload = null;
return ret;
}

///
version(mir_test)
@safe pure @nogc nothrow
unittest
{
struct S { double e; }

auto a = RCArray!S(10).asSlice[5 .. $];
a[3].e = 4;

auto s = a.toRCPtrAt(3);

assert(s._counter == 2);
assert(s.e == 4);
}

/++
Returns: RC array length of one constructed from the slim shared pointer.
The function has zero computation cost.
+/
RCArray!F toRCArray(F)(return SlimRCPtr!F context) @trusted
{
typeof(return) ret;
ret._payload = context._value;
context._value = null;
return ret;
}

///
version(mir_test)
@safe pure @nogc nothrow
unittest
{
struct S { double e; }

auto a = createSlimRC!S(4).toRCArray;
assert(a._counter == 1);
assert(a.length == 1);
assert(a[0].e == 4);
}
45 changes: 25 additions & 20 deletions source/mir/rc/ptr.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import mir.rc.context;
import mir.type_info;
import std.traits;

private static immutable allocationExcMsg = "mir_rcptr: out of memory error.";
private static immutable getExcMsg = "mir_rcptr: trying to use null value.";
package static immutable allocationExcMsg = "mir_rcptr: out of memory error.";
package static immutable getExcMsg = "mir_rcptr: trying to use null value.";

version (D_Exceptions)
{
import core.exception: OutOfMemoryError, InvalidMemoryOperationError;
private static immutable allocationError = new OutOfMemoryError(allocationExcMsg);
package static immutable allocationError = new OutOfMemoryError(allocationExcMsg);
}

/++
Expand All @@ -34,17 +34,17 @@ struct mir_rcptr(T)

///
static if (is(T == class) || is(T == interface))
private Unqual!T _value;
package Unqual!T _value;
else
private T* _value;
private mir_rc_context* _context;
package T* _value;
package mir_rc_context* _context;

private ref inout(mir_rc_context) context() inout scope return @trusted @property
package ref inout(mir_rc_context) context() inout scope return @trusted @property
{
return *_context;
}

private void _reset()
package void _reset()
{
_value = null;
_context = null;
Expand All @@ -55,7 +55,7 @@ struct mir_rcptr(T)
return cast(inout(void)*) _value;
}

private alias ThisTemplate = .mir_rcptr;
package alias ThisTemplate = .mir_rcptr;

/// ditto
alias opUnary(string op : "*") = _get_value;
Expand Down Expand Up @@ -122,7 +122,7 @@ struct mir_rcptr(T)

static if (!is(T == interface) && !__traits(isAbstractClass, T))
{
private this(Args...)(auto ref Args args)
package this(Args...)(auto ref Args args)
{
() @trusted {
_context = mir_rc_create(mir_get_type_info!T, 1, mir_get_payload_ptr!T);
Expand All @@ -146,28 +146,30 @@ struct mir_rcptr(T)
alias RCPtr = mir_rcptr;

/++
Returns: shared pointer of the member and the context from the current pointer.
+/
auto shareMember(string member, T, Args...)(return mir_rcptr!T context, auto ref Args args)
{
import core.lifetime: move;
void foo(A)(auto ref A) {}
static if (args.length)
{
// breaks safaty
if (false) foo(__traits(getMember, context._get_value, member)(forward!args));
return (()@trusted => createRCWithContext(context, __traits(getMember, context._get_value, member)(forward!args)))();
return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member)(forward!args), context.move))();
}
else
{
// breaks safaty
if (false) foo(__traits(getMember, context._get_value, member));
return (()@trusted => createRCWithContext(context, __traits(getMember, context._get_value, member)))();
return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member), context.move))();
}
}

/++
Returns: shared pointer constructed with current context.
+/
@system .mir_rcptr!R createRCWithContext(R, F)(return const mir_rcptr!F context, return R value)
@system .mir_rcptr!R createRCWithContext(R, F)(return R value, return const mir_rcptr!F context)
if (is(R == class) || is(R == interface))
{
typeof(return) ret;
Expand All @@ -179,7 +181,7 @@ Returns: shared pointer constructed with current context.
}

///ditto
@system .mir_rcptr!R createRCWithContext(R, F)(return const mir_rcptr!F context, return ref R value)
@system .mir_rcptr!R createRCWithContext(R, F)(return ref R value, return const mir_rcptr!F context)
if (!is(R == class) && !is(R == interface))
{
typeof(return) ret;
Expand All @@ -197,21 +199,24 @@ Provides polymorphism abilities for classes and structures with `alias this` syn
mir_rcptr!R castTo(R, T)(return mir_rcptr!T context) @trusted
if (isImplicitlyConvertible!(T, R))
{
return createRCWithContext(context, cast(R)context._get_value);
import core.lifetime: move;
return createRCWithContext(cast(R)context._get_value, move(context));
}

/// ditto
mir_rcptr!(const R) castTo(R, T)(return const mir_rcptr!T context) @trusted const
mir_rcptr!(const R) castTo(R, T)(return const mir_rcptr!T context) @trusted
if (isImplicitlyConvertible!(const T, const R))
{
return createRCWithContext(*cast(mir_rcptr!T*)&context, cast(const R)context._get_value);
import core.lifetime: move;
return createRCWithContext(cast(const R)context._get_value, move(*cast(mir_rcptr!T*)&context));
}

/// ditto
mir_rcptr!(immutable R) castTo(R, T)(return immutable mir_rcptr!T context) @trusted immutable
mir_rcptr!(immutable R) castTo(R, T)(return immutable mir_rcptr!T context) @trusted
if (isImplicitlyConvertible!(immutable T, immutable R))
{
return createRCWithContext(*cast(mir_rcptr!T*)&context, cast(immutable R)context._get_value);
import core.lifetime: move;
return createRCWithContext(cast(immutable R)context._get_value, move(*cast(mir_rcptr!T*)&context));
}


Expand Down Expand Up @@ -302,7 +307,7 @@ unittest

version(unittest):

private struct _test_unpure_system_dest_s__ {
package struct _test_unpure_system_dest_s__ {
static int numStructs;
int i;

Expand Down
Loading

0 comments on commit 65e0563

Please sign in to comment.