| // |
| // MessagePack for C++ memory pool |
| // |
| // Copyright (C) 2008-2010 FURUHASHI Sadayuki |
| // |
| // Distributed under the Boost Software License, Version 1.0. |
| // (See accompanying file LICENSE_1_0.txt or copy at |
| // http://www.boost.org/LICENSE_1_0.txt) |
| // |
| #ifndef MSGPACK_CPP03_ZONE_HPP |
| #define MSGPACK_CPP03_ZONE_HPP |
| |
| #include <cstdlib> |
| #include <memory> |
| #include <vector> |
| |
| #include "msgpack/versioning.hpp" |
| |
| #ifndef MSGPACK_ZONE_CHUNK_SIZE |
| #define MSGPACK_ZONE_CHUNK_SIZE 8192 |
| #endif |
| |
| #ifndef MSGPACK_ZONE_ALIGN |
| #define MSGPACK_ZONE_ALIGN sizeof(void*) |
| #endif |
| |
| <% GENERATION_LIMIT = 15 %> |
| namespace msgpack { |
| |
| /// @cond |
| MSGPACK_API_VERSION_NAMESPACE(v1) { |
| /// @endcond |
| |
| class zone { |
| struct finalizer { |
| finalizer(void (*func)(void*), void* data):m_func(func), m_data(data) {} |
| void operator()() { m_func(m_data); } |
| void (*m_func)(void*); |
| void* m_data; |
| }; |
| struct finalizer_array { |
| finalizer_array():m_tail(nullptr), m_end(nullptr), m_array(nullptr) {} |
| void call() { |
| finalizer* fin = m_tail; |
| for(; fin != m_array; --fin) (*(fin-1))(); |
| } |
| ~finalizer_array() { |
| call(); |
| ::free(m_array); |
| } |
| void clear() { |
| call(); |
| m_tail = m_array; |
| } |
| void push(void (*func)(void* data), void* data) |
| { |
| finalizer* fin = m_tail; |
| |
| if(fin == m_end) { |
| push_expand(func, data); |
| return; |
| } |
| |
| fin->m_func = func; |
| fin->m_data = data; |
| |
| ++m_tail; |
| } |
| void push_expand(void (*func)(void*), void* data) { |
| const size_t nused = m_end - m_array; |
| size_t nnext; |
| if(nused == 0) { |
| nnext = (sizeof(finalizer) < 72/2) ? |
| 72 / sizeof(finalizer) : 8; |
| } else { |
| nnext = nused * 2; |
| } |
| finalizer* tmp = |
| static_cast<finalizer*>(::realloc(m_array, sizeof(finalizer) * nnext)); |
| if(!tmp) { |
| throw std::bad_alloc(); |
| } |
| m_array = tmp; |
| m_end = tmp + nnext; |
| m_tail = tmp + nused; |
| new (m_tail) finalizer(func, data); |
| |
| ++m_tail; |
| } |
| finalizer* m_tail; |
| finalizer* m_end; |
| finalizer* m_array; |
| }; |
| struct chunk { |
| chunk* m_next; |
| }; |
| struct chunk_list { |
| chunk_list(size_t chunk_size) |
| { |
| chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + chunk_size)); |
| if(!c) { |
| throw std::bad_alloc(); |
| } |
| |
| m_head = c; |
| m_free = chunk_size; |
| m_ptr = reinterpret_cast<char*>(c) + sizeof(chunk); |
| c->m_next = nullptr; |
| } |
| ~chunk_list() |
| { |
| chunk* c = m_head; |
| while(c) { |
| chunk* n = c->m_next; |
| ::free(c); |
| c = n; |
| } |
| } |
| void clear(size_t chunk_size) |
| { |
| chunk* c = m_head; |
| while(true) { |
| chunk* n = c->m_next; |
| if(n) { |
| ::free(c); |
| c = n; |
| } else { |
| break; |
| } |
| } |
| m_head->m_next = nullptr; |
| m_free = chunk_size; |
| m_ptr = reinterpret_cast<char*>(m_head) + sizeof(chunk); |
| } |
| size_t m_free; |
| char* m_ptr; |
| chunk* m_head; |
| }; |
| size_t m_chunk_size; |
| chunk_list m_chunk_list; |
| finalizer_array m_finalizer_array; |
| |
| public: |
| zone(size_t chunk_size = MSGPACK_ZONE_CHUNK_SIZE) /* throw() */; |
| |
| public: |
| void* allocate_align(size_t size, size_t align = MSGPACK_ZONE_ALIGN); |
| void* allocate_no_align(size_t size); |
| |
| void push_finalizer(void (*func)(void*), void* data); |
| |
| template <typename T> |
| void push_finalizer(msgpack::unique_ptr<T> obj); |
| |
| void clear(); |
| |
| void swap(zone& o); |
| static void* operator new(std::size_t size) |
| { |
| void* p = ::malloc(size); |
| if (!p) throw std::bad_alloc(); |
| return p; |
| } |
| static void operator delete(void *p) /* throw() */ |
| { |
| ::free(p); |
| } |
| static void* operator new(std::size_t size, void* place) /* throw() */ |
| { |
| return ::operator new(size, place); |
| } |
| static void operator delete(void* p, void* place) /* throw() */ |
| { |
| ::operator delete(p, place); |
| } |
| /// @cond |
| <%0.upto(GENERATION_LIMIT) {|i|%> |
| template <typename T<%1.upto(i) {|j|%>, typename A<%=j%><%}%>> |
| T* allocate(<%=(1..i).map{|j|"A#{j} a#{j}"}.join(', ')%>); |
| <%}%> |
| /// @endcond |
| |
| private: |
| void undo_allocate(size_t size); |
| |
| template <typename T> |
| static void object_destruct(void* obj); |
| |
| template <typename T> |
| static void object_delete(void* obj); |
| |
| void* allocate_expand(size_t size); |
| private: |
| zone(const zone&); |
| zone& operator=(const zone&); |
| }; |
| |
| inline zone::zone(size_t chunk_size) /* throw() */ :m_chunk_size(chunk_size), m_chunk_list(m_chunk_size) |
| { |
| } |
| |
| inline void* zone::allocate_align(size_t size, size_t align) |
| { |
| char* aligned = |
| reinterpret_cast<char*>( |
| reinterpret_cast<size_t>( |
| (m_chunk_list.m_ptr + (align - 1))) / align * align); |
| size_t adjusted_size = size + (aligned - m_chunk_list.m_ptr); |
| if(m_chunk_list.m_free >= adjusted_size) { |
| m_chunk_list.m_free -= adjusted_size; |
| m_chunk_list.m_ptr += adjusted_size; |
| return aligned; |
| } |
| return reinterpret_cast<char*>( |
| reinterpret_cast<size_t>( |
| allocate_expand(size + (align - 1))) / align * align); |
| } |
| |
| inline void* zone::allocate_no_align(size_t size) |
| { |
| if(m_chunk_list.m_free < size) { |
| return allocate_expand(size); |
| } |
| |
| char* ptr = m_chunk_list.m_ptr; |
| m_chunk_list.m_free -= size; |
| m_chunk_list.m_ptr += size; |
| |
| return ptr; |
| } |
| |
| inline void* zone::allocate_expand(size_t size) |
| { |
| chunk_list* const cl = &m_chunk_list; |
| |
| size_t sz = m_chunk_size; |
| |
| while(sz < size) { |
| size_t tmp_sz = sz * 2; |
| if (tmp_sz <= sz) { |
| sz = size; |
| break; |
| } |
| sz = tmp_sz; |
| } |
| |
| chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + sz)); |
| if (!c) throw std::bad_alloc(); |
| |
| char* ptr = reinterpret_cast<char*>(c) + sizeof(chunk); |
| |
| c->m_next = cl->m_head; |
| cl->m_head = c; |
| cl->m_free = sz - size; |
| cl->m_ptr = ptr + size; |
| |
| return ptr; |
| } |
| |
| inline void zone::push_finalizer(void (*func)(void*), void* data) |
| { |
| m_finalizer_array.push(func, data); |
| } |
| |
| template <typename T> |
| inline void zone::push_finalizer(msgpack::unique_ptr<T> obj) |
| { |
| m_finalizer_array.push(&zone::object_delete<T>, obj.release()); |
| } |
| |
| inline void zone::clear() |
| { |
| m_finalizer_array.clear(); |
| m_chunk_list.clear(m_chunk_size); |
| } |
| |
| inline void zone::swap(zone& o) |
| { |
| using std::swap; |
| swap(m_chunk_size, o.m_chunk_size); |
| swap(m_chunk_list, o.m_chunk_list); |
| swap(m_finalizer_array, o.m_finalizer_array); |
| } |
| |
| template <typename T> |
| void zone::object_destruct(void* obj) |
| { |
| static_cast<T*>(obj)->~T(); |
| } |
| |
| template <typename T> |
| void zone::object_delete(void* obj) |
| { |
| delete static_cast<T*>(obj); |
| } |
| |
| inline void zone::undo_allocate(size_t size) |
| { |
| m_chunk_list.m_ptr -= size; |
| m_chunk_list.m_free += size; |
| } |
| |
| inline std::size_t aligned_size( |
| std::size_t size, |
| std::size_t align = MSGPACK_ZONE_ALIGN) { |
| return (size + align - 1) / align * align; |
| } |
| |
| /// @cond |
| <%0.upto(GENERATION_LIMIT) {|i|%> |
| template <typename T<%1.upto(i) {|j|%>, typename A<%=j%><%}%>> |
| T* zone::allocate(<%=(1..i).map{|j|"A#{j} a#{j}"}.join(', ')%>) |
| { |
| void* x = allocate_align(sizeof(T)); |
| try { |
| m_finalizer_array.push(&zone::object_destruct<T>, x); |
| } catch (...) { |
| undo_allocate(sizeof(T)); |
| throw; |
| } |
| try { |
| return new (x) T(<%=(1..i).map{|j|"a#{j}"}.join(', ')%>); |
| } catch (...) { |
| --m_finalizer_array.m_tail; |
| undo_allocate(sizeof(T)); |
| throw; |
| } |
| } |
| <%}%> |
| /// @endcond |
| |
| /// @cond |
| } // MSGPACK_API_VERSION_NAMESPACE(v1) |
| /// @endcond |
| |
| } // namespace msgpack |
| |
| #endif // MSGPACK_CPP03_ZONE_HPP |