$(o)/lib/regex.test: $(o)/lib/regex-t
$(o)/lib/unicode-utf8.test: $(o)/lib/unicode-utf8-t
$(o)/lib/hash-test.test: $(o)/lib/hash-test
-$(o)/lib/mempool.test: $(o)/lib/mempool-fmt-t $(o)/lib/mempool-str-t
+$(o)/lib/mempool.test: $(o)/lib/mempool-t $(o)/lib/mempool-fmt-t $(o)/lib/mempool-str-t
$(o)/lib/stkstring.test: $(o)/lib/stkstring-t
$(o)/lib/bitops.test: $(o)/lib/bit-ffs-t $(o)/lib/bit-fls-t
$(o)/lib/slists.test: $(o)/lib/slists-t
# Decide how will lib/partmap.c work
Set("PARTMAP_IS_MMAP") if IsSet("CPU_64BIT_POINTERS");
+# Option for lib/mempool.c
+Set("POOL_IS_MMAP");
+
# If debugging memory allocations:
#LIBS+=-lefence
#CDEBUG+=-DDEBUG_DMALLOC
* UCW Library -- Allocation of Large Aligned Buffers
*
* (c) 2006 Martin Mares <mj@ucw.cz>
+ * (c) 2007 Pavel Charvat <char@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
#include "lib/lib.h"
#include <sys/mman.h>
+#include <string.h>
+
+void *
+page_alloc(unsigned int len)
+{
+ ASSERT(!(len & (CPU_PAGE_SIZE-1)));
+ byte *p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (p == (byte*) MAP_FAILED)
+ die("Cannot mmap %d bytes of memory: %m", len);
+ return p;
+}
+
+void
+page_free(void *start, unsigned int len)
+{
+ ASSERT(!(len & (CPU_PAGE_SIZE-1)));
+ ASSERT(!((uintptr_t) start & (CPU_PAGE_SIZE-1)));
+ munmap(start, len);
+}
+
+void *
+page_realloc(void *start, unsigned int old_len, unsigned int new_len)
+{
+ void *p = page_alloc(new_len);
+ memcpy(p, start, MIN(old_len, new_len));
+ page_free(start, old_len);
+ return p;
+}
static unsigned int
big_round(unsigned int len)
#ifdef CONFIG_DEBUG
len += 2*CPU_PAGE_SIZE;
#endif
- byte *p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
- if (p == (byte*) MAP_FAILED)
- die("Cannot mmap %d bytes of memory: %m", len);
+ byte *p = page_alloc(len);
#ifdef CONFIG_DEBUG
mprotect(p, CPU_PAGE_SIZE, PROT_NONE);
mprotect(p+len-CPU_PAGE_SIZE, CPU_PAGE_SIZE, PROT_NONE);
big_free(void *start, unsigned int len)
{
byte *p = start;
- ASSERT(!((uintptr_t) p & (CPU_PAGE_SIZE-1)));
len = big_round(len);
#ifdef CONFIG_DEBUG
p -= CPU_PAGE_SIZE;
len += 2*CPU_PAGE_SIZE;
#endif
- munmap(p, len);
+ page_free(start, len);
}
#ifdef TEST
* (c) 1997--2007 Martin Mares <mj@ucw.cz>
* (c) 2005 Tomas Valla <tom@ucw.cz>
* (c) 2006 Robert Spalek <robert@ucw.cz>
+ * (c) 2007 Pavel Charvat <pchar@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
/* bigalloc.c */
-void *big_alloc(unsigned int len);
+void *page_alloc(unsigned int len) LIKE_MALLOC; // allocates a multiple of CPU_PAGE_SIZE bytes with mmap
+void page_free(void *start, unsigned int len);
+void *page_realloc(void *start, unsigned int old_len, unsigned int new_len);
+
+void *big_alloc(unsigned int len) LIKE_MALLOC; // allocate a large memory block in the most efficient way available
void big_free(void *start, unsigned int len);
#endif
* UCW Library -- Memory Pools (Formatting)
*
* (c) 2005 Martin Mares <mj@ucw.cz>
+ * (c) 2007 Pavel Charvat <pchar@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
#include <stdio.h>
#include <string.h>
-char *
-mp_vprintf(struct mempool *p, char *fmt, va_list args)
+static char *
+mp_vprintf_at(struct mempool *mp, uns ofs, const char *fmt, va_list args)
{
- char *ret = p->free;
- int free = p->last - p->free;
- if (!free)
- {
- ret = mp_alloc(p, 1);
- free = p->last - p->free;
- }
+ char *ret = mp_grow(mp, ofs + 1) + ofs;
va_list args2;
va_copy(args2, args);
- int cnt = vsnprintf(ret, free, fmt, args2);
+ int cnt = vsnprintf(ret, mp_avail(mp) - ofs, fmt, args2);
va_end(args2);
if (cnt < 0)
{
/* Our C library doesn't support C99 return value of vsnprintf, so we need to iterate */
- uns len = 128;
- char *buf;
do
{
- len *= 2;
- buf = alloca(len);
+ ret = mp_expand(mp) + ofs;
va_copy(args2, args);
- cnt = vsnprintf(buf, len, fmt, args2);
+ cnt = vsnprintf(ret, mp_avail(mp) - ofs, fmt, args2);
va_end(args2);
}
while (cnt < 0);
- ret = mp_alloc(p, cnt+1);
- memcpy(ret, buf, cnt+1);
}
- else if (cnt < free)
- p->free += cnt + 1;
- else
+ else if ((uns)cnt >= mp_avail(mp) - ofs)
{
- ret = mp_alloc(p, cnt+1);
+ ret = mp_grow(mp, cnt + 1) + ofs;
va_copy(args2, args);
- int cnt2 = vsnprintf(ret, cnt+1, fmt, args2);
+ int cnt2 = vsnprintf(ret, cnt + 1, fmt, args2);
va_end(args2);
ASSERT(cnt2 == cnt);
}
- return ret;
+ mp_end(mp, ret + cnt + 1);
+ return ret - ofs;
+}
+
+char *
+mp_vprintf(struct mempool *mp, const char *fmt, va_list args)
+{
+ mp_start(mp, 1);
+ return mp_vprintf_at(mp, 0, fmt, args);
}
char *
-mp_printf(struct mempool *p, char *fmt, ...)
+mp_printf(struct mempool *p, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
return res;
}
+char *
+mp_vprintf_append(struct mempool *mp, char *ptr, const char *fmt, va_list args)
+{
+ uns ofs = mp_open(mp, ptr);
+ ASSERT(ofs);
+ return mp_vprintf_at(mp, ofs - 1, fmt, args);
+}
+
+char *
+mp_printf_append(struct mempool *mp, char *ptr, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ char *res = mp_vprintf_append(mp, ptr, fmt, args);
+ va_end(args);
+ return res;
+}
+
#ifdef TEST
int main(void)
struct mempool *mp = mp_new(64);
char *x = mp_printf(mp, "<Hello, %s!>", "World");
fputs(x, stdout);
+ x = mp_printf_append(mp, x, "<Appended>");
+ fputs(x, stdout);
x = mp_printf(mp, "<Hello, %50s!>\n", "World");
fputs(x, stdout);
return 0;
* UCW Library -- Memory Pools (One-Time Allocation)
*
* (c) 1997--2001 Martin Mares <mj@ucw.cz>
+ * (c) 2007 Pavel Charvat <pchar@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
*/
+#undef LOCAL_DEBUG
+
#include "lib/lib.h"
#include "lib/mempool.h"
#include <string.h>
-struct memchunk {
- struct memchunk *next;
- byte data[0];
+#define MP_CHUNK_TAIL ALIGN_TO(sizeof(struct mempool_chunk), CPU_STRUCT_ALIGN)
+#define MP_SIZE_MAX (~0U - MP_CHUNK_TAIL - CPU_PAGE_SIZE)
+
+struct mempool_chunk {
+ struct mempool_chunk *next;
+ uns size;
};
+static uns
+mp_align_size(uns size)
+{
+#ifdef POOL_IS_MMAP
+ return ALIGN_TO(size + MP_CHUNK_TAIL, CPU_PAGE_SIZE) - MP_CHUNK_TAIL;
+#else
+ return ALIGN_TO(size, CPU_STRUCT_ALIGN);
+#endif
+}
+
+void
+mp_init(struct mempool *pool, uns chunk_size)
+{
+ chunk_size = mp_align_size(MAX(sizeof(struct mempool), chunk_size));
+ *pool = (struct mempool) {
+ .chunk_size = chunk_size,
+ .threshold = chunk_size >> 1,
+ .last_big = &pool->last_big };
+}
+
+static void *
+mp_new_big_chunk(uns size)
+{
+ struct mempool_chunk *chunk;
+ chunk = xmalloc(size + MP_CHUNK_TAIL) + size;
+ chunk->size = size;
+ return chunk;
+}
+
+static void
+mp_free_big_chunk(struct mempool_chunk *chunk)
+{
+ xfree((void *)chunk - chunk->size);
+}
+
+static void *
+mp_new_chunk(uns size)
+{
+#ifdef POOL_IS_MMAP
+ struct mempool_chunk *chunk;
+ chunk = page_alloc(size + MP_CHUNK_TAIL) + size;
+ chunk->size = size;
+ return chunk;
+#else
+ return mp_new_big_chunk(size);
+#endif
+}
+
+static void
+mp_free_chunk(struct mempool_chunk *chunk)
+{
+#ifdef POOL_IS_MMAP
+ page_free((void *)chunk - chunk->size, chunk->size + MP_CHUNK_TAIL);
+#else
+ mp_free_big_chunk(chunk);
+#endif
+}
+
struct mempool *
-mp_new(uns size)
+mp_new(uns chunk_size)
{
- struct mempool *p = xmalloc(sizeof(struct mempool));
+ chunk_size = mp_align_size(MAX(sizeof(struct mempool), chunk_size));
+ struct mempool_chunk *chunk = mp_new_chunk(chunk_size);
+ struct mempool *pool = (void *)chunk - chunk_size;
+ DBG("Creating mempool %p with %u bytes long chunks", pool, chunk_size);
+ chunk->next = NULL;
+ *pool = (struct mempool) {
+ .state = { .free = { chunk_size - sizeof(*pool) }, .last = { chunk } },
+ .chunk_size = chunk_size,
+ .threshold = chunk_size >> 1,
+ .last_big = &pool->last_big };
+ return pool;
+}
- size -= sizeof(struct memchunk);
- p->free = p->last = NULL;
- p->first = p->current = p->first_large = NULL;
- p->plast = &p->first;
- p->chunk_size = size;
- p->threshold = size / 3;
- return p;
+static void
+mp_free_chain(struct mempool_chunk *chunk)
+{
+ while (chunk)
+ {
+ struct mempool_chunk *next = chunk->next;
+ mp_free_chunk(chunk);
+ chunk = next;
+ }
+}
+
+static void
+mp_free_big_chain(struct mempool_chunk *chunk)
+{
+ while (chunk)
+ {
+ struct mempool_chunk *next = chunk->next;
+ mp_free_big_chunk(chunk);
+ chunk = next;
+ }
}
void
-mp_delete(struct mempool *p)
+mp_delete(struct mempool *pool)
{
- struct memchunk *c, *d;
+ DBG("Deleting mempool %p", pool);
+ mp_free_big_chain(pool->state.last[1]);
+ mp_free_chain(pool->unused);
+ mp_free_chain(pool->state.last[0]); // can contain the mempool structure
+}
- for(d=p->first; d; d = c)
+void
+mp_flush(struct mempool *pool)
+{
+ mp_free_big_chain(pool->state.last[1]);
+ struct mempool_chunk *chunk, *next;
+ for (chunk = pool->state.last[0]; chunk && (void *)chunk - chunk->size != pool; chunk = next)
{
- c = d->next;
- xfree(d);
+ next = chunk->next;
+ chunk->next = pool->unused;
+ pool->unused = chunk;
}
- for(d=p->first_large; d; d = c)
+ pool->state.last[0] = chunk;
+ pool->state.free[0] = chunk ? chunk->size - sizeof(*pool) : 0;
+ pool->state.last[1] = NULL;
+ pool->state.free[1] = 0;
+ pool->state.next = NULL;
+ pool->last_big = &pool->last_big;
+}
+
+static void
+mp_stats_chain(struct mempool_chunk *chunk, struct mempool_stats *stats, uns idx)
+{
+ while (chunk)
{
- c = d->next;
- xfree(d);
+ stats->chain_size[idx] += chunk->size + sizeof(*chunk);
+ stats->chain_count[idx]++;
+ chunk = chunk->next;
}
- xfree(p);
+ stats->total_size += stats->chain_size[idx];
}
void
-mp_flush(struct mempool *p)
+mp_stats(struct mempool *pool, struct mempool_stats *stats)
{
- struct memchunk *c;
+ bzero(stats, sizeof(*stats));
+ mp_stats_chain(pool->state.last[0], stats, 0);
+ mp_stats_chain(pool->state.last[1], stats, 1);
+ mp_stats_chain(pool->unused, stats, 2);
+}
- p->free = p->last = NULL;
- p->current = p->first;
- while (c = p->first_large)
+void *
+mp_alloc_internal(struct mempool *pool, uns size)
+{
+ struct mempool_chunk *chunk;
+ if (size <= pool->threshold)
{
- p->first_large = c->next;
- xfree(c);
+ pool->idx = 0;
+ if (pool->unused)
+ {
+ chunk = pool->unused;
+ pool->unused = chunk->next;
+ }
+ else
+ chunk = mp_new_chunk(pool->chunk_size);
+ chunk->next = pool->state.last[0];
+ pool->state.last[0] = chunk;
+ pool->state.free[0] = pool->chunk_size - size;
+ return (void *)chunk - pool->chunk_size;
}
+ else if (likely(size <= MP_SIZE_MAX))
+ {
+ pool->idx = 1;
+ uns aligned = ALIGN_TO(size, CPU_STRUCT_ALIGN);
+ chunk = mp_new_big_chunk(aligned);
+ chunk->next = pool->state.last[1];
+ pool->state.last[1] = chunk;
+ pool->state.free[1] = aligned - size;
+ return pool->last_big = (void *)chunk - aligned;
+ }
+ else
+ die("Cannot allocate %u bytes from a mempool", size);
+}
+
+void *
+mp_alloc(struct mempool *pool, uns size)
+{
+ return mp_alloc_fast(pool, size);
+}
+
+void *
+mp_alloc_noalign(struct mempool *pool, uns size)
+{
+ return mp_alloc_fast_noalign(pool, size);
+}
+
+void *
+mp_alloc_zero(struct mempool *pool, uns size)
+{
+ void *ptr = mp_alloc_fast(pool, size);
+ bzero(ptr, size);
+ return ptr;
+}
+
+void *
+mp_start_internal(struct mempool *pool, uns size)
+{
+ void *ptr = mp_alloc_internal(pool, size);
+ pool->state.free[pool->idx] += size;
+ return ptr;
}
void *
-mp_alloc(struct mempool *p, uns s)
+mp_start(struct mempool *pool, uns size)
{
- if (s <= p->threshold)
+ return mp_start_fast(pool, size);
+}
+
+void *
+mp_start_noalign(struct mempool *pool, uns size)
+{
+ return mp_start_fast_noalign(pool, size);
+}
+
+void *
+mp_grow_internal(struct mempool *pool, uns size)
+{
+ if (unlikely(size > MP_SIZE_MAX))
+ die("Cannot allocate %u bytes of memory", size);
+ uns avail = mp_avail(pool);
+ void *ptr = mp_ptr(pool);
+ if (pool->idx)
{
- byte *x = (byte *)(((uintptr_t) p->free + POOL_ALIGN - 1) & ~(uintptr_t)(POOL_ALIGN - 1));
- if (x + s > p->last)
- {
- struct memchunk *c;
-
- if (p->current)
- {
- /* Still have free chunks from previous incarnation */
- c = p->current;
- p->current = c->next;
- }
- else
- {
- c = xmalloc(sizeof(struct memchunk) + p->chunk_size);
- *p->plast = c;
- p->plast = &c->next;
- c->next = NULL;
- }
- x = c->data;
- p->last = x + p->chunk_size;
- }
- p->free = x + s;
- return x;
+ uns amortized = likely(avail <= MP_SIZE_MAX / 2) ? avail * 2 : MP_SIZE_MAX;
+ amortized = MAX(amortized, size);
+ amortized = ALIGN_TO(amortized, CPU_STRUCT_ALIGN);
+ struct mempool_chunk *chunk = pool->state.last[1], *next = chunk->next;
+ ptr = xrealloc(ptr, amortized + MP_CHUNK_TAIL);
+ chunk = ptr + amortized;
+ chunk->next = next;
+ chunk->size = amortized;
+ pool->state.last[1] = chunk;
+ pool->state.free[1] = amortized;
+ pool->last_big = ptr;
+ return ptr;
}
else
{
- struct memchunk *c = xmalloc(sizeof(struct memchunk) + s);
- c->next = p->first_large;
- p->first_large = c;
- return c->data;
+ void *p = mp_start_internal(pool, size);
+ memcpy(p, ptr, avail);
+ return p;
}
}
+uns
+mp_open(struct mempool *pool, void *ptr)
+{
+ return mp_open_fast(pool, ptr);
+}
+
+void *
+mp_realloc(struct mempool *pool, void *ptr, uns size)
+{
+ return mp_realloc_fast(pool, ptr, size);
+}
+
void *
-mp_alloc_zero(struct mempool *p, uns s)
+mp_realloc_zero(struct mempool *pool, void *ptr, uns size)
+{
+ uns old_size = mp_open_fast(pool, ptr);
+ ptr = mp_grow(pool, size);
+ if (size > old_size)
+ bzero(ptr + old_size, size - old_size);
+ mp_end(pool, ptr + size);
+ return ptr;
+}
+
+void
+mp_restore(struct mempool *pool, struct mempool_state *state)
+{
+ struct mempool_chunk *chunk, *next;
+ struct mempool_state s = *state;
+ for (chunk = pool->state.last[0]; chunk != s.last[0]; chunk = next)
+ {
+ next = chunk->next;
+ chunk->next = pool->unused;
+ pool->unused = chunk;
+ }
+ for (chunk = pool->state.last[1]; chunk != s.last[1]; chunk = next)
+ {
+ next = chunk->next;
+ mp_free_big_chunk(chunk);
+ }
+ pool->state = s;
+ pool->last_big = &pool->last_big;
+}
+
+struct mempool_state *
+mp_push(struct mempool *pool)
+{
+ struct mempool_state state = pool->state;
+ struct mempool_state *p = mp_alloc_fast(pool, sizeof(*p));
+ *p = state;
+ pool->state.next = p;
+ return p;
+}
+
+void
+mp_pop(struct mempool *pool)
+{
+ ASSERT(pool->state.next);
+ struct mempool_state state = pool->state;
+ mp_restore(pool, &state);
+}
+
+#ifdef TEST
+
+#include "lib/getopt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+static void
+fill(byte *ptr, uns len, uns magic)
{
- void *x = mp_alloc(p, s);
- bzero(x, s);
- return x;
+ while (len--)
+ *ptr++ = (magic++ & 255);
+}
+
+static void
+check(byte *ptr, uns len, uns magic, uns align)
+{
+ ASSERT(!((uintptr_t)ptr & (align - 1)));
+ while (len--)
+ if (*ptr++ != (magic++ & 255))
+ ASSERT(0);
+}
+
+int main(int argc, char **argv)
+{
+ srand(time(NULL));
+ log_init(argv[0]);
+ cf_def_file = NULL;
+ if (cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL) >= 0 || argc != optind)
+ die("Invalid usage");
+
+ uns max = 1000, n = 0, m = 0, can_realloc = 0;
+ void *ptr[max];
+ struct mempool_state *state[max];
+ uns len[max], num[max], align[max];
+ struct mempool *mp = mp_new(128), mp_static;
+
+ for (uns i = 0; i < 5000; i++)
+ {
+ for (uns j = 0; j < n; j++)
+ check(ptr[j], len[j], j, align[j]);
+#if 0
+ DBG("free_small=%u free_big=%u idx=%u chunk_size=%u last_big=%p", mp->state.free[0], mp->state.free[1], mp->idx, mp->chunk_size, mp->last_big);
+ for (struct mempool_chunk *ch = mp->state.last[0]; ch; ch = ch->next)
+ DBG("small %p %p %p %d", (byte *)ch - ch->size, ch, ch + 1, ch->size);
+ for (struct mempool_chunk *ch = mp->state.last[1]; ch; ch = ch->next)
+ DBG("big %p %p %p %d", (byte *)ch - ch->size, ch, ch + 1, ch->size);
+#endif
+ int r = random_max(100);
+ if ((r -= 1) < 0)
+ {
+ DBG("flush");
+ mp_flush(mp);
+ n = m = 0;
+ }
+ else if ((r -= 1) < 0)
+ {
+ DBG("delete & new");
+ mp_delete(mp);
+ if (random_max(2))
+ mp = mp_new(random_max(0x1000) + 1);
+ else
+ mp = &mp_static, mp_init(mp, random_max(512) + 1);
+ n = m = 0;
+ }
+ else if (n < max && (r -= 30) < 0)
+ {
+ len[n] = random_max(0x2000);
+ DBG("alloc(%u)", len[n]);
+ align[n] = random_max(2) ? CPU_STRUCT_ALIGN : 1;
+ ptr[n] = (align[n] == 1) ? mp_alloc_fast_noalign(mp, len[n]) : mp_alloc_fast(mp, len[n]);
+ DBG(" -> (%p)", ptr[n]);
+ fill(ptr[n], len[n], n);
+ n++;
+ can_realloc = 1;
+ }
+ else if (n < max && (r -= 20) < 0)
+ {
+ len[n] = random_max(0x2000);
+ DBG("start(%u)", len[n]);
+ align[n] = random_max(2) ? CPU_STRUCT_ALIGN : 1;
+ ptr[n] = (align[n] == 1) ? mp_start_fast_noalign(mp, len[n]) : mp_start_fast(mp, len[n]);
+ DBG(" -> (%p)", ptr[n]);
+ fill(ptr[n], len[n], n);
+ n++;
+ can_realloc = 1;
+ goto grow;
+ }
+ else if (can_realloc && n && (r -= 10) < 0)
+ {
+ if (mp_open(mp, ptr[n - 1]) != len[n - 1])
+ ASSERT(0);
+grow:
+ {
+ uns k = n - 1;
+ for (uns i = random_max(4); i--; )
+ {
+ uns l = len[k];
+ len[k] = random_max(0x2000);
+ DBG("grow(%u)", len[k]);
+ ptr[k] = mp_grow(mp, len[k]);
+ DBG(" -> (%p)", ptr[k]);
+ check(ptr[k], MIN(l, len[k]), k, align[k]);
+ fill(ptr[k], len[k], k);
+ }
+ mp_end(mp, ptr[k] + len[k]);
+ }
+ }
+ else if (can_realloc && n && (r -= 20) < 0)
+ {
+ uns i = n - 1, l = len[i];
+ DBG("realloc(%p, %u)", ptr[i], len[i]);
+ ptr[i] = mp_realloc(mp, ptr[i], len[i] = random_max(0x2000));
+ DBG(" -> (%p, %u)", ptr[i], len[i]);
+ check(ptr[i], MIN(len[i], l), i, align[i]);
+ fill(ptr[i], len[i], i);
+ }
+ else if (m < max && (r -= 5) < 0)
+ {
+ DBG("push(%u)", m);
+ num[m] = n;
+ state[m++] = mp_push(mp);
+ can_realloc = 0;
+ }
+ else if (m && (r -= 2) < 0)
+ {
+ m--;
+ DBG("pop(%u)", m);
+ mp_pop(mp);
+ n = num[m];
+ can_realloc = 0;
+ }
+ else if (m && (r -= 1) < 0)
+ {
+ uns i = random_max(m);
+ DBG("restore(%u)", i);
+ mp_restore(mp, state[i]);
+ n = num[m = i];
+ can_realloc = 0;
+ }
+ else if (can_realloc && n && (r -= 5) < 0)
+ ASSERT(mp_size(mp, ptr[n - 1]) == len[n - 1]);
+ }
+
+ mp_delete(mp);
+ return 0;
}
+
+#endif
* UCW Library -- Memory Pools
*
* (c) 1997--2005 Martin Mares <mj@ucw.cz>
+ * (c) 2007 Pavel Charvat <pchar@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
#ifndef _UCW_POOLS_H
#define _UCW_POOLS_H
-#ifndef POOL_ALIGN
-#define POOL_ALIGN CPU_STRUCT_ALIGN
-#endif
+/* Memory pool state (see mp_push(), ...) */
+struct mempool_state {
+ uns free[2];
+ void *last[2];
+ struct mempool_state *next;
+};
+/* Memory pool */
struct mempool {
- byte *free, *last;
- struct memchunk *first, *current, **plast;
- struct memchunk *first_large;
- uns chunk_size, threshold;
+ struct mempool_state state;
+ void *unused, *last_big;
+ uns chunk_size, threshold, idx;
};
-struct mempool *mp_new(uns);
-void mp_delete(struct mempool *);
-void mp_flush(struct mempool *);
-void *mp_alloc(struct mempool *, uns) LIKE_MALLOC;
-void *mp_alloc_zero(struct mempool *, uns) LIKE_MALLOC;
+/* Statistics (see mp_stats()) */
+struct mempool_stats {
+ uns total_size; /* Real allocated size in bytes */
+ uns chain_count[3]; /* Number of allocated chunks in small/big/unused chains */
+ uns chain_size[3]; /* Size of allocated chunks in small/big/unused chains */
+};
+
+/* Initialize a given mempool structure. Chunk size must be in the interval [1, UINT_MAX / 2] */
+void mp_init(struct mempool *pool, uns chunk_size);
+
+/* Allocate and initialize a new memory pool. See mp_init for chunk size limitations. */
+struct mempool *mp_new(uns chunk_size);
+
+/* Cleanup mempool initialized by mp_init or mp_new */
+void mp_delete(struct mempool *pool);
+
+/* Free all data on a memory pool (saves some empty chunks for later allocations) */
+void mp_flush(struct mempool *pool);
+
+/* Compute some statistics for debug purposes. See the definition of the mempool_stats structure. */
+void mp_stats(struct mempool *pool, struct mempool_stats *stats);
+
+
+/*** Allocation routines ***/
+
+/* For internal use only, do not call directly */
+void *mp_alloc_internal(struct mempool *pool, uns size) LIKE_MALLOC;
+
+/* The function allocates new <size> bytes on a given memory pool.
+ * If the <size> is zero, the resulting pointer is undefined,
+ * but it may be safely reallocated or used as the parameter
+ * to other functions below.
+ *
+ * The resulting pointer is always aligned to a multiple of
+ * CPU_STRUCT_ALIGN bytes and this condition remains true also
+ * after future reallocations.
+ */
+void *mp_alloc(struct mempool *pool, uns size);
+
+/* The same as mp_alloc, but the result may not be aligned */
+void *mp_alloc_noalign(struct mempool *pool, uns size);
+
+/* The same as mp_alloc, but fills the newly allocated data with zeroes */
+void *mp_alloc_zero(struct mempool *pool, uns size);
+
+/* Inlined version of mp_alloc() */
+static inline void *
+mp_alloc_fast(struct mempool *pool, uns size)
+{
+ uns avail = pool->state.free[0] & ~(CPU_STRUCT_ALIGN - 1);
+ if (size <= avail)
+ {
+ pool->state.free[0] = avail - size;
+ return pool->state.last[0] - avail;
+ }
+ else
+ return mp_alloc_internal(pool, size);
+}
+
+/* Inlined version of mp_alloc_noalign() */
+static inline void *
+mp_alloc_fast_noalign(struct mempool *pool, uns size)
+{
+ if (size <= pool->state.free[0])
+ {
+ void *ptr = pool->state.last[0] - pool->state.free[0];
+ pool->state.free[0] -= size;
+ return ptr;
+ }
+ else
+ return mp_alloc_internal(pool, size);
+}
+
+
+/*** Usage as a growing buffer ***/
+
+/* For internal use only, do not call directly */
+void *mp_start_internal(struct mempool *pool, uns size) LIKE_MALLOC;
+void *mp_grow_internal(struct mempool *pool, uns size);
-static inline void * LIKE_MALLOC
-mp_alloc_fast(struct mempool *p, uns l)
+static inline uns
+mp_idx(struct mempool *pool, void *ptr)
{
- byte *f = (void *) (((uintptr_t) p->free + POOL_ALIGN - 1) & ~(uintptr_t)(POOL_ALIGN - 1));
- byte *ee = f + l;
- if (ee > p->last)
- return mp_alloc(p, l);
- p->free = ee;
- return f;
+ return ptr == pool->last_big;
}
-static inline void * LIKE_MALLOC mp_alloc_fast_noalign(struct mempool *p, uns l)
+/* Open a new growing buffer (at least <size> bytes long).
+ * If the <size> is zero, the resulting pointer is undefined,
+ * but it may be safely reallocated or used as the parameter
+ * to other functions below.
+ *
+ * The resulting pointer is always aligned to a multiple of
+ * CPU_STRUCT_ALIGN bytes and this condition remains true also
+ * after future reallocations. There is an unaligned version as well.
+ *
+ * Keep in mind that you can't make any other <pool> allocations
+ * before you "close" the growing buffer with mp_end().
+ */
+void *mp_start(struct mempool *pool, uns size);
+void *mp_start_noalign(struct mempool *pool, uns size);
+
+/* Inlined version of mp_start() */
+static inline void *
+mp_start_fast(struct mempool *pool, uns size)
+{
+ uns avail = pool->state.free[0] & ~(CPU_STRUCT_ALIGN - 1);
+ if (size <= avail)
+ {
+ pool->idx = 0;
+ pool->state.free[0] = avail;
+ return pool->state.last[0] - avail;
+ }
+ else
+ return mp_start_internal(pool, size);
+}
+
+/* Inlined version of mp_start_noalign() */
+static inline void *
+mp_start_fast_noalign(struct mempool *pool, uns size)
{
- byte *f = p->free;
- byte *ee = f + l;
- if (ee > p->last)
- return mp_alloc(p, l);
- p->free = ee;
- return f;
+ if (size <= pool->state.free[0])
+ {
+ pool->idx = 0;
+ return pool->state.last[0] - pool->state.free[0];
+ }
+ else
+ return mp_start_internal(pool, size);
}
+/* Return start pointer of the growing buffer allocated by mp_start() or a similar function */
static inline void *
-mp_start_string(struct mempool *p, uns l)
+mp_ptr(struct mempool *pool)
{
- ASSERT(l <= p->chunk_size);
- return mp_alloc(p, l);
+ return pool->state.last[pool->idx] - pool->state.free[pool->idx];
}
+/* Return the number of bytes available for extending the growing buffer */
+static inline uns
+mp_avail(struct mempool *pool)
+{
+ return pool->state.free[pool->idx];
+}
+
+/* Grow the buffer allocated by mp_start() to be at least <size> bytes long
+ * (<size> may be less than mp_avail(), even zero). Reallocated buffer may
+ * change its starting position. The content will be unchanged to the minimum
+ * of the old and new sizes; newly allocated memory will be uninitialized.
+ * Multiple calls to mp_grow have amortized linear cost wrt. the maximum value of <size>. */
+static inline void *
+mp_grow(struct mempool *pool, uns size)
+{
+ return (size <= mp_avail(pool)) ? mp_ptr(pool) : mp_grow_internal(pool, size);
+}
+
+/* Grow the buffer by at least one byte -- equivalent to mp_grow(pool, mp_avail(pool) + 1) */
+static inline void *
+mp_expand(struct mempool *pool)
+{
+ return mp_grow_internal(pool, mp_avail(pool) + 1);
+}
+
+/* Close the growing buffer. The <end> must point just behind the data, you want to keep
+ * allocated (so it can be in the interval [mp_ptr(pool), mp_ptr(pool) + mp_avail(pool)]). */
static inline void
-mp_end_string(struct mempool *p, void *stop)
+mp_end(struct mempool *pool, void *end)
+{
+ pool->state.free[pool->idx] = pool->state.last[pool->idx] - end;
+}
+
+/* Return size in bytes of the last allocated memory block (with mp_alloc*() or mp_end()). */
+static inline uns
+mp_size(struct mempool *pool, void *ptr)
+{
+ uns idx = mp_idx(pool, ptr);
+ return pool->state.last[idx] - ptr - pool->state.free[idx];
+}
+
+/* Open the last memory block (allocated with mp_alloc*() or mp_end())
+ * for growing and return its size in bytes. The contents and the start pointer
+ * remain unchanged. Do not forget to call mp_end() to close it. */
+uns mp_open(struct mempool *pool, void *ptr);
+
+/* Inlined version of mp_open() */
+static inline uns
+mp_open_fast(struct mempool *pool, void *ptr)
{
- p->free = stop;
+ pool->idx = mp_idx(pool, ptr);
+ uns size = pool->state.last[pool->idx] - ptr - pool->state.free[pool->idx];
+ pool->state.free[pool->idx] += size;
+ return size;
}
-/* mempool-str.c */
+/* Reallocate the last memory block (allocated with mp_alloc*() or mp_end())
+ * to the new <size>. Behavior is similar to mp_grow(), but the resulting
+ * block is closed. */
+void *mp_realloc(struct mempool *pool, void *ptr, uns size);
+
+/* The same as mp_realloc(), but fills the additional bytes (if any) with zeroes */
+void *mp_realloc_zero(struct mempool *pool, void *ptr, uns size);
+
+/* Inlined version of mp_realloc() */
+static inline void *
+mp_realloc_fast(struct mempool *pool, void *ptr, uns size)
+{
+ mp_open_fast(pool, ptr);
+ ptr = mp_grow(pool, size);
+ mp_end(pool, ptr + size);
+ return ptr;
+}
+
+
+/*** Usage as a stack ***/
+
+/* Save the current state of a memory pool.
+ * Do not call this function with an opened growing buffer. */
+static inline void
+mp_save(struct mempool *pool, struct mempool_state *state)
+{
+ *state = pool->state;
+ pool->state.next = state;
+}
+
+/* Save the current state to a newly allocated mempool_state structure.
+ * Do not call this function with an opened growing buffer. */
+struct mempool_state *mp_push(struct mempool *pool);
+
+/* Restore the state saved by mp_save() or mp_push() and free all
+ * data allocated after that point (including the state structure itself).
+ * You can't reallocate the last memory block from the saved state. */
+void mp_restore(struct mempool *pool, struct mempool_state *state);
+
+/* Restore the state saved by the last call to mp_push().
+ * mp_pop() and mp_push() works as a stack so you can push more states safely. */
+void mp_pop(struct mempool *pool);
+
+
+/*** mempool-str.c ***/
char *mp_strdup(struct mempool *, char *) LIKE_MALLOC;
void *mp_memdup(struct mempool *, void *, uns) LIKE_MALLOC;
}
char *mp_strjoin(struct mempool *p, char **a, uns n, uns sep) LIKE_MALLOC;
-/* mempool-fmt.c */
-char *mp_printf(struct mempool *p, char *fmt, ...) FORMAT_CHECK(printf,2,3) LIKE_MALLOC;
-char *mp_vprintf(struct mempool *p, char *fmt, va_list args) LIKE_MALLOC;
+/*** mempool-fmt.c ***/
+
+char *mp_printf(struct mempool *mp, const char *fmt, ...) FORMAT_CHECK(printf,2,3) LIKE_MALLOC;
+char *mp_vprintf(struct mempool *mp, const char *fmt, va_list args) LIKE_MALLOC;
+char *mp_printf_append(struct mempool *mp, char *ptr, const char *fmt, ...) FORMAT_CHECK(printf,3,4);
+char *mp_vprintf_append(struct mempool *mp, char *ptr, const char *fmt, va_list args);
#endif
# Tests for mempool modules
+Run: obj/lib/mempool-t
+
Run: obj/lib/mempool-fmt-t
-Out: <Hello, World!><Hello, World!>
+Out: <Hello, World!><Hello, World!><Appended><Hello, World!>
Run: obj/lib/mempool-str-t
Out: <<12345>>