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