1 // Written in the D programming language.
2 /**
3 This module provides high-level interface for mi-mallocator
4 Copyright: Copyright 2019 Ernesto Castellotti <erny.castell@gmail.com>
5 License:   $(HTTP https://www.mozilla.org/en-US/MPL/2.0/, Mozilla Public License - Version 2.0).
6 Authors:   $(HTTP github.com/ErnyTech, Ernesto Castellotti)
7 */
8 module neomimalloc.mimallocator;
9 
10 /**
11  * High-level interface for mimalloc.
12  */
13 struct Mimallocator {
14     import std.experimental.allocator.common : platformAlignment;
15     import std.typecons : Ternary;
16 
17     /**
18      * Returns the global instance of this allocator type.
19      * Mimallocator is thread-safe, all methods are shared.
20      */
21     static shared Mimallocator instance;
22 
23     /**
24      * The alignment is a static constant equal to `platformAlignment`, which
25      * ensures proper alignment for any D data type.
26      */    
27     enum uint alignment = platformAlignment;
28 
29     /**
30      * Return the memory size that will be allocated asking for the minimum required size.
31      *
32      * Params:
33      *      size = The minimal required size in bytes.
34      *
35      * Returns:
36      *      the size n that will be allocated, where n >= size.
37      */
38     @trusted @nogc nothrow size_t goodAllocSize(size_t size) shared {
39         import neomimalloc.c.mimalloc : mi_good_size;
40 
41         return mi_good_size(size);
42     }
43 
44     /**
45      * Allocates the size expressed in bytes.
46      *
47      * Params:
48      *      bytes = Number of bytes to allocate.
49      *
50      * Returns:
51      *      An array with allocated memory or null if out of memory. Returns null if called with size 0.
52      */
53     @trusted @nogc pure nothrow void[] allocate(size_t bytes) shared {
54         import neomimalloc.c.mimalloc : mi_malloc;
55 
56         if (bytes == 0) {
57             return null;
58         }
59 
60         auto p = mi_malloc(bytes);
61         return p ? p[0 .. bytes] : null;
62     }
63 
64     /**
65      * Allocates the size expressed in bytes aligned by alignment.
66      *
67      * Params:
68      *      bytes = Number of bytes to allocate.
69      *      alignment = the minimal alignment of the allocated memory.
70      *
71      * Returns:
72      *      An array with aligned allocated memory or null if out of memory. Returns null if called with size 0.
73      */
74     @trusted @nogc pure nothrow void[] alignedAllocate(size_t bytes, uint alignment) shared {
75         import neomimalloc.c.mimalloc : mi_malloc_aligned;
76 
77         if (bytes == 0) {
78             return null;
79         }
80 
81         auto p = mi_malloc_aligned(bytes, alignment);
82         return p ? p[0 .. bytes] : null;
83     }
84 
85     /**
86      * Expands the array by increasing its length with the required delta.
87      *
88      * Params:
89      *      b = The array to be expanded (old size + delta).
90      *      delta = The dimension to be increased.
91      *
92      * Returns:
93      *   true if the expansion was successful, false if the array is null or the allocator has failed.  
94      */
95     @system @nogc pure nothrow bool expand(ref void[] b, size_t delta) shared {
96         import neomimalloc.c.mimalloc : mi_expand;
97 
98         if (delta == 0) {
99             return true;
100         }
101 
102         if (b is null) {
103             return false;
104         }
105 
106         auto newSize = b.length + delta;
107         auto p = cast(ubyte*) mi_expand(b.ptr, newSize);
108 
109         if (!p) {
110             return false;
111         }
112         
113         b = p[0 .. newSize];
114         return true;
115     }
116 
117     /**
118      * Re-allocate memory to newsize bytes.
119      *
120      * Params:
121      *      b = The array to be reallocated.
122      *      newSize = The new size that the array will take.
123      *
124      * Returns:
125      *   true if the reallocatiom was successful, false if the allocator has failed. 
126      */
127     @system @nogc pure nothrow bool reallocate(ref void[] b, size_t newSize) shared {
128         import neomimalloc.c.mimalloc : mi_realloc;
129 
130         auto p = cast(ubyte*) mi_realloc(b.ptr, newSize);
131 
132         if (!p) {
133             return false;
134         }
135 
136         b = p[0 .. newSize];
137         return true;
138     }
139 
140     /**
141      * Re-allocate memory to newsize bytes aligned by alignment.
142      *
143      * Params:
144      *      b = The array to be reallocated.
145      *      newSize = The new size that the array will take.
146      *      alignment = The minimal alignment of the allocated memory.
147      *
148      * Returns:
149      *   true if the reallocatiom was successful, false if the allocator has failed   
150      */
151     @system @nogc pure nothrow bool alignedReallocate(ref void[] b, size_t newSize, uint alignment) shared {
152         import neomimalloc.c.mimalloc : mi_realloc_aligned;
153 
154         auto p = cast(ubyte*) mi_realloc_aligned(b.ptr, newSize, alignment);
155 
156         if (!p) {
157             return false;
158         }
159 
160         b = p[0 .. newSize];
161         return true;
162     }
163 
164     /**
165      * Checks if the memory has been allocated by this allocator.
166      *
167      * Params:
168      *      b = The array to be verified.
169      *
170      * Returns:
171      *   Ternary.yes if the memory has been allocated by this allocator or Ternary.no if the memory is managed to other allocators.   
172      */
173     @trusted @nogc pure nothrow Ternary owns(const void[] b) shared {
174         auto result = implIsOwn(b.ptr);
175 
176         if (result) {
177             return Ternary.yes;
178         } else {
179             return Ternary.no;
180         }
181     }
182 
183     /**
184      * Resolves a pointer to get the full memory block.
185      *
186      * Params:
187      *      p = The pointer to resolve.
188      *      result = The array with the full memory block.
189      *
190      * Returns:
191      *   Ternary.no if the memory is managed to other allocators otherwise Ternary.yes.   
192      */
193     @trusted @nogc pure nothrow Ternary resolveInternalPointer(const void* p, ref void[] result) shared {
194         import neomimalloc.c.mimalloc : mi_check_owned;
195         import neomimalloc.c.mimalloc : mi_usable_size;
196 
197         auto pIsOwn = implIsOwn(p);
198 
199         if (!pIsOwn) {
200             result = null;
201             return Ternary.no;
202         }
203 
204         auto sizeOfP = mi_usable_size(p);
205         result = (cast(void*) p)[0 .. sizeOfP];
206         return Ternary.yes;
207     }
208 
209     /**
210      * Deallocate the specified memory block
211      *
212      * Params:
213      *      b = The memory block to be deallocated.
214      *
215      * Returns:
216      *   false if the array is null, otherwise true. 
217      */
218     @system @nogc pure nothrow bool deallocate(void[] b) shared {
219         import neomimalloc.c.mimalloc : mi_free;
220 
221         if (b is null) {
222             return true;
223         }
224 
225         mi_free(b.ptr);
226         return true;
227     }
228 
229     private @trusted @nogc pure nothrow bool implIsOwn(const void* p) shared {
230         import neomimalloc.c.mimalloc : mi_check_owned;
231 
232         if (!p) {
233             return false;
234         }
235 
236         return mi_check_owned(p);
237     }
238 }