1 module yu.memory.allocator.smartgcalloctor;
2 
3 import std.experimental.allocator;
4 
5 struct SmartGCAllocator {
6     import core.memory : GC;
7 
8     enum uint alignment = platformAlignment;
9 
10     pure nothrow @trusted void[] allocate(size_t bytes) shared {
11         if (!bytes)
12             return null;
13         auto p = GC.malloc(bytes);
14         return p ? p[0 .. bytes] : null;
15     }
16 
17     @system bool expand(ref void[] b, size_t delta) shared {
18         if (delta == 0)
19             return true;
20         if (b is null)
21             return false;
22         immutable curLength = GC.sizeOf(b.ptr);
23         assert(curLength != 0); // we have a valid GC pointer here
24         immutable desired = b.length + delta;
25         if (desired > curLength) // check to see if the current block can't hold the data
26         {
27             immutable sizeRequest = desired - curLength;
28             immutable newSize = GC.extend(b.ptr, sizeRequest, sizeRequest);
29             if (newSize == 0) {
30                 // expansion unsuccessful
31                 return false;
32             }
33             assert(newSize >= desired);
34         }
35         b = b.ptr[0 .. desired];
36         return true;
37     }
38 
39     pure nothrow @system bool reallocate(ref void[] b, size_t newSize) shared {
40         import core.exception : OutOfMemoryError;
41 
42         try {
43             auto p = cast(ubyte*) GC.realloc(b.ptr, newSize);
44             b = p[0 .. newSize];
45         }
46         catch (OutOfMemoryError) {
47             // leave the block in place, tell caller
48             return false;
49         }
50         return true;
51     }
52 
53     pure nothrow void[] resolveInternalPointer(void* p) shared {
54         auto r = GC.addrOf(p);
55         if (!r)
56             return null;
57         return r[0 .. GC.sizeOf(r)];
58     }
59 
60     pure nothrow @system bool deallocate(void[] b) shared {
61         GC.free(b.ptr);
62         return true;
63     }
64 
65     size_t goodAllocSize(size_t n) shared {
66         if (n == 0)
67             return 0;
68         if (n <= 16)
69             return 16;
70 
71         import core.bitop : bsr;
72 
73         auto largestBit = bsr(n - 1) + 1;
74         if (largestBit <= 12) // 4096 or less
75             return size_t(1) << largestBit;
76 
77         // larger, we use a multiple of 4096.
78         return ((n + 4095) / 4096) * 4096;
79     }
80 
81     static shared SmartGCAllocator instance;
82 
83     nothrow @trusted void collect() shared {
84         GC.collect();
85     }
86 
87     auto make(T, A...)(auto ref A args) shared {
88         auto construct() {
89             static if (is(T == class) || is(T == struct))
90                 return new T(args);
91             else {
92                 import std.algorithm.comparison : max;
93                 import std.conv : emplace;
94 
95                 auto m = this.allocate(max(stateSize!T, 1));
96                 if (!m.ptr)
97                     return null;
98                 scope (failure) {
99                     () @trusted{ this.deallocate(m); }();
100                 }
101                 // Assume cast is safe as allocation succeeded for `stateSize!T`
102                 auto p = () @trusted{ return cast(T*) m.ptr; }();
103                 emplace!T(p, args);
104                 return p;
105             }
106         }
107 
108         return construct();
109     }
110 
111 }