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 }