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 }