1 module std.experimental.allocator.gc_allocator;
2 import std.experimental.allocator.common;
3 
4 /**
5 D's built-in garbage-collected allocator.
6  */
7 struct GCAllocator
8 {
9     import core.memory : GC;
10     unittest { testAllocator!(() => GCAllocator.instance); }
11 
12     /**
13     The alignment is a static constant equal to $(D platformAlignment), which
14     ensures proper alignment for any D data type.
15     */
16     enum uint alignment = platformAlignment;
17 
18     /**
19     Standard allocator methods per the semantics defined above. The $(D
20     deallocate) and $(D reallocate) methods are $(D @system) because they may
21     move memory around, leaving dangling pointers in user code.
22     */
23     @trusted void[] allocate(size_t bytes) shared
24     {
25         if (!bytes) return null;
26         auto p = GC.malloc(bytes);
27         return p ? p[0 .. bytes] : null;
28     }
29 
30     /// Ditto
31     @trusted bool expand(ref void[] b, size_t delta) shared
32     {
33         if (delta == 0) return true;
34         if (b is null)
35         {
36             b = allocate(delta);
37             return b.length == delta;
38         }
39         immutable desired = b.length + delta;
40         immutable newSize = GC.extend(b.ptr, desired, desired);
41         if (newSize == 0)
42         {
43             // expansion unsuccessful
44             return false;
45         }
46         assert(newSize >= desired);
47         b = b.ptr[0 .. desired];
48         return true;
49     }
50 
51     /// Ditto
52     @system bool reallocate(ref void[] b, size_t newSize) shared
53     {
54         import core.exception : OutOfMemoryError;
55         try
56         {
57             auto p = cast(ubyte*) GC.realloc(b.ptr, newSize);
58             b = p[0 .. newSize];
59         }
60         catch (OutOfMemoryError)
61         {
62             // leave the block in place, tell caller
63             return false;
64         }
65         return true;
66     }
67 
68     /// Ditto
69     void[] resolveInternalPointer(void* p) shared
70     {
71         auto r = GC.addrOf(p);
72         if (!r) return null;
73         return r[0 .. GC.sizeOf(r)];
74     }
75 
76     /// Ditto
77     @system bool deallocate(void[] b) shared
78     {
79         GC.free(b.ptr);
80         return true;
81     }
82 
83     /**
84     Returns the global instance of this allocator type. The garbage collected
85     allocator is thread-safe, therefore all of its methods and `instance` itself
86     are $(D shared).
87     */
88 
89     static shared GCAllocator instance;
90 
91     // Leave it undocummented for now.
92     @trusted void collect() shared
93     {
94         GC.collect();
95     }
96 }
97 
98 ///
99 unittest
100 {
101     auto buffer = GCAllocator.instance.allocate(1024 * 1024 * 4);
102     // deallocate upon scope's end (alternatively: leave it to collection)
103     scope(exit) GCAllocator.instance.deallocate(buffer);
104     //...
105 }
106 
107 unittest
108 {
109     auto b = GCAllocator.instance.allocate(10_000);
110     assert(GCAllocator.instance.expand(b, 1));
111 }