pool: Support NULL pool argument to pool_alloc_unaligned().
[pspp-builds.git] / src / libpspp / pool.c
index 31a57e9fbeffcff245924efb8c6abc253d6a2276..7f3175d477bde1d758f5f358378fa448b30a60ab 100644 (file)
@@ -1,30 +1,32 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2000, 2010, 2011 Free Software Foundation, Inc.
 
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include "pool.h"
+
+#include "libpspp/pool.h"
+
+#include <stdint.h>
 #include <stdlib.h>
-#include "alloc.h"
-#include <libpspp/assertion.h>
-#include "message.h"
-#include "size_max.h"
-#include "str.h"
+
+#include "libpspp/assertion.h"
+#include "libpspp/message.h"
+#include "libpspp/temp-file.h"
+#include "libpspp/str.h"
+
+#include "gl/xalloc.h"
 
 /* Fast, low-overhead memory block suballocator. */
 struct pool
@@ -35,7 +37,7 @@ struct pool
   };
 
 /* Pool block. */
-struct pool_block 
+struct pool_block
   {
     struct pool_block *prev;
     struct pool_block *next;
@@ -47,12 +49,13 @@ enum
   {
     POOL_GIZMO_MALLOC,
     POOL_GIZMO_FILE,
+    POOL_GIZMO_TEMP_FILE,
     POOL_GIZMO_SUBPOOL,
     POOL_GIZMO_REGISTERED,
   };
 
 /* Pool routines can maintain objects (`gizmos') as well as doing
-   suballocation.  
+   suballocation.
    This structure is used to keep track of them. */
 struct pool_gizmo
   {
@@ -66,7 +69,7 @@ struct pool_gizmo
     /* Type-dependent info. */
     union
       {
-       FILE *file;             /* POOL_GIZMO_FILE. */
+       FILE *file;             /* POOL_GIZMO_FILE, POOL_GIZMO_TEMP_FILE. */
        struct pool *subpool;   /* POOL_GIZMO_SUBPOOL. */
 
        /* POOL_GIZMO_REGISTERED. */
@@ -101,8 +104,8 @@ union align
 
 /* 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.
+   debuggers like 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,
@@ -148,7 +151,7 @@ pool_create (void)
   block = xmalloc (BLOCK_SIZE);
   block->prev = block->next = block;
   block->ofs = POOL_BLOCK_SIZE + POOL_SIZE;
-  
+
   pool = (struct pool *) (((char *) block) + POOL_BLOCK_SIZE);
   pool->parent = NULL;
   pool->blocks = block;
@@ -164,7 +167,7 @@ pool_create (void)
 
    Meant for use indirectly via pool_create_container(). */
 void *
-pool_create_at_offset (size_t struct_size, size_t pool_member_offset) 
+pool_create_at_offset (size_t struct_size, size_t pool_member_offset)
 {
   struct pool *pool;
   char *struct_;
@@ -186,9 +189,9 @@ pool_destroy (struct pool *pool)
     return;
 
   /* Remove this pool from its parent's list of gizmos. */
-  if (pool->parent) 
+  if (pool->parent)
     delete_gizmo (pool->parent, (void *) (((char *) pool) + POOL_SIZE));
-  
+
   free_all_gizmos (pool);
 
   /* Free all the memory. */
@@ -206,25 +209,25 @@ 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. */ 
+   allocations.  To give back memory, use a subpool instead. */
 void
-pool_clear (struct pool *pool) 
+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) 
+        if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool)
           {
             cur->ofs += POOL_SIZE;
             if (pool->parent != NULL)
-              cur->ofs += POOL_GIZMO_SIZE; 
+              cur->ofs += POOL_GIZMO_SIZE;
           }
         cur = cur->next;
       }
@@ -244,7 +247,7 @@ pool_alloc (struct pool *pool, size_t amt)
 
   if (amt == 0)
     return NULL;
-  
+
 #ifndef DISCRETE_BLOCKS
   if (amt <= MAX_SUBALLOC)
     {
@@ -260,7 +263,7 @@ pool_alloc (struct pool *pool, size_t amt)
 
       /* No space in this block, so we must make other
          arrangements. */
-      if (b->next->ofs == 0) 
+      if (b->next->ofs == 0)
         {
           /* The next block is empty.  Use it. */
           b = b->next;
@@ -268,7 +271,7 @@ pool_alloc (struct pool *pool, size_t amt)
           if ((char *) b + POOL_BLOCK_SIZE == (char *) pool)
             b->ofs += POOL_SIZE;
         }
-      else 
+      else
         {
           /* Create a new block at the start of the list. */
           b = xmalloc (BLOCK_SIZE);
@@ -296,13 +299,14 @@ pool_alloc (struct pool *pool, size_t amt)
 void *
 pool_alloc_unaligned (struct pool *pool, size_t amt)
 {
-  assert (pool != NULL);
+  if (pool == NULL)
+    return xmalloc (amt);
 
 #ifndef DISCRETE_BLOCKS
   /* Strings need not be aligned on any boundary, but some
      operations may be more efficient when they are.  However,
      that's only going to help with reasonably long strings. */
-  if (amt < ALIGN_SIZE) 
+  if (amt < ALIGN_SIZE)
     {
       if (amt == 0)
         return NULL;
@@ -329,7 +333,7 @@ pool_alloc_unaligned (struct pool *pool, size_t amt)
    Terminates the program if the memory cannot be obtained,
    including the case where N * S overflows the range of size_t. */
 void *
-pool_nalloc (struct pool *pool, size_t n, size_t s) 
+pool_nalloc (struct pool *pool, size_t n, size_t s)
 {
   if (xalloc_oversized (n, s))
     xalloc_die ();
@@ -361,11 +365,24 @@ pool_clone_unaligned (struct pool *pool, const void *buffer, size_t size)
    the returned pointere may not be aligned properly for other
    types. */
 char *
-pool_strdup (struct pool *pool, const char *string) 
+pool_strdup (struct pool *pool, const char *string)
 {
   return pool_clone_unaligned (pool, string, strlen (string) + 1);
 }
 
+/* Duplicates the SIZE bytes of STRING, plus a trailing 0 byte,
+   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_strdup0 (struct pool *pool, const char *string, size_t size)
+{
+  char *new_string = pool_alloc_unaligned (pool, size + 1);
+  memcpy (new_string, string, size);
+  new_string[size] = '\0';
+  return new_string;
+}
+
 /* Formats FORMAT with the given ARGS in memory allocated from
    POOL and returns the formatted string. */
 char *
@@ -383,9 +400,9 @@ pool_vasprintf (struct pool *pool, const char *format, va_list args_)
   needed = vsnprintf (s, avail, format, args);
   va_end (args);
 
-  if (needed >= 0) 
+  if (needed >= 0)
     {
-      if (needed < avail) 
+      if (needed < avail)
         {
           /* Success.  Reserve the space that was actually used. */
           b->ofs += needed + 1;
@@ -401,7 +418,7 @@ pool_vasprintf (struct pool *pool, const char *format, va_list args_)
           va_end (args);
         }
     }
-  else 
+  else
     {
       /* Some old libc's returned -1 when the destination string
          was too short.  This should be uncommon these days and
@@ -424,7 +441,7 @@ pool_asprintf (struct pool *pool, const char *format, ...)
 {
   va_list args;
   char *string;
-  
+
   va_start (args, format);
   string = pool_vasprintf (pool, format, args);
   va_end (args);
@@ -466,13 +483,41 @@ pool_malloc (struct pool *pool, size_t amt)
    Terminates the program if the memory cannot be obtained,
    including the case where N * S overflows the range of size_t. */
 void *
-pool_nmalloc (struct pool *pool, size_t n, size_t s) 
+pool_nmalloc (struct pool *pool, size_t n, size_t s)
 {
   if (xalloc_oversized (n, s))
     xalloc_die ();
   return pool_malloc (pool, n * s);
 }
 
+/* Allocates AMT bytes using malloc(), to be managed by POOL,
+   zeros the block, and returns a pointer to the beginning of the
+   block.
+   If POOL is a null pointer, then allocates a normal memory block
+   with xmalloc().  */
+void *
+pool_zalloc (struct pool *pool, size_t amt)
+{
+  void *p = pool_malloc (pool, amt);
+  memset (p, 0, amt);
+  return p;
+}
+
+/* Allocates and returns N elements of S bytes each, to be
+   managed by POOL, and zeros the block.
+   If POOL is a null pointer, then allocates a normal memory block
+   with malloc().
+   N must be nonnegative, S must be positive.
+   Terminates the program if the memory cannot be obtained,
+   including the case where N * S overflows the range of size_t. */
+void *
+pool_calloc (struct pool *pool, size_t n, size_t s)
+{
+  void *p = pool_nmalloc (pool, n, s);
+  memset (p, 0, n * s);
+  return p;
+}
+
 /* Changes the allocation size of the specified memory block P managed
    by POOL to AMT bytes and returns a pointer to the beginning of the
    block.
@@ -658,7 +703,7 @@ pool_create_subpool (struct pool *pool)
 
   g = (void *) (((char *) subpool->blocks) + subpool->blocks->ofs);
   subpool->blocks->ofs += POOL_GIZMO_SIZE;
-  
+
   g->type = POOL_GIZMO_SUBPOOL;
   g->p.subpool = subpool;
 
@@ -672,14 +717,14 @@ pool_create_subpool (struct pool *pool)
    The subpool will be destroyed automatically when POOL is destroyed.
    It may also be destroyed explicitly in advance. */
 void
-pool_add_subpool (struct pool *pool, struct pool *subpool) 
+pool_add_subpool (struct pool *pool, struct pool *subpool)
 {
   struct pool_gizmo *g;
 
   assert (pool != NULL);
   assert (subpool != NULL);
   assert (subpool->parent == NULL);
-  
+
   g = pool_alloc (subpool, sizeof *g);
   g->type = POOL_GIZMO_SUBPOOL;
   g->p.subpool = subpool;
@@ -716,41 +761,78 @@ pool_fclose (struct pool *pool, FILE *file)
   return fclose (file);
 }
 
-/* Creates a temporary file with tmpfile() and returns a handle to it
-   if successful or a null pointer if not.
+/* Attaches FILE to POOL.
    The file will be closed automatically when POOL is destroyed, or it
    may be closed explicitly in advance using pool_fclose(), or
    detached from the pool with pool_detach_file(). */
+void
+pool_attach_file (struct pool *pool, FILE *file)
+{
+  struct pool_gizmo *g = pool_alloc (pool, sizeof *g);
+  g->type = POOL_GIZMO_FILE;
+  g->p.file = file;
+  add_gizmo (pool, g);
+}
+
+/* Detaches FILE from POOL. */
+void
+pool_detach_file (struct pool *pool, FILE *file)
+{
+  struct pool_gizmo *g;
+
+  for (g = pool->gizmos; g; g = g->next)
+    if (g->type == POOL_GIZMO_FILE && g->p.file == file)
+      {
+        delete_gizmo (pool, g);
+        return;
+      }
+}
+
+/* Creates a temporary file with create_temp_file() and returns a handle to it
+   if successful or a null pointer if not.
+   The file will be closed automatically when POOL is destroyed, or it
+   may be closed explicitly in advance using pool_fclose_temp_file(), or
+   detached from the pool with pool_detach_temp_file(). */
 FILE *
-pool_tmpfile (struct pool *pool) 
+pool_create_temp_file (struct pool *pool)
 {
-  FILE *file = tmpfile ();
+  FILE *file = create_temp_file ();
   if (file != NULL)
-    pool_attach_file (pool, file);
+    pool_attach_temp_file (pool, file);
   return file;
 }
 
-/* Attaches FILE to POOL.
+/* Closes file FILE managed by POOL.
+   FILE must have been opened with create_temp_file(). */
+void
+pool_fclose_temp_file (struct pool *pool, FILE *file)
+{
+  assert (pool && file);
+  pool_detach_temp_file (pool, file);
+  close_temp_file (file);
+}
+
+/* Attaches FILE, which must have been opened with create_temp_file(), to POOL.
    The file will be closed automatically when POOL is destroyed, or it
-   may be closed explicitly in advance using pool_fclose(), or
-   detached from the pool with pool_detach_file(). */
+   may be closed explicitly in advance using pool_fclose_temp_file(), or
+   detached from the pool with pool_detach_temp_file(). */
 void
-pool_attach_file (struct pool *pool, FILE *file)
+pool_attach_temp_file (struct pool *pool, FILE *file)
 {
   struct pool_gizmo *g = pool_alloc (pool, sizeof *g);
-  g->type = POOL_GIZMO_FILE;
+  g->type = POOL_GIZMO_TEMP_FILE;
   g->p.file = file;
   add_gizmo (pool, g);
 }
 
-/* Detaches FILE from POOL. */
+/* Detaches FILE that was opened with create_temp_file() from POOL. */
 void
-pool_detach_file (struct pool *pool, FILE *file)
+pool_detach_temp_file (struct pool *pool, FILE *file)
 {
   struct pool_gizmo *g;
 
   for (g = pool->gizmos; g; g = g->next)
-    if (g->type == POOL_GIZMO_FILE && g->p.file == file)
+    if (g->type == POOL_GIZMO_TEMP_FILE && g->p.file == file)
       {
         delete_gizmo (pool, g);
         return;
@@ -782,7 +864,7 @@ bool
 pool_unregister (struct pool *pool, void *p)
 {
   assert (pool && p);
-  
+
   {
     struct pool_gizmo *g;
 
@@ -793,7 +875,7 @@ pool_unregister (struct pool *pool, void *p)
          return true;
        }
   }
-  
+
   return false;
 }
 \f
@@ -815,12 +897,12 @@ pool_mark (struct pool *pool, struct pool_mark *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. */ 
+   instead. */
 void
 pool_release (struct pool *pool, const struct pool_mark *mark)
 {
   assert (pool && mark);
-  
+
   {
     struct pool_gizmo *cur, *next;
 
@@ -838,18 +920,18 @@ pool_release (struct pool *pool, const struct pool_mark *mark)
     else
       pool->gizmos = NULL;
   }
-  
+
   {
     struct pool_block *cur;
 
-    for (cur = pool->blocks; cur != mark->block; cur = cur->next) 
+    for (cur = pool->blocks; cur != mark->block; cur = cur->next)
       {
         cur->ofs = POOL_BLOCK_SIZE;
-        if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) 
+        if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool)
           {
             cur->ofs += POOL_SIZE;
             if (pool->parent != NULL)
-              cur->ofs += POOL_GIZMO_SIZE; 
+              cur->ofs += POOL_GIZMO_SIZE;
           }
       }
     pool->blocks = mark->block;
@@ -876,7 +958,7 @@ add_gizmo (struct pool *pool, struct pool_gizmo *gizmo)
 
   check_gizmo (pool, gizmo);
 }
+
 /* Removes GIZMO from POOL's gizmo list. */
 static void
 delete_gizmo (struct pool *pool, struct pool_gizmo *gizmo)
@@ -908,6 +990,9 @@ free_gizmo (struct pool_gizmo *gizmo)
     case POOL_GIZMO_FILE:
       fclose (gizmo->p.file);  /* Ignore errors. */
       break;
+    case POOL_GIZMO_TEMP_FILE:
+      close_temp_file (gizmo->p.file); /* Ignore errors. */
+      break;
     case POOL_GIZMO_SUBPOOL:
       gizmo->p.subpool->parent = NULL;
       pool_destroy (gizmo->p.subpool);
@@ -922,7 +1007,7 @@ free_gizmo (struct pool_gizmo *gizmo)
 
 /* Free all the gizmos in POOL. */
 static void
-free_all_gizmos (struct pool *pool) 
+free_all_gizmos (struct pool *pool)
 {
   struct pool_gizmo *cur, *next;
 
@@ -935,7 +1020,7 @@ free_all_gizmos (struct pool *pool)
 }
 
 static void
-check_gizmo (struct pool *p, struct pool_gizmo *g) 
+check_gizmo (struct pool *p, struct pool_gizmo *g)
 {
   assert (g->pool == p);
   assert (g->next == NULL || g->next->prev == g);