We're abusing the current ASCII driver by telling it to allocate a
[pspp-builds.git] / src / pool.c
index cce54af9cc1f8dc8b7ddd799de60e5f1440c7e65..d3b536b2c240d7b681f3456c5880ca677f6c9b2d 100644 (file)
 #if HAVE_CONFIG_H
 #include <config.h>
 #endif
-#include <assert.h>
+#include "pool.h"
+#include "error.h"
 #include <stdlib.h>
 #include "alloc.h"
-#include "pool.h"
+#include "str.h"
 
 /* Fast, low-overhead memory block suballocator. */
 struct pool
@@ -101,22 +102,18 @@ union align
 #define ALIGN_SIZE sizeof (union align)
 #endif
 
-/* DISCRETE_BLOCKS may be declared as nonzero to prevent suballocation
-   of blocks.  This is useful under memory debuggers like Checker
-   because it allows the source location of bugs to be more accurately
-   pinpointed.
+/* DISCRETE_BLOCKS may be declared as nonzero to prevent
+   suballocation of blocks.  This is useful under memory
+   debuggers like Checker or valgrind because it allows the
+   source location of bugs to be more accurately pinpointed.
 
    On the other hand, if we're testing the library, then we want to
    test the library's real functionality, not its crippled, slow,
    simplified functionality. */
-#if __CHECKER__  && !SELF_TEST
-#define DISCRETE_BLOCKS 1
-#endif
+/*#define DISCRETE_BLOCKS 1*/
 
 /* Enable debug code if appropriate. */
-#undef DEBUGGING
 #if SELF_TEST
-#define DEBUGGING 1
 #endif
 
 /* Size of each block allocated in the pool, in bytes.
@@ -143,6 +140,7 @@ static long serial = 0;
 /* Prototypes. */
 static void add_gizmo (struct pool *, struct pool_gizmo *);
 static void free_gizmo (struct pool_gizmo *);
+static void free_all_gizmos (struct pool *pool);
 static void delete_gizmo (struct pool *, struct pool_gizmo *);
 
 #if !PSPP
@@ -183,20 +181,14 @@ pool_destroy (struct pool *pool)
   if (pool == NULL)
     return;
 
+  /* Remove this pool from its parent's list of gizmos. */
   if (pool->parent) 
-    delete_gizmo (pool,
+    delete_gizmo (pool->parent,
                  (void *) (((char *) pool) + POOL_SIZE + POOL_BLOCK_SIZE));
 
-  {
-    struct pool_gizmo *cur, *next;
+  free_all_gizmos (pool);
 
-    for (cur = pool->gizmos; cur; cur = next)
-      {
-       next = cur->next;
-       free_gizmo (cur);
-      }
-  }
-  
+  /* Free all the memory. */
   {
     struct pool_block *cur, *next;
 
@@ -208,6 +200,30 @@ pool_destroy (struct pool *pool)
       }
   }
 }
+
+/* Release all the memory and gizmos in POOL.
+   Blocks are not given back with free() but kept for later
+   allocations.  To give back memory, use a subpool instead. */ 
+void
+pool_clear (struct pool *pool) 
+{
+  free_all_gizmos (pool);
+
+  /* Zero out block sizes. */
+  {
+    struct pool_block *cur;
+    
+    cur = pool->blocks;
+    do
+      {
+        cur->ofs = POOL_BLOCK_SIZE;
+        if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool)
+          cur->ofs += POOL_SIZE;
+        cur = cur->next;
+      }
+    while (cur != pool->blocks);
+  }
+}
 \f
 /* Suballocation routines. */
 
@@ -218,9 +234,10 @@ pool_alloc (struct pool *pool, size_t amt)
 {
   assert (pool != NULL);
   
-#if !DISCRETE_BLOCKS /* Help identify source of bugs for Checker users. */
+#ifndef DISCRETE_BLOCKS
   if (amt <= MAX_SUBALLOC)
     {
+      /* If there is space in this block, take it. */
       struct pool_block *b = pool->blocks;
       b->ofs = ROUND_UP (b->ofs, ALIGN_SIZE);
       if (b->ofs + amt <= BLOCK_SIZE)
@@ -230,49 +247,81 @@ pool_alloc (struct pool *pool, size_t amt)
          return p;
        }
 
-      b = xmalloc (BLOCK_SIZE);
-      b->next = pool->blocks;
-      b->prev = pool->blocks->prev;
-      b->ofs = POOL_BLOCK_SIZE + amt;
-
-      pool->blocks->prev->next = b;
-      pool->blocks = pool->blocks->prev = b;
-
-      return ((char *) b) + POOL_BLOCK_SIZE;
+      /* No space in this block, so we must make other
+         arrangements. */
+      if (b->next->ofs == 0) 
+        {
+          /* The next block is empty.  Use it. */
+          b = b->next;
+          b->ofs = POOL_BLOCK_SIZE;
+          if ((char *) b + POOL_BLOCK_SIZE == (char *) pool)
+            b->ofs += POOL_SIZE;
+        }
+      else 
+        {
+          /* Create a new block at the start of the list. */
+          b = xmalloc (BLOCK_SIZE);
+          b->next = pool->blocks;
+          b->prev = pool->blocks->prev;
+          b->ofs = POOL_BLOCK_SIZE;
+          pool->blocks->prev->next = b;
+          pool->blocks->prev = b;
+        }
+      pool->blocks = b;
+
+      /* Allocate space from B. */
+      b->ofs += amt;
+      return ((char *) b) + b->ofs - amt;
     }
   else
