1//
2// MessagePack for C++ memory pool
3//
4// Copyright (C) 2008-2010 FURUHASHI Sadayuki
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_CPP03_ZONE_HPP
11#define MSGPACK_CPP03_ZONE_HPP
12
13#include <cstdlib>
14#include <memory>
15#include <vector>
16
17#include "msgpack/versioning.hpp"
18
19#ifndef MSGPACK_ZONE_CHUNK_SIZE
20#define MSGPACK_ZONE_CHUNK_SIZE 8192
21#endif
22
23#ifndef MSGPACK_ZONE_ALIGN
24#define MSGPACK_ZONE_ALIGN sizeof(void*)
25#endif
26
27
28namespace msgpack {
29
30/// @cond
31MSGPACK_API_VERSION_NAMESPACE(v1) {
32/// @endcond
33
34class zone {
35    struct finalizer {
36        finalizer(void (*func)(void*), void* data):m_func(func), m_data(data) {}
37        void operator()() { m_func(m_data); }
38        void (*m_func)(void*);
39        void* m_data;
40    };
41    struct finalizer_array {
42        finalizer_array():m_tail(nullptr), m_end(nullptr), m_array(nullptr) {}
43        void call() {
44            finalizer* fin = m_tail;
45            for(; fin != m_array; --fin) (*(fin-1))();
46        }
47        ~finalizer_array() {
48            call();
49            ::free(m_array);
50        }
51        void clear() {
52            call();
53            m_tail = m_array;
54        }
55        void push(void (*func)(void* data), void* data)
56        {
57            finalizer* fin = m_tail;
58
59            if(fin == m_end) {
60                push_expand(func, data);
61                return;
62            }
63
64            fin->m_func = func;
65            fin->m_data = data;
66
67            ++m_tail;
68        }
69        void push_expand(void (*func)(void*), void* data) {
70            const size_t nused = m_end - m_array;
71            size_t nnext;
72            if(nused == 0) {
73                nnext = (sizeof(finalizer) < 72/2) ?
74                    72 / sizeof(finalizer) : 8;
75            } else {
76                nnext = nused * 2;
77            }
78            finalizer* tmp =
79                static_cast<finalizer*>(::realloc(m_array, sizeof(finalizer) * nnext));
80            if(!tmp) {
81                throw std::bad_alloc();
82            }
83            m_array     = tmp;
84            m_end   = tmp + nnext;
85            m_tail  = tmp + nused;
86            new (m_tail) finalizer(func, data);
87
88            ++m_tail;
89        }
90        finalizer* m_tail;
91        finalizer* m_end;
92        finalizer* m_array;
93    };
94    struct chunk {
95        chunk* m_next;
96    };
97    struct chunk_list {
98        chunk_list(size_t chunk_size)
99        {
100            chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + chunk_size));
101            if(!c) {
102                throw std::bad_alloc();
103            }
104
105            m_head = c;
106            m_free = chunk_size;
107            m_ptr  = reinterpret_cast<char*>(c) + sizeof(chunk);
108            c->m_next = nullptr;
109        }
110        ~chunk_list()
111        {
112            chunk* c = m_head;
113            while(c) {
114                chunk* n = c->m_next;
115                ::free(c);
116                c = n;
117            }
118        }
119        void clear(size_t chunk_size)
120        {
121            chunk* c = m_head;
122            while(true) {
123                chunk* n = c->m_next;
124                if(n) {
125                    ::free(c);
126                    c = n;
127                } else {
128                    break;
129                }
130            }
131            m_head->m_next = nullptr;
132            m_free = chunk_size;
133            m_ptr  = reinterpret_cast<char*>(m_head) + sizeof(chunk);
134        }
135        size_t m_free;
136        char* m_ptr;
137        chunk* m_head;
138    };
139    size_t m_chunk_size;
140    chunk_list m_chunk_list;
141    finalizer_array m_finalizer_array;
142
143public:
144    zone(size_t chunk_size = MSGPACK_ZONE_CHUNK_SIZE) /* throw() */;
145
146public:
147    void* allocate_align(size_t size, size_t align = MSGPACK_ZONE_ALIGN);
148    void* allocate_no_align(size_t size);
149
150    void push_finalizer(void (*func)(void*), void* data);
151
152    template <typename T>
153    void push_finalizer(msgpack::unique_ptr<T> obj);
154
155    void clear();
156
157    void swap(zone& o);
158    static void* operator new(std::size_t size)
159    {
160        void* p = ::malloc(size);
161        if (!p) throw std::bad_alloc();
162        return p;
163    }
164    static void operator delete(void *p) /* throw() */
165    {
166        ::free(p);
167    }
168    static void* operator new(std::size_t size, void* place) /* throw() */
169    {
170        return ::operator new(size, place);
171    }
172    static void operator delete(void* p, void* place) /* throw() */
173    {
174        ::operator delete(p, place);
175    }
176    /// @cond
177
178    template <typename T>
179    T* allocate();
180
181    template <typename T, typename A1>
182    T* allocate(A1 a1);
183
184    template <typename T, typename A1, typename A2>
185    T* allocate(A1 a1, A2 a2);
186
187    template <typename T, typename A1, typename A2, typename A3>
188    T* allocate(A1 a1, A2 a2, A3 a3);
189
190    template <typename T, typename A1, typename A2, typename A3, typename A4>
191    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4);
192
193    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5>
194    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5);
195
196    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
197    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6);
198
199    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
200    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7);
201
202    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
203    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8);
204
205    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
206    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9);
207
208    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
209    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10);
210
211    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
212    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11);
213
214    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
215    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12);
216
217    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
218    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13);
219
220    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14>
221    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14);
222
223    template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15>
224    T* allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15);
225
226    /// @endcond
227
228private:
229    void undo_allocate(size_t size);
230
231    template <typename T>
232    static void object_destruct(void* obj);
233
234    template <typename T>
235    static void object_delete(void* obj);
236
237    void* allocate_expand(size_t size);
238private:
239    zone(const zone&);
240    zone& operator=(const zone&);
241};
242
243inline zone::zone(size_t chunk_size) /* throw() */ :m_chunk_size(chunk_size), m_chunk_list(m_chunk_size)
244{
245}
246
247inline void* zone::allocate_align(size_t size, size_t align)
248{
249    char* aligned =
250        reinterpret_cast<char*>(
251            reinterpret_cast<size_t>(
252                (m_chunk_list.m_ptr + (align - 1))) / align * align);
253    size_t adjusted_size = size + (aligned - m_chunk_list.m_ptr);
254    if(m_chunk_list.m_free >= adjusted_size) {
255        m_chunk_list.m_free -= adjusted_size;
256        m_chunk_list.m_ptr  += adjusted_size;
257        return aligned;
258    }
259    return reinterpret_cast<char*>(
260        reinterpret_cast<size_t>(
261            allocate_expand(size + (align - 1))) / align * align);
262}
263
264inline void* zone::allocate_no_align(size_t size)
265{
266    if(m_chunk_list.m_free < size) {
267        return allocate_expand(size);
268    }
269
270    char* ptr = m_chunk_list.m_ptr;
271    m_chunk_list.m_free -= size;
272    m_chunk_list.m_ptr  += size;
273
274    return ptr;
275}
276
277inline void* zone::allocate_expand(size_t size)
278{
279    chunk_list* const cl = &m_chunk_list;
280
281    size_t sz = m_chunk_size;
282
283    while(sz < size) {
284        size_t tmp_sz = sz * 2;
285        if (tmp_sz <= sz) {
286            sz = size;
287            break;
288        }
289        sz = tmp_sz;
290    }
291
292    chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + sz));
293    if (!c) throw std::bad_alloc();
294
295    char* ptr = reinterpret_cast<char*>(c) + sizeof(chunk);
296
297    c->m_next  = cl->m_head;
298    cl->m_head = c;
299    cl->m_free = sz - size;
300    cl->m_ptr  = ptr + size;
301
302    return ptr;
303}
304
305inline void zone::push_finalizer(void (*func)(void*), void* data)
306{
307    m_finalizer_array.push(func, data);
308}
309
310template <typename T>
311inline void zone::push_finalizer(msgpack::unique_ptr<T> obj)
312{
313    m_finalizer_array.push(&zone::object_delete<T>, obj.release());
314}
315
316inline void zone::clear()
317{
318    m_finalizer_array.clear();
319    m_chunk_list.clear(m_chunk_size);
320}
321
322inline void zone::swap(zone& o)
323{
324    using std::swap;
325    swap(m_chunk_size, o.m_chunk_size);
326    swap(m_chunk_list, o.m_chunk_list);
327    swap(m_finalizer_array, o.m_finalizer_array);
328}
329
330template <typename T>
331void zone::object_destruct(void* obj)
332{
333    static_cast<T*>(obj)->~T();
334}
335
336template <typename T>
337void zone::object_delete(void* obj)
338{
339    delete static_cast<T*>(obj);
340}
341
342inline void zone::undo_allocate(size_t size)
343{
344    m_chunk_list.m_ptr  -= size;
345    m_chunk_list.m_free += size;
346}
347
348inline std::size_t aligned_size(
349    std::size_t size,
350    std::size_t align = MSGPACK_ZONE_ALIGN) {
351    return (size + align - 1) / align * align;
352}
353
354/// @cond
355
356template <typename T>
357T* zone::allocate()
358{
359    void* x = allocate_align(sizeof(T));
360    try {
361        m_finalizer_array.push(&zone::object_destruct<T>, x);
362    } catch (...) {
363        undo_allocate(sizeof(T));
364        throw;
365    }
366    try {
367        return new (x) T();
368    } catch (...) {
369        --m_finalizer_array.m_tail;
370        undo_allocate(sizeof(T));
371        throw;
372    }
373}
374
375template <typename T, typename A1>
376T* zone::allocate(A1 a1)
377{
378    void* x = allocate_align(sizeof(T));
379    try {
380        m_finalizer_array.push(&zone::object_destruct<T>, x);
381    } catch (...) {
382        undo_allocate(sizeof(T));
383        throw;
384    }
385    try {
386        return new (x) T(a1);
387    } catch (...) {
388        --m_finalizer_array.m_tail;
389        undo_allocate(sizeof(T));
390        throw;
391    }
392}
393
394template <typename T, typename A1, typename A2>
395T* zone::allocate(A1 a1, A2 a2)
396{
397    void* x = allocate_align(sizeof(T));
398    try {
399        m_finalizer_array.push(&zone::object_destruct<T>, x);
400    } catch (...) {
401        undo_allocate(sizeof(T));
402        throw;
403    }
404    try {
405        return new (x) T(a1, a2);
406    } catch (...) {
407        --m_finalizer_array.m_tail;
408        undo_allocate(sizeof(T));
409        throw;
410    }
411}
412
413template <typename T, typename A1, typename A2, typename A3>
414T* zone::allocate(A1 a1, A2 a2, A3 a3)
415{
416    void* x = allocate_align(sizeof(T));
417    try {
418        m_finalizer_array.push(&zone::object_destruct<T>, x);
419    } catch (...) {
420        undo_allocate(sizeof(T));
421        throw;
422    }
423    try {
424        return new (x) T(a1, a2, a3);
425    } catch (...) {
426        --m_finalizer_array.m_tail;
427        undo_allocate(sizeof(T));
428        throw;
429    }
430}
431
432template <typename T, typename A1, typename A2, typename A3, typename A4>
433T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4)
434{
435    void* x = allocate_align(sizeof(T));
436    try {
437        m_finalizer_array.push(&zone::object_destruct<T>, x);
438    } catch (...) {
439        undo_allocate(sizeof(T));
440        throw;
441    }
442    try {
443        return new (x) T(a1, a2, a3, a4);
444    } catch (...) {
445        --m_finalizer_array.m_tail;
446        undo_allocate(sizeof(T));
447        throw;
448    }
449}
450
451template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5>
452T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)
453{
454    void* x = allocate_align(sizeof(T));
455    try {
456        m_finalizer_array.push(&zone::object_destruct<T>, x);
457    } catch (...) {
458        undo_allocate(sizeof(T));
459        throw;
460    }
461    try {
462        return new (x) T(a1, a2, a3, a4, a5);
463    } catch (...) {
464        --m_finalizer_array.m_tail;
465        undo_allocate(sizeof(T));
466        throw;
467    }
468}
469
470template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
471T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6)
472{
473    void* x = allocate_align(sizeof(T));
474    try {
475        m_finalizer_array.push(&zone::object_destruct<T>, x);
476    } catch (...) {
477        undo_allocate(sizeof(T));
478        throw;
479    }
480    try {
481        return new (x) T(a1, a2, a3, a4, a5, a6);
482    } catch (...) {
483        --m_finalizer_array.m_tail;
484        undo_allocate(sizeof(T));
485        throw;
486    }
487}
488
489template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
490T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7)
491{
492    void* x = allocate_align(sizeof(T));
493    try {
494        m_finalizer_array.push(&zone::object_destruct<T>, x);
495    } catch (...) {
496        undo_allocate(sizeof(T));
497        throw;
498    }
499    try {
500        return new (x) T(a1, a2, a3, a4, a5, a6, a7);
501    } catch (...) {
502        --m_finalizer_array.m_tail;
503        undo_allocate(sizeof(T));
504        throw;
505    }
506}
507
508template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
509T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8)
510{
511    void* x = allocate_align(sizeof(T));
512    try {
513        m_finalizer_array.push(&zone::object_destruct<T>, x);
514    } catch (...) {
515        undo_allocate(sizeof(T));
516        throw;
517    }
518    try {
519        return new (x) T(a1, a2, a3, a4, a5, a6, a7, a8);
520    } catch (...) {
521        --m_finalizer_array.m_tail;
522        undo_allocate(sizeof(T));
523        throw;
524    }
525}
526
527template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
528T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9)
529{
530    void* x = allocate_align(sizeof(T));
531    try {
532        m_finalizer_array.push(&zone::object_destruct<T>, x);
533    } catch (...) {
534        undo_allocate(sizeof(T));
535        throw;
536    }
537    try {
538        return new (x) T(a1, a2, a3, a4, a5, a6, a7, a8, a9);
539    } catch (...) {
540        --m_finalizer_array.m_tail;
541        undo_allocate(sizeof(T));
542        throw;
543    }
544}
545
546template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
547T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10)
548{
549    void* x = allocate_align(sizeof(T));
550    try {
551        m_finalizer_array.push(&zone::object_destruct<T>, x);
552    } catch (...) {
553        undo_allocate(sizeof(T));
554        throw;
555    }
556    try {
557        return new (x) T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
558    } catch (...) {
559        --m_finalizer_array.m_tail;
560        undo_allocate(sizeof(T));
561        throw;
562    }
563}
564
565template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
566T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11)
567{
568    void* x = allocate_align(sizeof(T));
569    try {
570        m_finalizer_array.push(&zone::object_destruct<T>, x);
571    } catch (...) {
572        undo_allocate(sizeof(T));
573        throw;
574    }
575    try {
576        return new (x) T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
577    } catch (...) {
578        --m_finalizer_array.m_tail;
579        undo_allocate(sizeof(T));
580        throw;
581    }
582}
583
584template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
585T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12)
586{
587    void* x = allocate_align(sizeof(T));
588    try {
589        m_finalizer_array.push(&zone::object_destruct<T>, x);
590    } catch (...) {
591        undo_allocate(sizeof(T));
592        throw;
593    }
594    try {
595        return new (x) T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
596    } catch (...) {
597        --m_finalizer_array.m_tail;
598        undo_allocate(sizeof(T));
599        throw;
600    }
601}
602
603template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
604T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13)
605{
606    void* x = allocate_align(sizeof(T));
607    try {
608        m_finalizer_array.push(&zone::object_destruct<T>, x);
609    } catch (...) {
610        undo_allocate(sizeof(T));
611        throw;
612    }
613    try {
614        return new (x) T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13);
615    } catch (...) {
616        --m_finalizer_array.m_tail;
617        undo_allocate(sizeof(T));
618        throw;
619    }
620}
621
622template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14>
623T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14)
624{
625    void* x = allocate_align(sizeof(T));
626    try {
627        m_finalizer_array.push(&zone::object_destruct<T>, x);
628    } catch (...) {
629        undo_allocate(sizeof(T));
630        throw;
631    }
632    try {
633        return new (x) T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14);
634    } catch (...) {
635        --m_finalizer_array.m_tail;
636        undo_allocate(sizeof(T));
637        throw;
638    }
639}
640
641template <typename T, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15>
642T* zone::allocate(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, A14 a14, A15 a15)
643{
644    void* x = allocate_align(sizeof(T));
645    try {
646        m_finalizer_array.push(&zone::object_destruct<T>, x);
647    } catch (...) {
648        undo_allocate(sizeof(T));
649        throw;
650    }
651    try {
652        return new (x) T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15);
653    } catch (...) {
654        --m_finalizer_array.m_tail;
655        undo_allocate(sizeof(T));
656        throw;
657    }
658}
659
660/// @endcond
661
662/// @cond
663}  // MSGPACK_API_VERSION_NAMESPACE(v1)
664/// @endcond
665
666}  // namespace msgpack
667
668#endif // MSGPACK_CPP03_ZONE_HPP
669