-/* 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, 2012 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
};
/* Pool block. */
-struct pool_block
+struct pool_block
{
struct pool_block *prev;
struct pool_block *next;
{
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
{
/* 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. */
void (*fp) (void);
long l;
double d;
+
+ /* glibc jmp_buf on ia64 requires 16-byte alignment. This ensures it. */
+ size_t s[2];
};
/* This should be the alignment size used by malloc(). The size of
- the union above is correct, if not optimal, in all known cases. */
+ the union above is correct, if not optimal, in all known cases.
+
+ This is normally 8 bytes for 32-bit architectures and 16 bytes for 64-bit
+ architectures. */
#define ALIGN_SIZE sizeof (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,
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;
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_;
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. */
/* 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;
}
if (amt == 0)
return NULL;
-
+
#ifndef DISCRETE_BLOCKS
if (amt <= MAX_SUBALLOC)
{
/* 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;
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);
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;
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 ();
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 *
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;
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
{
va_list args;
char *string;
-
+
va_start (args, format);
string = pool_vasprintf (pool, format, args);
va_end (args);
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.
g = (void *) (((char *) subpool->blocks) + subpool->blocks->ofs);
subpool->blocks->ofs += POOL_GIZMO_SIZE;
-
+
g->type = POOL_GIZMO_SUBPOOL;
g->p.subpool = subpool;
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;
assert (pool && file_name && mode);
f = fopen (file_name, mode);
- if (f == NULL)
- return NULL;
+ if (f != NULL)
+ pool_attach_file (pool, f);
return f;
}
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;
}
/* Unregisters previously registered P from POOL.
- Returns nonzero only if P was found to be registered in POOL. */
-int
+ Returns true only if P was found to be registered in POOL. */
+bool
pool_unregister (struct pool *pool, void *p)
{
assert (pool && p);
-
+
{
struct pool_gizmo *g;
if (g->type == POOL_GIZMO_REGISTERED && g->p.registered.p == p)
{
delete_gizmo (pool, g);
- return 1;
+ return true;
}
}
-
- return 0;
+
+ return false;
}
\f
/* Partial freeing. */
/* 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;
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;
check_gizmo (pool, gizmo);
}
-
+
/* Removes GIZMO from POOL's gizmo list. */
static void
delete_gizmo (struct pool *pool, 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);
/* 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;
}
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);