From f63692e10ee837870563a646402c33d5e3ca105b Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Mon, 20 Dec 2021 15:16:16 +0100 Subject: [PATCH] add debug, bits, and pool --- c/bits.h | 167 +++++++++++++++++++++++++++++++++++++++++ c/debug.c | 111 ++++++++++++++++++++++++++++ c/debug.h | 83 +++++++++++++++++++++ c/pool.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ c/pool.h | 77 +++++++++++++++++++ 5 files changed, 654 insertions(+) create mode 100644 c/bits.h create mode 100644 c/debug.c create mode 100644 c/debug.h create mode 100644 c/pool.c create mode 100644 c/pool.h diff --git a/c/bits.h b/c/bits.h new file mode 100644 index 0000000..fd32c3a --- /dev/null +++ b/c/bits.h @@ -0,0 +1,167 @@ +/* bits.h - bits functions. + * + * Copyright (C) 2021 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ +#ifndef BITS_H +#define BITS_H + +#include + +/* next include will define __WORDSIZE: 32 or 64 + */ +#include + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +/* no plan to support 32bits for now... + */ +#if __WORDSIZE != 64 +ERROR_64_BYTES_WORDSIZE_ONLY +#endif + +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef unsigned int uint; +typedef unsigned char uchar; + +/* count trailing zeroes : 00101000 -> 3 + * ^^^ + */ +static inline int ctz64(u64 n) +{ +# if __has_builtin(__builtin_ctzl) +# ifdef DEBUG_BITS + log_f(1, "builtin ctzl.\n"); +# endif + return __builtin_ctzl(n); + +# elif __has_builtin(__builtin_clzl) +# ifdef DEBUG_BITS + log_f(1, "builtin clzl.\n"); +# endif + return __WORDSIZE - (__builtin_clzl(n & -n) + 1); + +# else +# ifdef DEBUG_BITS + log_f(1, "emulated.\n"); +# endif + return popcount64((n & −n) − 1); +# endif +} + +/* count leading zeroes : 00101000 -> 2 + * ^^ + */ +static inline int clz64(u64 n) +{ +# if __has_builtin(__builtin_clzl) +# ifdef DEBUG_BITS + log_f(1, "builtin.\n"); +# endif + return __builtin_clzl(n); + +# else +# ifdef DEBUG_BITS + log_f(1, "emulated.\n"); +# endif + u64 r, q; + + r = (n > 0xFFFFFFFF) << 5; n >>= r; + q = (n > 0xFFFF) << 4; n >>= q; r |= q; + q = (n > 0xFF ) << 3; n >>= q; r |= q; + q = (n > 0xF ) << 2; n >>= q; r |= q; + q = (n > 0x3 ) << 1; n >>= q; r |= q; + r |= (n >> 1); + return __WORDSIZE - r - 1; +# endif +} + +/* find first set : 00101000 -> 4 + * ^ + */ +static inline uint ffs64(u64 n) +{ +# if __has_builtin(__builtin_ffsl) +# ifdef DEBUG_BITS + log_f(1, "builtin ffsl.\n"); +# endif + return __builtin_ffsll(n); + +# elif __has_builtin(__builtin_ctzl) +# ifdef DEBUG_BITS + log_f(1, "builtin ctzl.\n"); +# endif + if (n == 0) + return (0); + return __builtin_ctzl(n) + 1; + +# else +# ifdef DEBUG_BITS + log_f(1, "emulated.\n"); +# endif + return popcount64(n ^ ~-n); +# endif +} + +static inline int popcount64(u64 n) +{ +# if __has_builtin(__builtin_popcountl) +# ifdef DEBUG_BITS + log_f(1, "builtin.\n"); +# endif + return __builtin_popcountl(n); + +# else +# ifdef DEBUG_BITS + log_f(1, "emulated.\n"); +# endif + int count = 0; + while (n) { + count++; + n &= (n - 1); + } + return count; +# endif +} + +/** bit_for_each64 - iterate over an u64 bits + * @pos: an int used as current bit + * @tmp: a temp u64 used as temporary storage + * @ul: the u64 to loop over + * + * Usage: + * u64 u=139, _t; // u=b10001011 + * int cur; + * bit_for_each64(cur, _t, u) { + * printf("%d\n", cur); + * } + * This will display the position of each bit set in ul: 1, 2, 4, 8 + * + * I should probably re-think the implementation... + */ +#define bit_for_each64(pos, tmp, ul) \ + for (tmp = ul, pos = ffs64(tmp); tmp; tmp &= (tmp - 1), pos = ffs64(tmp)) + +/** or would it be more useful (counting bits from zero instead of 1) ? + */ +#define bit_for_each64_2(pos, tmp, ul) \ + for (tmp = ul, pos = ctz64(tmp); tmp; tmp ^= 1<. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include +#include +#include + +#ifndef DEBUG_DEBUG +#define DEBUG_DEBUG +#endif + +#include "debug.h" + +#define NANOSEC 1000000000 /* nano sec in sec */ +#define MILLISEC 1000000 /* milli sec in sec */ + +static s64 timer_start; /* in nanosecond */ +static u32 debug_level=0; + +void debug_level_set(u32 level) +{ + debug_level = level; + + log(1, "debug level set to %u\n", level); +} + +void debug_init(u32 level) +{ + struct timespec timer; + + debug_level_set(level); + if (!clock_gettime(CLOCK_MONOTONIC, &timer)) { + timer_start = timer.tv_sec * NANOSEC + timer.tv_nsec; + } + else { + timer_start = 0; + } + log(0, "timer started.\n"); +} + +inline static s64 timer_elapsed() +{ + struct timespec timer; + + clock_gettime(CLOCK_MONOTONIC, &timer); + return (timer.tv_sec * NANOSEC + timer.tv_nsec) - timer_start; +} + + +/* void debug - log function + * @timestamp : boolean + * @indent : indent level (2 spaces each) + * @src : source file/func name (or NULL) + * @line : line number + */ +void debug(u32 level, bool timestamp, u32 indent, const char *src, + u32 line, const char *fmt, ...) +{ + if (level > debug_level) + return; + + va_list ap; + + if (indent) + printf("%*s", 2*(indent-1), ""); + + if (timestamp) { + s64 diff = timer_elapsed(); + printf("%ld.%03ld ", diff/NANOSEC, (diff/1000000)%1000); + printf("%010ld ", diff); + } + + if (src) { + if (line) + printf("[%s:%u] ", src, line); + else + printf("[%s] ", src); + } + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +#ifdef BIN_debug +#include + +int main() +{ + int foo=1; + debug_init(5); + + log(0, "log0=%d\n", foo++); + log(1, "log1=%d\n", foo++); + log(2, "log2=%d\n", foo++); + log_i(2, "log_i 2=%d\n", foo++); + log_i(5, "log_i 5=%d\n", foo++); + log_i(6, "log_i 6=%d\n", foo++); + log_it(4, "log_it 4=%d\n", foo++); + log_f(1, "log_f 5=%d\n", foo++); +} +#endif diff --git a/c/debug.h b/c/debug.h new file mode 100644 index 0000000..e16081f --- /dev/null +++ b/c/debug.h @@ -0,0 +1,83 @@ +/* move.h - debug/log management. + * + * Copyright (C) 2021 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include +#include + +#include "bits.h" + +#ifdef DEBUG_DEBUG +void debug_init(u32 level); +void debug_level_set(u32 level); +void debug_devel_set(u32 level); +void debug(u32 level, bool timestamp, u32 indent, + const char *src, u32 line, const char *, ...); +#else /* DEBUG_DEBUG */ +#define _unused __attribute__((__unused__)) +static inline void debug_init(_unused u32 level) {} +static inline void debug_level_set(_unused u32 level) {} +static inline void debug_devel_set(_unused u32 level) {} +static inline void debug(_unused u32 level, _unused bool timestamp, + _unused u32 indent, _unused const char *src, + _unused u32 line, const char *, ...) {} +#undef _unused +#endif /* DEBUG_DEBUG */ + +/* format: only printf + */ +#define log(level, fmt, args...) \ + debug((level), false, 0, NULL, 0, fmt, ##args) + +/* format : indent, no func name, no timestamp + * >>>>val=2 + */ +#define log_i(level, fmt, args...) \ + debug((level), false, (level), NULL, 0, fmt, ##args) + +/* format : func name, no indent, no timestamp + * [foo] val=2 + */ +#define log_f(level, fmt, args...) \ + debug((level), false, 0, __func__, 0, fmt, ##args) + +/* format : func name, no indent, no timestamp + * >>>> [foo:15] val=2 + */ +#define log_if(level, fmt, args...) \ + debug((level), false, (level), __func__, __LINE__, fmt, ##args) + +/* format : func name, indent, timestamp + * >>>>foo:15 val=2 + */ +#define log_it(level, fmt, args...) \ + debug((level), true, (level), __func__, __LINE__, fmt, ##args) + +/* format: file name, no indent, no timestamp + * foo:15 val=2 + * + * #define log_f(level, fmt, args...) \ + * debug((level), false, 0, __FILE__, __LINE__, fmt, args) + */ + +#else +#define log(level, fmt, args...) +#define log_i(...) +#define log_f(...) +#define log_if(...) +#define log_it(...) +#define log_f(...) + +#endif /* DEBUG_H */ diff --git a/c/pool.c b/c/pool.c new file mode 100644 index 0000000..7383758 --- /dev/null +++ b/c/pool.c @@ -0,0 +1,216 @@ +/* pool.c - A simple pool manager. + * + * Copyright (C) 2021 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include +#include +#include +#include +#include "list.h" +#include "pool.h" +#include "debug.h" +#include "bits.h" + +void pool_stats(pool_t *pool) +{ + if (pool) { +# ifdef DEBUG_POOL + block_t *block; + + log_f(1, "[%s] pool [%p]: blocks:%u avail:%u alloc:%u grow:%u eltsize:%lu\n", + pool->name, (void *)pool, pool->nblocks, pool->available, pool->allocated, + pool->growsize, pool->eltsize); + log(5, "\tblocks: "); + list_for_each_entry(block, &pool->list_blocks, list_blocks) { + log(5, "%p ", block); + } + log(5, "\n"); +# endif + } +} + +pool_t *pool_create(const char *name, u32 growsize, size_t eltsize) +{ + pool_t *pool; + +# ifdef DEBUG_POOL + log_f(1, "name=[%s] growsize=%u eltsize=%lu\n", + name, growsize, eltsize); +# endif + /* we need at least this space in struct */ + if (eltsize < sizeof (struct list_head)) + return NULL; + if ((pool = malloc(sizeof (*pool)))) { + strncpy(pool->name, name, POOL_NAME_LENGTH - 1); + pool->name[POOL_NAME_LENGTH - 1] = 0; + pool->growsize = growsize; + pool->eltsize = eltsize; + pool->available = 0; + pool->allocated = 0; + pool->nblocks = 0; + INIT_LIST_HEAD(&pool->list_available); + INIT_LIST_HEAD(&pool->list_blocks); + } + return pool; +} + +static u32 _pool_add(pool_t *pool, struct list_head *elt) +{ +# ifdef DEBUG_POOL + log_f(6, "pool=%p &head=%p elt=%p off1=%lu off2=%lu\n", + (void *)pool, + (void *)&pool->list_available, + (void *)elt, + (void *)&pool->list_available-(void *)pool, + offsetof(pool_t, list_available)); +# endif + + list_add(elt, &pool->list_available); + return ++pool->available; +} + +u32 pool_add(pool_t *pool, void *elt) +{ + return _pool_add(pool, elt); +} + +static struct list_head *_pool_get(pool_t *pool) +{ + struct list_head *res = pool->list_available.next; + pool->available--; + list_del(res); + return res; +} + +void *pool_get(pool_t *pool) +{ + if (!pool) + return NULL; + if (!pool->available) { + block_t *block = malloc(sizeof(block_t) + pool->eltsize * pool->growsize); + void *cur; + u32 i; + + if (!block) { +# ifdef DEBUG_POOL + log_f(1, "[%s]: failed block allocation\n"); +# endif + return NULL; + } + + /* maintain list of allocated blocks + */ + list_add(&block->list_blocks, &pool->list_blocks); + pool->nblocks++; + +# ifdef DEBUG_POOL + log_f(1, "[%s]: growing pool from %u to %u elements. block=%p nblocks=%u\n", + pool->name, + pool->allocated, + pool->allocated + pool->growsize, + block, + pool->nblocks); +# endif + + pool->allocated += pool->growsize; + for (i = 0; i < pool->growsize; ++i) { + cur = block->data + i * pool->eltsize; +# ifdef DEBUG_POOL + log_f(7, "alloc=%p cur=%p\n", block, cur); +# endif + _pool_add(pool, (struct list_head *)cur); + } + //pool_stats(pool); + } + /* this is the effective address if the object (and also the + * pool list_head address) + */ + return _pool_get(pool); +} + +void pool_destroy(pool_t *pool) +{ + block_t *block, *tmp; + if (!pool) + return; + /* release memory blocks */ +# ifdef DEBUG_POOL + log_f(1, "[%s]: releasing %d blocks and main structure\n", pool->name, pool->nblocks); + log(5, "blocks:"); +# endif + list_for_each_entry_safe(block, tmp, &pool->list_blocks, list_blocks) { + list_del(&block->list_blocks); + free(block); +# ifdef DEBUG_POOL + log(5, " %p", block); +# endif + } +# ifdef DEBUG_POOL + log(5, "\n"); +# endif + free(pool); +} + +#ifdef BIN_pool +struct d { + u16 data1; + char c; + struct list_head list; +}; + +static LIST_HEAD (head); + +int main(int ac, char**av) +{ + pool_t *pool; + int total; + int action=0; + u16 icur=0; + char ccur='z'; + struct d *elt; + + debug_init(3); + log_f(1, "%s: sizeof(d)=%lu sizeof(*d)=%lu off=%lu\n", *av, sizeof(elt), + sizeof(*elt), offsetof(struct d, list)); + + if ((pool = pool_create("dummy", 3, sizeof(*elt)))) { + pool_stats(pool); + for (int cur=1; curdata1 = icur++; + elt->c = ccur--; + list_add(&elt->list, &head); + } + pool_stats(pool); + action = 1; + } else { /* remove one elt from list */ + log_f(2, "deleting %d elements\n", total); + for (int i = 0; i < total; ++i) { + if (!list_empty(&head)) { + elt = list_last_entry(&head, struct d, list); + printf("elt=[%d, %c]\n", elt->data1, elt->c); + list_del(&elt->list); + pool_add(pool, elt); + } + } + pool_stats(pool); + action = 0; + } + } + } + pool_stats(pool); +} +#endif diff --git a/c/pool.h b/c/pool.h new file mode 100644 index 0000000..f64024e --- /dev/null +++ b/c/pool.h @@ -0,0 +1,77 @@ +/* pool.h - A simple memory pool manager. + * + * Copyright (C) 2021 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#ifndef POOL_H +#define POOL_H + +#include +#include +#include "list.h" +#include "bits.h" + +#define POOL_NAME_LENGTH (16) /* max name length including trailing \0 */ + +typedef struct { + struct list_head list_blocks; /* list of allocated blocks in pool */ + char data[]; /* objects block */ +} block_t; + +typedef struct { + char name[POOL_NAME_LENGTH]; /* pool name */ + u32 available; /* current available elements */ + u32 allocated; /* total objects allocated */ + u32 growsize; /* number of objects per block allocated */ + size_t eltsize; /* object size */ + u32 nblocks; /* number of blocks allocated */ + struct list_head list_available; /* available nodes */ + struct list_head list_blocks; /* allocated blocks */ +} pool_t; + +/** + * pool_stats - display some pool statistics + * @pool: the pool address. + */ +void pool_stats(pool_t *pool); + +/** + * pool_create - create a new memory pool + * @name: the name to give to the pool. + * @grow: the number of elements to add when no more available. + * @size: the size of an element in pool. + */ +pool_t *pool_create(const char *name, u32 grow, size_t size); + +/** + * pool_get - get an element from a pool + * @pool: the pool address. + */ +void *pool_get(pool_t *pool); + +/** + * pool_add - add (release) an element to a pool + * @pool: the pool address. + * @elt: the address of the object to add to the pool. + */ +u32 pool_add(pool_t *pool, void *elt); + +/** + * pool_destroy - destroy a pool. + * @pool: the pool address. + * + * Attention: All memory is freed, but no check is done whether all pool + * elements has been released. Referencing any pool object after this call + * is strongly discouraged. + */ +void pool_destroy(pool_t *pool); + +#endif