1 module memutils.utils;
2 
3 import memutils.allocators;
4 import memutils.constants;
5 import memutils.vector : Array;
6 import memutils.helpers : UnConst, memset, memcpy;
7 import memutils.memory;
8 
9 import std.traits : hasMember, isPointer, hasIndirections, hasElaborateDestructor, isArray, ReturnType;
10 
11 struct ThreadMem {
12 	nothrow:
13 	@trusted:
14 	mixin ConvenienceAllocators!(LocklessFreeList, ThreadMem);
15 }
16 
17 // Reserved for containers
18 struct Malloc {
19 	enum ident = Mallocator;
20 }
21 // Reserved for containers
22 struct CTFE {
23 	
24 }
25 
26 // overloaded for AppMem, otherwise uses ThreadMem
27 @trusted extern(C) nothrow {
28     void[] FL_allocate(size_t n);
29 	void[] FL_reallocate(void[] mem, size_t n);
30 	void FL_deallocate(void[] mem);
31 }
32 /*
33 template PoolAllocator(T)
34 {
35 nothrow:
36 	enum ElemSize = AllocSize!T;
37 	Pool m_allocator;
38 
39 	this(Pool allocator) {
40 		m_allocator = allocator;
41 	}
42 
43 
44 	alias TR = RefTypeOf!T;
45 
46 	TR alloc(ARGS...)(auto ref ARGS args)
47 	{
48 		auto mem = m_allocator.alloc(ElemSize);
49 		TR omem = cast(TR)mem;
50 		
51 		logTrace("PoolObjectAllocator.alloc initialize");
52 		*omem = T(args);
53 		return omem;
54 
55 	}
56 
57 	void free(TR obj)
58 	{
59 
60 		TR objc = obj;
61 		import memutils.helpers : destructRecurse;
62 		static if (is(TR == T*) && hasElaborateDestructor!T) {
63 			logTrace("ObjectAllocator.free Pointer destr ", T.stringof);
64 			destructRecurse(*objc);
65 		} 
66 		else static if (hasElaborateDestructor!TR) {
67 			logTrace("ObjectAllocator.free other destr ", T.stringof);
68 			destructRecurse(objc);
69 		} 
70 
71 		m_allocator.free((cast(void*)obj)[0 .. ElemSize]);
72 
73 	}
74 }
75 */
76 
77 nothrow:
78 
79 struct ObjectAllocator(T, ALLOC = ThreadMem)
80 {
81 nothrow:
82 	enum ElemSize = AllocSize!T;
83 
84 	static if (ALLOC.stringof == "PoolStack") {
85 		ReturnType!(ALLOC.top) function() m_getAlloc = &ALLOC.top;
86 	} else static if (!hasMember!(ALLOC, "ident") && ALLOC.stringof != "void") {
87 		ALLOC* m_allocator;
88 		this(ALLOC* base) {
89 			m_allocator = base;
90 		}
91 	}
92 	enum NOGC = true;
93 
94 	alias TR = RefTypeOf!T;
95 	TR alloc(ARGS...)(auto ref ARGS args)
96 	{
97 		if (__ctfe) {
98 				assert(__ctfe);
99 				return new T(args);
100 		} else {
101 			static if (ALLOC.stringof == "PoolStack") {
102 				auto mem = m_getAlloc().alloc(ElemSize);
103 			}
104 			else static if (ALLOC.stringof == "void") {
105 				auto mem = FL_allocate(ElemSize);
106 			} else {
107 				static if (hasMember!(ALLOC, "ident"))
108 					auto allocator_ = getAllocator!(ALLOC.ident)(false);
109 				else
110 					auto allocator_ = m_allocator;
111 				auto mem = allocator_.alloc(ElemSize);
112 			}
113 			TR omem = cast(TR)mem;
114 			
115 			logTrace("ObjectAllocator.alloc initialize");
116 			*omem = T(args);
117 			return omem;
118 		}
119 
120 	}
121 
122 	void free(TR obj)
123 	{
124 
125 		if (!__ctfe) {
126 			TR objc = obj;
127 			import memutils.helpers : destructRecurse;
128 			static if (is(TR == T*) && hasElaborateDestructor!T) {
129 				logTrace("ObjectAllocator.free Pointer destr ", T.stringof);
130 				destructRecurse(*objc);
131 			} 
132 			else static if (hasElaborateDestructor!TR) {
133 				logTrace("ObjectAllocator.free other destr ", T.stringof);
134 				destructRecurse(objc);
135 			} 
136 
137 			static if (ALLOC.stringof == "CTFE") {
138 				return;
139 			} else static if (ALLOC.stringof == "PoolStack") {
140 				m_getAlloc().free((cast(void*)obj)[0 .. ElemSize]);
141 			}
142 			else static if (ALLOC.stringof == "void") {
143 				FL_deallocate((cast(void*)obj)[0 .. ElemSize]);
144 			}
145 			else {
146 				static if (hasMember!(ALLOC, "ident"))
147 					auto a = getAllocator!(ALLOC.ident)(true);
148 				else
149 					auto a = m_allocator;
150 				a.free((cast(void*)obj)[0 .. ElemSize]);
151 			}
152 		}
153 
154 	}
155 }
156 
157 /// Allocates an array without touching the memory.
158 T[] allocArray(T, ALLOC = ThreadMem)(size_t n, ALLOC* base = null)
159 {
160 	static enum TSize = T.sizeof;
161 
162 	if (__ctfe) {
163 		assert(__ctfe);
164 		return new T[n];
165 	} else {
166 		static if (ALLOC.stringof == "void") {
167 			auto mem = FL_allocate(TSize * n);
168 			return (cast(T*)mem.ptr)[0 .. n];
169 		} else {
170 			static if (ALLOC.stringof == "PoolStack")
171 				auto allocator = ALLOC.top;
172 			else static if (hasMember!(ALLOC, "ident")) 
173 				auto allocator = getAllocator!(ALLOC.ident)(false);
174 			else static if (ALLOC.stringof != "void")
175 				auto allocator = base;
176 			auto mem = allocator.alloc(TSize * n);
177 			// logTrace("alloc ", T.stringof, ": ", mem.ptr);
178 			auto ret = (cast(T*)mem.ptr)[0 .. n];
179 			// logTrace("alloc ", ALLOC.stringof, ": ", mem.ptr, ":", mem.length);
180 
181 			// don't touch the memory - all practical uses of this function will handle initialization.
182 			return ret;
183 		}
184 	}
185 	
186 }
187 
188 T[] reallocArray(T, ALLOC = ThreadMem)(T[] array, size_t n, ALLOC* base = null) {
189 	static enum TSize = T.sizeof;
190 	assert(n > array.length, "Cannot reallocate to smaller sizes");
191 	
192 	if (__ctfe) {
193 		assert(__ctfe);
194 		auto arr = new T[n];
195 		arr[0 .. array.length] = array[0 .. $];
196 		return arr;
197 	} else {
198 		static if (ALLOC.stringof == "void") {
199 			auto mem = FL_reallocate((cast(void*)array.ptr)[0 .. array.length * TSize], TSize * n);
200 			return (cast(T*)mem.ptr)[0 .. n];
201 		} else {
202 			static if (ALLOC.stringof == "PoolStack")
203 				auto allocator = ALLOC.top;
204 			else static if (hasMember!(ALLOC, "ident")) 
205 				auto allocator = getAllocator!(ALLOC.ident)(false);
206 			else
207 				auto allocator = base;
208 
209 			// logTrace("realloc before ", ALLOC.stringof, ": ", cast(void*)array.ptr, ":", array.length);
210 
211 			//logTrace("realloc fre ", T.stringof, ": ", array.ptr);
212 			auto mem = allocator.realloc((cast(void*)array.ptr)[0 .. array.length * TSize], TSize * n);
213 			//logTrace("realloc ret ", T.stringof, ": ", mem.ptr);
214 			auto ret = (cast(T*)mem.ptr)[0 .. n];
215 			// logTrace("realloc after ", ALLOC.stringof, ": ", mem.ptr, ":", mem.length);
216 
217 				
218 			return ret;
219 		}
220 	}
221 }
222 
223 nothrow void freeArray(T, ALLOC = ThreadMem)(auto ref T[] array, size_t max_destroy = size_t.max, size_t offset = 0, ALLOC* base = null)
224 {
225 	static enum TSize = T.sizeof;
226 	
227 	static if (hasElaborateDestructor!T) { // calls destructors, but not for indirections...
228 		size_t i;
229 		foreach (ref e; array) {
230 			if (i < offset) { i++; continue; }
231 			if (i + offset == max_destroy) break;
232 			import memutils.helpers : destructRecurse;
233 			destructRecurse(e);
234 			i++;
235 		}
236 	}
237 
238 	static if (ALLOC.stringof == "void") {
239 		FL_deallocate((cast(void*)array.ptr)[0 .. array.length * TSize]);
240 	} 
241 	else static if (ALLOC.stringof != "CTFE") {
242 		static if (ALLOC.stringof == "PoolStack")
243 			auto allocator = ALLOC.top;
244 		else static if (hasMember!(ALLOC, "ident")) {
245 			auto allocator = getAllocator!(ALLOC.ident)(true);
246 			if (allocator == typeof(allocator).init) return;
247 		} else {
248 			auto allocator = base;
249 		}
250 
251 		allocator.free((cast(void*)array.ptr)[0 .. array.length * TSize]);
252 	}
253 
254 	array = null;
255 
256 }
257 
258 mixin template ConvenienceAllocators(alias ALLOC, alias THIS) {
259 	package enum ident = ALLOC;
260 nothrow:
261 static:
262 	// objects
263 	auto alloc(T, ARGS...)(auto ref ARGS args) 
264 		if (!isArray!T)
265 	{
266 		return ObjectAllocator!(T, THIS)().alloc(args);
267 	}
268 	
269 	void free(T)(auto ref T* obj)
270 		if (!isArray!T && !is(T : Object))
271 	{
272 		scope(exit) obj = null;
273 		ObjectAllocator!(T, THIS)().free(obj);
274 	}
275 	
276 	void free(T)(auto ref T obj)
277 		if (!isArray!T && is(T  : Object))
278 	{
279 		scope(exit) obj = null;
280 		ObjectAllocator!(T, THIS)().free(obj);
281 	}
282 
283 	/// arrays
284 	auto alloc(T)(size_t n)
285 		if (isArray!T)
286 	{
287 		static if (is(T == void[])) {
288 			return getAllocator!ALLOC().alloc(n);
289 		} else {
290 			alias ElType = UnConst!(typeof(T.init[0]));
291 			return allocArray!(ElType, THIS)(n);
292 		}
293 	}
294 
295 	auto copy(T)(auto ref T arr)
296 		if (isArray!T)
297 	{
298 		alias ElType = UnConst!(typeof(arr[0]));
299 		enum ElSize = ElType.sizeof;
300 		auto arr_copy = allocArray!(ElType, THIS)(arr.length);
301 		memcpy(arr_copy.ptr, arr.ptr, arr.length * ElSize);
302 
303 		return cast(T)arr_copy;
304 	}
305 
306 	auto realloc(T)(auto ref T arr, size_t n, bool zeroise_mem = true)
307 		if (isArray!T)
308 	{
309 		static if (is(T == void[])) {
310 			return getAllocator!ALLOC().realloc(arr, n, zeroise_mem);
311 		} else {
312 			alias ElType = UnConst!(typeof(arr[0]));
313 			scope(exit) arr = null;
314 			auto arr_copy = reallocArray!(typeof(arr[0]), THIS)(arr, n);
315 			return cast(T) arr_copy;
316 		}
317 	}
318 	
319 	void free(T)(auto ref T arr, bool zeroise_mem = false)
320 		if (isArray!T)
321 	{
322 		static if (is(T == void[])) {
323 			return getAllocator!ALLOC().free(arr, zeroise_mem);
324 		} else {
325 			alias ElType = typeof(arr[0]);
326 			scope(exit) arr = null;
327 			freeArray!(ElType, THIS)(arr);
328 		}
329 	}
330 
331 }