commit 9b2d0b43f570f6e6dced034a98114f33f16d45a1
parent a2b8e595dff950014cb36ce17bd8882c634b7642
Author: vaplv <vaplv@free.fr>
Date: Tue, 19 Sep 2017 11:58:10 +0200
Merge branch 'release-0.5'
Diffstat:
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;
}