/* * MessagePack for C memory pool implementation * * Copyright (C) 2008-2009 FURUHASHI Sadayuki * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "msgpack/zone.h" #include #include static inline bool init_chunk_array(msgpack_zone_chunk_array* ca, size_t chunk_size) { // glibcは72バイト以下のmallocが高速 const size_t nfirst = (sizeof(msgpack_zone_chunk) < 72/2) ? 72 / sizeof(msgpack_zone_chunk) : 8; msgpack_zone_chunk* array = (msgpack_zone_chunk*)malloc( sizeof(msgpack_zone_chunk) * nfirst); if(!array) { return false; } const size_t sz = chunk_size; char* ptr = (char*)malloc(sz); if(!ptr) { free(array); return NULL; } ca->tail = array; ca->end = array + nfirst; ca->array = array; array[0].free = sz; array[0].ptr = ptr; array[0].alloc = ptr; return true; } static inline void destroy_chunk_array(msgpack_zone_chunk_array* ca) { msgpack_zone_chunk* chunk = ca->array; for(; chunk != ca->tail+1; ++chunk) { free(chunk->alloc); } free(ca->array); } void* msgpack_zone_malloc(msgpack_zone* zone, size_t size) { msgpack_zone_chunk_array* const ca = &zone->chunk_array; msgpack_zone_chunk* chunk = ca->tail; if(chunk->free > size) { // chunkに空き容量がある // 空き容量を消費して返す char* ptr = chunk->ptr; chunk->ptr += size; chunk->free -= size; return ptr; } chunk = ++ca->tail; if(chunk == ca->end) { // ca->arrayに空きがない // ca->arrayを拡張する const size_t nused = ca->end - ca->array; const size_t nnext = (ca->end - ca->array) * 2; chunk = (msgpack_zone_chunk*)realloc(ca->array, sizeof(msgpack_zone_chunk) * nnext); if(!chunk) { return NULL; } ca->array = chunk; ca->end = chunk + nnext; chunk = ca->tail = chunk + nused; } size_t sz = zone->chunk_size; while(sz < size) { sz *= 2; } char* ptr = (char*)malloc(sz); if(!ptr) { return NULL; } chunk->free = sz - size; chunk->ptr = ptr + size; chunk->alloc = ptr; return ptr; } static inline void init_finalizer_array(msgpack_zone_finalizer_array* fa) { fa->tail = NULL; fa->end = NULL; fa->array = NULL; } static inline void destroy_finalizer_array(msgpack_zone_finalizer_array* fa) { // 逆順に呼び出し msgpack_zone_finalizer* fin = fa->tail; for(; fin != fa->array; --fin) { (*(fin-1)->func)((fin-1)->data); } free(fa->array); } bool msgpack_zone_push_finalizer(msgpack_zone* zone, void (*func)(void* data), void* data) { msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; msgpack_zone_finalizer* fin = fa->tail; if(fin == fa->end) { // fa->arrayに空きがない // fa->arrayを拡張する size_t nnext; const size_t nused = fa->end - fa->array; if(nused == 0) { // 初回の呼び出し:fa->tail == fa->end == fa->array == NULL // glibcは72バイト以下のmallocが高速 nnext = (sizeof(msgpack_zone_finalizer) < 72/2) ? 72 / sizeof(msgpack_zone_finalizer) : 8; } else { nnext = (fa->end - fa->array) * 2; } fin = (msgpack_zone_finalizer*)realloc(fa->array, sizeof(msgpack_zone_finalizer) * nnext); if(!fin) { return false; } fa->array = fin; fa->end = fin + nnext; fin = fa->tail = fin + nused; } fin->func = func; fin->data = data; ++fa->tail; return true; } bool msgpack_zone_is_empty(msgpack_zone* zone) { msgpack_zone_chunk_array* const ca = &zone->chunk_array; msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; return ca->array[0].ptr == ca->array[0].alloc && ca->tail == ca->array && fa->tail == fa->array; } bool msgpack_zone_init(msgpack_zone* zone, size_t chunk_size) { zone->chunk_size = chunk_size; if(!init_chunk_array(&zone->chunk_array, chunk_size)) { return false; } init_finalizer_array(&zone->finalizer_array); return true; } void msgpack_zone_destroy(msgpack_zone* zone) { destroy_finalizer_array(&zone->finalizer_array); destroy_chunk_array(&zone->chunk_array); } msgpack_zone* msgpack_zone_new(size_t chunk_size) { msgpack_zone* zone = (msgpack_zone*)malloc(sizeof(msgpack_zone)); if(zone == NULL) { return NULL; } if(!msgpack_zone_init(zone, chunk_size)) { free(zone); return NULL; } return zone; } void msgpack_zone_free(msgpack_zone* zone) { msgpack_zone_destroy(zone); free(zone); }