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 }