]> mj.ucw.cz Git - libucw.git/commitdiff
Merged extended mempool from the dev-playground branch.
authorPavel Charvat <pavel.charvat@netcentrum.cz>
Thu, 29 Mar 2007 07:22:45 +0000 (09:22 +0200)
committerPavel Charvat <pavel.charvat@netcentrum.cz>
Thu, 29 Mar 2007 07:22:45 +0000 (09:22 +0200)
New features:
- reallocating of the last block (similar to a growing buffer)
- saving and restoring states (similar to a stack)

lib/Makefile
lib/autoconf.cfg
lib/bigalloc.c
lib/lib.h
lib/mempool-fmt.c
lib/mempool.c
lib/mempool.h
lib/mempool.t

index f815f3b64bdfa9b5077a7cec7851263ac03ac31b..83f2bcad409368a7eeb5327771d6262316633522 100644 (file)
@@ -93,7 +93,7 @@ TESTS+=$(addprefix $(o)/lib/,regex.test unicode-utf8.test hash-test.test mempool
 $(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
index 43e4a7ec6ce94d1b861afb5b815c9a3c88fa6ef5..bf372573cfd135d582c47726573e2d8cbb6a8f32 100644 (file)
@@ -252,6 +252,9 @@ if (IsSet("CONFIG_LARGE_FILES") && IsSet("CONFIG_LINUX")) {
 # 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
index ad6aeff0cd523e9acc97bcc53408e8367abb989d..91845b389309f522ecd715f47d64d83e6d875140 100644 (file)
@@ -2,6 +2,7 @@
  *     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)
@@ -24,9 +53,7 @@ big_alloc(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);
@@ -39,13 +66,12 @@ void
 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
index ea795c92d8f220a0c4ab405114301e2462c630a5..0a3c17ebe9d2188a8ad4c05e26f63cb5b9a95210 100644 (file)
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -4,6 +4,7 @@
  *     (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.
@@ -265,7 +266,11 @@ byte *str_unesc(byte *dest, byte *src);
 
 /* 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
index b65789f54da6dcafd51b04f34f32b4bd595c28cb..397a7721da18c473505da893f49dd50e72fee7cb 100644 (file)
@@ -2,6 +2,7 @@
  *     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);
@@ -68,6 +64,24 @@ mp_printf(struct mempool *p, char *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)
@@ -75,6 +89,8 @@ 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;
index fffbbd299651f95022e15d8bae4836c45753eb8b..7201c44ca000bbf66c7e3d082f6cdb903a189c21 100644 (file)
  *     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
index d16fa6e09162abe44b14dd771c42e2ad49ecb3a8..c95b8ade2b5affc174e088acd50a03efd1f63c1c 100644 (file)
@@ -2,6 +2,7 @@
  *     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;
@@ -73,9 +273,12 @@ mp_strcat(struct mempool *mp, char *x, char *y)
 }
 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
index 7a1f29785b9e5be66e92305ea5807b8628203783..bebe922bc0f893a9979b8a3085746d9b7f00c618 100644 (file)
@@ -1,7 +1,9 @@
 # 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>>