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 }