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