rsys

Basic data structures and low-level features
git clone git://git.meso-star.fr/rsys.git
Log | Files | Refs | README | LICENSE

commit 9b2d0b43f570f6e6dced034a98114f33f16d45a1
parent a2b8e595dff950014cb36ce17bd8882c634b7642
Author: vaplv <vaplv@free.fr>
Date:   Tue, 19 Sep 2017 11:58:10 +0200

Merge branch 'release-0.5'

Diffstat:
MREADME.md | 22+++++++++++++++++-----
Mcmake/CMakeLists.txt | 7++++++-
Asrc/big_buffer.c | 36++++++++++++++++++++++++++++++++++++
Asrc/big_buffer.h | 434+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/clock_time.c | 9++++++---
Msrc/clock_time.h | 14+++++++++++---
Msrc/dynamic_array.h | 7+++++++
Msrc/mem_allocator.c | 312++-----------------------------------------------------------------------------
Msrc/mem_allocator.h | 23++++++++++++++++++++---
Asrc/mem_lifo_allocator.c | 304+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mem_proxy_allocator.c | 320+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_big_buffer.c | 369+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_mem_allocator.c | 15+++++++++++++++
Msrc/test_time.c | 33+++++++++++++++++++++++++--------
14 files changed, 1576 insertions(+), 329 deletions(-)