-#endif /* !DISCRETE_BLOCKS */
+#endif
     return pool_malloc (pool, amt);
 }
 
-/* Duplicates STRING within POOL and returns a pointer to the
-   duplicate. */
+/* Duplicates STRING, which has LENGTH characters, within POOL,
+   and returns a pointer to the duplicate.  LENGTH should not
+   include the null terminator, which is always added to the
+   duplicate.  For use only with strings, because the returned
+   pointere may not be aligned properly for other types. */
 char *
-pool_strdup (struct pool *pool, const char *string)
+pool_strndup (struct pool *pool, const char *string, size_t length)
 {
-  size_t amt;
-  void *p;
+  size_t size;
+  char *copy;
 
   assert (pool && string);
-  amt = strlen (string) + 1;
+  size = length + 1;
 
   /* Note that strings need not be aligned on any boundary. */
+#ifndef DISCRETE_BLOCKS
   {
-#if !DISCRETE_BLOCKS
     struct pool_block *const b = pool->blocks;
 
-    if (b->ofs + amt <= BLOCK_SIZE)
+    if (b->ofs + size <= BLOCK_SIZE)
       {
-       p = ((char *) b) + b->ofs;
-       b->ofs += amt;
+        copy = ((char *) b) + b->ofs;
+        b->ofs += size;
       }
     else
-#endif
-      p = pool_alloc (pool, amt);
+      copy = pool_alloc (pool, size);
   }
+#else
+  copy = pool_alloc (pool, size);
+#endif
 
-  memcpy (p, string, amt);
-  return p;
+  memcpy (copy, string, length);
+  copy[length] = '\0';
+  return copy;
+}
+
+/* Duplicates null-terminated STRING, within POOL, and returns a
+   pointer to the duplicate.  For use only with strings, because
+   the returned pointere may not be aligned properly for other
+   types. */
+char *
+pool_strdup (struct pool *pool, const char *string) 
+{
+  return pool_strndup (pool, string, strlen (string));
 }
 \f
 /* Standard allocation routines. */
@@ -484,7 +533,10 @@ pool_mark (struct pool *pool, struct pool_mark *mark)
   mark->serial = serial;
 }
 
-/* Restores to POOL the state recorded in MARK. */
+/* Restores to POOL the state recorded in MARK.
+   Emptied blocks are not given back with free() but kept for
+   later allocations.  To get that behavior, use a subpool
+   instead. */ 
 void
 pool_release (struct pool *pool, const struct pool_mark *mark)
 {
@@ -509,21 +561,16 @@ pool_release (struct pool *pool, const struct pool_mark *mark)
   }
   
   {
-    struct pool_block *cur, *next, *last;
+    struct pool_block *cur;
 
-    last = pool->blocks->prev;
-    for (cur = pool->blocks; cur != mark->block; cur = next)
+    for (cur = pool->blocks; cur != mark->block; cur = cur->next) 
       {
-       next = cur->next;
-       assert (next != cur);
-
-       free (cur);
+        cur->ofs = POOL_BLOCK_SIZE;
+        if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool)
+          cur->ofs += POOL_SIZE; 
       }
-
-    cur->prev = last;
-    last->next = pool->blocks = cur;
-  
-    cur->ofs = mark->ofs;
+    pool->blocks = mark->block;
+    pool->blocks->ofs = mark->ofs;
   }
 }
 \f
@@ -584,6 +631,19 @@ free_gizmo (struct pool_gizmo *gizmo)
       assert (0);
     }
 }
+
+/* Free all the gizmos in POOL. */
+static void
+free_all_gizmos (struct pool *pool) 
+{
+  struct pool_gizmo *cur, *next;
+
+  for (cur = pool->gizmos; cur; cur = next)
+    {
+      next = cur->next;
+      free_gizmo (cur);
+    }
+}
 \f
 /* Memory allocation. */