1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /**
6  * Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and
7  * $(D_PSYMBOL free).
8  *
9  * Copyright: Eugene Wissner 2017-2020.
10  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
11  *                  Mozilla Public License, v. 2.0).
12  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
13  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/mallocator.d,
14  *                 tanya/memory/mallocator.d)
15  */
16 module tanya.memory.mallocator;
17 
18 import core.stdc.stdlib;
19 import tanya.memory.allocator;
20 
21 /**
22  * Wrapper for $(D_PSYMBOL malloc)/$(D_PSYMBOL realloc)/$(D_PSYMBOL free) from
23  * the C standard library.
24  */
25 final class Mallocator : Allocator
26 {
27     private alias MallocType = extern (C) void* function(size_t)
28                                @nogc nothrow pure @system;
29     private alias FreeType = extern (C) void function(void*)
30                              @nogc nothrow pure @system;
31     private alias ReallocType = extern (C) void* function(void*, size_t)
32                                 @nogc nothrow pure @system;
33 
34     /**
35      * Allocates $(D_PARAM size) bytes of memory.
36      *
37      * Params:
38      *  size = Amount of memory to allocate.
39      *
40      * Returns: The pointer to the new allocated memory.
41      */
42     void[] allocate(size_t size) @nogc nothrow pure shared @system
43     {
44         if (size == 0)
45         {
46             return null;
47         }
48         auto p = (cast(MallocType) &malloc)(size + psize);
49 
50         return p is null ? null : p[psize .. psize + size];
51     }
52 
53     ///
54     @nogc nothrow pure @system unittest
55     {
56         auto p = Mallocator.instance.allocate(20);
57         assert(p.length == 20);
58         Mallocator.instance.deallocate(p);
59 
60         p = Mallocator.instance.allocate(0);
61         assert(p.length == 0);
62     }
63 
64     /**
65      * Deallocates a memory block.
66      *
67      * Params:
68      *  p = A pointer to the memory block to be freed.
69      *
70      * Returns: Whether the deallocation was successful.
71      */
72     bool deallocate(void[] p) @nogc nothrow pure shared @system
73     {
74         if (p !is null)
75         {
76             (cast(FreeType) &free)(p.ptr - psize);
77         }
78         return true;
79     }
80 
81     ///
82     @nogc nothrow pure @system unittest
83     {
84         void[] p;
85         assert(Mallocator.instance.deallocate(p));
86 
87         p = Mallocator.instance.allocate(10);
88         assert(Mallocator.instance.deallocate(p));
89     }
90 
91     /**
92      * Reallocating in place isn't supported.
93      *
94      * Params:
95      *  p    = A pointer to the memory block.
96      *  size = Size of the reallocated block.
97      *
98      * Returns: $(D_KEYWORD false).
99      */
100     bool reallocateInPlace(ref void[] p, size_t size)
101     @nogc nothrow pure shared @system
102     {
103         cast(void) size;
104         return false;
105     }
106 
107     ///
108     @nogc nothrow pure @system unittest
109     {
110         void[] p;
111         assert(!Mallocator.instance.reallocateInPlace(p, 8));
112     }
113 
114     /**
115      * Increases or decreases the size of a memory block.
116      *
117      * Params:
118      *  p    = A pointer to the memory block.
119      *  size = Size of the reallocated block.
120      *
121      * Returns: Whether the reallocation was successful.
122      */
123     bool reallocate(ref void[] p, size_t size)
124     @nogc nothrow pure shared @system
125     {
126         if (size == 0)
127         {
128             if (deallocate(p))
129             {
130                 p = null;
131                 return true;
132             }
133         }
134         else if (p is null)
135         {
136             p = allocate(size);
137             return p is null ? false : true;
138         }
139         else
140         {
141             auto r = (cast(ReallocType) &realloc)(p.ptr - psize, size + psize);
142 
143             if (r !is null)
144             {
145                 p = r[psize .. psize + size];
146                 return true;
147             }
148         }
149         return false;
150     }
151 
152     ///
153     @nogc nothrow pure @system unittest
154     {
155         void[] p;
156 
157         assert(Mallocator.instance.reallocate(p, 20));
158         assert(p.length == 20);
159 
160         assert(Mallocator.instance.reallocate(p, 30));
161         assert(p.length == 30);
162 
163         assert(Mallocator.instance.reallocate(p, 10));
164         assert(p.length == 10);
165 
166         assert(Mallocator.instance.reallocate(p, 0));
167         assert(p is null);
168     }
169 
170     /**
171      * Returns: The alignment offered.
172      */
173     @property uint alignment() const @nogc nothrow pure @safe shared
174     {
175         return (void*).alignof;
176     }
177 
178     static private shared(Mallocator) instantiate() @nogc nothrow @system
179     {
180         if (instance_ is null)
181         {
182             const size = __traits(classInstanceSize, Mallocator) + psize;
183             void* p = malloc(size);
184 
185             if (p !is null)
186             {
187                 p[psize .. size] = typeid(Mallocator).initializer[];
188                 instance_ = cast(shared Mallocator) p[psize .. size].ptr;
189             }
190         }
191         return instance_;
192     }
193 
194     /**
195      * Static allocator instance and initializer.
196      *
197      * Returns: The global $(D_PSYMBOL Allocator) instance.
198      */
199     static @property shared(Mallocator) instance() @nogc nothrow pure @system
200     {
201         return (cast(GetPureInstance!Mallocator) &instantiate)();
202     }
203 
204     ///
205     @nogc nothrow pure @system unittest
206     {
207         assert(instance is instance);
208     }
209 
210     private enum ushort psize = 8;
211 
212     private shared static Mallocator instance_;
213 }