improve mempool implementation

Wed, 28 Jun 2023 20:07:52 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 28 Jun 2023 20:07:52 +0200
changeset 727
d92a59f5d261
parent 726
44986c0e2b05
child 728
71c4ac35c1ce

improve mempool implementation

docs/src/features.md file | annotate | diff | comparison | revisions
src/CMakeLists.txt file | annotate | diff | comparison | revisions
src/basic_mempool.c file | annotate | diff | comparison | revisions
src/cx/basic_mempool.h file | annotate | diff | comparison | revisions
src/cx/mempool.h file | annotate | diff | comparison | revisions
src/mempool.c file | annotate | diff | comparison | revisions
tests/CMakeLists.txt file | annotate | diff | comparison | revisions
tests/test_basic_mempool.cpp file | annotate | diff | comparison | revisions
tests/test_mempool.cpp file | annotate | diff | comparison | revisions
     1.1 --- a/docs/src/features.md	Wed Jun 28 19:18:01 2023 +0200
     1.2 +++ b/docs/src/features.md	Wed Jun 28 20:07:52 2023 +0200
     1.3 @@ -30,7 +30,7 @@
     1.4  struct my_allocator_state {
     1.5      size_t total;
     1.6      size_t avail;
     1.7 -    char[] mem;
     1.8 +    char mem[];
     1.9  };
    1.10  
    1.11  static cx_allocator_class my_allocator_class = {
    1.12 @@ -91,10 +91,6 @@
    1.13  
    1.14  *Header file:* [mempool.h](api/mempool_8h.html)
    1.15  
    1.16 -### Basic Memory Pool
    1.17 -
    1.18 -*Header file:* [basic_mempool.h](api/basic__mempool_8h.html)
    1.19 -
    1.20  ## Iterator
    1.21  
    1.22  *Header file:* [iterator.h](api/iterator_8h.html)
     2.1 --- a/src/CMakeLists.txt	Wed Jun 28 19:18:01 2023 +0200
     2.2 +++ b/src/CMakeLists.txt	Wed Jun 28 20:07:52 2023 +0200
     2.3 @@ -9,7 +9,7 @@
     2.4          map.c
     2.5          hash_key.c
     2.6          hash_map.c
     2.7 -        basic_mempool.c
     2.8 +        mempool.c
     2.9          printf.c
    2.10          compare.c
    2.11  )
    2.12 @@ -28,7 +28,6 @@
    2.13          cx/hash_key.h
    2.14          cx/hash_map.h
    2.15          cx/mempool.h
    2.16 -        cx/basic_mempool.h
    2.17          cx/printf.h
    2.18          cx/compare.h
    2.19          )
     3.1 --- a/src/basic_mempool.c	Wed Jun 28 19:18:01 2023 +0200
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,235 +0,0 @@
     3.4 -/*
     3.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 - *
     3.7 - * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     3.8 - *
     3.9 - * Redistribution and use in source and binary forms, with or without
    3.10 - * modification, are permitted provided that the following conditions are met:
    3.11 - *
    3.12 - *   1. Redistributions of source code must retain the above copyright
    3.13 - *      notice, this list of conditions and the following disclaimer.
    3.14 - *
    3.15 - *   2. Redistributions in binary form must reproduce the above copyright
    3.16 - *      notice, this list of conditions and the following disclaimer in the
    3.17 - *      documentation and/or other materials provided with the distribution.
    3.18 - *
    3.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    3.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    3.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    3.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    3.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    3.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    3.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    3.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    3.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    3.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    3.29 - * POSSIBILITY OF SUCH DAMAGE.
    3.30 - */
    3.31 -
    3.32 -#include "cx/basic_mempool.h"
    3.33 -#include "cx/utils.h"
    3.34 -#include <string.h>
    3.35 -
    3.36 -#define of_chk_(n) if (SIZE_MAX - sizeof(cx_destructor_func) < (n)) return NULL
    3.37 -
    3.38 -/** Internal structure for denoting pooled memory. */
    3.39 -typedef struct {
    3.40 -    /** The destructor. */
    3.41 -    cx_destructor_func destructor;
    3.42 -    /**
    3.43 -     * Access to the first byte of the polled memory.
    3.44 -     */
    3.45 -    char c;
    3.46 -} cx_basic_mempool_memory;
    3.47 -
    3.48 -static int cx_basic_mempool_chcap(
    3.49 -        struct cx_basic_mempool_s *pool,
    3.50 -        size_t newcap
    3.51 -) {
    3.52 -    if (newcap < pool->ndata) {
    3.53 -        return 1;
    3.54 -    }
    3.55 -
    3.56 -    size_t newcapsz;
    3.57 -    if (cx_szmul(newcap, sizeof(void *), &newcapsz)) {
    3.58 -        return 1;
    3.59 -    }
    3.60 -
    3.61 -    void **data = realloc(pool->data, newcapsz);
    3.62 -    if (data) {
    3.63 -        pool->data = data;
    3.64 -        pool->size = newcap;
    3.65 -        return 0;
    3.66 -    } else {
    3.67 -        return 1;
    3.68 -    }
    3.69 -}
    3.70 -
    3.71 -void *cx_malloc_basic_mempool(
    3.72 -        void *data,
    3.73 -        size_t n
    3.74 -) {
    3.75 -    of_chk_(n);
    3.76 -    struct cx_basic_mempool_s *pool = data;
    3.77 -
    3.78 -    if (pool->ndata >= pool->size) {
    3.79 -        size_t newcap = pool->size * 2;
    3.80 -        if (newcap < pool->size || cx_basic_mempool_chcap(pool, newcap)) {
    3.81 -            return NULL;
    3.82 -        }
    3.83 -    }
    3.84 -
    3.85 -    cx_basic_mempool_memory *mem = malloc(sizeof(cx_destructor_func) + n);
    3.86 -    if (mem == NULL) {
    3.87 -        return NULL;
    3.88 -    }
    3.89 -
    3.90 -    mem->destructor = NULL;
    3.91 -    pool->data[pool->ndata] = mem;
    3.92 -    pool->ndata++;
    3.93 -
    3.94 -    return &(mem->c);
    3.95 -}
    3.96 -
    3.97 -void *cx_calloc_basic_mempool(
    3.98 -        void *data,
    3.99 -        size_t nelem,
   3.100 -        size_t elsize
   3.101 -) {
   3.102 -    size_t msz;
   3.103 -    if (cx_szmul(nelem, elsize, &msz)) {
   3.104 -        return NULL;
   3.105 -    }
   3.106 -    void *ptr = cx_malloc_basic_mempool(data, msz);
   3.107 -    if (ptr == NULL) {
   3.108 -        return NULL;
   3.109 -    }
   3.110 -    memset(ptr, 0, nelem * elsize);
   3.111 -    return ptr;
   3.112 -}
   3.113 -
   3.114 -void *cx_realloc_basic_mempool(
   3.115 -        void *data,
   3.116 -        void *ptr,
   3.117 -        size_t n
   3.118 -) {
   3.119 -    of_chk_(n);
   3.120 -    struct cx_basic_mempool_s *pool = data;
   3.121 -
   3.122 -    char *mem = ((char *) ptr) - sizeof(cx_destructor_func);
   3.123 -    char *newm = (char *) realloc(mem, n + sizeof(cx_destructor_func));
   3.124 -    if (newm == NULL) {
   3.125 -        return NULL;
   3.126 -    }
   3.127 -    if (mem != newm) {
   3.128 -        cx_for_n(i, pool->ndata) {
   3.129 -            if (pool->data[i] == mem) {
   3.130 -                pool->data[i] = newm;
   3.131 -                return newm + sizeof(cx_destructor_func);
   3.132 -            }
   3.133 -        }
   3.134 -        abort();
   3.135 -    } else {
   3.136 -        return newm + sizeof(cx_destructor_func);
   3.137 -    }
   3.138 -}
   3.139 -
   3.140 -void cx_free_basic_mempool(
   3.141 -        void *data,
   3.142 -        void *ptr
   3.143 -) {
   3.144 -    struct cx_basic_mempool_s *pool = data;
   3.145 -
   3.146 -    cx_basic_mempool_memory *mem = (cx_basic_mempool_memory *)
   3.147 -            ((char *) ptr - sizeof(cx_destructor_func));
   3.148 -    cx_for_n(i, pool->ndata) {
   3.149 -        if (mem == pool->data[i]) {
   3.150 -            if (mem->destructor != NULL) {
   3.151 -                mem->destructor(&(mem->c));
   3.152 -            }
   3.153 -            free(mem);
   3.154 -            size_t last_index = pool->ndata - 1;
   3.155 -            if (i != last_index) {
   3.156 -                pool->data[i] = pool->data[last_index];
   3.157 -                pool->data[last_index] = NULL;
   3.158 -            }
   3.159 -            pool->ndata--;
   3.160 -            return;
   3.161 -        }
   3.162 -    }
   3.163 -    abort();
   3.164 -}
   3.165 -
   3.166 -void cx_basic_mempool_destroy(CxMempool *p) {
   3.167 -    struct cx_basic_mempool_s *pool = (struct cx_basic_mempool_s *) p;
   3.168 -    cx_basic_mempool_memory *mem;
   3.169 -    cx_for_n(i, pool->ndata) {
   3.170 -        mem = (cx_basic_mempool_memory *) pool->data[i];
   3.171 -        if (mem) {
   3.172 -            if (mem->destructor) {
   3.173 -                mem->destructor(&(mem->c));
   3.174 -            }
   3.175 -            free(mem);
   3.176 -        }
   3.177 -    }
   3.178 -    free(pool->data);
   3.179 -    free((void *) p->allocator);
   3.180 -    free(pool);
   3.181 -}
   3.182 -
   3.183 -void cx_basic_mempool_set_destr(
   3.184 -        __attribute__((__unused__)) CxMempool *pool,
   3.185 -        void *ptr,
   3.186 -        cx_destructor_func func
   3.187 -) {
   3.188 -    *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func;
   3.189 -}
   3.190 -
   3.191 -static cx_allocator_class cx_basic_mempool_allocator_class = {
   3.192 -        cx_malloc_basic_mempool,
   3.193 -        cx_realloc_basic_mempool,
   3.194 -        cx_calloc_basic_mempool,
   3.195 -        cx_free_basic_mempool
   3.196 -};
   3.197 -
   3.198 -static cx_mempool_class cx_basic_mempool_class = {
   3.199 -        cx_basic_mempool_destroy,
   3.200 -        cx_basic_mempool_set_destr,
   3.201 -};
   3.202 -
   3.203 -CxMempool *cxBasicMempoolCreate(size_t capacity) {
   3.204 -    size_t poolsize;
   3.205 -    if (cx_szmul(capacity, sizeof(void *), &poolsize)) {
   3.206 -        return NULL;
   3.207 -    }
   3.208 -
   3.209 -    struct cx_basic_mempool_s *pool =
   3.210 -            malloc(sizeof(struct cx_basic_mempool_s));
   3.211 -    if (pool == NULL) {
   3.212 -        return NULL;
   3.213 -    }
   3.214 -
   3.215 -
   3.216 -    CxAllocator *provided_allocator = malloc(sizeof(CxAllocator));
   3.217 -    if (!provided_allocator) {
   3.218 -        free(pool);
   3.219 -        return NULL;
   3.220 -    }
   3.221 -    provided_allocator->cl = &cx_basic_mempool_allocator_class;
   3.222 -    provided_allocator->data = pool;
   3.223 -
   3.224 -    pool->base.cl = &cx_basic_mempool_class;
   3.225 -    pool->base.allocator = provided_allocator;
   3.226 -
   3.227 -    pool->data = malloc(poolsize);
   3.228 -    if (pool->data == NULL) {
   3.229 -        free(provided_allocator);
   3.230 -        free(pool);
   3.231 -        return NULL;
   3.232 -    }
   3.233 -
   3.234 -    pool->ndata = 0;
   3.235 -    pool->size = capacity;
   3.236 -
   3.237 -    return (CxMempool *) pool;
   3.238 -}
     4.1 --- a/src/cx/basic_mempool.h	Wed Jun 28 19:18:01 2023 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,76 +0,0 @@
     4.4 -/*
     4.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     4.6 - *
     4.7 - * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     4.8 - *
     4.9 - * Redistribution and use in source and binary forms, with or without
    4.10 - * modification, are permitted provided that the following conditions are met:
    4.11 - *
    4.12 - *   1. Redistributions of source code must retain the above copyright
    4.13 - *      notice, this list of conditions and the following disclaimer.
    4.14 - *
    4.15 - *   2. Redistributions in binary form must reproduce the above copyright
    4.16 - *      notice, this list of conditions and the following disclaimer in the
    4.17 - *      documentation and/or other materials provided with the distribution.
    4.18 - *
    4.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    4.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    4.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    4.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    4.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    4.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    4.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    4.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    4.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    4.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    4.29 - * POSSIBILITY OF SUCH DAMAGE.
    4.30 - */
    4.31 -/**
    4.32 - * \file basic_mempool.h
    4.33 - * \brief Implementation of a basic memory pool.
    4.34 - * \author Mike Becker
    4.35 - * \author Olaf Wintermann
    4.36 - * \version 3.0
    4.37 - * \copyright 2-Clause BSD License
    4.38 - */
    4.39 -
    4.40 -#ifndef UCX_BASIC_MEMPOOL_H
    4.41 -#define UCX_BASIC_MEMPOOL_H
    4.42 -
    4.43 -#include "mempool.h"
    4.44 -
    4.45 -#ifdef __cplusplus
    4.46 -extern "C" {
    4.47 -#endif
    4.48 -
    4.49 -/**
    4.50 - * Basic array-based memory pool.
    4.51 - */
    4.52 -struct cx_basic_mempool_s {
    4.53 -    /** Inherit base structure members. */
    4.54 -    CxMempool base;
    4.55 -
    4.56 -    /** List of pointers to pooled memory. */
    4.57 -    void **data;
    4.58 -
    4.59 -    /** Number of pooled memory items. */
    4.60 -    size_t ndata;
    4.61 -
    4.62 -    /** Memory pool size. */
    4.63 -    size_t size;
    4.64 -};
    4.65 -
    4.66 -/**
    4.67 - * Creates a basic array-based memory pool.
    4.68 - *
    4.69 - * @param capacity the initial capacity of the pool
    4.70 - * @return the created memory pool or \c NULL if allocation failed
    4.71 - */
    4.72 -__attribute__((__warn_unused_result__))
    4.73 -CxMempool *cxBasicMempoolCreate(size_t capacity);
    4.74 -
    4.75 -#ifdef __cplusplus
    4.76 -} // extern "C"
    4.77 -#endif
    4.78 -
    4.79 -#endif // UCX_BASIC_MEMPOOL_H
     5.1 --- a/src/cx/mempool.h	Wed Jun 28 19:18:01 2023 +0200
     5.2 +++ b/src/cx/mempool.h	Wed Jun 28 20:07:52 2023 +0200
     5.3 @@ -44,24 +44,31 @@
     5.4  extern "C" {
     5.5  #endif
     5.6  
     5.7 -/**
     5.8 - * Memory pool class type.
     5.9 - */
    5.10 -typedef struct cx_mempool_class_s cx_mempool_class;
    5.11 +/** Internal structure for pooled memory. */
    5.12 +struct cx_mempool_memory_s;
    5.13  
    5.14  /**
    5.15   * The basic structure of a memory pool.
    5.16   * Should be the first member of an actual memory pool implementation.
    5.17   */
    5.18  struct cx_mempool_s {
    5.19 +    /** The provided allocator. */
    5.20 +    CxAllocator const *allocator;
    5.21 +
    5.22      /**
    5.23 -     * The pool class definition.
    5.24 +     * A destructor that shall be automatically registered for newly allocated memory.
    5.25 +     * This destructor MUST NOT free the memory.
    5.26       */
    5.27 -    cx_mempool_class *cl;
    5.28 -    /**
    5.29 -     * The provided allocator.
    5.30 -     */
    5.31 -    CxAllocator const *allocator;
    5.32 +    cx_destructor_func auto_destr;
    5.33 +
    5.34 +    /** Array of pooled memory. */
    5.35 +    struct cx_mempool_memory_s **data;
    5.36 +
    5.37 +    /** Number of pooled memory items. */
    5.38 +    size_t size;
    5.39 +
    5.40 +    /** Memory pool capacity. */
    5.41 +    size_t capacity;
    5.42  };
    5.43  
    5.44  /**
    5.45 @@ -70,22 +77,27 @@
    5.46  typedef struct cx_mempool_s CxMempool;
    5.47  
    5.48  /**
    5.49 - * The class definition for a memory pool.
    5.50 + * Creates an array-based memory pool with a shared destructor function.
    5.51 + *
    5.52 + * This destructor MUST NOT free the memory.
    5.53 + *
    5.54 + * @param capacity the initial capacity of the pool
    5.55 + * @param destr the destructor function to use for allocated memory
    5.56 + * @return the created memory pool or \c NULL if allocation failed
    5.57   */
    5.58 -struct cx_mempool_class_s {
    5.59 -    /** Member function for destroying the pool. */
    5.60 -    __attribute__((__nonnull__))
    5.61 -    void (*destroy)(CxMempool *pool);
    5.62 +__attribute__((__warn_unused_result__))
    5.63 +CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func destr);
    5.64  
    5.65 -    /** Member function for setting a destructor. */
    5.66 -    __attribute__((__nonnull__))
    5.67 -    void (*set_destructor)(
    5.68 -            CxMempool *pool,
    5.69 -            void *memory,
    5.70 -            cx_destructor_func fnc
    5.71 -    );
    5.72 -};
    5.73 -
    5.74 +/**
    5.75 + * Creates a basic array-based memory pool.
    5.76 + *
    5.77 + * @param capacity the initial capacity of the pool
    5.78 + * @return the created memory pool or \c NULL if allocation failed
    5.79 + */
    5.80 +__attribute__((__warn_unused_result__))
    5.81 +static inline CxMempool *cxBasicMempoolCreate(size_t capacity) {
    5.82 +    return cxMempoolCreate(capacity, NULL);
    5.83 +}
    5.84  
    5.85  /**
    5.86   * Destroys a memory pool including their contents.
    5.87 @@ -93,27 +105,42 @@
    5.88   * @param pool the memory pool to destroy
    5.89   */
    5.90  __attribute__((__nonnull__))
    5.91 -static inline void cxMempoolDestroy(CxMempool *pool) {
    5.92 -    pool->cl->destroy(pool);
    5.93 -}
    5.94 +void cxMempoolDestroy(CxMempool *pool);
    5.95  
    5.96  /**
    5.97 - * Sets a destructor function for an allocated memory object.
    5.98 + * Sets the destructor function for a specific allocated memory object.
    5.99   *
   5.100 - * If the memory is not managed by the pool, the behavior is undefined.
   5.101 + * If the memory is not managed by a UCX memory pool, the behavior is undefined.
   5.102 + * The destructor MUST NOT free the memory.
   5.103   *
   5.104 - * @param pool the pool
   5.105 - * @param memory the objected allocated in the pool
   5.106 + * @param memory the object allocated in the pool
   5.107   * @param fnc the destructor function
   5.108   */
   5.109  __attribute__((__nonnull__))
   5.110 -static inline void cxMempoolSetDestructor(
   5.111 +void cxMempoolSetDestructor(
   5.112 +        void *memory,
   5.113 +        cx_destructor_func fnc
   5.114 +);
   5.115 +
   5.116 +/**
   5.117 + * Registers foreign memory with this pool.
   5.118 + *
   5.119 + * The destructor, in contrast to memory allocated by the pool, MUST free the memory.
   5.120 + *
   5.121 + * A small portion of memory will be allocated to register the information in the pool.
   5.122 + * If that allocation fails, this function will return non-zero.
   5.123 + *
   5.124 + * @param pool the pool
   5.125 + * @param memory the object allocated in the pool
   5.126 + * @param destr the destructor function
   5.127 + * @return zero on success, non-zero on failure
   5.128 + */
   5.129 +__attribute__((__nonnull__))
   5.130 +int cxMempoolRegister(
   5.131          CxMempool *pool,
   5.132          void *memory,
   5.133 -        cx_destructor_func fnc
   5.134 -) {
   5.135 -    pool->cl->set_destructor(pool, memory, fnc);
   5.136 -}
   5.137 +        cx_destructor_func destr
   5.138 +);
   5.139  
   5.140  #ifdef __cplusplus
   5.141  } // extern "C"
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/mempool.c	Wed Jun 28 20:07:52 2023 +0200
     6.3 @@ -0,0 +1,232 @@
     6.4 +/*
     6.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     6.6 + *
     6.7 + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     6.8 + *
     6.9 + * Redistribution and use in source and binary forms, with or without
    6.10 + * modification, are permitted provided that the following conditions are met:
    6.11 + *
    6.12 + *   1. Redistributions of source code must retain the above copyright
    6.13 + *      notice, this list of conditions and the following disclaimer.
    6.14 + *
    6.15 + *   2. Redistributions in binary form must reproduce the above copyright
    6.16 + *      notice, this list of conditions and the following disclaimer in the
    6.17 + *      documentation and/or other materials provided with the distribution.
    6.18 + *
    6.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    6.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    6.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    6.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    6.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    6.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    6.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    6.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    6.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    6.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    6.29 + * POSSIBILITY OF SUCH DAMAGE.
    6.30 + */
    6.31 +
    6.32 +#include "cx/mempool.h"
    6.33 +#include "cx/utils.h"
    6.34 +#include <string.h>
    6.35 +
    6.36 +struct cx_mempool_memory_s {
    6.37 +    /** The destructor. */
    6.38 +    cx_destructor_func destructor;
    6.39 +    /** The actual memory. */
    6.40 +    char c[];
    6.41 +};
    6.42 +
    6.43 +static void *cx_mempool_malloc(
    6.44 +        void *p,
    6.45 +        size_t n
    6.46 +) {
    6.47 +    struct cx_mempool_s *pool = p;
    6.48 +
    6.49 +    if (pool->size >= pool->capacity) {
    6.50 +        size_t newcap = pool->capacity - (pool->capacity % 16) + 16;
    6.51 +        struct cx_mempool_memory_s **newdata = realloc(pool->data, newcap*sizeof(struct cx_mempool_memory_s*));
    6.52 +        if (newdata == NULL) {
    6.53 +            return NULL;
    6.54 +        }
    6.55 +        pool->data = newdata;
    6.56 +        pool->capacity = newcap;
    6.57 +    }
    6.58 +
    6.59 +    struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n);
    6.60 +    if (mem == NULL) {
    6.61 +        return NULL;
    6.62 +    }
    6.63 +
    6.64 +    mem->destructor = pool->auto_destr;
    6.65 +    pool->data[pool->size] = mem;
    6.66 +    pool->size++;
    6.67 +
    6.68 +    return mem->c;
    6.69 +}
    6.70 +
    6.71 +static void *cx_mempool_calloc(
    6.72 +        void *p,
    6.73 +        size_t nelem,
    6.74 +        size_t elsize
    6.75 +) {
    6.76 +    size_t msz;
    6.77 +    if (cx_szmul(nelem, elsize, &msz)) {
    6.78 +        return NULL;
    6.79 +    }
    6.80 +    void *ptr = cx_mempool_malloc(p, msz);
    6.81 +    if (ptr == NULL) {
    6.82 +        return NULL;
    6.83 +    }
    6.84 +    memset(ptr, 0, nelem * elsize);
    6.85 +    return ptr;
    6.86 +}
    6.87 +
    6.88 +static void *cx_mempool_realloc(
    6.89 +        void *p,
    6.90 +        void *ptr,
    6.91 +        size_t n
    6.92 +) {
    6.93 +    struct cx_mempool_s *pool = p;
    6.94 +
    6.95 +    struct cx_mempool_memory_s *mem, *newm;
    6.96 +    mem = (struct cx_mempool_memory_s*)(((char *) ptr) - sizeof(cx_destructor_func));
    6.97 +    newm = realloc(mem, n + sizeof(cx_destructor_func));
    6.98 +
    6.99 +    if (newm == NULL) {
   6.100 +        return NULL;
   6.101 +    }
   6.102 +    if (mem != newm) {
   6.103 +        cx_for_n(i, pool->size) {
   6.104 +            if (pool->data[i] == mem) {
   6.105 +                pool->data[i] = newm;
   6.106 +                return ((char*)newm) + sizeof(cx_destructor_func);
   6.107 +            }
   6.108 +        }
   6.109 +        abort();
   6.110 +    } else {
   6.111 +        return ptr;
   6.112 +    }
   6.113 +}
   6.114 +
   6.115 +static void cx_mempool_free(
   6.116 +        void *p,
   6.117 +        void *ptr
   6.118 +) {
   6.119 +    struct cx_mempool_s *pool = p;
   6.120 +
   6.121 +    struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *)
   6.122 +            ((char *) ptr - sizeof(cx_destructor_func));
   6.123 +
   6.124 +    cx_for_n(i, pool->size) {
   6.125 +        if (mem == pool->data[i]) {
   6.126 +            if (mem->destructor) {
   6.127 +                mem->destructor(mem->c);
   6.128 +            }
   6.129 +            free(mem);
   6.130 +            size_t last_index = pool->size - 1;
   6.131 +            if (i != last_index) {
   6.132 +                pool->data[i] = pool->data[last_index];
   6.133 +                pool->data[last_index] = NULL;
   6.134 +            }
   6.135 +            pool->size--;
   6.136 +            return;
   6.137 +        }
   6.138 +    }
   6.139 +    abort();
   6.140 +}
   6.141 +
   6.142 +void cxMempoolDestroy(CxMempool *pool) {
   6.143 +    struct cx_mempool_memory_s *mem;
   6.144 +    cx_for_n(i, pool->size) {
   6.145 +        mem = pool->data[i];
   6.146 +        if (mem->destructor) {
   6.147 +            mem->destructor(mem->c);
   6.148 +        }
   6.149 +        free(mem);
   6.150 +    }
   6.151 +    free(pool->data);
   6.152 +    free((void*) pool->allocator);
   6.153 +    free(pool);
   6.154 +}
   6.155 +
   6.156 +void cxMempoolSetDestructor(
   6.157 +        void *ptr,
   6.158 +        cx_destructor_func func
   6.159 +) {
   6.160 +    *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func;
   6.161 +}
   6.162 +
   6.163 +struct cx_mempool_foreign_mem_s {
   6.164 +    cx_destructor_func destr;
   6.165 +    void* mem;
   6.166 +};
   6.167 +
   6.168 +static void cx_mempool_destr_foreign_mem(void* ptr) {
   6.169 +    struct cx_mempool_foreign_mem_s *fm = ptr;
   6.170 +    fm->destr(fm->mem);
   6.171 +}
   6.172 +
   6.173 +int cxMempoolRegister(
   6.174 +        CxMempool *pool,
   6.175 +        void *memory,
   6.176 +        cx_destructor_func destr
   6.177 +) {
   6.178 +    struct cx_mempool_foreign_mem_s *fm = cx_mempool_malloc(
   6.179 +            pool,
   6.180 +            sizeof(struct cx_mempool_foreign_mem_s)
   6.181 +    );
   6.182 +    if (fm == NULL) return 1;
   6.183 +
   6.184 +    fm->mem = memory;
   6.185 +    fm->destr = destr;
   6.186 +    *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem;
   6.187 +
   6.188 +    return 0;
   6.189 +}
   6.190 +
   6.191 +static cx_allocator_class cx_mempool_allocator_class = {
   6.192 +        cx_mempool_malloc,
   6.193 +        cx_mempool_realloc,
   6.194 +        cx_mempool_calloc,
   6.195 +        cx_mempool_free
   6.196 +};
   6.197 +
   6.198 +CxMempool *cxMempoolCreate(
   6.199 +        size_t capacity,
   6.200 +        cx_destructor_func destr
   6.201 +) {
   6.202 +    size_t poolsize;
   6.203 +    if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) {
   6.204 +        return NULL;
   6.205 +    }
   6.206 +
   6.207 +    struct cx_mempool_s *pool =
   6.208 +            malloc(sizeof(struct cx_mempool_s));
   6.209 +    if (pool == NULL) {
   6.210 +        return NULL;
   6.211 +    }
   6.212 +
   6.213 +    CxAllocator *provided_allocator = malloc(sizeof(CxAllocator));
   6.214 +    if (provided_allocator == NULL) {
   6.215 +        free(pool);
   6.216 +        return NULL;
   6.217 +    }
   6.218 +    provided_allocator->cl = &cx_mempool_allocator_class;
   6.219 +    provided_allocator->data = pool;
   6.220 +
   6.221 +    pool->allocator = provided_allocator;
   6.222 +
   6.223 +    pool->data = malloc(poolsize);
   6.224 +    if (pool->data == NULL) {
   6.225 +        free(provided_allocator);
   6.226 +        free(pool);
   6.227 +        return NULL;
   6.228 +    }
   6.229 +
   6.230 +    pool->size = 0;
   6.231 +    pool->capacity = capacity;
   6.232 +    pool->auto_destr = destr;
   6.233 +
   6.234 +    return (CxMempool *) pool;
   6.235 +}
     7.1 --- a/tests/CMakeLists.txt	Wed Jun 28 19:18:01 2023 +0200
     7.2 +++ b/tests/CMakeLists.txt	Wed Jun 28 20:07:52 2023 +0200
     7.3 @@ -26,7 +26,7 @@
     7.4          test_hash_key.cpp
     7.5          test_map.cpp
     7.6          test_map_generics.c
     7.7 -        test_basic_mempool.cpp
     7.8 +        test_mempool.cpp
     7.9          test_printf.cpp
    7.10          selftest.cpp
    7.11          util_allocator.cpp
     8.1 --- a/tests/test_basic_mempool.cpp	Wed Jun 28 19:18:01 2023 +0200
     8.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.3 @@ -1,154 +0,0 @@
     8.4 -/*
     8.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     8.6 - *
     8.7 - * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     8.8 - *
     8.9 - * Redistribution and use in source and binary forms, with or without
    8.10 - * modification, are permitted provided that the following conditions are met:
    8.11 - *
    8.12 - *   1. Redistributions of source code must retain the above copyright
    8.13 - *      notice, this list of conditions and the following disclaimer.
    8.14 - *
    8.15 - *   2. Redistributions in binary form must reproduce the above copyright
    8.16 - *      notice, this list of conditions and the following disclaimer in the
    8.17 - *      documentation and/or other materials provided with the distribution.
    8.18 - *
    8.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    8.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    8.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    8.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    8.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    8.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    8.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    8.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    8.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    8.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    8.29 - * POSSIBILITY OF SUCH DAMAGE.
    8.30 - */
    8.31 -
    8.32 -#include "cx/basic_mempool.h"
    8.33 -#include "util_allocator.h"
    8.34 -#include <gtest/gtest.h>
    8.35 -
    8.36 -class CxBasicMempool : public ::testing::Test {
    8.37 -protected:
    8.38 -    CxMempool *pool = nullptr;
    8.39 -
    8.40 -    void TearDown() override {
    8.41 -        if (pool != nullptr) {
    8.42 -            cxMempoolDestroy(pool);
    8.43 -        }
    8.44 -    }
    8.45 -};
    8.46 -
    8.47 -TEST_F(CxBasicMempool, Create) {
    8.48 -    pool = cxBasicMempoolCreate(16);
    8.49 -    ASSERT_NE(pool->allocator, nullptr);
    8.50 -    ASSERT_NE(pool->cl, nullptr);
    8.51 -    EXPECT_NE(pool->cl->destroy, nullptr);
    8.52 -    ASSERT_NE(pool->allocator->cl, nullptr);
    8.53 -    EXPECT_EQ(pool->allocator->data, pool);
    8.54 -    EXPECT_NE(pool->allocator->cl->malloc, nullptr);
    8.55 -    EXPECT_NE(pool->allocator->cl->calloc, nullptr);
    8.56 -    EXPECT_NE(pool->allocator->cl->realloc, nullptr);
    8.57 -    EXPECT_NE(pool->allocator->cl->free, nullptr);
    8.58 -
    8.59 -    auto basic_pool = reinterpret_cast<cx_basic_mempool_s *>(pool);
    8.60 -    EXPECT_EQ(basic_pool->size, 16);
    8.61 -    EXPECT_EQ(basic_pool->ndata, 0);
    8.62 -    EXPECT_NE(basic_pool->data, nullptr);
    8.63 -}
    8.64 -
    8.65 -TEST_F(CxBasicMempool, malloc) {
    8.66 -    pool = cxBasicMempoolCreate(4);
    8.67 -    auto basic_pool = reinterpret_cast<cx_basic_mempool_s *>(pool);
    8.68 -    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    8.69 -    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    8.70 -    EXPECT_EQ(basic_pool->ndata, 2);
    8.71 -    EXPECT_EQ(basic_pool->size, 4);
    8.72 -    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    8.73 -    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    8.74 -    EXPECT_EQ(basic_pool->ndata, 4);
    8.75 -    EXPECT_EQ(basic_pool->size, 4);
    8.76 -    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    8.77 -    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    8.78 -    EXPECT_EQ(basic_pool->ndata, 6);
    8.79 -    EXPECT_GE(basic_pool->size, 6);
    8.80 -}
    8.81 -
    8.82 -TEST_F(CxBasicMempool, calloc) {
    8.83 -    pool = cxBasicMempoolCreate(4);
    8.84 -
    8.85 -    auto test = (int *) cxCalloc(pool->allocator, 2, sizeof(int));
    8.86 -    ASSERT_NE(test, nullptr);
    8.87 -    EXPECT_EQ(test[0], 0);
    8.88 -    EXPECT_EQ(test[1], 0);
    8.89 -}
    8.90 -
    8.91 -static unsigned test_destructor_called = 0;
    8.92 -
    8.93 -static void test_destructor([[maybe_unused]] void *mem) {
    8.94 -    test_destructor_called++;
    8.95 -}
    8.96 -
    8.97 -TEST_F(CxBasicMempool, destructor) {
    8.98 -    pool = cxBasicMempoolCreate(4);
    8.99 -    auto data = cxMalloc(pool->allocator, sizeof(int));
   8.100 -    *((int *) data) = 13;
   8.101 -    cxMempoolSetDestructor(pool, data, test_destructor);
   8.102 -    EXPECT_EQ(*((int *) data), 13);
   8.103 -    test_destructor_called = 0;
   8.104 -    cxFree(pool->allocator, data);
   8.105 -    EXPECT_EQ(test_destructor_called, 1);
   8.106 -    data = cxMalloc(pool->allocator, sizeof(int));
   8.107 -    cxMempoolSetDestructor(pool, data, test_destructor);
   8.108 -    cxMempoolDestroy(pool);
   8.109 -    pool = nullptr;
   8.110 -    EXPECT_EQ(test_destructor_called, 2);
   8.111 -}
   8.112 -
   8.113 -TEST_F(CxBasicMempool, realloc) {
   8.114 -    pool = cxBasicMempoolCreate(4);
   8.115 -    auto data = cxMalloc(pool->allocator, sizeof(int));
   8.116 -    *((int *) data) = 13;
   8.117 -    cxMempoolSetDestructor(pool, data, test_destructor);
   8.118 -
   8.119 -    void *rdata = data;
   8.120 -    unsigned n = 1;
   8.121 -    while (rdata == data) {
   8.122 -        n <<= 1;
   8.123 -        ASSERT_LT(n, 65536); // eventually the memory should be moved elsewhere
   8.124 -        rdata = cxRealloc(pool->allocator, data, n * sizeof(intptr_t));
   8.125 -    }
   8.126 -
   8.127 -    EXPECT_EQ(*((int *) rdata), 13);
   8.128 -    // test if destructor is still intact
   8.129 -    test_destructor_called = 0;
   8.130 -    cxFree(pool->allocator, rdata);
   8.131 -    EXPECT_EQ(test_destructor_called, 1);
   8.132 -}
   8.133 -
   8.134 -
   8.135 -TEST_F(CxBasicMempool, free) {
   8.136 -    pool = cxBasicMempoolCreate(4);
   8.137 -    auto basic_pool = reinterpret_cast<cx_basic_mempool_s *>(pool);
   8.138 -
   8.139 -    void *mem1;
   8.140 -    void *mem2;
   8.141 -
   8.142 -    mem1 = cxMalloc(pool->allocator, 16);
   8.143 -    cxFree(pool->allocator, mem1);
   8.144 -    EXPECT_EQ(basic_pool->ndata, 0);
   8.145 -
   8.146 -    cxMalloc(pool->allocator, 16);
   8.147 -    cxMalloc(pool->allocator, 16);
   8.148 -    mem1 = cxMalloc(pool->allocator, 16);
   8.149 -    cxMalloc(pool->allocator, 16);
   8.150 -    mem2 = cxMalloc(pool->allocator, 16);
   8.151 -
   8.152 -    EXPECT_EQ(basic_pool->ndata, 5);
   8.153 -    cxFree(pool->allocator, mem1);
   8.154 -    EXPECT_EQ(basic_pool->ndata, 4);
   8.155 -    cxFree(pool->allocator, mem2);
   8.156 -    EXPECT_EQ(basic_pool->ndata, 3);
   8.157 -}
   8.158 \ No newline at end of file
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/tests/test_mempool.cpp	Wed Jun 28 20:07:52 2023 +0200
     9.3 @@ -0,0 +1,153 @@
     9.4 +/*
     9.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     9.6 + *
     9.7 + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     9.8 + *
     9.9 + * Redistribution and use in source and binary forms, with or without
    9.10 + * modification, are permitted provided that the following conditions are met:
    9.11 + *
    9.12 + *   1. Redistributions of source code must retain the above copyright
    9.13 + *      notice, this list of conditions and the following disclaimer.
    9.14 + *
    9.15 + *   2. Redistributions in binary form must reproduce the above copyright
    9.16 + *      notice, this list of conditions and the following disclaimer in the
    9.17 + *      documentation and/or other materials provided with the distribution.
    9.18 + *
    9.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    9.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    9.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    9.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    9.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    9.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    9.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    9.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    9.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    9.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    9.29 + * POSSIBILITY OF SUCH DAMAGE.
    9.30 + */
    9.31 +
    9.32 +#include "cx/mempool.h"
    9.33 +#include "util_allocator.h"
    9.34 +#include <gtest/gtest.h>
    9.35 +
    9.36 +TEST(Mempool, Create) {
    9.37 +    auto pool = cxBasicMempoolCreate(16);
    9.38 +    ASSERT_EQ(pool->auto_destr, nullptr);
    9.39 +    ASSERT_NE(pool->allocator, nullptr);
    9.40 +    ASSERT_NE(pool->allocator->cl, nullptr);
    9.41 +    EXPECT_EQ(pool->allocator->data, pool);
    9.42 +    EXPECT_NE(pool->allocator->cl->malloc, nullptr);
    9.43 +    EXPECT_NE(pool->allocator->cl->calloc, nullptr);
    9.44 +    EXPECT_NE(pool->allocator->cl->realloc, nullptr);
    9.45 +    EXPECT_NE(pool->allocator->cl->free, nullptr);
    9.46 +    EXPECT_EQ(pool->capacity, 16);
    9.47 +    EXPECT_EQ(pool->size, 0);
    9.48 +    EXPECT_NE(pool->data, nullptr);
    9.49 +    cxMempoolDestroy(pool);
    9.50 +}
    9.51 +
    9.52 +TEST(Mempool, malloc) {
    9.53 +    auto pool = cxBasicMempoolCreate(4);
    9.54 +    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    9.55 +    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    9.56 +    EXPECT_EQ(pool->size, 2);
    9.57 +    EXPECT_EQ(pool->capacity, 4);
    9.58 +    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    9.59 +    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    9.60 +    EXPECT_EQ(pool->size, 4);
    9.61 +    EXPECT_EQ(pool->capacity, 4);
    9.62 +    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    9.63 +    EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr);
    9.64 +    EXPECT_EQ(pool->size, 6);
    9.65 +    EXPECT_GE(pool->capacity, 6);
    9.66 +    cxMempoolDestroy(pool);
    9.67 +}
    9.68 +
    9.69 +TEST(Mempool, calloc) {
    9.70 +    auto pool = cxBasicMempoolCreate(4);
    9.71 +
    9.72 +    auto test = (int *) cxCalloc(pool->allocator, 2, sizeof(int));
    9.73 +    ASSERT_NE(test, nullptr);
    9.74 +    EXPECT_EQ(test[0], 0);
    9.75 +    EXPECT_EQ(test[1], 0);
    9.76 +    cxMempoolDestroy(pool);
    9.77 +}
    9.78 +
    9.79 +static unsigned test_destructor_called;
    9.80 +
    9.81 +static void test_destructor([[maybe_unused]] void *mem) {
    9.82 +    test_destructor_called++;
    9.83 +}
    9.84 +
    9.85 +TEST(Mempool, realloc) {
    9.86 +    auto pool = cxMempoolCreate(4, test_destructor);
    9.87 +    ASSERT_EQ(pool->auto_destr, test_destructor);
    9.88 +    auto data = cxMalloc(pool->allocator, sizeof(int));
    9.89 +    *((int *) data) = 13;
    9.90 +
    9.91 +    void *rdata = data;
    9.92 +    unsigned n = 1;
    9.93 +    while (rdata == data) {
    9.94 +        n <<= 1;
    9.95 +        ASSERT_LT(n, 65536); // eventually the memory should be moved elsewhere
    9.96 +        rdata = cxRealloc(pool->allocator, data, n * sizeof(intptr_t));
    9.97 +    }
    9.98 +
    9.99 +    EXPECT_EQ(*((int *) rdata), 13);
   9.100 +    // test if destructor is still intact
   9.101 +    test_destructor_called = 0;
   9.102 +    cxFree(pool->allocator, rdata);
   9.103 +    EXPECT_EQ(test_destructor_called, 1);
   9.104 +    cxMempoolDestroy(pool);
   9.105 +}
   9.106 +
   9.107 +
   9.108 +TEST(Mempool, free) {
   9.109 +    auto pool = cxBasicMempoolCreate(4);
   9.110 +
   9.111 +    void *mem1;
   9.112 +    void *mem2;
   9.113 +
   9.114 +    mem1 = cxMalloc(pool->allocator, 16);
   9.115 +    cxFree(pool->allocator, mem1);
   9.116 +    EXPECT_EQ(pool->size, 0);
   9.117 +
   9.118 +    cxMalloc(pool->allocator, 16);
   9.119 +    cxMalloc(pool->allocator, 16);
   9.120 +    mem1 = cxMalloc(pool->allocator, 16);
   9.121 +    cxMalloc(pool->allocator, 16);
   9.122 +    mem2 = cxMalloc(pool->allocator, 16);
   9.123 +
   9.124 +    EXPECT_EQ(pool->size, 5);
   9.125 +    cxFree(pool->allocator, mem1);
   9.126 +    EXPECT_EQ(pool->size, 4);
   9.127 +    cxFree(pool->allocator, mem2);
   9.128 +    EXPECT_EQ(pool->size, 3);
   9.129 +    cxMempoolDestroy(pool);
   9.130 +}
   9.131 +
   9.132 +TEST(Mempool, Destroy) {
   9.133 +    auto pool = cxBasicMempoolCreate(4);
   9.134 +    auto data = cxMalloc(pool->allocator, sizeof(int));
   9.135 +    *((int *) data) = 13;
   9.136 +    cxMempoolSetDestructor(data, test_destructor);
   9.137 +    EXPECT_EQ(*((int *) data), 13);
   9.138 +    test_destructor_called = 0;
   9.139 +    cxFree(pool->allocator, data);
   9.140 +    EXPECT_EQ(test_destructor_called, 1);
   9.141 +    data = cxMalloc(pool->allocator, sizeof(int));
   9.142 +    cxMempoolSetDestructor(data, test_destructor);
   9.143 +    cxMempoolDestroy(pool);
   9.144 +    EXPECT_EQ(test_destructor_called, 2);
   9.145 +}
   9.146 +
   9.147 +TEST(Mempool, Register) {
   9.148 +    auto pool = cxBasicMempoolCreate(4);
   9.149 +    auto data = cxMalloc(pool->allocator, sizeof(int));
   9.150 +    test_destructor_called = 0;
   9.151 +    cxMempoolSetDestructor(data, test_destructor);
   9.152 +    int donotfree = 0;
   9.153 +    cxMempoolRegister(pool, &donotfree, test_destructor);
   9.154 +    cxMempoolDestroy(pool);
   9.155 +    EXPECT_EQ(test_destructor_called, 2);
   9.156 +}

mercurial