1 /**
2     Utility functions for memory management
3 
4     Copyright: © 2012-2013 RejectedSoftware e.K.
5     		   © 2014-2015 Etienne Cimon
6     License: Subject to the terms of the MIT license.
7     Authors: Sönke Ludwig, Etienne Cimon
8 */
9 module memutils.allocators;
10 
11 public import memutils.constants;
12 import core.thread : Fiber;
13 import core.exception : OutOfMemoryError;
14 import core.stdc.stdlib;
15 import core.memory;
16 import core.sync.mutex;
17 import std.conv;
18 import std.exception : enforceEx;
19 import std.traits;
20 import std.algorithm;
21 import std.traits : ReturnType;
22 import memutils.hashmap : HashMap;
23 import memutils.pool;
24 import memutils.memory;
25 import memutils.debugger;
26 import memutils.cryptosafe;
27 import memutils.freelist;
28 import memutils.utils : Malloc;
29 import core.thread : thread_isMainThread;
30 
31 static if (HasDebugAllocations) {
32 	pragma(msg, "Memory debugger enabled");
33 	alias LocklessAllocator = DebugAllocator!(AutoFreeListAllocator!(MallocAllocator));
34 	static if (HasCryptoSafe)
35 		alias CryptoSafeAllocator = DebugAllocator!(SecureAllocator!(DebugAllocator!(AutoFreeListAllocator!(MallocAllocator))));
36 	alias ProxyGCAllocator = DebugAllocator!GCAllocator;
37 }
38 else {
39 	alias LocklessAllocator = AutoFreeListAllocator!(MallocAllocator);
40 	static if (HasCryptoSafe)
41 		alias CryptoSafeAllocator = SecureAllocator!LocklessAllocator;
42 	alias ProxyGCAllocator = GCAllocator;
43 }
44 
45 interface Allocator {
46 	enum size_t alignment = 0x10;
47 
48 	enum size_t alignmentMask = alignment-1;
49 	
50 	void[] alloc(size_t sz)
51 	out {
52 		static if (!HasSecurePool) assert((cast(size_t)__result.ptr & alignmentMask) == 0, "alloc() returned misaligned data.");
53 	}
54 
55 	void[] realloc(void[] mem, size_t new_sz)
56 	in {
57 		assert(mem.ptr !is null, "realloc() called with null array.");
58 		static if (!HasSecurePool) assert((cast(size_t)mem.ptr & alignmentMask) == 0, "misaligned pointer passed to realloc().");
59 	}
60 	out { static if (!HasSecurePool) assert((cast(size_t)__result.ptr & alignmentMask) == 0, "realloc() returned misaligned data."); }
61 
62 	void free(void[] mem)
63 	in {
64 		assert(mem.ptr !is null, "free() called with null array.");
65 		static if (!HasSecurePool) assert((cast(size_t)mem.ptr & alignmentMask) == 0, "misaligned pointer passed to free().");
66 	}
67 }
68 
69 package:
70 
71 public auto getAllocator(int ALLOC)() {
72 	static if (ALLOC == LocklessFreeList) alias R = LocklessAllocator;
73 	else static if (ALLOC == NativeGC) alias R = ProxyGCAllocator;
74 	else static if (HasCryptoSafe && ALLOC == CryptoSafe) alias R = CryptoSafeAllocator;
75 	else static if (ALLOC == Mallocator) alias R = MallocAllocator;
76 	else static assert(false, "Invalid allocator specified");
77 
78 	static if (ALLOC == NativeGC) {	
79 		static __gshared R alloc;
80 		
81 		if (!alloc) {
82 			alloc = new R;
83 		}
84 		return alloc;
85 	}
86 	else return getAllocator!R();
87 }
88 
89 R getAllocator(R)() {
90 	static R alloc;
91 	if (!alloc) {
92 		alloc = new R;
93 	}
94 	return alloc;
95 }
96 
97 static if (HasBotan)
98 shared static ~this() {
99    .exit(0); // TODO: Fix invariant error. Maybe this is due to an erroneous treap in GC
100 }
101 
102 version(LDC) shared static ~this() { .exit(0); } // LDC unit tests failing otherwise
103 
104 size_t alignedSize(size_t sz)
105 {
106 	return ((sz + Allocator.alignment - 1) / Allocator.alignment) * Allocator.alignment;
107 }
108 
109 void ensureValidMemory(void[] mem)
110 {
111 	auto bytes = cast(ubyte[])mem;
112 	swap(bytes[0], bytes[$-1]);
113 	swap(bytes[0], bytes[$-1]);
114 }
115 
116 void* extractUnalignedPointer(void* base)
117 {
118 	ubyte misalign = *(cast(const(ubyte)*)base-1);
119 	assert(misalign <= Allocator.alignment);
120 	return base - misalign;
121 }
122 
123 void* adjustPointerAlignment(void* base)
124 {
125 	ubyte misalign = Allocator.alignment - (cast(size_t)base & Allocator.alignmentMask);
126 	base += misalign;
127 	*(cast(ubyte*)base-1) = misalign;
128 	return base;
129 }
130 
131 template AllocSize(T)
132 {
133 	static if (is(T == class)) {
134 		// workaround for a strange bug where AllocSize!SSLStream == 0: TODO: dustmite!
135 		enum dummy = T.stringof ~ __traits(classInstanceSize, T).stringof;
136 		enum AllocSize = __traits(classInstanceSize, T);
137 	} else {
138 		enum AllocSize = T.sizeof;
139 	}
140 }
141 
142 template RefTypeOf(T) {
143 	static if( is(T == class) || __traits(isAbstractClass, T) || is(T == interface) ){
144 		alias RefTypeOf = T;
145 	} else {
146 		alias RefTypeOf = T*;
147 	}
148 }
149 
150 
151 unittest {
152 	void testAlign(void* p, size_t adjustment) {
153 		void* pa = adjustPointerAlignment(p);
154 		assert((cast(size_t)pa & Allocator.alignmentMask) == 0, "Non-aligned pointer.");
155 		assert(*(cast(const(ubyte)*)pa-1) == adjustment, "Invalid adjustment "~to!string(p)~": "~to!string(*(cast(const(ubyte)*)pa-1)));
156 		void* pr = extractUnalignedPointer(pa);
157 		assert(pr == p, "Recovered base != original");
158 	}
159 	void* ptr = .malloc(0x40);
160 	ptr += Allocator.alignment - (cast(size_t)ptr & Allocator.alignmentMask);
161 	testAlign(ptr++, 0x10);
162 	testAlign(ptr++, 0x0F);
163 	testAlign(ptr++, 0x0E);
164 	testAlign(ptr++, 0x0D);
165 	testAlign(ptr++, 0x0C);
166 	testAlign(ptr++, 0x0B);
167 	testAlign(ptr++, 0x0A);
168 	testAlign(ptr++, 0x09);
169 	testAlign(ptr++, 0x08);
170 	testAlign(ptr++, 0x07);
171 	testAlign(ptr++, 0x06);
172 	testAlign(ptr++, 0x05);
173 	testAlign(ptr++, 0x04);
174 	testAlign(ptr++, 0x03);
175 	testAlign(ptr++, 0x02);
176 	testAlign(ptr++, 0x01);
177 	testAlign(ptr++, 0x10);
178 }
179 
180 unittest {
181 	foreach( i; 0 .. 20 ){
182 		auto ia = alignedSize(i);
183 		assert(ia >= i);
184 		assert((ia & Allocator.alignmentMask) == 0);
185 		assert(ia < i+Allocator.alignment);
186 	}
187 }