1 /**
2 	Memory pool with destructors, useful for scoped allocators.
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.pool;
10 
11 import memutils.allocators;
12 import std.conv : emplace;
13 import std.algorithm : min, max;
14 import memutils.vector;
15 
16 final class PoolAllocator(Base : Allocator)
17 {
18 	public int id = -1; // intrusive ID used for ScopedPools
19 
20 	static align(8) struct Pool { Pool* next; void[] data; void[] remaining; }
21 
22 	private {
23 		Allocator m_baseAllocator;
24 		Pool* m_freePools;
25 		Pool* m_fullPools;
26 		Vector!(void delegate()) m_destructors;
27 		int m_pools;
28 	}
29 	public size_t m_poolSize;
30 	
31 	this(size_t pool_size = 64*1024)
32 	{
33 		m_poolSize = pool_size;
34 		m_baseAllocator = getAllocator!Base();
35 	}
36 
37 	void[] alloc(size_t sz)
38 	{
39 		auto aligned_sz = alignedSize(sz);
40 
41 		Pool* pprev = null;
42 		Pool* p = cast(Pool*)m_freePools;
43 		size_t i;
44 		while(i < m_pools && p && p.remaining.length < aligned_sz ) {
45 			pprev = p;
46 			p = p.next;
47 			i++;
48 		}
49 		
50 		if( !p || p.remaining.length == 0 || p.remaining.length < aligned_sz ) {
51 			auto pmem = m_baseAllocator.alloc(AllocSize!Pool);
52 			
53 			p = emplace!Pool(pmem);
54 			p.data = m_baseAllocator.alloc(max(aligned_sz, m_poolSize));
55 			p.remaining = p.data;
56 			p.next = cast(Pool*)m_freePools;
57 			m_freePools = p;
58 			m_pools++;
59 			pprev = null;
60 		}
61 		logTrace("0 .. ", aligned_sz, " but remaining: ", p.remaining.length);
62 		auto ret = p.remaining[0 .. aligned_sz];
63 		logTrace("p.remaining: ", aligned_sz, " .. ", p.remaining.length);
64 		p.remaining = p.remaining[aligned_sz .. $];
65 		if( !p.remaining.length ){
66 			if( pprev ) {
67 				pprev.next = p.next;
68 			} else {
69 				m_freePools = p.next;
70 			}
71 			p.next = cast(Pool*)m_fullPools;
72 			m_fullPools = p;
73 		}
74 		//logDebug("PoolAllocator ", id, " allocated ", sz, " with ", totalSize());
75 		
76 		return ret[0 .. sz];
77 	}
78 	
79 	void[] realloc(void[] arr, size_t newsize)
80 	{
81 		auto aligned_sz = alignedSize(arr.length);
82 		auto aligned_newsz = alignedSize(newsize);
83 		logTrace("realloc: ", arr.ptr, " sz ", arr.length, " aligned: ", aligned_sz, " => ", newsize, " aligned: ", aligned_newsz);
84 		if( aligned_newsz <= aligned_sz ) return arr.ptr[0 .. newsize];
85 		
86 		auto pool = m_freePools;
87 		bool last_in_pool = pool && arr.ptr+aligned_sz == pool.remaining.ptr;
88 		if( last_in_pool && pool.remaining.length+aligned_sz >= aligned_newsz ) {
89 			pool.remaining = pool.remaining[aligned_newsz-aligned_sz .. $];
90 			arr = arr.ptr[0 .. aligned_newsz];
91 			assert(arr.ptr+arr.length == pool.remaining.ptr, "Last block does not align with the remaining space!?");
92 			return arr[0 .. newsize];
93 		} else {
94 			auto ret = alloc(newsize);
95 			assert(ret.ptr >= arr.ptr+aligned_sz || ret.ptr+ret.length <= arr.ptr, "New block overlaps old one!?");
96 			ret[0 .. min(arr.length, newsize)] = arr[0 .. min(arr.length, newsize)];
97 			return ret;
98 		}
99 	}
100 	
101 	void free(void[] mem)
102 	{
103 
104 	}
105 	
106 	void freeAll()
107 	{
108 		//logDebug("Destroying ", totalSize(), " of data, allocated: ", allocatedSize());
109 		// destroy all initialized objects
110 		foreach_reverse (ref dtor; m_destructors[])
111 			dtor();
112 
113 		destroy(m_destructors);
114 
115 		size_t i;
116 		// put all full Pools into the free pools list
117 		for (Pool* p = cast(Pool*)m_fullPools, pnext; p && i < m_pools; (p = pnext), i++) {
118 			pnext = p.next;
119 			p.next = cast(Pool*)m_freePools;
120 			m_freePools = cast(Pool*)p;
121 		}
122 		i=0;
123 		// free up all pools
124 		for (Pool* p = cast(Pool*)m_freePools; p && i < m_pools; (p = p.next), i++) {
125 			p.remaining = p.data;
126 		}
127 	}
128 	
129 	void reset()
130 	{
131 		//logDebug("Reset()");
132 		freeAll();
133 		Pool* pnext;
134 		size_t i;
135 		for (auto p = cast(Pool*)m_freePools; p && i < m_pools; (p = pnext), i++) {
136 			pnext = p.next;
137 			m_baseAllocator.free(p.data);
138 			m_baseAllocator.free((cast(void*)p)[0 .. AllocSize!Pool]);
139 		}
140 		m_freePools = null;
141 		
142 	}
143 
144 	package void onDestroy(void delegate() dtor) {
145 		m_destructors ~= dtor;
146 	}
147 
148 	package void removeArrayDtors(void delegate() last_dtor, size_t n) {
149 		bool found;
150 		foreach_reverse(i, ref el; m_destructors[]) {
151 			if (el == last_dtor)
152 			{
153 				Vector!(void delegate()) arr;
154 				if (n >= i)
155 					arr ~= m_destructors[0 .. i-n+1];
156 				if (i != m_destructors.length - 1)
157 					arr ~= m_destructors[i+1 .. $];
158 				m_destructors[] = arr[];
159 				found = true;
160 				break;
161 			}
162 		}
163 		assert(found);
164 	}
165 
166 	@property size_t totalSize()
167 	{
168 		size_t amt = 0;
169 		size_t i;
170 		for (auto p = m_fullPools; p && i < m_pools; (p = p.next), i++)
171 			amt += p.data.length;
172 		i=0;
173 		for (auto p = m_freePools; p && i < m_pools; (p = p.next), i++)
174 			amt += p.data.length;
175 		return amt;
176 	}
177 	
178 	@property size_t allocatedSize()
179 	{
180 		size_t amt = 0;
181 		size_t i;
182 		for (auto p = m_fullPools; p && i < m_pools; (p = p.next), i++)
183 			amt += p.data.length;
184 		i = 0;
185 		for (auto p = m_freePools; p && i < m_pools; (p = p.next), i++)
186 			amt += p.data.length - p.remaining.length;
187 		return amt;
188 	}
189 
190 	~this() { reset(); }
191 }