diff --git a/README.md b/README.md @@ -17,16 +17,28 @@ project can be now edited, built, tested and installed as any CMake project. ## Release notes +### Version 0.5 + +- Add the `big_buffer` container, i.e. out of core dynamic array of POD data. +- Update the `clock_time` API: the `time_<add|current|sub>` functions + return a pointer toward the result. +- Add the `time_zero` function that cleans-up the submitted time. +- Add a Last In First Out (LIFO) allocator. It uses a pre-allocated memory pool + to store a stack of allocated memory blocks. A memory block is allocated on + top of the stack. On "free" invocation, it is marked as freed but it is + effectively removed from the allocated memory when it lies on top of the + stack. + ### Version 0.4 - Add the `double2`, `double3`, `double4`, `double33`, `double22` and -`double44` data types that provide the same functionalities of their `float` -alternative. + `double44` data types that provide the same functionalities of their `float` + alternative. - Add the `purge` function to the hash table and the dynamic array data -structures. This function not only resets the state of the structure, as the -`clear` function, but also frees its internal memory. + structures. This function not only resets the state of the structure, as the + `clear` function, but also frees its internal memory. - Implement a new image API that provides and explicit image data structure. -The old API is still available but is deprecated. + The old API is still available but is deprecated. ## License diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -34,17 +34,20 @@ include(rcmake) # Configure and define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 4) +set(VERSION_MINOR 5) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(RSYS_FILES_SRC + big_buffer.c clock_time.c cstr.c image.c library.c logger.c mem_allocator.c + mem_lifo_allocator.c + mem_proxy_allocator.c quaternion.c str.c) @@ -62,6 +65,7 @@ set(RSYS_FILES_INC io_c99.h) set(RSYS_FILES_INC_API algorithm.h + big_buffer.h binary_heap.h clock_time.h condition.h @@ -165,6 +169,7 @@ if(NOT NO_TEST) new_test(test_algorithm) new_test(test_atomic) + new_test(test_big_buffer rsys) new_test(test_binary_heap rsys) new_test(test_cstr rsys) new_test(test_double2 ${MATH_LIB}) diff --git a/src/big_buffer.c b/src/big_buffer.c @@ -0,0 +1,36 @@ +/* Copyright (C) 2013-2017 Vincent Forest (vaplv@free.fr) + * + * The RSys library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The RSys library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L /* ftruncate support */ + +#include "big_buffer.h" + +#ifdef COMPILER_CL + #include <io.h> /* _chsize support */ +#else + #include <unistd.h> /* ftruncate support */ +#endif + +res_T +truncate__(FILE* file, const size_t sz) +{ + ASSERT(file); +#ifdef OS_WINDOWS + return _chsize(_fileno(file), (long)sz) == 0 ? RES_OK : RES_IO_ERR; +#else + return ftruncate(fileno(file), (off_t)sz) == 0 ? RES_OK : RES_IO_ERR; +#endif +} + diff --git a/src/big_buffer.h b/src/big_buffer.h @@ -0,0 +1,434 @@ +/* Copyright (C) 2013-2017 Vincent Forest (vaplv@free.fr) + * + * The RSys library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The RSys library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ + +#if !defined(BIGBUF_NAME) && !defined(BIGBUF_DATA) +#ifndef BIGBUF_H +#define BIGBUF_H + +#include "rsys.h" +#include "math.h" + +#include <stdio.h> +#include <string.h> + +/* Internal enumerate used to define the mode of the buffered data */ +enum bigbuf_mode__ { BIGBUF_READ__, BIGBUF_WRITE__ }; + +enum bigbuf_flag { + BIGBUF_LOAD_STREAM = BIT(1) +}; + +RSYS_API res_T +truncate__ + (FILE* file, + const size_t sz); + +#endif /* BIGBUF_H */ +#else + +/* + * Generate the big buffer data type and functions wrt the following macros: + * - BIGBUF_NAME: prefix of the big buffer functions & type; + * - BIGBUF_DATA: type of the data registered into the big buffer; + * - BIGBUF_ALIGNMENT: optional alignment of the buffer that stores the in + * buffered data of the big buffer. By default use the maximum between the + * default alignment of the BIGBUF_DATA and 16 bytes; + * BIGBUF_DATA is buffered + * + * The name of the generated type is: struct bigbuf_<BIGBUF_NAME> and the + * rule to generate the function name is bigbuf_<BIGBUF_NAME>_<FUNCTION_NAME>. + */ + +#ifndef BIGBUF_NAME + #error "Missing the BIGBUF_NAME macro defining the structure name" +#endif +#ifndef BIGBUF_DATA + #error "Missing the BIGBUF_DATA macro defining the buffer data type" +#endif + +#define BIGBUF__ CONCAT(bigbuf_, BIGBUF_NAME) +#define BIGBUF_FUNC__(Func) CONCAT(CONCAT(CONCAT(bigbuf_, BIGBUF_NAME),_), Func) +#ifndef BIGBUF_ALIGNMENT + #define BIGBUF_ALIGNMENT__ MMAX(ALIGNOF(BIGBUF_DATA), 16) +#else + STATIC_ASSERT(IS_POW2(BIGBUF_ALIGNMENT), + BIGBUF_ALIGNMENT_must_be_a_power_of_2); + #define BIGBUF_ALIGNMENT__ MMAX(ALIGNOF(BIGBUF_DATA), 16) +#endif + +struct BIGBUF__ { + struct { /* Buffered data */ + BIGBUF_DATA* data; + size_t capacity; + size_t size; + } buf; + + enum bigbuf_mode__ mode; /* Mode of the buffered data */ + + FILE* file; /* Stream where buffer data are written */ + size_t buf_index; /* Id of the first big buffer item store into `buf' */ + size_t size; /* Number of items stored into the big buffer */ + long head; /* Position indicator into `file' toward the first data */ + long end; /* Position indicator into `file' behind the last data */ + int temp_file; /* Define if `file' is a temporary file or not */ + + struct mem_allocator* allocator; +}; + +/******************************************************************************* + * Internal helper functions + ******************************************************************************/ +static INLINE res_T +BIGBUF_FUNC__(flush__)(struct BIGBUF__* bigbuf) +{ + BIGBUF_DATA* buf_data; + size_t buf_size, write_size; + long cur, offset; + int err; + res_T res = RES_OK; + ASSERT(bigbuf); + + if(bigbuf->mode == BIGBUF_READ__) goto exit; + if(!bigbuf->buf.size) goto exit; + + buf_size = bigbuf->buf.size; + buf_data = bigbuf->buf.data; + + cur = ftell(bigbuf->file); + offset = (long)(sizeof(BIGBUF_DATA)*bigbuf->buf_index) - cur + bigbuf->head; + err = fseek(bigbuf->file, offset, SEEK_CUR); + if(err < 0) { + res = RES_IO_ERR; + goto error; + } + + write_size = fwrite(buf_data, sizeof(BIGBUF_DATA), buf_size, bigbuf->file); + + if(bigbuf->buf_index + buf_size == bigbuf->size) { + bigbuf->end = bigbuf->end + (long)write_size * (long)sizeof(BIGBUF_DATA); + } + + if(write_size == buf_size) { + bigbuf->buf.size = 0; /* Clear buffered data */ + bigbuf->buf_index = bigbuf->size; + } else if(write_size) { + size_t cp_size = buf_size - write_size; + memmove(buf_data, buf_data + write_size, cp_size); + bigbuf->buf.size = cp_size; + bigbuf->buf_index = bigbuf->buf_index + write_size; + res = RES_IO_ERR; + } else { + res = RES_IO_ERR; + } + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +static INLINE res_T +BIGBUF_FUNC__(fetch__)(struct BIGBUF__* bigbuf, const size_t index) +{ + BIGBUF_DATA* buf_data; + size_t buf_size; + size_t buf_index; + size_t read_size; + long cur, offset; + int err; + res_T res = RES_OK; + ASSERT(bigbuf && index < bigbuf->size); + + if(index>=bigbuf->buf_index && index<bigbuf->buf_index + bigbuf->buf.size) + goto exit; + + buf_size = bigbuf->buf.size; + buf_data = bigbuf->buf.data; + + if(bigbuf->mode == BIGBUF_WRITE__) { + res = BIGBUF_FUNC__(flush__)(bigbuf); + if(res != RES_OK) goto error; + } + + buf_index = index / bigbuf->buf.capacity * bigbuf->buf.capacity; + + cur = ftell(bigbuf->file); + offset = (long)(sizeof(BIGBUF_DATA)*buf_index) - cur + bigbuf->head; + err = fseek(bigbuf->file, offset, SEEK_CUR); + if(err < 0) { + res = RES_IO_ERR; + goto error; + } + + buf_size = MMIN(bigbuf->size - buf_index, bigbuf->buf.capacity); + bigbuf->buf.size = buf_size; + + read_size = fread(buf_data, sizeof(BIGBUF_DATA), buf_size, bigbuf->file); + bigbuf->buf_index = buf_index; + bigbuf->mode = BIGBUF_READ__; + + if(read_size != buf_size) { + bigbuf->buf.size = read_size; + res = RES_IO_ERR; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Big buffer API + ******************************************************************************/ +static INLINE res_T +BIGBUF_FUNC__(flush)(struct BIGBUF__* bigbuf) +{ + size_t write_size; + int err; + res_T res = RES_OK; + ASSERT(bigbuf); + + /* Flush buffered data */ + res = BIGBUF_FUNC__(flush__)(bigbuf); + if(res != RES_OK) goto error; + + /* Write the big buffer header */ + err = fseek(bigbuf->file, bigbuf->head - (long)sizeof(size_t), SEEK_SET); + if(err < 0) { + res = RES_IO_ERR; + goto error; + } + write_size = fwrite(&bigbuf->size, sizeof(size_t), 1, bigbuf->file); + if(write_size != 1) { + res = RES_IO_ERR; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +static INLINE void +BIGBUF_FUNC__(release)(struct BIGBUF__* bigbuf) +{ + ASSERT(bigbuf); + if(bigbuf->file) { + CHECK(BIGBUF_FUNC__(flush)(bigbuf), RES_OK); + if(bigbuf->temp_file) fclose(bigbuf->file); + } + if(bigbuf->buf.data) MEM_RM(bigbuf->allocator, bigbuf->buf.data); +} + +static INLINE res_T +BIGBUF_FUNC__(init) + (struct mem_allocator* allocator, /* May be NULL <=> use default allocator */ + const size_t bufsz, /* # buffered data */ + FILE* stream, /* May be NULL <=> internally manage the stream */ + int mask, /* Combination of bigbuf_flag */ + struct BIGBUF__* bigbuf) +{ + size_t size; + res_T res = RES_OK; + ASSERT(bigbuf); + memset(bigbuf, 0, sizeof(struct BIGBUF__)); + + if(!stream && (mask & BIGBUF_LOAD_STREAM)) { + res = RES_BAD_ARG; + goto error; + } + if(!bufsz) { + res = RES_BAD_ARG; + goto error; + } + + bigbuf->allocator = allocator ? allocator : &mem_default_allocator; + bigbuf->buf.capacity = bufsz; + size = bigbuf->buf.capacity * sizeof(BIGBUF_DATA); + + bigbuf->buf.data = MEM_ALLOC_ALIGNED + (bigbuf->allocator, size, BIGBUF_ALIGNMENT__); + if(!bigbuf->buf.data) { + res = RES_MEM_ERR; + goto error; + } + memset(bigbuf->buf.data, 0, size); + bigbuf->buf.size = 0; + + if(stream) { + bigbuf->file = stream; + bigbuf->temp_file = 0; + } else { + bigbuf->file = tmpfile(); + if(!bigbuf->file) { + res = RES_IO_ERR; + goto error; + } + bigbuf->temp_file = 1; + } + + if(mask & BIGBUF_LOAD_STREAM) { + /* Read the big buffer header */ + size_t rsize = fread(&bigbuf->size, sizeof(size_t), 1, bigbuf->file); + if(rsize != 1) { + res = RES_IO_ERR; + goto error; + } + } else { + /* Reserve space for the big buffer header storing the big buffer size */ + size_t wsize; + bigbuf->size = 0; + wsize = fwrite(&bigbuf->size, sizeof(size_t), 1, bigbuf->file); + if(wsize != 1) { + res = RES_IO_ERR; + goto error; + } + } + + bigbuf->buf_index = bigbuf->size; + bigbuf->head = ftell(bigbuf->file); + bigbuf->end = bigbuf->head + (long)bigbuf->size * (long)sizeof(BIGBUF_DATA); + bigbuf->mode = BIGBUF_WRITE__; + +exit: + return res; +error: + /* Reset the file to NULL to not flush buffered data during bigbuf release */ + if(bigbuf->temp_file) fclose(bigbuf->file); + bigbuf->file = NULL; + BIGBUF_FUNC__(release)(bigbuf); + goto exit; +} + +static INLINE res_T +BIGBUF_FUNC__(push_back)(struct BIGBUF__* bigbuf, const BIGBUF_DATA* data) +{ + ASSERT(bigbuf); + + if(bigbuf->size == SIZE_MAX) return RES_MEM_ERR; + if(bigbuf->mode == BIGBUF_READ__) { + bigbuf->buf.size = 0; /* Clear buffered data */ + bigbuf->buf_index = bigbuf->size; + } + + ASSERT(bigbuf->buf.size < bigbuf->buf.capacity); + bigbuf->buf.data[bigbuf->buf.size++] = *data; /* Push back */ + bigbuf->mode = BIGBUF_WRITE__; + ++bigbuf->size; + + if(bigbuf->buf.size == bigbuf->buf.capacity) { + return BIGBUF_FUNC__(flush__)(bigbuf); + } else { + return RES_OK; + } +} + +static FINLINE size_t +BIGBUF_FUNC__(size_get)(const struct BIGBUF__* bigbuf) +{ + ASSERT(bigbuf); + return bigbuf->size; +} + +static INLINE res_T +BIGBUF_FUNC__(resize)(struct BIGBUF__* bigbuf, const size_t size) +{ + res_T res = RES_OK; + ASSERT(bigbuf); + + if(size == bigbuf->size) goto exit; + + if(size < bigbuf->size) { + if(bigbuf->buf_index >= size) { + /* Clear all buffered data */ + bigbuf->buf.size = 0; + bigbuf->buf_index = size; + } + if(bigbuf->buf_index + bigbuf->buf.size > size) { + /* Clear some buffered data */ + bigbuf->buf.size = size - bigbuf->buf_index; + } + } + bigbuf->size = size; + bigbuf->end = bigbuf->head + (long)(bigbuf->size*sizeof(BIGBUF_DATA)); + + res = truncate__ + (bigbuf->file, bigbuf->size*sizeof(BIGBUF_DATA) + sizeof(size_t)/*header*/); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +static FINLINE res_T +BIGBUF_FUNC__(clear)(struct BIGBUF__* bigbuf) +{ + return BIGBUF_FUNC__(resize)(bigbuf, 0); +} + +static INLINE res_T +BIGBUF_FUNC__(set) + (struct BIGBUF__* bigbuf, const size_t at, const BIGBUF_DATA* data) +{ + res_T res = RES_OK; + ASSERT(bigbuf && data); + + if(at >= bigbuf->size) { + res = RES_BAD_ARG; + goto error; + } + + res = BIGBUF_FUNC__(fetch__)(bigbuf, at); + if(res != RES_OK) goto error; + + ASSERT(at >= bigbuf->buf_index); + bigbuf->buf.data[at - bigbuf->buf_index] = *data; + bigbuf->mode = BIGBUF_WRITE__; + +exit: + return res; +error: + goto exit; +} + +static INLINE const BIGBUF_DATA* +BIGBUF_FUNC__(at)(struct BIGBUF__* bigbuf, const size_t at) +{ + res_T res = RES_OK; (void)res; + ASSERT(bigbuf && at < bigbuf->size); + + res = BIGBUF_FUNC__(fetch__)(bigbuf, at); + ASSERT(res == RES_OK); + + ASSERT(at >= bigbuf->buf_index); + return bigbuf->buf.data + at - bigbuf->buf_index; +} + +#undef BIGBUF_ALIGNMENT +#undef BIGBUF_DATA +#undef BIGBUF_NAME + +#undef BIGBUF__ +#undef BIGBUF_ALIGNMENT__ +#undef BIGBUF_FUNC__ + +#endif /* !BIGBUF_NAME || !BIGBUF_TYPE */ + diff --git a/src/clock_time.c b/src/clock_time.c @@ -42,7 +42,7 @@ #define NSEC_PER_HOUR__ ((int64_t)60 * NSEC_PER_MIN__) #define NSEC_PER_DAY__ ((int64_t)24 * NSEC_PER_HOUR__) -void +struct time* time_current(struct time* t) { #ifdef CLOCK_TIME_WINDOWS @@ -82,9 +82,10 @@ time_current(struct time* t) t->sec = (int64_t)time.tv_sec; t->nsec = (int64_t)time.tv_nsec; #endif + return t; } -void +struct time* time_sub(struct time* res, const struct time* a, const struct time* b) { ASSERT(res && a && b); @@ -94,9 +95,10 @@ time_sub(struct time* res, const struct time* a, const struct time* b) --res->sec; res->nsec += 1000000000L; } + return res; } -void +struct time* time_add(struct time* res, const struct time* a, const struct time* b) { ASSERT(res && a && b); @@ -107,6 +109,7 @@ time_add(struct time* res, const struct time* a, const struct time* b) ++res->sec; res->nsec -= 1000000000L; } + return res; } int64_t diff --git a/src/clock_time.h b/src/clock_time.h @@ -37,19 +37,27 @@ enum time_unit { TIME_DAY = BIT(6) }; +static FINLINE struct time* +time_zero(struct time* time) +{ + ASSERT(time); + time->sec = time->nsec = 0; + return time; +} + BEGIN_DECLS -RSYS_API void +RSYS_API struct time* time_current (struct time* time); -RSYS_API void +RSYS_API struct time* time_sub (struct time* res, const struct time* a, const struct time* b); -RSYS_API void +RSYS_API struct time* time_add (struct time* res, const struct time* a, diff --git a/src/dynamic_array.h b/src/dynamic_array.h @@ -245,6 +245,13 @@ DARRAY_FUNC__(size_get)(const struct DARRAY_TYPE__* darray) return darray->size; } +static INLINE size_t +DARRAY_FUNC__(capacity)(const struct DARRAY_TYPE__* darray) +{ + ASSERT(darray); + return darray->capacity; +} + static INLINE DARRAY_DATA* DARRAY_FUNC__(data_get)(struct DARRAY_TYPE__* darray) { diff --git a/src/mem_allocator.c b/src/mem_allocator.c @@ -389,242 +389,6 @@ default_dump } /******************************************************************************* - * Proxy allocator functions - ******************************************************************************/ -#define PROXY_DEFAULT_ALIGNMENT 8 - -struct proxy_data { - struct mem_allocator* allocator; - struct mutex* mutex; - struct mem_node* node_list; -}; - -struct mem_node { - struct mem_node* next; - struct mem_node* prev; - size_t size; - const char* filename; - unsigned int fileline; - char reserved[2]; -}; - -static void* -proxy_alloc_aligned - (void* data, - const size_t size, - const size_t align, - const char* filename, - const unsigned int fileline) -{ - struct proxy_data* proxy_data = NULL; - char* mem = NULL; - size_t node_header_size = 0; - size_t node_size = 0; - size_t align_adjusted = 0; - struct mem_node* node = NULL; - - ASSERT(data); - proxy_data = data; - - if((IS_POW2(align) == 0) || align > 32768) - return NULL; - align_adjusted = align < PROXY_DEFAULT_ALIGNMENT - ? PROXY_DEFAULT_ALIGNMENT : align; - - node_header_size = ALIGN_SIZE(sizeof(struct mem_node), align_adjusted); - node_size = node_header_size + size; - node = MEM_ALLOC_ALIGNED(proxy_data->allocator, node_size, align_adjusted); - if(!node) - return NULL; - - mem = (char*)((uintptr_t)node + (uintptr_t)node_header_size); - mem[-1] = (char)(align_adjusted & 0xFF); - mem[-2] = (char)((align_adjusted >> 8) & 0xFF); - node->prev = NULL; - node->filename = filename; - node->fileline = fileline; - node->size = size; - - mutex_lock(proxy_data->mutex); - node->next = proxy_data->node_list; - if(proxy_data->node_list) - proxy_data->node_list->prev = node; - proxy_data->node_list = node; - mutex_unlock(proxy_data->mutex); - return mem; -} - -static void* -proxy_alloc - (void* data, - const size_t size, - const char* filename, - const unsigned int fileline) -{ - return proxy_alloc_aligned - (data, size, PROXY_DEFAULT_ALIGNMENT, filename, fileline); -} - -static void* -proxy_calloc - (void* data, - const size_t nbelmts, - const size_t size, - const char* filename, - const unsigned int fileline) -{ - size_t allocation_size = nbelmts * size; - void* mem = proxy_alloc_aligned - (data, allocation_size, PROXY_DEFAULT_ALIGNMENT, filename, fileline); - if(mem) - mem = memset(mem, 0, allocation_size); - return mem; -} - -static void -proxy_free(void* data, void* mem) -{ - if(mem) { - struct proxy_data* proxy_data = NULL; - struct mem_node* node = NULL; - uintptr_t alignment = 0; - - ASSERT(data); - proxy_data = data; - - alignment = (uintptr_t)(((char*)mem)[-1] | (((char*)mem)[-2] << 8)); - node = - (void*)((uintptr_t)mem - ALIGN_SIZE(sizeof(struct mem_node), alignment)); - - mutex_lock(proxy_data->mutex); - if(node->prev) { - node->prev->next = node->next; - } - if(node->next) { - node->next->prev = node->prev; - } - if(node->prev == NULL) { - proxy_data->node_list = node->next; - } - mutex_unlock(proxy_data->mutex); - MEM_RM(proxy_data->allocator, node); - } -} - -static void* -proxy_realloc - (void* data, - void* mem, - const size_t size, - const char* filename, - const unsigned int fileline) -{ - if(size == 0) { - proxy_free(data, mem); - return NULL; - } else if(mem == NULL) { - return proxy_alloc_aligned - (data, size, PROXY_DEFAULT_ALIGNMENT, filename, fileline); - } else { - struct mem_node* node = NULL; - uintptr_t node_header_size = 0; - uintptr_t alignment = 0; - - alignment = (uintptr_t)(((char*)mem)[-1] | (((char*)mem)[-2] << 8)); - node_header_size = ALIGN_SIZE(sizeof(struct mem_node), alignment); - node = (void*)((uintptr_t)mem - node_header_size); - - if(node->size == size) { - return mem; - } else { - void* dst = proxy_alloc_aligned - (data, size, alignment, filename, fileline); - if(!dst) { - proxy_free(data, mem); - return NULL; - } else { - dst = memcpy(dst, mem, size < node->size ? size : node->size); - proxy_free(data, mem); - return dst; - } - } - } -} - -static size_t -proxy_mem_size(void* data, void* mem) -{ - const uintptr_t alignment = (uintptr_t) - (((char*)mem)[-1] | (((char*)mem)[-2] << 8)); - struct mem_node* node = (struct mem_node*) - ((uintptr_t)mem - ALIGN_SIZE(sizeof(struct mem_node), alignment)); - struct proxy_data* proxy_data = (struct proxy_data*)data; - ASSERT(data); - return MEM_SIZE(proxy_data->allocator, node); -} - -static size_t -proxy_allocated_size(const void* data) -{ - const struct proxy_data* proxy_data = NULL; - struct mem_node* node = NULL; - size_t allocated_size = 0; - - ASSERT(data); - proxy_data = data; - mutex_lock(proxy_data->mutex); - for(node = proxy_data->node_list; node != NULL; node = node->next) { - allocated_size += MEM_SIZE(proxy_data->allocator, node); - } - mutex_unlock(proxy_data->mutex); - return allocated_size; -} - -static size_t -proxy_dump - (const void* data, - char* dump, - const size_t max_dump_len) -{ - const struct proxy_data* proxy_data = NULL; - struct mem_node* node = NULL; - size_t dump_len = 0; - size_t avaible_dump_space = max_dump_len ? max_dump_len - 1 /*NULL char*/ : 0; - - ASSERT(data && (!max_dump_len || dump)); - proxy_data = data; - - mutex_lock(proxy_data->mutex); - for(node = proxy_data->node_list; node != NULL; node = node->next) { - if(dump) { - const int len = snprintf - (dump, - avaible_dump_space, - "%lu bytes allocated at %s:%u%s", - (long unsigned)MEM_SIZE(proxy_data->allocator, node), - node->filename ? node->filename : "none", - node->fileline, - node->next ? ".\n" : "."); - ASSERT(len >= 0); - dump_len += (size_t)len; - - if((size_t)len < avaible_dump_space) { - dump += len; - avaible_dump_space -= (size_t)len; - } else if(dump) { - dump[avaible_dump_space] = '\0'; - avaible_dump_space = 0; - dump = NULL; - } - } - } - mutex_unlock(proxy_data->mutex); - return dump_len; -} - -#undef PROXY_DEFAULT_ALIGNMENT - -/******************************************************************************* * Default allocator ******************************************************************************/ static struct alloc_counter default_alloc_counter = {0, 0}; @@ -642,72 +406,6 @@ struct mem_allocator mem_default_allocator = { }; /******************************************************************************* - * Proxy allocator - ******************************************************************************/ -res_T -mem_init_proxy_allocator - (struct mem_allocator* proxy_allocator, - struct mem_allocator* allocator) -{ - struct proxy_data* proxy_data = NULL; - res_T res = RES_OK; - - if(!allocator || !proxy_allocator) { - res = RES_BAD_ARG; - goto error; - } - - proxy_data = MEM_CALLOC(allocator, 1, sizeof(struct proxy_data)); - if(!proxy_data) { - res = RES_MEM_ERR; - goto error; - } - proxy_data->mutex = mutex_create(); - if(!proxy_data->mutex) { - res = RES_MEM_ERR; - goto error; - } - proxy_data->allocator = allocator; - proxy_data->node_list = NULL; - - proxy_allocator->alloc = proxy_alloc; - proxy_allocator->calloc = proxy_calloc; - proxy_allocator->realloc = proxy_realloc; - proxy_allocator->mem_size = proxy_mem_size; - proxy_allocator->alloc_aligned = proxy_alloc_aligned; - proxy_allocator->rm = proxy_free; - proxy_allocator->allocated_size = proxy_allocated_size; - proxy_allocator->dump = proxy_dump; - proxy_allocator->data = (void*)proxy_data; - -exit: - return res; -error: - if(proxy_data) { - if(proxy_data->mutex) mutex_destroy(proxy_data->mutex); - MEM_RM(allocator, proxy_data); - } - if(proxy_allocator) - memset(proxy_allocator, 0, sizeof(struct mem_allocator)); - goto exit; -} - -void -mem_shutdown_proxy_allocator(struct mem_allocator* proxy) -{ - struct proxy_data* proxy_data = NULL; - struct mem_allocator* allocator = NULL; - - ASSERT(proxy); - proxy_data = proxy->data; - ASSERT(proxy_data->node_list == NULL); - mutex_destroy(proxy_data->mutex); - allocator = proxy_data->allocator; - MEM_RM(allocator, proxy_data); - memset(proxy, 0, sizeof(struct mem_allocator)); -} - -/******************************************************************************* * Regular allocator ******************************************************************************/ res_T @@ -720,6 +418,7 @@ mem_init_regular_allocator(struct mem_allocator* allocator) res = RES_BAD_ARG; goto error; } + memset(allocator, 0, sizeof(struct mem_allocator)); counter = mem_calloc(1, sizeof(struct alloc_counter)); if(!counter) { @@ -740,8 +439,7 @@ mem_init_regular_allocator(struct mem_allocator* allocator) exit: return res; error: - if(counter) mem_rm(counter); - if(allocator) memset(allocator, 0, sizeof(struct mem_allocator)); + if(allocator) mem_shutdown_regular_allocator(allocator); goto exit; } @@ -752,7 +450,9 @@ mem_shutdown_regular_allocator(struct mem_allocator* allocator) ASSERT(allocator); counter = allocator->data; - ASSERT(!counter->allocated_size && !counter->nb_allocs); - mem_rm(counter); + if(counter) { + ASSERT(!counter->allocated_size && !counter->nb_allocs); + mem_rm(counter); + } memset(allocator, 0, sizeof(struct mem_allocator)); } diff --git a/src/mem_allocator.h b/src/mem_allocator.h @@ -104,7 +104,7 @@ BEGIN_DECLS RSYS_API struct mem_allocator mem_default_allocator; /******************************************************************************* - * Regular allocation functions + * Regular allocation functions. ******************************************************************************/ RSYS_API void* mem_alloc(const size_t size); RSYS_API void* mem_calloc(const size_t nelmts, const size_t size); @@ -115,7 +115,7 @@ RSYS_API size_t mem_size(void* ptr); RSYS_API size_t mem_allocated_size(void); /******************************************************************************* - * Proxy allocator + * Proxy allocator - Register the filename and the fileline of the allocation. ******************************************************************************/ RSYS_API res_T mem_init_proxy_allocator @@ -127,7 +127,7 @@ mem_shutdown_proxy_allocator (struct mem_allocator* proxy_allocator); /******************************************************************************* - * Regular allocator + * Regular allocator - Wrap the regular allocation functions. ******************************************************************************/ RSYS_API res_T mem_init_regular_allocator @@ -137,6 +137,23 @@ RSYS_API void mem_shutdown_regular_allocator (struct mem_allocator* allocator); +/******************************************************************************* + * LIFO allocator - Allocate the memory in a preallocated memory chunk wrt to a + * LIFO pattern; the last allocated entry is the first that can be deallocated. + * If the entry to delete is not on top of the LIFO stack, it is marked as + * freed and will be effectively removed when it will be on top of the LIFO + * stack. + ******************************************************************************/ +RSYS_API res_T +mem_init_lifo_allocator + (struct mem_allocator* lifo_allocator, + struct mem_allocator* allocator, + const size_t size); /* Overall size that can be allocated by the allocator */ + +RSYS_API void +mem_shutdown_lifo_allocator + (struct mem_allocator* lifo_allocator); + END_DECLS #endif /* MEM_ALLOCATOR_H */ diff --git a/src/mem_lifo_allocator.c b/src/mem_lifo_allocator.c @@ -0,0 +1,304 @@ +/* Copyright (C) 2013-2017 Vincent Forest (vaplv@free.fr) + * + * The RSys library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The RSys library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L /* snprintf support */ + +#include "io_c99.h" +#include "math.h" +#include "mem_allocator.h" +#include "mutex.h" +#include <string.h> + +struct lifo_data { + char* top; + char* stack; + size_t remain; + size_t capacity; + struct mem_allocator* allocator; + struct mutex* mutex; +}; + +/* + * Stack entry memory layout: + * +----+-----------+-----------+------+----------+--------+-----------+ + * |... | A: 16bits | B: 48bits | DATA | C: 15bit | D: 1bit| E: 48bits | + * +----+-----------+-----------+------+----------+--------+-----------+ + * \___________Header_________/ \____________Footer___________/ + * + * A: size of the header in bytes + * B: size of DATA in bytes + * C: dummy data + * D: define whether the entry is in used or not + * E: size of DATA in bytes + */ + +#define LIFO_DEFAULT_ALIGNMENT 8 + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void* +lifo_alloc_aligned + (void* data, + const size_t size, + const size_t align, + const char* filename, + const unsigned int fileline) +{ + struct lifo_data* lifo = data; + size_t align_adjusted; + size_t header_size; + size_t footer_size; + size_t data_size; + size_t entry_size; + char* mem; + int64_t header; + int64_t footer; + ASSERT(data); + (void)filename, (void)fileline; + + if(!IS_POW2(align) || align > 32768) + return NULL; + + align_adjusted = align < LIFO_DEFAULT_ALIGNMENT + ? LIFO_DEFAULT_ALIGNMENT : align; + + header_size = ALIGN_SIZE(sizeof(int64_t), align_adjusted); + footer_size = sizeof(int64_t); + data_size = ALIGN_SIZE(size, sizeof(int64_t)); + entry_size = header_size + data_size + footer_size; + + ASSERT(data_size < (((int64_t)1<<48)-1)); + ASSERT(header_size < (1<<16)-1); + header = (int64_t)data_size | ((int64_t)header_size<<48); + footer = (int64_t)data_size | ((int64_t)1<<48); + + mutex_lock(lifo->mutex); + { /* Critical section */ + if(lifo->remain < entry_size) { + mem = NULL; + } else { + lifo->remain -= entry_size; + mem = lifo->top + header_size; + ASSERT(IS_ALIGNED(lifo->top, LIFO_DEFAULT_ALIGNMENT)); + lifo->top += entry_size; + *(int64_t*)(mem - sizeof(int64_t)) = header; + *(int64_t*)(mem + data_size) = footer; + } + } + mutex_unlock(lifo->mutex); + return mem; +} + +static void* +lifo_alloc + (void* data, + const size_t size, + const char* filename, + const unsigned int fileline) +{ + return lifo_alloc_aligned + (data, size, LIFO_DEFAULT_ALIGNMENT, filename, fileline); +} + +static void* +lifo_calloc + (void* data, + const size_t nelmts, + const size_t size, + const char* filename, + const unsigned int fileline) +{ + size_t allocation_size = nelmts * size; + void* mem = lifo_alloc(data, allocation_size, filename, fileline); + if(mem) mem = memset(mem, 0, allocation_size); + return mem; +} + +static void +lifo_free(void* data, void* mem) +{ + struct lifo_data* lifo = data; + int64_t header; + int64_t footer; + size_t data_size; + int64_t* pfooter; + char* ptr; + char* end; + ASSERT(data); + + if(!mem) return; + + ptr = mem; + header = *(int64_t*)(ptr-sizeof(int64_t)); + data_size = header & (((int64_t)1<<48)-1); + pfooter = (int64_t*)(ptr + data_size); + end = ptr + data_size + sizeof(int64_t); + + mutex_lock(lifo->mutex); + *pfooter &= ~((int64_t)1<<48); /* No more in use */ + if(end == lifo->top) { /* Pop */ + size_t header_size = (size_t)(header >> 48); + char* top = ptr - header_size; + + lifo->remain += data_size + header_size + sizeof(int64_t)/*footer size*/; + + while(top != lifo->stack) { /* Pop all free entries */ + ptr = top - sizeof(int64_t); + footer = *(int64_t*)ptr; + if(footer & ((int64_t)1<<48)) break; /* In use */ + + data_size = footer & (((int64_t)1<<48)-1); + ptr -= data_size; + + header = *(int64_t*)(ptr-sizeof(int64_t)); + ASSERT(data_size == (size_t)(header & (((int64_t)1<<48)-1))); + header_size = (size_t)(header>>48); + top = ptr - header_size; + + lifo->remain += data_size + header_size + sizeof(int64_t)/*footer_size*/; + ASSERT(lifo->remain <= lifo->capacity); + } + lifo->top = top; + } + mutex_unlock(lifo->mutex); +} + +static void* +lifo_realloc + (void* data, + void* mem, + const size_t size, + const char* filename, + const unsigned int fileline) +{ + if(size==0) { + lifo_free(data, mem); + return NULL; + } else { + size_t mem_size; + int64_t mem_header; + char* new_mem = lifo_alloc(data, size, filename, fileline); + if(!new_mem || !mem) return new_mem; + mem_header = *(int64_t*)((char*)mem - sizeof(int64_t)); + mem_size = (size_t)(mem_header & (((int64_t)1<<48)-1)); + memcpy(new_mem, mem, mem_size); + lifo_free(data, mem); + return new_mem; + } +} + +static size_t +lifo_mem_size(void* data, void* mem) +{ + int64_t header; + (void)data; + header = *(int64_t*)((char*)mem-sizeof(int64_t)); + return (size_t)(header & (((int64_t)1<<48)-1)); +} + +static size_t +lifo_allocated_size(const void* data) +{ + const struct lifo_data* lifo = data; + size_t size; + ASSERT(data); + mutex_lock(lifo->mutex); + size = lifo->capacity - lifo->remain; + mutex_unlock(lifo->mutex); + return size; +} + +static size_t +lifo_dump(const void* data, char* dump, const size_t max_dump_len) +{ + size_t len; + + len = (size_t)snprintf + (dump, max_dump_len, "%lu bytes allocated.", lifo_allocated_size(data)); + if(len >= (max_dump_len-1)) /* -1 <=> null char */ + dump[max_dump_len-1] = '\0'; + return len; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +mem_init_lifo_allocator + (struct mem_allocator* lifo_allocator, + struct mem_allocator* allocator, + const size_t size) +{ + struct lifo_data* lifo = NULL; + res_T res = RES_OK; + + if(!allocator || !lifo_allocator) { + res = RES_BAD_ARG; + goto error; + } + memset(lifo_allocator, 0, sizeof(struct mem_allocator)); + + lifo = MEM_CALLOC(allocator, 1, sizeof(struct lifo_data)); + if(!lifo) { + res = RES_MEM_ERR; + goto error; + } + lifo_allocator->data = (void*)lifo; + lifo->allocator = allocator; + lifo->stack = MEM_ALLOC_ALIGNED(allocator, size, LIFO_DEFAULT_ALIGNMENT); + if(!lifo->stack) { + res = RES_MEM_ERR; + goto error; + } + lifo->mutex = mutex_create(); + if(!lifo->mutex) { + res = RES_MEM_ERR; + goto error; + } + lifo->top = lifo->stack; + lifo->capacity = size; + lifo->remain = size; + + lifo_allocator->alloc = lifo_alloc; + lifo_allocator->calloc = lifo_calloc; + lifo_allocator->realloc = lifo_realloc; + lifo_allocator->mem_size = lifo_mem_size; + lifo_allocator->alloc_aligned = lifo_alloc_aligned; + lifo_allocator->rm = lifo_free; + lifo_allocator->allocated_size = lifo_allocated_size; + lifo_allocator->dump = lifo_dump; + +exit: + return res; +error: + if(lifo_allocator) + mem_shutdown_lifo_allocator(lifo_allocator); + goto exit; +} + +void +mem_shutdown_lifo_allocator(struct mem_allocator* allocator) +{ + struct lifo_data* lifo; + ASSERT(allocator); + lifo = allocator->data; + if(lifo) { + if(lifo->mutex) mutex_destroy(lifo->mutex); + if(lifo->stack) MEM_RM(lifo->allocator, lifo->stack); + MEM_RM(lifo->allocator, lifo); + } + memset(allocator, 0, sizeof(struct mem_allocator)); +} diff --git a/src/mem_proxy_allocator.c b/src/mem_proxy_allocator.c @@ -0,0 +1,320 @@ +/* Copyright (C) 2013-2017 Vincent Forest (vaplv@free.fr) + * + * The RSys library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The RSys library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L /* snprintf support */ + +#include "io_c99.h" +#include "math.h" +#include "mem_allocator.h" +#include "mutex.h" + +#include <string.h> + +struct proxy_data { + struct mem_allocator* allocator; + struct mutex* mutex; + struct mem_node* node_list; +}; + +struct mem_node { + struct mem_node* next; + struct mem_node* prev; + size_t size; + const char* filename; + unsigned int fileline; + char reserved[2]; +}; + +#define PROXY_DEFAULT_ALIGNMENT 8 + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void* +proxy_alloc_aligned + (void* data, + const size_t size, + const size_t align, + const char* filename, + const unsigned int fileline) +{ + struct proxy_data* proxy_data = NULL; + char* mem = NULL; + size_t node_header_size = 0; + size_t node_size = 0; + size_t align_adjusted = 0; + struct mem_node* node = NULL; + + ASSERT(data); + proxy_data = data; + + if((IS_POW2(align) == 0) || align > 32768) + return NULL; + align_adjusted = align < PROXY_DEFAULT_ALIGNMENT + ? PROXY_DEFAULT_ALIGNMENT : align; + + node_header_size = ALIGN_SIZE(sizeof(struct mem_node), align_adjusted); + node_size = node_header_size + size; + node = MEM_ALLOC_ALIGNED(proxy_data->allocator, node_size, align_adjusted); + if(!node) + return NULL; + + mem = (char*)((uintptr_t)node + (uintptr_t)node_header_size); + mem[-1] = (char)(align_adjusted & 0xFF); + mem[-2] = (char)((align_adjusted >> 8) & 0xFF); + node->prev = NULL; + node->filename = filename; + node->fileline = fileline; + node->size = size; + + mutex_lock(proxy_data->mutex); + node->next = proxy_data->node_list; + if(proxy_data->node_list) + proxy_data->node_list->prev = node; + proxy_data->node_list = node; + mutex_unlock(proxy_data->mutex); + return mem; +} + +static void* +proxy_alloc + (void* data, + const size_t size, + const char* filename, + const unsigned int fileline) +{ + return proxy_alloc_aligned + (data, size, PROXY_DEFAULT_ALIGNMENT, filename, fileline); +} + +static void* +proxy_calloc + (void* data, + const size_t nbelmts, + const size_t size, + const char* filename, + const unsigned int fileline) +{ + size_t allocation_size = nbelmts * size; + void* mem = proxy_alloc_aligned + (data, allocation_size, PROXY_DEFAULT_ALIGNMENT, filename, fileline); + if(mem) + mem = memset(mem, 0, allocation_size); + return mem; +} + +static void +proxy_free(void* data, void* mem) +{ + if(mem) { + struct proxy_data* proxy_data = NULL; + struct mem_node* node = NULL; + uintptr_t alignment = 0; + + ASSERT(data); + proxy_data = data; + + alignment = (uintptr_t)(((char*)mem)[-1] | (((char*)mem)[-2] << 8)); + node = + (void*)((uintptr_t)mem - ALIGN_SIZE(sizeof(struct mem_node), alignment)); + + mutex_lock(proxy_data->mutex); + if(node->prev) { + node->prev->next = node->next; + } + if(node->next) { + node->next->prev = node->prev; + } + if(node->prev == NULL) { + proxy_data->node_list = node->next; + } + mutex_unlock(proxy_data->mutex); + MEM_RM(proxy_data->allocator, node); + } +} + +static void* +proxy_realloc + (void* data, + void* mem, + const size_t size, + const char* filename, + const unsigned int fileline) +{ + if(size == 0) { + proxy_free(data, mem); + return NULL; + } else if(mem == NULL) { + return proxy_alloc_aligned + (data, size, PROXY_DEFAULT_ALIGNMENT, filename, fileline); + } else { + struct mem_node* node = NULL; + uintptr_t node_header_size = 0; + uintptr_t alignment = 0; + + alignment = (uintptr_t)(((char*)mem)[-1] | (((char*)mem)[-2] << 8)); + node_header_size = ALIGN_SIZE(sizeof(struct mem_node), alignment); + node = (void*)((uintptr_t)mem - node_header_size); + + if(node->size == size) { + return mem; + } else { + void* dst = proxy_alloc_aligned + (data, size, alignment, filename, fileline); + if(!dst) { + proxy_free(data, mem); + return NULL; + } else { + dst = memcpy(dst, mem, size < node->size ? size : node->size); + proxy_free(data, mem); + return dst; + } + } + } +} + +static size_t +proxy_mem_size(void* data, void* mem) +{ + const uintptr_t alignment = (uintptr_t) + (((char*)mem)[-1] | (((char*)mem)[-2] << 8)); + struct mem_node* node = (struct mem_node*) + ((uintptr_t)mem - ALIGN_SIZE(sizeof(struct mem_node), alignment)); + struct proxy_data* proxy_data = (struct proxy_data*)data; + ASSERT(data); + return MEM_SIZE(proxy_data->allocator, node); +} + +static size_t +proxy_allocated_size(const void* data) +{ + const struct proxy_data* proxy_data = NULL; + struct mem_node* node = NULL; + size_t allocated_size = 0; + + ASSERT(data); + proxy_data = data; + mutex_lock(proxy_data->mutex); + for(node = proxy_data->node_list; node != NULL; node = node->next) { + allocated_size += MEM_SIZE(proxy_data->allocator, node); + } + mutex_unlock(proxy_data->mutex); + return allocated_size; +} + +static size_t +proxy_dump + (const void* data, + char* dump, + const size_t max_dump_len) +{ + const struct proxy_data* proxy_data = NULL; + struct mem_node* node = NULL; + size_t dump_len = 0; + size_t avaible_dump_space = max_dump_len ? max_dump_len - 1 /*NULL char*/ : 0; + + ASSERT(data && (!max_dump_len || dump)); + proxy_data = data; + + mutex_lock(proxy_data->mutex); + for(node = proxy_data->node_list; node != NULL; node = node->next) { + if(dump) { + const int len = snprintf + (dump, + avaible_dump_space, + "%lu bytes allocated at %s:%u%s", + (long unsigned)MEM_SIZE(proxy_data->allocator, node), + node->filename ? node->filename : "none", + node->fileline, + node->next ? ".\n" : "."); + ASSERT(len >= 0); + dump_len += (size_t)len; + + if((size_t)len < avaible_dump_space) { + dump += len; + avaible_dump_space -= (size_t)len; + } else if(dump) { + dump[avaible_dump_space] = '\0'; + avaible_dump_space = 0; + dump = NULL; + } + } + } + mutex_unlock(proxy_data->mutex); + return dump_len; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ + +res_T +mem_init_proxy_allocator + (struct mem_allocator* proxy_allocator, + struct mem_allocator* allocator) +{ + struct proxy_data* proxy_data = NULL; + res_T res = RES_OK; + + if(!allocator || !proxy_allocator) { + res = RES_BAD_ARG; + goto error; + } + memset(proxy_allocator, 0, sizeof(struct mem_allocator)); + proxy_data = MEM_CALLOC(allocator, 1, sizeof(struct proxy_data)); + if(!proxy_data) { + res = RES_MEM_ERR; + goto error; + } + proxy_allocator->data = (void*)proxy_data; + + proxy_data->allocator = allocator; + proxy_data->mutex = mutex_create(); + if(!proxy_data->mutex) { + res = RES_MEM_ERR; + goto error; + } + proxy_allocator->alloc = proxy_alloc; + proxy_allocator->calloc = proxy_calloc; + proxy_allocator->realloc = proxy_realloc; + proxy_allocator->mem_size = proxy_mem_size; + proxy_allocator->alloc_aligned = proxy_alloc_aligned; + proxy_allocator->rm = proxy_free; + proxy_allocator->allocated_size = proxy_allocated_size; + proxy_allocator->dump = proxy_dump; + +exit: + return res; +error: + if(proxy_allocator) + mem_shutdown_proxy_allocator(proxy_allocator); + goto exit; +} + +void +mem_shutdown_proxy_allocator(struct mem_allocator* proxy) +{ + struct proxy_data* proxy_data = NULL; + + ASSERT(proxy); + proxy_data = proxy->data; + if(proxy_data) { + ASSERT(proxy_data->node_list == NULL); + if(proxy_data->mutex) mutex_destroy(proxy_data->mutex); + MEM_RM(proxy_data->allocator, proxy_data); + } + memset(proxy, 0, sizeof(struct mem_allocator)); +} + diff --git a/src/test_big_buffer.c b/src/test_big_buffer.c @@ -0,0 +1,369 @@ +/* Copyright (C) 2013-2017 Vincent Forest (vaplv@free.fr) + * + * The RSys library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The RSys library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ + +#include "big_buffer.h" +#include "test_utils.h" + +#include <stdlib.h> + +#define BIGBUF_NAME byte +#define BIGBUF_DATA char +#include "big_buffer.h" + +#define BIGBUF_NAME integer +#define BIGBUF_DATA int +#define BIGBUF_ALIGNMENT 64 +#include "big_buffer.h" + +#define BIGBUF_NAME real +#define BIGBUF_DATA double +#include "big_buffer.h" + +static INLINE double +rand_canonic(void) +{ + return (double)rand()/(double)(RAND_MAX-1); +} + +static void +test_byte(struct mem_allocator* allocator) +{ + struct bigbuf_byte bytes; + size_t i; + char byte; + + CHECK(bigbuf_byte_init(allocator, 4, NULL, BIGBUF_LOAD_STREAM, &bytes), RES_BAD_ARG); + CHECK(bigbuf_byte_init(allocator, 0, NULL, 0, &bytes), RES_BAD_ARG); + CHECK(bigbuf_byte_init(allocator, 4, NULL, 0, &bytes), RES_OK); + CHECK(bigbuf_byte_size_get(&bytes), 0); + + FOR_EACH(i, 0, 32) { + byte = (char)i; + CHECK(bigbuf_byte_push_back(&bytes, &byte), RES_OK); + } + + CHECK(bigbuf_byte_size_get(&bytes), 32); + + FOR_EACH(i, 0, bigbuf_byte_size_get(&bytes)) { + byte = *bigbuf_byte_at(&bytes, i); + CHECK((size_t)byte, i); + } + + FOR_EACH(i, 0, 16) { + size_t id = (size_t)(rand_canonic() * (double)bigbuf_byte_size_get(&bytes)); + byte = *bigbuf_byte_at(&bytes, id); + CHECK((size_t)byte, id); + } + + FOR_EACH(i, 0, 32) { + byte = (char)(i + 32); + CHECK(bigbuf_byte_push_back(&bytes, &byte), RES_OK); + } + + FOR_EACH(i, 0, bigbuf_byte_size_get(&bytes)) { + byte = *bigbuf_byte_at(&bytes, i); + CHECK((size_t)byte, i); + } + + CHECK(bigbuf_byte_size_get(&bytes), 64); + byte = *bigbuf_byte_at(&bytes, 61); + + CHECK(bigbuf_byte_resize(&bytes, 63), RES_OK); + CHECK(bigbuf_byte_size_get(&bytes), 63); + byte = *bigbuf_byte_at(&bytes, 62); + CHECK(byte, 62); + + byte = 63; + CHECK(bigbuf_byte_push_back(&bytes, &byte), RES_OK); + CHECK(bigbuf_byte_size_get(&bytes), 64); + FOR_EACH(i, 0, bigbuf_byte_size_get(&bytes)) { + byte = *bigbuf_byte_at(&bytes, i); + CHECK((size_t)byte, i); + } + + CHECK(bigbuf_byte_resize(&bytes, 32), RES_OK); + CHECK(bigbuf_byte_resize(&bytes, 32), RES_OK); + + byte = *bigbuf_byte_at(&bytes, 31); + CHECK(byte, 31); + FOR_EACH(i, 0, 32) { + byte = (char)(i + 32); + CHECK(bigbuf_byte_push_back(&bytes, &byte), RES_OK); + } + FOR_EACH(i, 0, bigbuf_byte_size_get(&bytes)) { + byte = *bigbuf_byte_at(&bytes, i); + CHECK((size_t)byte, i); + } + + CHECK(bigbuf_byte_resize(&bytes, 128), RES_OK); + CHECK(bigbuf_byte_size_get(&bytes), 128); + FOR_EACH(i, 0, bigbuf_byte_size_get(&bytes)) { + byte = *bigbuf_byte_at(&bytes, i); + if(i < 64) { + CHECK((size_t)byte, i); + } else { + CHECK(byte, 0); + } + } + + CHECK(bigbuf_byte_set(&bytes, 128, &byte), RES_BAD_ARG); + FOR_EACH(i, 0, 64) { + byte = (char)(i+64); + CHECK(bigbuf_byte_set(&bytes, (size_t)byte, &byte), RES_OK); + byte = *bigbuf_byte_at(&bytes, (size_t)byte - 1); + CHECK((size_t)byte, i+63); + } + FOR_EACH(i, 0, bigbuf_byte_size_get(&bytes)) { + byte = *bigbuf_byte_at(&bytes, i); + CHECK((size_t)byte, i); + } + + CHECK(bigbuf_byte_clear(&bytes), RES_OK); + CHECK(bigbuf_byte_size_get(&bytes), 0); + CHECK(bigbuf_byte_set(&bytes, 0, &byte), RES_BAD_ARG); + FOR_EACH(i, 0, 16) { + byte = (char)i; + CHECK(bigbuf_byte_push_back(&bytes, &byte), RES_OK); + } + CHECK(bigbuf_byte_size_get(&bytes), 16); + FOR_EACH(i, 0, bigbuf_byte_size_get(&bytes)) { + byte = *bigbuf_byte_at(&bytes, i); + CHECK((size_t)byte, i); + } + + bigbuf_byte_release(&bytes); +} + +static void +test_integer(struct mem_allocator* allocator) +{ + struct bigbuf_integer ints; + struct bigbuf_integer ints2; + FILE* stream; + size_t i; + int integer; + + NCHECK(stream = tmpfile(), NULL); + + CHECK(bigbuf_integer_init(NULL, 1, stream, 0, &ints), RES_OK); + + FOR_EACH(i, 0, 999) { + integer = (int)i; + CHECK(bigbuf_integer_push_back(&ints, &integer), RES_OK); + } + + FOR_EACH(i, 0, 666) { + size_t id = (size_t)(rand_canonic() * (double)bigbuf_integer_size_get(&ints)); + integer = *bigbuf_integer_at(&ints, id); + CHECK((size_t)integer, id); + } + + FOR_EACH(i, 0, 666) { + integer = (int)i + 999; + CHECK(bigbuf_integer_push_back(&ints, &integer), RES_OK); + } + + CHECK(bigbuf_integer_size_get(&ints), 1665); + FOR_EACH(i, 0, bigbuf_integer_size_get(&ints)) { + integer = *bigbuf_integer_at(&ints, i); + CHECK((size_t)integer, i); + } + + bigbuf_integer_release(&ints); + + rewind(stream); + CHECK(bigbuf_integer_init(allocator, 1, stream, BIGBUF_LOAD_STREAM, &ints), RES_OK); + CHECK(bigbuf_integer_size_get(&ints), 1665); + + FOR_EACH(i, 0, 666) { + size_t id = (size_t)(rand_canonic() * (double)bigbuf_integer_size_get(&ints)); + integer = *bigbuf_integer_at(&ints, id); + CHECK((size_t)integer, id); + } + + FOR_EACH(i, 0, 1024) { + integer = (int)i + 1665; + CHECK(bigbuf_integer_push_back(&ints, &integer), RES_OK); + } + CHECK(bigbuf_integer_size_get(&ints), 2689); + bigbuf_integer_release(&ints); + + rewind(stream); + CHECK(bigbuf_integer_init(NULL, 1, stream, BIGBUF_LOAD_STREAM, &ints2), RES_OK); + CHECK(bigbuf_integer_size_get(&ints2), bigbuf_integer_size_get(&ints)); + CHECK(bigbuf_integer_size_get(&ints2), 2689); + + FOR_EACH(i, 0, bigbuf_integer_size_get(&ints2)) { + integer = *bigbuf_integer_at(&ints2, i); + CHECK((size_t)integer, i); + } + + CHECK(bigbuf_integer_resize(&ints2, 999), RES_OK); + CHECK(bigbuf_integer_size_get(&ints2), 999); + FOR_EACH(i, 0, bigbuf_integer_size_get(&ints2)) { + integer = *bigbuf_integer_at(&ints2, i); + integer = -integer; + CHECK(bigbuf_integer_set(&ints2, i, &integer), RES_OK); + if(i) { + integer = *bigbuf_integer_at(&ints2, i-1); + CHECK(integer, 1 - (int)i); + } + } + + FOR_EACH(i, 0, bigbuf_integer_size_get(&ints2)) { + integer = *bigbuf_integer_at(&ints2, i); + CHECK(integer, -(int)i); + } + + CHECK(bigbuf_integer_clear(&ints2), RES_OK); + CHECK(bigbuf_integer_size_get(&ints2), 0); + CHECK(bigbuf_integer_set(&ints2, 0, &integer), RES_BAD_ARG); + FOR_EACH(i, 0, 64) { + integer = (int)i; + CHECK(bigbuf_integer_push_back(&ints2, &integer), RES_OK); + } + CHECK(bigbuf_integer_size_get(&ints2), 64); + FOR_EACH(i, 0, bigbuf_integer_size_get(&ints2)) { + integer = *bigbuf_integer_at(&ints2, i); + CHECK(integer, (int)i); + } + + bigbuf_integer_release(&ints2); + fclose(stream); +} + +static void +test_real(struct mem_allocator* allocator) +{ + struct bigbuf_real reals; + struct bigbuf_real reals2; + FILE* stream; + double real; + size_t ids[32]; + size_t i; + + NCHECK(stream = tmpfile(), NULL); + CHECK(bigbuf_real_init(allocator, 45, stream, BIGBUF_LOAD_STREAM, &reals), RES_IO_ERR); + CHECK(bigbuf_real_init(allocator, 45, stream, 0, &reals), RES_OK); + + FOR_EACH(i, 0, 4000) { + real = (double)i / 100.0; + CHECK(bigbuf_real_push_back(&reals, &real), RES_OK); + } + + CHECK(bigbuf_real_size_get(&reals), 4000); + + FOR_EACH(i, 0, 10) { + size_t id = (size_t)(rand_canonic() * (double)bigbuf_real_size_get(&reals)); + real = *bigbuf_real_at(&reals, id); + CHECK(real, (double)id/100.0); + } + + rewind(stream); + + FOR_EACH(i, 0, 2000) { + real = (double)(i+4000) / 100.0; + CHECK(bigbuf_real_push_back(&reals, &real), RES_OK); + } + + bigbuf_real_flush(&reals); + + rewind(stream); + bigbuf_real_init(allocator, 45, stream, BIGBUF_LOAD_STREAM, &reals2); + + CHECK(bigbuf_real_size_get(&reals), 6000); + CHECK(bigbuf_real_size_get(&reals2), 6000); + + FOR_EACH(i, 0, bigbuf_real_size_get(&reals)) { + real = *bigbuf_real_at(&reals, i); + CHECK(real, (double)i/100.0); + } + + FOR_EACH(i, 0, 300) { + size_t id = (size_t)(rand_canonic() * (double)bigbuf_real_size_get(&reals2)); + real = *bigbuf_real_at(&reals2, id); + CHECK(real, (double)id/100.0); + } + + real = *bigbuf_real_at(&reals2, 5990); + CHECK(real, 59.90); + CHECK(bigbuf_real_resize(&reals2, 5992), RES_OK); + real = *bigbuf_real_at(&reals2, 5990); + CHECK(real, 59.90); + CHECK(bigbuf_real_resize(&reals2, 5990), RES_OK); + real = *bigbuf_real_at(&reals2, 5989); + CHECK(real, 59.89); + CHECK(bigbuf_real_resize(&reals2, 5985), RES_OK); + real = *bigbuf_real_at(&reals2, 5984); + CHECK(real, 59.84); + + FOR_EACH(i, 0, sizeof(ids)/sizeof(size_t)) { + ids[i] = (size_t)(rand_canonic() * (double)bigbuf_real_size_get(&reals2)); + real = (double)ids[i]; + CHECK(bigbuf_real_set(&reals2, ids[i], &real), RES_OK); + } + + CHECK(bigbuf_real_size_get(&reals2), 5985); + FOR_EACH(i, 0, bigbuf_real_size_get(&reals2)) { + size_t j; + real = *bigbuf_real_at(&reals2, i); + + FOR_EACH(j, 0, sizeof(ids)/sizeof(size_t)) { + if(i == ids[j]) break; + } + + if(j == sizeof(ids)/sizeof(size_t)) { + CHECK(real, (double)i/100.0); + } else { + CHECK(real, (double)i); + } + } + + CHECK(bigbuf_real_clear(&reals2), RES_OK); + CHECK(bigbuf_real_size_get(&reals2), 0); + CHECK(bigbuf_real_set(&reals2, 0, &real), RES_BAD_ARG); + FOR_EACH(i, 0, 64) { + real = (double)i; + CHECK(bigbuf_real_push_back(&reals2, &real), RES_OK); + } + CHECK(bigbuf_real_size_get(&reals2), 64); + FOR_EACH(i, 0, bigbuf_real_size_get(&reals2)) { + real = *bigbuf_real_at(&reals2, i); + CHECK(real, (double)i); + } + + bigbuf_real_release(&reals); + bigbuf_real_release(&reals2); + fclose(stream); +} + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator_proxy; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator_proxy, &mem_default_allocator); + + test_byte(&allocator_proxy); + test_integer(&allocator_proxy); + test_real(&allocator_proxy); + + check_memory_allocator(&allocator_proxy); + mem_shutdown_proxy_allocator(&allocator_proxy); + CHECK(mem_allocated_size(), 0); + return 0; +} + diff --git a/src/test_mem_allocator.c b/src/test_mem_allocator.c @@ -157,11 +157,15 @@ main(int argc, char** argv) test_allocator(&mem_default_allocator); printf("\n-- Regular allocator\n"); + CHECK(mem_init_regular_allocator(NULL), RES_BAD_ARG); CHECK(mem_init_regular_allocator(&allocator), RES_OK); test_allocator(&allocator); mem_shutdown_regular_allocator(&allocator); printf("\n-- Proxy allocator of default allocator\n"); + CHECK(mem_init_proxy_allocator(NULL, NULL), RES_BAD_ARG); + CHECK(mem_init_proxy_allocator(&allocator, NULL), RES_BAD_ARG); + CHECK(mem_init_proxy_allocator(NULL, &mem_default_allocator), RES_BAD_ARG); CHECK(mem_init_proxy_allocator(&allocator, &mem_default_allocator), RES_OK); test_allocator(&allocator); @@ -172,12 +176,23 @@ main(int argc, char** argv) mem_shutdown_proxy_allocator(&allocator2); mem_shutdown_proxy_allocator(&allocator); + printf("\n-- LIFO allocator\n"); + CHECK(mem_init_lifo_allocator(NULL, NULL, 4096), RES_BAD_ARG); + CHECK(mem_init_lifo_allocator(&allocator, NULL, 4096), RES_BAD_ARG); + CHECK(mem_init_lifo_allocator(NULL, &mem_default_allocator, 4096), RES_BAD_ARG); + CHECK(mem_init_lifo_allocator + (&allocator, &mem_default_allocator, 4096), RES_OK); + CHECK(MEM_ALLOC(&allocator, 4097), NULL); + test_allocator(&allocator); + mem_shutdown_lifo_allocator(&allocator); + CHECK(MEM_AREA_OVERLAP(mem, sizeof(int[2]), mem + 2, sizeof(int[6])), 0); CHECK(MEM_AREA_OVERLAP(mem + 4, sizeof(int[4]), mem, sizeof(int[4])), 0); CHECK(MEM_AREA_OVERLAP(mem, sizeof(int[2]), mem + 1, sizeof(int[7])), 1); CHECK(MEM_AREA_OVERLAP(mem + 7, sizeof(int[1]), mem, sizeof(int[8])), 1); CHECK(MEM_ALLOCATED_SIZE(&mem_default_allocator), 0); + CHECK(mem_allocated_size(), 0); return 0; } diff --git a/src/test_time.c b/src/test_time.c @@ -25,11 +25,11 @@ main(int argc, char** argv) int64_t i = 0; (void)argc, (void)argv; - time_current(&start); + CHECK(time_current(&start), &start); FOR_EACH(i, 0, INT32_MAX / 64); /* Active wait */ - time_current(&end); + CHECK(time_current(&end), &end); - time_sub(&res, &end, &start); + CHECK(time_sub(&res, &end, &start), &res); time = time_val(&res, TIME_NSEC); CHECK(time >= 0, 1 ); CHECK(time_val(&res, TIME_USEC), time / 1000); @@ -38,14 +38,31 @@ main(int argc, char** argv) time_dump (&res, TIME_SEC|TIME_MSEC|TIME_USEC, NULL, dump, sizeof(dump)); - printf("%s--\n", dump); + printf(">>> %s\n", dump); time_dump(&res, TIME_ALL, NULL, dump, sizeof(dump)); - printf("%s--\n", dump); + printf(">>> %s\n", dump); - time_sub(&res, &end, &end); + CHECK(time_add(&res, &res, &res), &res); + CHECK(time_val(&res, TIME_NSEC), 2*time); time_dump(&res, TIME_ALL, NULL, dump, sizeof(dump)); - printf("%s--\n", dump); + printf(">>> %s\n", dump); + + time = time_val(&res, TIME_NSEC); + CHECK(time_zero(&start), &start); + CHECK(time_val(&start, TIME_NSEC), 0); + CHECK(time_add(&res, &res, &start), &res); + CHECK(time_val(&res, TIME_NSEC), time); + CHECK(time_add(&start, &res, &start), &start); + CHECK(time_val(&start, TIME_NSEC), time); + CHECK(time_sub(&res, time_zero(&start), &res), &res); + CHECK(time_val(&res, TIME_NSEC), -time); + + CHECK(time_sub(&res, &end, &end), &res); + CHECK(time_val(&res, TIME_NSEC), 0); + time_dump(&res, TIME_ALL, NULL, dump, sizeof(dump)); + printf(">>> %s\n", dump); time_dump(&res, 0, NULL, dump, sizeof(dump)); - printf("%s--\n", dump); + printf(">>> %s\n", dump); + return 0; }