├── .gitignore ├── LICENSE.md ├── Makefile ├── README.md ├── clib.json ├── include └── mem_pool.h ├── package.json ├── src ├── buffer.c ├── fixed.c ├── internals.h ├── utils.c └── variable.c └── test ├── fixed_test.c ├── main.c ├── minunit └── minunit.h └── variable_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *vim* 3 | *ycm* 4 | *.o 5 | *.so 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -std=c11 -g -Wall -Wextra -ftrapv -Wshadow -Wundef -Wcast-align -Wunreachable-code -O1 2 | SRC = src/*.c 3 | TEST_SRC = test/*.c 4 | 5 | OBJ = mem_pool.o 6 | LIB = libmem_pool.so 7 | 8 | INCLUDE_DIR = /usr/local/include/mem_pool 9 | 10 | 11 | .PHONY: test 12 | 13 | 14 | build: 15 | rm -f ./*.o 16 | $(CC) $(CFLAGS) -c -fpic $(SRC) 17 | $(CC) -shared -fpic -o $(LIB) ./*.o -lpthread 18 | 19 | install: build 20 | sudo mv $(LIB) /usr/local/lib 21 | sudo mkdir -p $(INCLUDE_DIR) 22 | sudo cp include/*.h $(INCLUDE_DIR) 23 | 24 | #-D _POSIX_C_SOURCE=199309 Needed by minunit.h to enable some time related stuff 25 | compile-test: install 26 | $(CC) $(CFLAGS) -D _POSIX_C_SOURCE=199309L $(TEST_SRC) -lmem_pool -o test.o 27 | 28 | test: compile-test 29 | ./test.o 30 | 31 | test-valgrind: compile-test 32 | valgrind --track-origins=yes --leak-check=full --show-reachable=yes ./test.o 33 | 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Memory Pool 2 | 3 | Dynamic memory pool implementation, for reusable fixed, or variable sized memory blocks, using `pthread mutex` locks. 4 | 5 | ### Installation 6 | 7 | If you don't want to include the source in your project, you can install it as a dynamic library via `make install` and link against it with `-lmem_pool` 8 | 9 | For a quick check, run `make test` or `make test-valgrind` 10 | 11 | You can also install via [`clib`](https://github.com/clibs/clib): `clib install Isty001/mem-pool` 12 | 13 | ### How it works 14 | 15 | There are two main use cases when allocating: 16 | * [Fixed sized objects](#fixed-pool) 17 | * This is useful when you want to store a large number of the same size of objects, this type of pool does a bit less work to allocate new blocks 18 | * [Variable sized objects](#variable-pool) 19 | * This is basically a generic allocator, that is capable of allocating a **best fitting** block by storing some meta data: 20 | ``` 21 | | Pool | 22 | | Buffer | Buffer | 23 | | H | B | H | B | H | B | H| B | H| B | 24 | ``` 25 | *H: Header 26 | B: Actual Block* 27 | 28 | When allocating a given size of block, a header is also stored which contains the size of the block, and pointer to the previous header in the buffer. This is required to merge neighbouring blocks when the pointer is given back to the pool, so it can be defragmented. The stored size also helps us to find the best fitting block from the free list. 29 | 30 | All the pointers given by the pools are pointing to *aligned* blocks. 31 | 32 | ## Ussage & API 33 | 34 | To use the library you only need to `#include `. Every function returns one of the `MemPoolError` enum values, thus making the error checking pretty simple. 35 | 36 | ```c 37 | MemPoolErr err; 38 | 39 | if (MEM_POOL_ERR_OK != (err = pool_*())) { 40 | //handle err 41 | } 42 | ``` 43 | 44 | ## FixedMemPool 45 | 46 | Initialization: 47 | 48 | ```c 49 | size_t block_size = sizeof(struct test); 50 | size_t increase_count = 500; 51 | FixedMemPool *pool 52 | 53 | pool_fixed_init(&pool, block_size, increase_count); 54 | ``` 55 | 56 | The pool'll be able to provide `block_size` blocks, and when it runs out of memory, will *actually* allocate a new internal buffer `increase_count` * `block_size size` 57 | 58 | 59 | Get a block: 60 | 61 | ```c 62 | void *ptr; 63 | 64 | pool_fixed_alloc(pool, (void **)&ptr); 65 | ``` 66 | 67 | It can make sense to iterate through a pool of objects when you know that all of them are of the same type: 68 | 69 | ```c 70 | static MemPoolForeachStatus callback(void *item) 71 | { 72 | if (should_stop(item)) { 73 | return MEM_POOL_FOREACH_STOP; 74 | } 75 | 76 | return MEM_POOL_FOREACH_CONTINUE; 77 | } 78 | 79 | pool_fixed_foreach(pool, callback); 80 | ``` 81 | 82 | To check if a pointer is from the pool: 83 | 84 | ```c 85 | if (MEM_POOL_ERR_OK == pool_fixed_is_associated(pool, ptr)) { /* */ } 86 | ``` 87 | Won't check if the block is not used anymore. 88 | 89 | 90 | When not needed anymore, give the pointer back to the pool's free list, and make it reusable. 91 | 92 | ```c 93 | pool_fixed_free(pool, ptr); 94 | ``` 95 | 96 | To actually `free` all the memory allocated: 97 | 98 | ```c 99 | pool_fixed_destroy(pool); 100 | ``` 101 | 102 | ## VariableMemPool 103 | 104 | This type of pool has a very similar API. 105 | 106 | Initialization: 107 | 108 | ```c 109 | size_t grow_size = 500; 110 | size_t tolerance_percent = 20; 111 | VariableMemPool *pool; 112 | 113 | pool_variable_init(&pool, grow_size, tolerance_percent); 114 | ``` 115 | `grow_size` deremines the size of a new buffer required from malloc when no more free (fitting) space left. When trying to figure out a realistic size for your use case, take into account the it also includes the (aligned) size of the headers too. 116 | 117 | `tolerance_percent` is the maximum difference in percentage when looking for best fitting blocks from the free list. You can alternatively pas `MEM_POOL_NO_BEST_FIT` to skip this check. 118 | 119 | 120 | Get a block: 121 | 122 | ```c 123 | void *ptr; 124 | 125 | pool_variable_alloc(pool, sizeof(some_type), (void **)&ptr); 126 | ``` 127 | 128 | To check if a pointer is from the pool: 129 | 130 | ```c 131 | if (MEM_POOL_ERR_OK == pool_variable_is_associated(pool, ptr)) { /* */ } 132 | ``` 133 | 134 | The pool is able to tell the *aligned* size of the block via 135 | ```c 136 | pool_variable_aligned_sizeof(pool, ptr, &size) 137 | ``` 138 | 139 | In order to make the piece of memory reusable: 140 | 141 | ```c 142 | pool_variable_free(pool, ptr); 143 | ``` 144 | Before appending to the free list, this function will attempt to merge neighbouring free memory blocks (including the space used by their headers) in the given buffer. 145 | 146 | To actually free all the memory allocated: 147 | 148 | ```c 149 | pool_variable_destroy(pool); 150 | ``` 151 | 152 | -------------------------------------------------------------------------------- /clib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mem-pool", 3 | "version": "1.1.2", 4 | "repo": "isty001/mem-pool", 5 | "description": "Dynamic memory pool implementation, for reusable fixed, or variable sized memory blocks, using pthread mutex locks", 6 | "keywords": ["memory-pool", "memory", "pool", "pthread", "c", "alignment", "mutex", "memorypool", "locks", "buffer", "variable"], 7 | "license": "MIT", 8 | "src": ["src/buffer.c", "src/fixed.c", "src/internals.h", "src/utils.c", "src/variable.c", "include/mem_pool.h"] 9 | } 10 | -------------------------------------------------------------------------------- /include/mem_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef MEM_POOL_H 2 | #define MEM_POOL_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | /** 11 | * Pass this as the second argument to pool_variable_init if you wan to skip 12 | * best fit checks 13 | */ 14 | /* #define MEM_POOL_NO_BEST_FIT 101 */ 15 | static const uint16_t MEM_POOL_NO_BEST_FIT = 101; 16 | 17 | 18 | typedef struct FixedMemPool FixedMemPool; 19 | 20 | typedef struct VariableMemPool VariableMemPool; 21 | 22 | /** 23 | * Return one of these from the callback function to either continue or stop the iteration 24 | */ 25 | typedef enum { 26 | MEM_POOL_FOREACH_STOP, 27 | MEM_POOL_FOREACH_CONTINUE 28 | } MemPoolForeachStatus; 29 | 30 | typedef MemPoolForeachStatus (*FixedPoolForeach)(void *block); 31 | 32 | typedef enum { 33 | MEM_POOL_ERR_OK, 34 | MEM_POOL_ERR_MUTEX_INIT, 35 | MEM_POOL_ERR_MUTEX_DESTROY, 36 | MEM_POOL_ERR_LOCK, 37 | MEM_POOL_ERR_UNLOCK, 38 | MEM_POOL_ERR_MALLOC, 39 | MEM_POOL_ERR_UNKNOWN_BLOCK 40 | } MemPoolError; 41 | 42 | /** 43 | * initializes a new MemPool, with the given block size. If it runs out of space, 44 | * it'll create a new internal Buffer with increase_count * block_size size 45 | */ 46 | MemPoolError pool_fixed_init(FixedMemPool **pool, size_t block_size, size_t increase_count); 47 | 48 | MemPoolError pool_fixed_alloc(FixedMemPool *pool, void **ptr); 49 | 50 | /** 51 | * @return MEM_POOL_ERR_OK if associated MEM_POOL_ERR_UNKNOWN if not, or the error indicating the failure 52 | */ 53 | MemPoolError pool_fixed_is_associated(FixedMemPool *pool, void *ptr); 54 | 55 | /** 56 | * Iterates through all the blocks allocated with the given pool 57 | */ 58 | MemPoolError pool_fixed_foreach(FixedMemPool *pool, FixedPoolForeach callback); 59 | 60 | /** 61 | * The memory block is not actually freed, just given back to the pool to reuse it 62 | * 63 | * @return -1 if the pointer is not known by the pool, 0 otherwise 64 | */ 65 | MemPoolError pool_fixed_free(FixedMemPool *pool, void *ptr); 66 | 67 | MemPoolError pool_fixed_destroy(FixedMemPool *pool); 68 | 69 | /** 70 | * grow_size deremines the size of a new buffer required from malloc when no more free (fitting) space left 71 | * tolerance_percent is the maximum difference in percentage when looking for best fitting free blocks 72 | * if not reuqired, use MEM_POOL_NO_BEST_FIT 73 | */ 74 | MemPoolError pool_variable_init(VariableMemPool **pool, size_t grow_size, uint16_t tolerance_percent); 75 | 76 | MemPoolError pool_variable_alloc(VariableMemPool *pool, size_t size, void **ptr); 77 | 78 | /** 79 | * @return MEM_POOL_ERR_OK if associated MEM_POOL_ERR_UNKNOWN if not, or the error indicating the failure 80 | */ 81 | MemPoolError pool_variable_is_associated(VariableMemPool *pool, void *ptr); 82 | 83 | /* 84 | * Before appending to the free list, this function will attempt to merge neighbouring memory blocks 85 | * (including the space used by their headers) in the given buffer. 86 | */ 87 | MemPoolError pool_variable_free(VariableMemPool *pool, void *ptr); 88 | 89 | MemPoolError pool_variable_destroy(VariableMemPool *pool); 90 | 91 | /* 92 | * @return same as pool_variable_is_associated 93 | * The *aligned* size of the block is written to the size_t pointer 94 | */ 95 | MemPoolError pool_variable_aligned_sizeof(VariableMemPool *pool, void *ptr, size_t *size); 96 | 97 | /** 98 | * Rounds up the size to the correct alignment 99 | */ 100 | size_t mem_align(size_t size); 101 | 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mem-pool", 3 | "version": "1.1.2", 4 | "repo": "isty001/mem-pool", 5 | "description": "Dynamic memory pool implementation, for reusable fixed, or variable sized memory blocks, using pthread mutex locks", 6 | "keywords": ["memory-pool", "memory", "pool", "pthread", "c", "alignment", "mutex", "memorypool", "locks", "buffer", "variable"], 7 | "license": "MIT", 8 | "src": ["src/buffer.c", "src/fixed.c", "src/internals.h", "src/utils.c", "src/variable.c", "include/mem_pool.h"] 9 | } 10 | -------------------------------------------------------------------------------- /src/buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "internals.h" 3 | 4 | Buffer *buffer_new(size_t size) 5 | { 6 | Buffer *buff = malloc(sizeof(Buffer)); 7 | 8 | if (!buff) { 9 | return NULL; 10 | } 11 | 12 | buff->start = malloc(size); 13 | 14 | if (!buff->start) { 15 | free(buff); 16 | return NULL; 17 | } 18 | 19 | if (!memset(buff->start, 0, size)) { 20 | free(buff); 21 | return NULL; 22 | } 23 | 24 | buff->curr_ptr = buff->start; 25 | buff->next = NULL; 26 | buff->prev_ptr = NULL; 27 | buff->end = buff->curr_ptr + size; 28 | 29 | return buff; 30 | } 31 | 32 | Buffer *buffer_list_find(Buffer *head, void *ptr) 33 | { 34 | while (head) { 35 | if (buffer_has(head, ptr)) { 36 | return head; 37 | } 38 | head = head->next; 39 | } 40 | 41 | return NULL; 42 | } 43 | 44 | void buffer_list_destroy(Buffer *head) 45 | { 46 | Buffer *buff; 47 | 48 | while (head) { 49 | buff = head; 50 | head = head->next; 51 | free(buff->start); 52 | free(buff); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/fixed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "internals.h" 3 | #include "../include/mem_pool.h" 4 | 5 | 6 | typedef struct Block Block; 7 | 8 | 9 | struct Block { 10 | Block *next; 11 | }; 12 | 13 | struct FixedMemPool { 14 | size_t memb_size; 15 | size_t buff_size; 16 | Buffer *buff_head; 17 | Buffer *buff_last; 18 | Block *block_head; 19 | pthread_mutex_t mutex; 20 | }; 21 | 22 | 23 | MemPoolError pool_fixed_init(FixedMemPool **pool, size_t block_size, size_t increase_count) 24 | { 25 | *pool = malloc(sizeof(FixedMemPool)); 26 | if (!*pool) { 27 | return MEM_POOL_ERR_MALLOC; 28 | } 29 | (*pool)->memb_size = mem_align(max(block_size, sizeof(Block))); 30 | (*pool)->buff_size = increase_count * (*pool)->memb_size; 31 | (*pool)->buff_head = buffer_new((*pool)->buff_size); 32 | 33 | if (!(*pool)->buff_head) { 34 | return MEM_POOL_ERR_MALLOC; 35 | } 36 | 37 | (*pool)->buff_last = (*pool)->buff_head; 38 | (*pool)->block_head = NULL; 39 | 40 | mutex_init((*pool)); 41 | 42 | return MEM_POOL_ERR_OK; 43 | } 44 | 45 | static MemPoolError from_free_list(FixedMemPool *pool, void **ptr) 46 | { 47 | Block *tmp = pool->block_head; 48 | pool->block_head = tmp->next; 49 | *ptr = tmp; 50 | unlock(pool); 51 | 52 | return MEM_POOL_ERR_OK; 53 | } 54 | 55 | static MemPoolError from_buffer(FixedMemPool *pool, Buffer *buff, void **ptr) 56 | { 57 | *ptr = buff->curr_ptr; 58 | buff->curr_ptr += pool->memb_size; 59 | unlock(pool); 60 | 61 | return MEM_POOL_ERR_OK; 62 | } 63 | 64 | MemPoolError pool_fixed_alloc(FixedMemPool *pool, void **ptr) 65 | { 66 | lock(pool); 67 | 68 | if (pool->block_head) { 69 | return from_free_list(pool, ptr); 70 | } 71 | Buffer *buff = pool->buff_last; 72 | 73 | if (buff->curr_ptr == buff->end) { 74 | buff = buffer_new(pool->buff_size); 75 | pool->buff_last->next = buff; 76 | pool->buff_last = buff; 77 | } 78 | 79 | return from_buffer(pool, buff, ptr); 80 | } 81 | 82 | MemPoolError pool_fixed_is_associated(FixedMemPool *pool, void *ptr) 83 | { 84 | MemPoolError err; 85 | poool_is_associated(pool, ptr, err); 86 | 87 | return err; 88 | } 89 | 90 | MemPoolError pool_fixed_foreach(FixedMemPool *pool, FixedPoolForeach callback) 91 | { 92 | lock(pool); 93 | 94 | Buffer *buff = pool->buff_head; 95 | 96 | while (buff) { 97 | for (char *block = buff->start; block < (char *)buff->curr_ptr; block += pool->memb_size) { 98 | if (MEM_POOL_FOREACH_STOP == callback(block)) { 99 | break; 100 | } 101 | } 102 | buff = buff->next; 103 | } 104 | unlock(pool); 105 | 106 | return MEM_POOL_ERR_OK; 107 | } 108 | 109 | MemPoolError pool_fixed_free(FixedMemPool *pool, void *ptr) 110 | { 111 | lock(pool); 112 | 113 | if (!buffer_list_has(pool->buff_head, ptr)) { 114 | unlock(pool); 115 | return MEM_POOL_ERR_UNKNOWN_BLOCK; 116 | } 117 | 118 | Block *new = (Block *) ptr; 119 | Block *tmp = pool->block_head; 120 | pool->block_head = new; 121 | new->next = tmp; 122 | 123 | unlock(pool); 124 | 125 | return MEM_POOL_ERR_OK; 126 | } 127 | 128 | MemPoolError pool_fixed_destroy(FixedMemPool *pool) 129 | { 130 | pool_destroy(pool); 131 | 132 | return MEM_POOL_ERR_OK; 133 | } 134 | -------------------------------------------------------------------------------- /src/internals.h: -------------------------------------------------------------------------------- 1 | #ifndef MEM_POOL_INTERNALS_H 2 | #define MEM_POOL_INTERNALS_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "../include/mem_pool.h" 10 | 11 | 12 | #define check_pthread(res, err) \ 13 | if (0 != (res)) { \ 14 | return err; \ 15 | } 16 | 17 | #define mutex_init(pool) \ 18 | check_pthread(pthread_mutex_init(&pool->mutex, NULL), MEM_POOL_ERR_MUTEX_INIT); 19 | 20 | #define mutext_destroy(pool) \ 21 | check_pthread(pthread_mutex_destroy(&pool->mutex), MEM_POOL_ERR_MUTEX_DESTROY); 22 | 23 | #define lock(pool) \ 24 | check_pthread(pthread_mutex_lock(&pool->mutex), MEM_POOL_ERR_LOCK); 25 | 26 | #define unlock(pool) \ 27 | check_pthread(pthread_mutex_unlock(&pool->mutex), MEM_POOL_ERR_UNLOCK); 28 | 29 | #define pool_destroy(pool) \ 30 | buffer_list_destroy(pool->buff_head); \ 31 | mutext_destroy(pool) \ 32 | free(pool); 33 | 34 | #define poool_is_associated(pool, ptr, err) \ 35 | lock(pool); \ 36 | if (buffer_list_has(pool->buff_head, ptr)) { \ 37 | err = MEM_POOL_ERR_OK; \ 38 | } else { \ 39 | err = MEM_POOL_ERR_UNKNOWN_BLOCK; \ 40 | } \ 41 | unlock(pool) \ 42 | 43 | #define buffer_list_has(head, ptr) (NULL != buffer_list_find(head, ptr)) 44 | 45 | 46 | typedef struct Buffer Buffer; 47 | 48 | struct Buffer { 49 | char *start; 50 | char *prev_ptr; 51 | char *curr_ptr; /* This is only tracked for variadic blocks */ 52 | char *end; 53 | Buffer *next; 54 | }; 55 | 56 | typedef struct Header Header; 57 | 58 | typedef struct SizedBlock SizedBlock; 59 | 60 | struct Header { 61 | size_t size; 62 | SizedBlock *prev_in_buff; 63 | }; 64 | 65 | 66 | static inline size_t max(size_t a, size_t b) 67 | { 68 | return a > b ? a : b; 69 | } 70 | 71 | Buffer *buffer_new(size_t size); 72 | 73 | void buffer_list_destroy(Buffer *head); 74 | 75 | Buffer *buffer_list_find(Buffer *head, void *ptr); 76 | 77 | static inline bool buffer_has_space(Buffer *buff, size_t size) 78 | { 79 | return (char *)buff->end - (char *)buff->curr_ptr >= (long)size; 80 | } 81 | 82 | static inline bool buffer_has(Buffer *buff, char *ptr) 83 | { 84 | return ptr >= buff->start && ptr <= buff->end; 85 | } 86 | 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | size_t mem_align(size_t size) 6 | { 7 | size_t align = alignof(max_align_t); 8 | 9 | if (size % align) { 10 | return size + (align - size % align); 11 | } 12 | 13 | return size; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/variable.c: -------------------------------------------------------------------------------- 1 | #include "internals.h" 2 | #include "../include/mem_pool.h" 3 | 4 | 5 | struct SizedBlock { 6 | Header header; 7 | SizedBlock *next_in_free_list; 8 | }; 9 | 10 | struct VariableMemPool { 11 | size_t buff_size; 12 | size_t header_size; 13 | uint16_t tolerance_percent; 14 | Buffer *buff_head; 15 | Buffer *buff_last; 16 | SizedBlock *block_head; 17 | pthread_mutex_t mutex; 18 | }; 19 | 20 | 21 | MemPoolError pool_variable_init(VariableMemPool **pool, size_t grow_size, uint16_t tolerance_percent) 22 | { 23 | *pool = malloc(sizeof(VariableMemPool)); 24 | if (!*pool) { 25 | return MEM_POOL_ERR_MALLOC; 26 | } 27 | /* (*pool)->tolerance_percent = tolerance_percent <= MEM_POOL_NO_BEST_FIT ? MEM_POOL_NO_BEST_FIT : tolerance_percent; */ 28 | (*pool)->tolerance_percent = tolerance_percent >= MEM_POOL_NO_BEST_FIT ? MEM_POOL_NO_BEST_FIT : tolerance_percent; 29 | (*pool)->header_size = mem_align(sizeof(Header)); 30 | (*pool)->buff_size = grow_size; 31 | (*pool)->buff_head = buffer_new(grow_size); 32 | 33 | if (!(*pool)->buff_head) { 34 | return MEM_POOL_ERR_MALLOC; 35 | } 36 | 37 | (*pool)->buff_last = (*pool)->buff_head; 38 | (*pool)->block_head = NULL; 39 | 40 | mutex_init((*pool)); 41 | 42 | return MEM_POOL_ERR_OK; 43 | } 44 | 45 | static void *from_buffer(Buffer *buff, size_t header_size, size_t block_size) 46 | { 47 | Header *header = (void *) buff->curr_ptr; 48 | header->size = block_size; 49 | header->prev_in_buff = (void *) buff->prev_ptr; 50 | 51 | buff->prev_ptr = buff->curr_ptr; 52 | buff->curr_ptr += (header_size + block_size); 53 | 54 | return (char *)header + header_size; 55 | } 56 | 57 | static void *best_fit_from_free_list(VariableMemPool *pool, size_t required_size) 58 | { 59 | SizedBlock **curr = &pool->block_head; 60 | size_t block_size; 61 | long diff; 62 | size_t diff_percent; 63 | 64 | while (*curr) { 65 | block_size = (*curr)->header.size; 66 | diff = labs((long) block_size - (long) required_size); 67 | diff_percent = ((size_t) diff * 100) / ((block_size + required_size) / 2); 68 | 69 | if (MEM_POOL_NO_BEST_FIT == pool->tolerance_percent || diff_percent <= pool->tolerance_percent) { 70 | SizedBlock *block = *curr; 71 | *curr = (*curr)->next_in_free_list; 72 | 73 | return (char *)block + pool->header_size; 74 | } 75 | curr = &(*curr)->next_in_free_list; 76 | } 77 | 78 | return NULL; 79 | } 80 | 81 | MemPoolError pool_variable_alloc(VariableMemPool *pool, size_t size, void **ptr) 82 | { 83 | lock(pool); 84 | 85 | Buffer *buff = pool->buff_last; 86 | size_t block_size = mem_align(size); 87 | 88 | if (pool->block_head && (*ptr = best_fit_from_free_list(pool, block_size))) { 89 | unlock(pool); 90 | 91 | return MEM_POOL_ERR_OK; 92 | } 93 | 94 | if (!buffer_has_space(buff, pool->header_size + block_size)) { 95 | buff->next = buffer_new(pool->header_size + max(pool->buff_size, block_size)); 96 | buff = buff->next; 97 | pool->buff_last = buff; 98 | } 99 | *ptr = from_buffer(buff, pool->header_size, block_size); 100 | unlock(pool); 101 | 102 | return MEM_POOL_ERR_OK; 103 | } 104 | 105 | static int delete_block_from_free_list(VariableMemPool *pool, SizedBlock *block) 106 | { 107 | SizedBlock **curr = &pool->block_head; 108 | 109 | while (*curr) { 110 | if ((*curr) == block) { 111 | *curr = block->next_in_free_list; 112 | return 1; 113 | } 114 | curr = &(*curr)->next_in_free_list; 115 | } 116 | 117 | return 0; 118 | } 119 | 120 | MemPoolError pool_variable_is_associated(VariableMemPool *pool, void *ptr) 121 | { 122 | MemPoolError err; 123 | poool_is_associated(pool, ptr, err); 124 | 125 | return err; 126 | } 127 | 128 | static SizedBlock *append(SizedBlock *to, SizedBlock *from, size_t header_size) 129 | { 130 | to->header.size += from->header.size + header_size; 131 | 132 | return to; 133 | } 134 | 135 | static SizedBlock *merge_next_free_blocks(VariableMemPool *pool, Buffer *buff, SizedBlock *block) 136 | { 137 | void *next = NULL; 138 | 139 | while (1) { 140 | next = (SizedBlock *)((char *)block + block->header.size + pool->header_size); 141 | 142 | if (buffer_has(buff, next) && delete_block_from_free_list(pool, next)) { 143 | block = append(block, next, pool->header_size); 144 | } else { 145 | break; 146 | } 147 | } 148 | 149 | return block; 150 | } 151 | 152 | static SizedBlock *merge_previous_free_blocks(VariableMemPool *pool, SizedBlock *block) 153 | { 154 | SizedBlock *prev = block->header.prev_in_buff; 155 | 156 | while (prev) { 157 | if (!delete_block_from_free_list(pool, prev)) { 158 | break; 159 | } 160 | block = append(prev, block, pool->header_size); 161 | prev = prev->header.prev_in_buff; 162 | } 163 | 164 | return block; 165 | } 166 | 167 | static SizedBlock *defragment(VariableMemPool *pool, Buffer *buff, SizedBlock *block) 168 | { 169 | block = merge_next_free_blocks(pool, buff, block); 170 | block = merge_previous_free_blocks(pool, block); 171 | 172 | return block; 173 | } 174 | 175 | MemPoolError pool_variable_free(VariableMemPool *pool, void *ptr) 176 | { 177 | lock(pool); 178 | 179 | Buffer *buff = buffer_list_find(pool->buff_head, ptr); 180 | SizedBlock *new = (SizedBlock *)((char *)ptr - pool->header_size); 181 | 182 | if (!buff) { 183 | unlock(pool); 184 | return MEM_POOL_ERR_UNKNOWN_BLOCK; 185 | } else { 186 | new = defragment(pool, buff, new); 187 | } 188 | SizedBlock *tmp = pool->block_head; 189 | pool->block_head = new; 190 | new->next_in_free_list = tmp; 191 | 192 | unlock(pool); 193 | 194 | return MEM_POOL_ERR_OK; 195 | } 196 | 197 | MemPoolError pool_variable_aligned_sizeof(VariableMemPool *pool, void *ptr, size_t *size) 198 | { 199 | lock(pool); 200 | 201 | if (!buffer_list_find(pool->buff_head, ptr)) { 202 | return MEM_POOL_ERR_UNKNOWN_BLOCK; 203 | } 204 | 205 | SizedBlock *block = (SizedBlock *)((char *)ptr - pool->header_size); 206 | *size = block->header.size;; 207 | 208 | unlock(pool); 209 | 210 | return MEM_POOL_ERR_OK; 211 | } 212 | 213 | MemPoolError pool_variable_destroy(VariableMemPool *pool) 214 | { 215 | pool_destroy(pool); 216 | 217 | return MEM_POOL_ERR_OK; 218 | } 219 | -------------------------------------------------------------------------------- /test/fixed_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "minunit.h" 6 | 7 | 8 | MU_TEST(test_pool) 9 | { 10 | int expected = 10; 11 | int align = alignof(max_align_t); 12 | 13 | expected = expected + (align - expected % align); 14 | 15 | FixedMemPool *pool; 16 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_init(&pool, 10, 2)); 17 | 18 | void *ptr1, *ptr2, *ptr3, *ptr4, *prev; 19 | 20 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_alloc(pool, &ptr1)); 21 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_alloc(pool, &ptr2)); 22 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_alloc(pool, &ptr3)); 23 | 24 | mu_assert_int_eq(expected, ptr2 - ptr1); 25 | mu_assert(ptr3 - ptr2 > expected, "This should be in a new Buffer"); 26 | 27 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_is_associated(pool, ptr1)); 28 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_is_associated(pool, ptr2)); 29 | 30 | prev = ptr1; 31 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_free(pool, ptr1)); 32 | 33 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_alloc(pool, &ptr4)); 34 | mu_assert(prev == ptr4, "The same pointer should be returned from the free list"); 35 | 36 | void *unknown = NULL; 37 | mu_assert_int_eq(MEM_POOL_ERR_UNKNOWN_BLOCK, pool_fixed_is_associated(pool, unknown)); 38 | mu_assert_int_eq(MEM_POOL_ERR_UNKNOWN_BLOCK, pool_fixed_free(pool, unknown)); 39 | 40 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_destroy(pool)); 41 | } 42 | 43 | static int ADDED = 0; 44 | 45 | static MemPoolForeachStatus add_items(int *item) 46 | { 47 | if (50 == *item) { 48 | return MEM_POOL_FOREACH_STOP; 49 | } 50 | ADDED += *item; 51 | 52 | return MEM_POOL_FOREACH_CONTINUE; 53 | } 54 | 55 | MU_TEST(test_foreach) 56 | { 57 | FixedMemPool *pool; 58 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_init(&pool, sizeof(int), 2)); 59 | int *a, *b, *c; 60 | 61 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_alloc(pool, (void **)&a)); 62 | *a = 100; 63 | 64 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_alloc(pool, (void **)&b)); 65 | *b = 200; 66 | 67 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_fixed_alloc(pool, (void **)&c)); 68 | *c = 50; 69 | 70 | pool_fixed_foreach(pool, (FixedPoolForeach) add_items); 71 | 72 | mu_assert_int_eq(300, ADDED); 73 | 74 | pool_fixed_destroy(pool); 75 | } 76 | 77 | void run_fixed_pool_test(void) 78 | { 79 | MU_RUN_TEST(test_pool); 80 | MU_RUN_TEST(test_foreach); 81 | 82 | MU_REPORT(); 83 | } 84 | -------------------------------------------------------------------------------- /test/main.c: -------------------------------------------------------------------------------- 1 | void run_fixed_pool_test(void); 2 | void run_variable_pool_test(void); 3 | 4 | int main(void) 5 | { 6 | run_fixed_pool_test(); 7 | run_variable_pool_test(); 8 | 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /test/minunit/minunit.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 David Siñuela Pastor, siu.4coders@gmail.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | #ifndef __MINUNIT_H__ 24 | #define __MINUNIT_H__ 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | #if defined(_WIN32) 31 | #include 32 | 33 | #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) 34 | 35 | /* Change POSIX C SOURCE version for pure c99 compilers */ 36 | #if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L 37 | #undef _POSIX_C_SOURCE 38 | #define _POSIX_C_SOURCE 200112L 39 | #endif 40 | 41 | #include /* POSIX flags */ 42 | #include /* clock_gettime(), time() */ 43 | #include /* gethrtime(), gettimeofday() */ 44 | #include 45 | #include 46 | 47 | #if defined(__MACH__) && defined(__APPLE__) 48 | #include 49 | #include 50 | #endif 51 | 52 | #else 53 | #error "Unable to define timers for an unknown OS." 54 | #endif 55 | 56 | #include 57 | #include 58 | 59 | /* Maximum length of last message */ 60 | #define MINUNIT_MESSAGE_LEN 1024 61 | /* Do not change */ 62 | #define MINUNIT_EPSILON 1E-12 63 | 64 | /* Misc. counters */ 65 | static int minunit_run = 0; 66 | static int minunit_assert = 0; 67 | static int minunit_fail = 0; 68 | static int minunit_status = 0; 69 | 70 | /* Timers */ 71 | static double minunit_real_timer = 0; 72 | static double minunit_proc_timer = 0; 73 | 74 | /* Last message */ 75 | static char minunit_last_message[MINUNIT_MESSAGE_LEN]; 76 | 77 | /* Test setup and teardown function pointers */ 78 | static void (*minunit_setup)(void) = NULL; 79 | static void (*minunit_teardown)(void) = NULL; 80 | 81 | /* Definitions */ 82 | #define MU_TEST(method_name) static void method_name() 83 | #define MU_TEST_SUITE(suite_name) static void suite_name() 84 | 85 | #define MU__SAFE_BLOCK(block) do {\ 86 | block\ 87 | } while(0) 88 | 89 | /* Run test suite and unset setup and teardown functions */ 90 | #define MU_RUN_SUITE(suite_name) MU__SAFE_BLOCK(\ 91 | suite_name();\ 92 | minunit_setup = NULL;\ 93 | minunit_teardown = NULL;\ 94 | ) 95 | 96 | /* Configure setup and teardown functions */ 97 | #define MU_SUITE_CONFIGURE(setup_fun, teardown_fun) MU__SAFE_BLOCK(\ 98 | minunit_setup = setup_fun;\ 99 | minunit_teardown = teardown_fun;\ 100 | ) 101 | 102 | /* Test runner */ 103 | #define MU_RUN_TEST(test) MU__SAFE_BLOCK(\ 104 | if (minunit_real_timer==0 && minunit_real_timer==0) {\ 105 | minunit_real_timer = mu_timer_real();\ 106 | minunit_proc_timer = mu_timer_cpu();\ 107 | }\ 108 | if (minunit_setup) (*minunit_setup)();\ 109 | minunit_status = 0;\ 110 | test();\ 111 | minunit_run++;\ 112 | if (minunit_status) {\ 113 | minunit_fail++;\ 114 | printf("F");\ 115 | printf("\n%s\n", minunit_last_message);\ 116 | }\ 117 | fflush(stdout);\ 118 | if (minunit_teardown) (*minunit_teardown)();\ 119 | ) 120 | 121 | /* Report */ 122 | #define MU_REPORT() MU__SAFE_BLOCK(\ 123 | double minunit_end_real_timer;\ 124 | double minunit_end_proc_timer;\ 125 | printf("\n\n%d tests, %d assertions, %d failures\n", minunit_run, minunit_assert, minunit_fail);\ 126 | minunit_end_real_timer = mu_timer_real();\ 127 | minunit_end_proc_timer = mu_timer_cpu();\ 128 | printf("\nFinished in %.8f seconds (real) %.8f seconds (proc)\n\n",\ 129 | minunit_end_real_timer - minunit_real_timer,\ 130 | minunit_end_proc_timer - minunit_proc_timer);\ 131 | ) 132 | 133 | /* Assertions */ 134 | #define mu_check(test) MU__SAFE_BLOCK(\ 135 | minunit_assert++;\ 136 | if (!(test)) {\ 137 | snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, #test);\ 138 | minunit_status = 1;\ 139 | return;\ 140 | } else {\ 141 | printf(".");\ 142 | }\ 143 | ) 144 | 145 | #define mu_fail(message) MU__SAFE_BLOCK(\ 146 | minunit_assert++;\ 147 | snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, message);\ 148 | minunit_status = 1;\ 149 | return;\ 150 | ) 151 | 152 | #define mu_assert(test, message) MU__SAFE_BLOCK(\ 153 | minunit_assert++;\ 154 | if (!(test)) {\ 155 | snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, message);\ 156 | minunit_status = 1;\ 157 | return;\ 158 | } else {\ 159 | printf(".");\ 160 | }\ 161 | ) 162 | 163 | #define mu_assert_int_eq(expected, result) MU__SAFE_BLOCK(\ 164 | int minunit_tmp_e;\ 165 | int minunit_tmp_r;\ 166 | minunit_assert++;\ 167 | minunit_tmp_e = (expected);\ 168 | minunit_tmp_r = (result);\ 169 | if (minunit_tmp_e != minunit_tmp_r) {\ 170 | snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d expected but was %d", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\ 171 | minunit_status = 1;\ 172 | return;\ 173 | } else {\ 174 | printf(".");\ 175 | }\ 176 | ) 177 | 178 | #define mu_assert_double_eq(expected, result) MU__SAFE_BLOCK(\ 179 | double minunit_tmp_e;\ 180 | double minunit_tmp_r;\ 181 | minunit_assert++;\ 182 | minunit_tmp_e = (expected);\ 183 | minunit_tmp_r = (result);\ 184 | if (fabs(minunit_tmp_e-minunit_tmp_r) > MINUNIT_EPSILON) {\ 185 | snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %g expected but was %g", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\ 186 | minunit_status = 1;\ 187 | return;\ 188 | } else {\ 189 | printf(".");\ 190 | }\ 191 | ) 192 | 193 | /* 194 | * The following two functions were written by David Robert Nadeau 195 | * from http://NadeauSoftware.com/ and distributed under the 196 | * Creative Commons Attribution 3.0 Unported License 197 | */ 198 | 199 | /** 200 | * Returns the real time, in seconds, or -1.0 if an error occurred. 201 | * 202 | * Time is measured since an arbitrary and OS-dependent start time. 203 | * The returned real time is only useful for computing an elapsed time 204 | * between two calls to this function. 205 | */ 206 | static double mu_timer_real( ) 207 | { 208 | #if defined(_WIN32) 209 | FILETIME tm; 210 | ULONGLONG t; 211 | #if defined(NTDDI_WIN8) && NTDDI_VERSION >= NTDDI_WIN8 212 | /* Windows 8, Windows Server 2012 and later. ---------------- */ 213 | GetSystemTimePreciseAsFileTime( &tm ); 214 | #else 215 | /* Windows 2000 and later. ---------------------------------- */ 216 | GetSystemTimeAsFileTime( &tm ); 217 | #endif 218 | t = ((ULONGLONG)tm.dwHighDateTime << 32) | (ULONGLONG)tm.dwLowDateTime; 219 | return (double)t / 10000000.0; 220 | 221 | #elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) 222 | /* HP-UX, Solaris. ------------------------------------------ */ 223 | return (double)gethrtime( ) / 1000000000.0; 224 | 225 | #elif defined(__MACH__) && defined(__APPLE__) 226 | /* OSX. ----------------------------------------------------- */ 227 | static double timeConvert = 0.0; 228 | if ( timeConvert == 0.0 ) 229 | { 230 | mach_timebase_info_data_t timeBase; 231 | (void)mach_timebase_info( &timeBase ); 232 | timeConvert = (double)timeBase.numer / 233 | (double)timeBase.denom / 234 | 1000000000.0; 235 | } 236 | return (double)mach_absolute_time( ) * timeConvert; 237 | 238 | #elif defined(_POSIX_VERSION) 239 | /* POSIX. --------------------------------------------------- */ 240 | #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) 241 | { 242 | struct timespec ts; 243 | #if defined(CLOCK_MONOTONIC_PRECISE) 244 | /* BSD. --------------------------------------------- */ 245 | const clockid_t id = CLOCK_MONOTONIC_PRECISE; 246 | #elif defined(CLOCK_MONOTONIC_RAW) 247 | /* Linux. ------------------------------------------- */ 248 | const clockid_t id = CLOCK_MONOTONIC_RAW; 249 | #elif defined(CLOCK_HIGHRES) 250 | /* Solaris. ----------------------------------------- */ 251 | const clockid_t id = CLOCK_HIGHRES; 252 | #elif defined(CLOCK_MONOTONIC) 253 | /* AIX, BSD, Linux, POSIX, Solaris. ----------------- */ 254 | const clockid_t id = CLOCK_MONOTONIC; 255 | #elif defined(CLOCK_REALTIME) 256 | /* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */ 257 | const clockid_t id = CLOCK_REALTIME; 258 | #else 259 | const clockid_t id = (clockid_t)-1; /* Unknown. */ 260 | #endif /* CLOCK_* */ 261 | if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) 262 | return (double)ts.tv_sec + 263 | (double)ts.tv_nsec / 1000000000.0; 264 | /* Fall thru. */ 265 | } 266 | #endif /* _POSIX_TIMERS */ 267 | 268 | /* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */ 269 | struct timeval tm; 270 | gettimeofday( &tm, NULL ); 271 | return (double)tm.tv_sec + (double)tm.tv_usec / 1000000.0; 272 | #else 273 | return -1.0; /* Failed. */ 274 | #endif 275 | } 276 | 277 | /** 278 | * Returns the amount of CPU time used by the current process, 279 | * in seconds, or -1.0 if an error occurred. 280 | */ 281 | static double mu_timer_cpu( ) 282 | { 283 | #if defined(_WIN32) 284 | /* Windows -------------------------------------------------- */ 285 | FILETIME createTime; 286 | FILETIME exitTime; 287 | FILETIME kernelTime; 288 | FILETIME userTime; 289 | if ( GetProcessTimes( GetCurrentProcess( ), 290 | &createTime, &exitTime, &kernelTime, &userTime ) != -1 ) 291 | { 292 | SYSTEMTIME userSystemTime; 293 | if ( FileTimeToSystemTime( &userTime, &userSystemTime ) != -1 ) 294 | return (double)userSystemTime.wHour * 3600.0 + 295 | (double)userSystemTime.wMinute * 60.0 + 296 | (double)userSystemTime.wSecond + 297 | (double)userSystemTime.wMilliseconds / 1000.0; 298 | } 299 | 300 | #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) 301 | /* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */ 302 | 303 | #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) 304 | /* Prefer high-res POSIX timers, when available. */ 305 | { 306 | clockid_t id; 307 | struct timespec ts; 308 | #if _POSIX_CPUTIME > 0 309 | /* Clock ids vary by OS. Query the id, if possible. */ 310 | if ( clock_getcpuclockid( 0, &id ) == -1 ) 311 | #endif 312 | #if defined(CLOCK_PROCESS_CPUTIME_ID) 313 | /* Use known clock id for AIX, Linux, or Solaris. */ 314 | id = CLOCK_PROCESS_CPUTIME_ID; 315 | #elif defined(CLOCK_VIRTUAL) 316 | /* Use known clock id for BSD or HP-UX. */ 317 | id = CLOCK_VIRTUAL; 318 | #else 319 | id = (clockid_t)-1; 320 | #endif 321 | if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) 322 | return (double)ts.tv_sec + 323 | (double)ts.tv_nsec / 1000000000.0; 324 | } 325 | #endif 326 | 327 | #if defined(RUSAGE_SELF) 328 | { 329 | struct rusage rusage; 330 | if ( getrusage( RUSAGE_SELF, &rusage ) != -1 ) 331 | return (double)rusage.ru_utime.tv_sec + 332 | (double)rusage.ru_utime.tv_usec / 1000000.0; 333 | } 334 | #endif 335 | 336 | #if defined(_SC_CLK_TCK) 337 | { 338 | const double ticks = (double)sysconf( _SC_CLK_TCK ); 339 | struct tms tms; 340 | if ( times( &tms ) != (clock_t)-1 ) 341 | return (double)tms.tms_utime / ticks; 342 | } 343 | #endif 344 | 345 | #if defined(CLOCKS_PER_SEC) 346 | { 347 | clock_t cl = clock( ); 348 | if ( cl != (clock_t)-1 ) 349 | return (double)cl / (double)CLOCKS_PER_SEC; 350 | } 351 | #endif 352 | 353 | #endif 354 | 355 | return -1; /* Failed. */ 356 | } 357 | 358 | #ifdef __cplusplus 359 | } 360 | #endif 361 | 362 | #endif /* __MINUNIT_H__ */ 363 | -------------------------------------------------------------------------------- /test/variable_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "minunit.h" 7 | #include "../src/internals.h" 8 | 9 | 10 | struct test { 11 | char str[6]; 12 | uint16_t i; 13 | }; 14 | 15 | 16 | #define item_size(t) mem_align(sizeof(size_t)) + mem_align(sizeof(t)) 17 | #define header_of(ptr, header_size) ((Header *)((char *)ptr - header_size)) 18 | #define assert_size_in_header(block_size, header_size, ptr) mu_assert_int_eq(mem_align(block_size), header_of(ptr, header_size)->size); 19 | 20 | 21 | static void assert_initials(const size_t header_size, int *num, struct test *structure, struct test *array) 22 | { 23 | assert_size_in_header(sizeof(int), header_size, num); 24 | mu_assert_int_eq(100, *num); 25 | 26 | assert_size_in_header(sizeof(struct test), header_size, structure) 27 | mu_assert_int_eq(10, structure->i); 28 | mu_assert_int_eq(0, strcmp(structure->str, "Hello")); 29 | 30 | mu_assert_int_eq(20, array[0].i); 31 | mu_assert_int_eq(0, strcmp(array[0].str, "Test")); 32 | 33 | mu_assert_int_eq(30, array[1].i); 34 | mu_assert_int_eq(0, strcmp(array[1].str, "C")); 35 | } 36 | 37 | static void test_defragmentation_in_first_buff(VariableMemPool *pool, int *num, struct test *structure) 38 | { 39 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_free(pool, num)); 40 | 41 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_free(pool, structure)); 42 | 43 | // The two freed *aligned* blocks, including a Header should be enough for this 44 | struct test *arr; 45 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, 5 * sizeof(struct test), (void **)&arr)); 46 | 47 | mu_assert((void *)num == (void *)arr, ""); 48 | } 49 | 50 | static void test_defragmentation_in_second_buff(VariableMemPool *pool, struct test *array) 51 | { 52 | int *num_1, *num_2; 53 | 54 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_free(pool, array)); 55 | 56 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, sizeof(int), (void **)&num_1)); 57 | mu_assert((void *)num_1 == (void *)array, ""); 58 | 59 | *num_1 = 100; 60 | 61 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, sizeof(int), (void **)&num_2)); 62 | *num_2 = 200; 63 | 64 | mu_assert_int_eq(100, *num_1); 65 | mu_assert_int_eq(200, *num_2); 66 | } 67 | 68 | /** 69 | * Initial Layout: 70 | * 71 | * Buffer 1: |h| int num |h| struct test structure 72 | * Buffer 2: |h| struct test array[2] 73 | */ 74 | MU_TEST(test_alloc) 75 | { 76 | const size_t header_size = mem_align(sizeof(Header)); 77 | 78 | VariableMemPool *pool; 79 | mu_assert_int_eq( 80 | MEM_POOL_ERR_OK, 81 | pool_variable_init(&pool, 2 * item_size(Header) + item_size(int) + item_size(struct test), 10) 82 | ); 83 | 84 | int *num; 85 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, sizeof(int), (void **)&num)); 86 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_is_associated(pool, num)); 87 | *num = 100; 88 | 89 | struct test *structure; 90 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, sizeof(struct test), (void **)&structure)); 91 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_is_associated(pool, structure)); 92 | 93 | structure->i = 10; 94 | memcpy(structure->str, "Hello", 5); 95 | structure->str[5] = '\0'; 96 | 97 | struct test *array; 98 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, 2 * sizeof(struct test), (void **)&array)); 99 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_is_associated(pool, array)); 100 | 101 | array[0].i = 20; 102 | array[1].i = 30; 103 | 104 | memcpy(array[0].str, "Test", 4); 105 | array[0].str[4] = '\0'; 106 | 107 | memcpy(array[1].str, "C", 1); 108 | array[1].str[1] = '\0'; 109 | 110 | assert_initials(header_size, num, structure, array); 111 | 112 | test_defragmentation_in_first_buff(pool, num, structure); 113 | test_defragmentation_in_second_buff(pool, array); 114 | 115 | mu_assert_int_eq(MEM_POOL_ERR_UNKNOWN_BLOCK, pool_variable_is_associated(pool, NULL)); 116 | 117 | pool_variable_destroy(pool); 118 | } 119 | 120 | MU_TEST(test_complex_defragmentation) 121 | { 122 | size_t header_size = mem_align(sizeof(Header)); 123 | 124 | char *a, *b, *c; 125 | VariableMemPool *pool; 126 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_init(&pool, 200, MEM_POOL_NO_BEST_FIT)); 127 | 128 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, sizeof(char), (void **)&a)); 129 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, sizeof(char), (void **)&b)); 130 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, sizeof(char), (void **)&c)); 131 | 132 | *a = 'a'; 133 | *b = 'b'; 134 | *c = 'c'; 135 | 136 | mu_assert_int_eq('a', *a); 137 | assert_size_in_header(sizeof(char), header_size, a); 138 | 139 | mu_assert_int_eq('b', *b); 140 | assert_size_in_header(sizeof(char), header_size, b); 141 | 142 | mu_assert_int_eq('c', *c); 143 | assert_size_in_header(sizeof(char), header_size, c); 144 | 145 | // These three should be merged into one block 146 | mu_assert_int_eq(0, pool_variable_free(pool, c)); 147 | mu_assert_int_eq(0, pool_variable_free(pool, a)); 148 | mu_assert_int_eq(0, pool_variable_free(pool, b)); 149 | 150 | char *def; 151 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, 3 * sizeof(char), (void **)&def)); 152 | 153 | /** 154 | * We set MEM_NO_BEST_FIT so we will retrieve the first free block which is was formed from 155 | * the previous, *separately* allocated three aligned blocks and their aligned headers 156 | * but it'll also keep space for its own new header 157 | */ 158 | mu_assert_int_eq( 159 | 2 * mem_align(sizeof(Header)) + 3 * mem_align(sizeof(char)), 160 | header_of(def, header_size)->size 161 | ); 162 | mu_assert(def == a, ""); 163 | 164 | pool_variable_destroy(pool); 165 | } 166 | 167 | MU_TEST(test_sizeof) 168 | { 169 | VariableMemPool *pool; 170 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_init(&pool, 200, 10)); 171 | 172 | void *a, *b, *c; 173 | 174 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, 60, &a)); 175 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, 600, &c)); 176 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, 12, &b)); 177 | 178 | size_t size = 0; 179 | 180 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_aligned_sizeof(pool, c, &size)); 181 | mu_assert_int_eq(mem_align(600), size); 182 | 183 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_aligned_sizeof(pool, a, &size)); 184 | mu_assert_int_eq(mem_align(60), size); 185 | 186 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_aligned_sizeof(pool, b, &size)); 187 | mu_assert_int_eq(mem_align(12), size); 188 | 189 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_destroy(pool)); 190 | } 191 | 192 | MU_TEST(test_multi_blocks) 193 | { 194 | VariableMemPool *pool; 195 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_init(&pool, 1, 10)); 196 | 197 | void *a, *b, *c; 198 | 199 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, 60, &a)); 200 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, 600, &c)); 201 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_alloc(pool, 12, &b)); 202 | 203 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_free(pool, a)); 204 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_free(pool, b)); 205 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_free(pool, c)); 206 | 207 | mu_assert_int_eq(MEM_POOL_ERR_OK, pool_variable_destroy(pool)); 208 | } 209 | 210 | void run_variable_pool_test(void) 211 | { 212 | MU_RUN_TEST(test_alloc); 213 | MU_RUN_TEST(test_complex_defragmentation); 214 | MU_RUN_TEST(test_sizeof); 215 | MU_RUN_TEST(test_multi_blocks); 216 | 217 | MU_REPORT(); 218 | } 219 | --------------------------------------------------------------------------------