1 module tame.nogc; 2 3 import core.stdc.string : strdup, memcpy, strlen; 4 import core.stdc.stdlib : malloc, free; 5 import std.exception : assumeUnique; 6 import std.traits; 7 8 // Edited From https://github.com/AuburnSounds/Dplug/blob/master/core/dplug/core/nogc.d 9 10 // This module provides many utilities to deal with @nogc nothrow, in a situation with the runtime disabled. 11 12 version (LDC) { 13 pragma(LDC_no_moduleinfo); 14 } 15 16 // 17 // Fake @nogc 18 // 19 20 debug { 21 auto assumeNoGC(T)(T t) { 22 static if (isFunctionPointer!T || isDelegate!T) { 23 enum attrs = functionAttributes!T | FunctionAttribute.nogc; 24 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs))t; 25 } else 26 static assert(0); 27 } 28 29 auto assumeNothrowNoGC(T)(T t) { 30 static if (isFunctionPointer!T || isDelegate!T) { 31 enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_; 32 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs))t; 33 } else 34 static assert(0); 35 } 36 37 unittest { 38 void funcThatDoesGC() { 39 int a = 4; 40 int[] _ = [a, a, a]; 41 } 42 43 void anotherFunction() nothrow @nogc { 44 assumeNothrowNoGC(() { funcThatDoesGC(); })(); 45 } 46 47 void aThirdFunction() @nogc { 48 assumeNoGC(() { funcThatDoesGC(); })(); 49 } 50 } 51 52 } 53 54 nothrow @nogc: 55 56 /// Allocates a slice with `malloc`. 57 T[] mallocSlice(T)(size_t count) { 58 T[] slice = mallocSliceNoInit!T(count); 59 static if (is(T == struct)) { 60 // we must avoid calling struct destructors with uninitialized memory 61 for (size_t i = 0; i < count; ++i) { 62 T uninitialized; 63 memcpy(&slice[i], &uninitialized, T.sizeof); 64 } 65 } else 66 slice[0 .. count] = T.init; 67 return slice; 68 } 69 70 /// Allocates a slice with `malloc`, but does not initialize the content. 71 T[] mallocSliceNoInit(T)(size_t count) { 72 T* p = cast(T*)malloc(count * T.sizeof); 73 return p[0 .. count]; 74 } 75 76 /// Frees a slice allocated with `mallocSlice`. 77 void freeSlice(T)(const(T)[] slice) { 78 free(cast(void*)slice.ptr); // const cast here 79 } 80 81 /// Duplicates a slice with `malloc`. Equivalent to `.dup` 82 /// Has to be cleaned-up with `free(slice.ptr)` or `freeSlice(slice)`. 83 T[] mallocDup(T)(const(T)[] slice) if (!is(T == struct)) { 84 T[] copy = mallocSliceNoInit!T(slice.length); 85 memcpy(copy.ptr, slice.ptr, slice.length * T.sizeof); 86 return copy; 87 } 88 89 /// Duplicates a slice with `malloc`. Equivalent to `.idup` 90 /// Has to be cleaned-up with `free(slice.ptr)` or `freeSlice(slice)`. 91 immutable(T)[] mallocIDup(T)(const(T)[] slice) if (!is(T == struct)) { 92 return assumeUnique(mallocDup!T(slice)); 93 } 94 95 /// Duplicates a zero-terminated string with `malloc`, return a `char[]`. Equivalent to `.dup` 96 /// Has to be cleaned-up with `free(s.ptr)`. 97 /// Note: The zero-terminating byte is preserved. This allow to have a string which also can be converted 98 /// to a C string with `.ptr`. However the zero byte is not included in slice length. 99 char[] stringDup(const(char)* cstr) { 100 assert(cstr !is null); 101 size_t len = strlen(cstr); 102 char* copy = strdup(cstr); 103 return copy[0 .. len]; 104 } 105 106 char[] stringDup(string str) => stringDup(str.ptr); 107 108 /// Duplicates a zero-terminated string with `malloc`, return a `string`. Equivalent to `.idup` 109 /// Has to be cleaned-up with `free(s.ptr)`. 110 /// Note: The zero-terminating byte is preserved. This allow to have a string which also can be converted 111 /// to a C string with `.ptr`. However the zero byte is not included in slice length. 112 string stringIDup(const(char)* cstr) { 113 return assumeUnique(stringDup(cstr)); 114 }