1 module dparasail.memory;
2 
3 import std.traits : isPointer, isSomeFunction, ReturnType, isSafe;
4 import std.string : strip;
5 import core.lifetime : move;
6 import std.typecons : RefCounted, RefCountedAutoInitialize;
7 import std.traits : Unqual;
8 import core.atomic : atomicOp;
9 import parasail;
10 import core.stdc.stdlib : calloc, free;
11 
12 /// can we use @live for scope checking? 
13 enum dip1000Enabled = isSafe!((int x) => *&x);
14 
15 static if(dip1000Enabled)
16     pragma(msg, "Using -dip1000 for scope checking and safety");
17 
18 /// Template struct that performs reference
19 /// counting on htslib pointers and destroys with specified function
20 template ParasailMemory(T)
21 {
22     enum freeMix = strip(T.stringof,"_") ~ "_free";
23     mixin("alias destroyFun = "~freeMix~";");
24     static assert(isSomeFunction!destroyFun && is(ReturnType!destroyFun == void));
25     
26     struct SafeParasailPtr(T)
27     if(!isPointer!T)
28     {
29         @safe @nogc nothrow:
30 
31         /// data pointer
32         T * ptr;
33         /// reference counting
34         shared int* refct;
35         /// initialized?
36         bool initialized;
37 
38         /// ctor that respects scope
39         this(T * rawPtr) @trusted return scope
40         {
41             this.ptr = rawPtr;
42             this.refct = cast(shared int *) calloc(int.sizeof,1);
43             (*this.refct) = 1;
44             this.initialized = true;
45         }
46         
47         /// postblit that respects scope
48         this(this) @trusted return scope
49         {
50             if(initialized)atomicOp!"+="(*this.refct, 1);
51         }
52 
53         /// allow SafeHtslibPtr to be used as 
54         /// underlying ptr type
55         alias getRef this;
56 
57         /// get underlying data pointer
58         @property nothrow pure @nogc
59         ref inout(T*) getRef() inout return
60         {
61             return ptr;
62         }
63 
64         /// take ownership of underlying data pointer
65         @property nothrow pure @nogc
66         T* moveRef()
67         {
68             T * ptr;
69             move(this.getRef, ptr);
70             return ptr;
71         }
72         ~this() @trusted return scope
73         {
74             /// if destroy function return is void 
75             /// just destroy
76             /// else if int
77             /// destroy then check return value 
78             /// else don't compile
79             if(!this.initialized) return;
80             if(atomicOp!"-="(*this.refct, 1)) return;
81             if(this.ptr){
82                 free(cast(int*)this.refct);
83                 destroyFun(this.ptr);
84             }
85         }
86     }
87     alias ParasailMemory = SafeParasailPtr!T;
88 }
89 
90 /// reference counted bam1_t wrapper
91 /// can be used directly as a bam1_t *
92 alias ParasailMatrix = ParasailMemory!(parasail_matrix_t);
93 
94 /// reference counted bam_hdr_t wrapper
95 /// can be used directly as a bam_hdr_t *
96 alias ParasailProfile = ParasailMemory!(parasail_profile_t);
97 
98 /// reference counted bam_hdr_t wrapper
99 /// can be used directly as a bam_hdr_t *
100 alias ParasailResultPtr = ParasailMemory!(parasail_result_t);
101 
102 unittest
103 {
104     import std.range : chunks;
105     auto dna = parasail_matrix_lookup("dnafull");
106     auto rc1 = ParasailMatrix(parasail_matrix_copy(dna));
107     parasail_matrix_set_value(rc1, 2, 3, 0);
108     assert(rc1 != null);
109     // No more allocation, add just one extra reference count
110     auto rc2 = rc1;
111     // Reference semantics
112     parasail_matrix_set_value(rc1, 2, 3, 0);
113 
114     import std.stdio;
115     writeln(rc1.matrix[0..rc1.size * rc1.size].chunks(16));
116     assert(rc1.size == 16);
117     assert(rc1.matrix[2*rc1.size + 3] == 0);
118 }