From: Attractive Chaos Date: Mon, 22 Dec 2025 04:35:26 +0000 (-0500) Subject: added block arena X-Git-Url: http://www.kaiwu.me/postgresql/commit/static/gitweb.js?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=klib.git added block arena --- diff --git a/kbarena.c b/kbarena.c new file mode 100644 index 0000000..220d20f --- /dev/null +++ b/kbarena.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include "kbarena.h" + +#define kom_malloc(type, cnt) ((type*)malloc((cnt) * sizeof(type))) +#define kom_calloc(type, cnt) ((type*)calloc((cnt), sizeof(type))) +#define kom_realloc(type, ptr, cnt) ((type*)realloc((ptr), (cnt) * sizeof(type))) + +#define kom_grow(type, ptr, __i, __m, __succ) do { /* make enough room for ptr[__i] */ \ + *(__succ) = 1; \ + if ((__i) >= (__m)) { \ + size_t new_m = (__i) + 16ULL + (((__i) + 1ULL) >> 1); \ + type *new_p; \ + new_p = kom_realloc(type, (ptr), new_m); \ + if (new_p) (__m) = new_m, (ptr) = new_p; \ + else *(__succ) = 0; \ + } \ + } while (0) + +typedef struct { + uint32_t blen, max_blk; // block length and max number of blocks + uint32_t bi, bo; // current block index and block offset + uint8_t **b; // blocks + uint32_t n_stack, m_stack; // stack size and capacity + uint64_t *stack; // saved states (two uint32_t packed together) +} kbarena_t; + +void *kba_init(uint32_t blen) +{ + kbarena_t *ba; + if (blen == 0) return 0; + ba = kom_calloc(kbarena_t, 1); + if (ba == 0) return 0; + ba->blen = (blen + 7) / 8 * 8; // round up to 8 bytes + ba->max_blk = 4; + ba->b = kom_calloc(uint8_t*, ba->max_blk); + ba->b[0] = kom_malloc(uint8_t, ba->blen); // TODO: use aligned alloc + return ba; +} + +void kba_destroy(void *ba_) +{ + kbarena_t *ba = (kbarena_t*)ba_; + uint32_t i; + if (ba == 0) return; + for (i = 0; i < ba->max_blk; ++i) + if (ba->b[i]) free(ba->b[i]); + free(ba->b); free(ba->stack); free(ba); +} + +size_t kba_capacity(const void *ba_) +{ + const kbarena_t *ba = (const kbarena_t*)ba_; + uint32_t i; + size_t cap = 0; + for (i = 0; i < ba->max_blk; ++i) + if (ba->b[i]) cap += ba->blen; + return cap; +} + +void *kba_alloc(void *ba_, uint32_t sz, uint32_t aln) +{ + kbarena_t *ba = (kbarena_t*)ba_; + uint32_t pad = -(int64_t)ba->bo & (aln - 1); + void *p; + if (sz == 0 || sz > ba->blen || (aln & (aln - 1)) != 0) return 0; + if ((uint64_t)ba->bo + sz + pad > ba->blen) { // no room in the current block + uint32_t old_max = ba->max_blk; + int succ; + kom_grow(uint8_t*, ba->b, ba->bi + 1, ba->max_blk, &succ); + if (!succ) return 0; + if (ba->max_blk > old_max) + memset(&ba->b[old_max], 0, (ba->max_blk - old_max) * sizeof(void*)); + if (ba->b[ba->bi + 1] == 0) { + ba->b[ba->bi + 1] = kom_malloc(uint8_t, ba->blen); // TODO: use aligned malloc + if (ba->b[ba->bi + 1] == 0) return 0; + } + ++ba->bi, ba->bo = 0, pad = 0; + } + p = (void*)&ba->b[ba->bi][ba->bo + pad]; + ba->bo += sz + pad; + return p; +} + +int kba_save(void *ba_) +{ + kbarena_t *ba = (kbarena_t*)ba_; + int succ; + kom_grow(uint64_t, ba->stack, ba->n_stack, ba->m_stack, &succ); + if (!succ) return -1; + ba->stack[ba->n_stack++] = (uint64_t)ba->bi << 32 | ba->bo; + return 0; +} + +int kba_restore(void *ba_) +{ + uint64_t x; + kbarena_t *ba = (kbarena_t*)ba_; + if (ba->n_stack == 0) return -1; + x = ba->stack[--ba->n_stack]; + ba->bi = x>>32, ba->bo = (uint32_t)x; + return 0; +} diff --git a/kbarena.h b/kbarena.h new file mode 100644 index 0000000..c660589 --- /dev/null +++ b/kbarena.h @@ -0,0 +1,46 @@ +#ifndef AC_KBARENA_H +#define AC_KBARENA_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize a blocked arena + * + * @param blen block length in bytes + * + * @return pointer to a blocked arena + */ +void *kba_init(unsigned blen); + +/** Destroy a blocked arena */ +void kba_destroy(void *kba); + +/** + * Allocate memory from a blocked arena + * + * @param len size of the memory to allocate + * @param aln alignment; must be power of 2 + * + * @return pointer to the memory on success; NULL on insufficient memory or + * when len is longer than block length + */ +void *kba_alloc(void *kba, unsigned len, unsigned aln); + +/** Save the state */ +int kba_save(void *kba); + +/** Restore the state (effectively free all memory allocated after the pairing save) */ +int kba_restore(void *kba); + +/** Get the capacity in byte */ +size_t kba_capacity(const void *ba_); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/Makefile b/test/Makefile index c27d721..3df6294 100644 --- a/test/Makefile +++ b/test/Makefile @@ -70,3 +70,6 @@ kthread_test2:kthread_test2.c ../kthread.c ketopt_test:ketopt_test.c ../ketopt.h $(CC) $(CFLAGS) -o $@ ketopt_test.c + +kbarena_test:kbarena_test.c ../kbarena.h ../kbarena.c + $(CC) $(CFLAGS) -o $@ $< ../kbarena.c diff --git a/test/kbarena_test.c b/test/kbarena_test.c new file mode 100644 index 0000000..8b46ee8 --- /dev/null +++ b/test/kbarena_test.c @@ -0,0 +1,19 @@ +#include +#include "kbarena.h" + +int main(void) +{ + int i; + void *ba; + ba = kba_init(1000); + for (i = 0; i < 100; ++i) + kba_alloc(ba, 23, 1); + kba_save(ba); + for (i = 0; i < 100; ++i) + kba_alloc(ba, 29, 1); + kba_restore(ba); + for (i = 0; i < 100; ++i) + kba_alloc(ba, 29, 1); + kba_destroy(ba); + return 0; +}