1 module bc.core.memory; 2 3 import std.traits : hasIndirections, isDelegate, isFunctionPointer, isPointer, PointerTarget; 4 public import core.memory : pureFree; 5 6 version (D_Exceptions) 7 { 8 import core.exception : onOutOfMemoryError; 9 private enum allocationFailed = `onOutOfMemoryError();`; 10 } 11 else 12 { 13 private enum allocationFailed = `assert(0, "Memory allocation failed");`; 14 } 15 16 /** 17 * Allocates class or struct on the heap. 18 * It automatically emplaces it to tyhe allocated memory with provided args. 19 * On error, `onOutOfMemoryError` assert is called. 20 * 21 * When `gcRoot` is set, it also sets the memory range to be scanned by GC for pointers (off by default). 22 */ 23 auto heapAlloc(T, Args...) (Args args) @trusted 24 if (is(T == struct)) 25 { 26 pragma(inline); 27 import core.lifetime : emplace; 28 29 // allocate memory for the object 30 auto memory = enforceMalloc(T.sizeof); 31 32 // call T's constructor and emplace instance on newly allocated memory 33 return emplace!(T, Args)(memory[0..T.sizeof], args); 34 } 35 36 /** 37 * Deallocates `heapAlloc` allocated memory. 38 * It automatically calls the object destructor and removes it from GC scanning (no effect if not 39 * added there) 40 */ 41 void heapDealloc(T)(ref T obj) @trusted 42 if (isPointer!T && is(PointerTarget!T == struct)) 43 { 44 pragma(inline); 45 import std.traits : hasElaborateDestructor; 46 47 alias U = PointerTarget!T; 48 static if (hasElaborateDestructor!(PointerTarget!T)) destroy(*obj); 49 50 // free memory occupied by object 51 pureFree(cast(void*)obj); 52 obj = null; 53 } 54 55 @safe unittest 56 { 57 struct Foo 58 { 59 int i; 60 this(int i) { this.i = i; } 61 } 62 63 Foo* f = heapAlloc!Foo(42); 64 assert(f.i == 42); 65 f.heapDealloc(); 66 assert(f is null); 67 } 68 69 // NOTE: these are copy pasted from: https://github.com/dlang/phobos/blob/master/std/internal/memory.d 70 71 /+ 72 Mnemonic for `enforce!OutOfMemoryError(malloc(size))` that (unlike malloc) 73 can be considered pure because it causes the program to abort if the result 74 of the allocation is null, with the consequence that errno will not be 75 visibly changed by calling this function. Note that `malloc` can also 76 return `null` in non-failure situations if given an argument of 0. Hence, 77 it is a programmer error to use this function if the requested allocation 78 size is logically permitted to be zero. `enforceCalloc` and `enforceRealloc` 79 work analogously. 80 All these functions are usable in `betterC`. 81 +/ 82 void* enforceMalloc()(size_t size) @nogc nothrow pure @safe 83 { 84 auto result = fakePureMalloc(size); 85 if (!result) mixin(allocationFailed); 86 return result; 87 } 88 89 // ditto 90 void* enforceCalloc()(size_t nmemb, size_t size) @nogc nothrow pure @safe 91 { 92 auto result = fakePureCalloc(nmemb, size); 93 if (!result) mixin(allocationFailed); 94 return result; 95 } 96 97 // ditto 98 void* enforceRealloc()(void* ptr, size_t size) @nogc nothrow pure @system 99 { 100 auto result = fakePureRealloc(ptr, size); 101 if (!result) mixin(allocationFailed); 102 return result; 103 } 104 105 // Purified for local use only. 106 extern (C) @nogc nothrow pure private 107 { 108 pragma(mangle, "malloc") void* fakePureMalloc(size_t) @safe; 109 pragma(mangle, "calloc") void* fakePureCalloc(size_t nmemb, size_t size) @safe; 110 pragma(mangle, "realloc") void* fakePureRealloc(void* ptr, size_t size) @system; 111 }