Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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