1 //
2 // MessagePack for C++ memory pool
3 //
4 // Copyright (C) 2008-2013 FURUHASHI Sadayuki and KONDO Takatoshi
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // (See accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 //
10 #ifndef MSGPACK_CPP11_ZONE_HPP
11 #define MSGPACK_CPP11_ZONE_HPP
12
13 #include "msgpack/versioning.hpp"
14 #include "msgpack/cpp_config.hpp"
15 #include "msgpack/zone_decl.hpp"
16
17 #include <cstdlib>
18 #include <memory>
19 #include <vector>
20
21 namespace msgpack {
22
23 /// @cond
MSGPACK_API_VERSION_NAMESPACE(v1)24 MSGPACK_API_VERSION_NAMESPACE(v1) {
25 /// @endcond
26
27 class zone {
28 private:
29 struct finalizer {
30 finalizer(void (*func)(void*), void* data):m_func(func), m_data(data) {}
31 void operator()() { m_func(m_data); }
32 void (*m_func)(void*);
33 void* m_data;
34 };
35 struct finalizer_array {
36 finalizer_array():m_tail(MSGPACK_NULLPTR), m_end(MSGPACK_NULLPTR), m_array(MSGPACK_NULLPTR) {}
37 void call() {
38 finalizer* fin = m_tail;
39 for(; fin != m_array; --fin) (*(fin-1))();
40 }
41 ~finalizer_array() {
42 call();
43 ::free(m_array);
44 }
45 void clear() {
46 call();
47 m_tail = m_array;
48 }
49 void push(void (*func)(void* data), void* data)
50 {
51 finalizer* fin = m_tail;
52
53 if(fin == m_end) {
54 push_expand(func, data);
55 return;
56 }
57
58 fin->m_func = func;
59 fin->m_data = data;
60
61 ++m_tail;
62 }
63 void push_expand(void (*func)(void*), void* data) {
64 const size_t nused = m_end - m_array;
65 size_t nnext;
66 if(nused == 0) {
67 nnext = (sizeof(finalizer) < 72/2) ?
68 72 / sizeof(finalizer) : 8;
69 } else {
70 nnext = nused * 2;
71 }
72 finalizer* tmp =
73 static_cast<finalizer*>(::realloc(m_array, sizeof(finalizer) * nnext));
74 if(!tmp) {
75 throw std::bad_alloc();
76 }
77 m_array = tmp;
78 m_end = tmp + nnext;
79 m_tail = tmp + nused;
80 new (m_tail) finalizer(func, data);
81
82 ++m_tail;
83 }
84 finalizer_array(finalizer_array&& other) noexcept
85 :m_tail(other.m_tail), m_end(other.m_end), m_array(other.m_array)
86 {
87 other.m_tail = MSGPACK_NULLPTR;
88 other.m_end = MSGPACK_NULLPTR;
89 other.m_array = MSGPACK_NULLPTR;
90 }
91 finalizer_array& operator=(finalizer_array&& other) noexcept
92 {
93 this->~finalizer_array();
94 new (this) finalizer_array(std::move(other));
95 return *this;
96 }
97
98 finalizer* m_tail;
99 finalizer* m_end;
100 finalizer* m_array;
101
102 private:
103 finalizer_array(const finalizer_array&);
104 finalizer_array& operator=(const finalizer_array&);
105 };
106 struct chunk {
107 chunk* m_next;
108 };
109 struct chunk_list {
110 chunk_list(size_t chunk_size)
111 {
112 chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + chunk_size));
113 if(!c) {
114 throw std::bad_alloc();
115 }
116
117 m_head = c;
118 m_free = chunk_size;
119 m_ptr = reinterpret_cast<char*>(c) + sizeof(chunk);
120 c->m_next = MSGPACK_NULLPTR;
121 }
122 ~chunk_list()
123 {
124 chunk* c = m_head;
125 while(c) {
126 chunk* n = c->m_next;
127 ::free(c);
128 c = n;
129 }
130 }
131 void clear(size_t chunk_size)
132 {
133 chunk* c = m_head;
134 while(true) {
135 chunk* n = c->m_next;
136 if(n) {
137 ::free(c);
138 c = n;
139 } else {
140 m_head = c;
141 break;
142 }
143 }
144 m_head->m_next = MSGPACK_NULLPTR;
145 m_free = chunk_size;
146 m_ptr = reinterpret_cast<char*>(m_head) + sizeof(chunk);
147 }
148 chunk_list(chunk_list&& other) noexcept
149 :m_free(other.m_free), m_ptr(other.m_ptr), m_head(other.m_head)
150 {
151 other.m_head = MSGPACK_NULLPTR;
152 }
153 chunk_list& operator=(chunk_list&& other) noexcept
154 {
155 this->~chunk_list();
156 new (this) chunk_list(std::move(other));
157 return *this;
158 }
159
160 size_t m_free;
161 char* m_ptr;
162 chunk* m_head;
163 private:
164 chunk_list(const chunk_list&);
165 chunk_list& operator=(const chunk_list&);
166 };
167 size_t m_chunk_size;
168 chunk_list m_chunk_list;
169 finalizer_array m_finalizer_array;
170
171 public:
172 zone(size_t chunk_size = MSGPACK_ZONE_CHUNK_SIZE) noexcept;
173
174 public:
175 void* allocate_align(size_t size, size_t align = MSGPACK_ZONE_ALIGN);
176 void* allocate_no_align(size_t size);
177
178 void push_finalizer(void (*func)(void*), void* data);
179
180 template <typename T>
181 void push_finalizer(msgpack::unique_ptr<T> obj);
182
183 void clear();
184
185 void swap(zone& o);
186
187 static void* operator new(std::size_t size)
188 {
189 void* p = ::malloc(size);
190 if (!p) throw std::bad_alloc();
191 return p;
192 }
193 static void operator delete(void *p) noexcept
194 {
195 ::free(p);
196 }
197 static void* operator new(std::size_t /*size*/, void* mem) noexcept
198 {
199 return mem;
200 }
201 static void operator delete(void * /*p*/, void* /*mem*/) noexcept
202 {
203 }
204
205 template <typename T, typename... Args>
206 T* allocate(Args... args);
207
208 zone(zone&&) = default;
209 zone& operator=(zone&&) = default;
210 zone(const zone&) = delete;
211 zone& operator=(const zone&) = delete;
212
213 private:
214 void undo_allocate(size_t size);
215
216 template <typename T>
217 static void object_destruct(void* obj);
218
219 template <typename T>
220 static void object_delete(void* obj);
221
222 static char* get_aligned(char* ptr, size_t align);
223
224 char* allocate_expand(size_t size);
225 };
226
227 inline zone::zone(size_t chunk_size) noexcept:m_chunk_size(chunk_size), m_chunk_list(m_chunk_size)
228 {
229 }
230
231 inline char* zone::get_aligned(char* ptr, size_t align)
232 {
233 return
234 reinterpret_cast<char*>(
235 reinterpret_cast<size_t>(
236 (ptr + (align - 1))) / align * align);
237 }
238
239 inline void* zone::allocate_align(size_t size, size_t align)
240 {
241 char* aligned = get_aligned(m_chunk_list.m_ptr, align);
242 size_t adjusted_size = size + (aligned - m_chunk_list.m_ptr);
243 if (m_chunk_list.m_free < adjusted_size) {
244 size_t enough_size = size + align - 1;
245 char* ptr = allocate_expand(enough_size);
246 aligned = get_aligned(ptr, align);
247 adjusted_size = size + (aligned - m_chunk_list.m_ptr);
248 }
249 m_chunk_list.m_free -= adjusted_size;
250 m_chunk_list.m_ptr += adjusted_size;
251 return aligned;
252 }
253
254 inline void* zone::allocate_no_align(size_t size)
255 {
256 char* ptr = m_chunk_list.m_ptr;
257 if(m_chunk_list.m_free < size) {
258 ptr = allocate_expand(size);
259 }
260 m_chunk_list.m_free -= size;
261 m_chunk_list.m_ptr += size;
262
263 return ptr;
264 }
265
266 inline char* zone::allocate_expand(size_t size)
267 {
268 chunk_list* const cl = &m_chunk_list;
269
270 size_t sz = m_chunk_size;
271
272 while(sz < size) {
273 size_t tmp_sz = sz * 2;
274 if (tmp_sz <= sz) {
275 sz = size;
276 break;
277 }
278 sz = tmp_sz;
279 }
280
281 chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + sz));
282 if (!c) throw std::bad_alloc();
283
284 char* ptr = reinterpret_cast<char*>(c) + sizeof(chunk);
285
286 c->m_next = cl->m_head;
287 cl->m_head = c;
288 cl->m_free = sz;
289 cl->m_ptr = ptr;
290
291 return ptr;
292 }
293
294 inline void zone::push_finalizer(void (*func)(void*), void* data)
295 {
296 m_finalizer_array.push(func, data);
297 }
298
299 template <typename T>
300 inline void zone::push_finalizer(msgpack::unique_ptr<T> obj)
301 {
302 m_finalizer_array.push(&zone::object_delete<T>, obj.release());
303 }
304
305 inline void zone::clear()
306 {
307 m_finalizer_array.clear();
308 m_chunk_list.clear(m_chunk_size);
309 }
310
311 inline void zone::swap(zone& o)
312 {
313 std::swap(*this, o);
314 }
315
316 template <typename T>
317 void zone::object_delete(void* obj)
318 {
319 delete static_cast<T*>(obj);
320 }
321
322 template <typename T>
323 void zone::object_destruct(void* obj)
324 {
325 static_cast<T*>(obj)->~T();
326 }
327
328 inline void zone::undo_allocate(size_t size)
329 {
330 m_chunk_list.m_ptr -= size;
331 m_chunk_list.m_free += size;
332 }
333
334
335 template <typename T, typename... Args>
336 T* zone::allocate(Args... args)
337 {
338 void* x = allocate_align(sizeof(T), MSGPACK_ZONE_ALIGNOF(T));
339 try {
340 m_finalizer_array.push(&zone::object_destruct<T>, x);
341 } catch (...) {
342 undo_allocate(sizeof(T));
343 throw;
344 }
345 try {
346 return new (x) T(args...);
347 } catch (...) {
348 --m_finalizer_array.m_tail;
349 undo_allocate(sizeof(T));
350 throw;
351 }
352 }
353
354 inline std::size_t aligned_size(
355 std::size_t size,
356 std::size_t align) {
357 return (size + align - 1) / align * align;
358 }
359
360 /// @cond
361 } // MSGPACK_API_VERSION_NAMESPACE(v1)
362 /// @endcond
363
364 } // namespace msgpack
365
366 #endif // MSGPACK_CPP11_ZONE_HPP
367