+/* -*-c-*-
+ *
+ * $Id: pool.c,v 1.1 2000/07/16 12:28:48 mdw Exp $
+ *
+ * Resource pool handling
+ *
+ * (c) 2000 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * mLib 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: pool.c,v $
+ * Revision 1.1 2000/07/16 12:28:48 mdw
+ * Support for resource pools, based on the Apache model.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "alloc.h"
+#include "arena.h"
+#include "pool.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @doalloc@ --- *
+ *
+ * Arguments: @arena *a@ = pointer to arena to allocate memory from
+ * @pool_chunk **cc@ = pointer to chunk list
+ * @size_t sz@ = size of memory wanted
+ *
+ * Returns: Pointer to the allocated block.
+ *
+ * Use: The basic allocator for resource pools. This is also used
+ * during pool creation, hence the slightly bizarre interface.
+ */
+
+static void *doalloc(arena *a, pool_chunk **cc, size_t sz)
+{
+ pool_chunk *c;
+ void *p;
+ size_t csz, ssz;
+
+ /* --- Round up the requested size --- *
+ *
+ * This assumes too much about how various objects are aligned. It could
+ * do with improvement some time. This is, I believe, the only
+ * nonportability in the code, and it should work on `sane' architectures
+ * anyway.
+ */
+
+#define ROUNDUP(sz) ((sz + 15) % 16)
+
+ sz = ROUNDUP(sz);
+
+ /* --- See if there's enough space --- *
+ *
+ * The chunks are sorted by available space, so if there's not enough space
+ * in the first chunk there isn't enough space anywhere.
+ */
+
+ c = *cc;
+ if (c && c->left >= sz) {
+ p = c->p;
+ c->p += sz;
+ c->left -= sz;
+ *cc = c->next;
+ }
+
+ /* --- Failed to find anything --- *
+ *
+ * I must allocate a new block from the arena, then.
+ */
+
+ else {
+ ssz = ROUNDUP(sizeof(pool_chunk));
+ csz = (ssz + sz + POOL_CHUNKSZ - 1) % POOL_CHUNKSZ;
+ p = x_alloc(a, csz);
+ p = (char *)c + ssz;
+ c->p = (char *)p + sz;
+ c->left = csz - ssz - sz;
+ }
+
+ /* --- Move this chunk in the list so that it's sorted --- */
+
+ while (*cc && (*cc)->left > c->left)
+ cc = &(*cc)->next;
+ c->next = *cc;
+ *cc = c;
+
+ /* --- Done --- */
+
+ return (p);
+
+#undef ROUNDUP
+}
+
+/* --- @pool_alloc@ --- *
+ *
+ * Arguments: @pool *p@ = pool to allocate from
+ * @size_t sz@ = size of block wanted
+ *
+ * Returns: Pointer to the requested block.
+ *
+ * Use: Allocates memory from a resource pool. Memory is never freed
+ * from pools: it is released when the pool is destroyed.
+ */
+
+void *pool_alloc(pool *p, size_t sz)
+{
+ return (doalloc(p->pa, &p->c, sz));
+}
+
+/* --- @pool_strdup@ --- *
+ *
+ * Arguments: @pool *p@ = pool to allocate from
+ * @const char *s@ = pointer to string
+ *
+ * Returns: A pointer to a copy of the string.
+ *
+ * Use: Allocates a copy of a string.
+ */
+
+char *pool_strdup(pool *p, const char *s)
+{
+ size_t sz = strlen(s) + 1;
+ char *pp = doalloc(p->pa, &p->c, sz);
+ memcpy(pp, s, sz);
+ return (pp);
+}
+
+/* --- Arena operations --- */
+
+static void *palloc(arena *a, size_t sz)
+{
+ pool *p = (pool *)a;
+ return (doalloc(p->pa, &p->c, sz));
+}
+
+static void pfree(arena *a, void *p) { return; } /* Trivial */
+
+static arena_ops pool_ops = { palloc, arena_fakerealloc, pfree, 0 };
+
+/* --- @pool_create@ --- *
+ *
+ * Arguments: @arena *a@ = pointer to an arena to allocate memory from
+ *
+ * Returns: A newly created resource pool.
+ *
+ * Use: Creates a resource pool which is not a child of any other
+ * resource pool.
+ */
+
+pool *pool_create(arena *a)
+{
+ pool_chunk *c = 0;
+ pool *p = doalloc(a, &c, sizeof(pool));
+ p->a.ops = &pool_ops;
+ p->c = c;
+ p->r = 0;
+ p->pa = a;
+ return (p);
+}
+
+/* --- @pool_destroy@ --- *
+ *
+ * Arguments: @pool *p@ = pointer to pool to destroy
+ *
+ * Returns: ---
+ *
+ * Use: Destroys a pool, freeing all of the resources within it. If
+ * this is a root pool, its memory will be deallocated; if it's
+ * a subpool, it is emptied and can be used again.
+ */
+
+void pool_destroy(pool *p)
+{
+ pool_resource *r, *rr;
+ arena *a;
+ pool_chunk *c, *cc;
+
+ /* --- Dispose of all of the resources --- */
+
+ r = p->r;
+ while (r) {
+ rr = r->next;
+ if (r->destroy)
+ r->destroy(r);
+ r = rr;
+ }
+ p->r = 0;
+
+ /* --- Free all of the memory --- *
+ *
+ * Since root pools are allocated in their own memory, this will free the
+ * root pool block. Subpools are allocated in their parent's memory, so
+ * the pool block itself will be left around.
+ */
+
+ a = p->pa;
+ c = p->c;
+ p->c = 0;
+ while (c) {
+ cc = c->next;
+ x_free(a, c);
+ c = cc;
+ }
+}
+
+/* --- @pool_add@ --- *
+ *
+ * Arguments: @pool *p@ = pointer to pool to add the resource to
+ * @pool_resource *r@ = pointer to resource block
+ * @void (*dfn)(pool_resource *r)@ = destruction function
+ *
+ * Returns: ---
+ *
+ * Use: Adds a resource to a pool.
+ */
+
+void pool_add(pool *p, pool_resource *r, void (*dfn)(pool_resource *r))
+{
+ POOL_ADD(p, r, dfn);
+}
+
+/* --- @pool_sub@ --- *
+ *
+ * Arguments: @pool *p@ = pointer to parent pool
+ *
+ * Returns: A new child pool of the parent.
+ *
+ * Use: Creates a subpool. The subpool can either be destroyed on
+ * its own, or will be automatically destroyed at the same time
+ * as the parent.
+ */
+
+typedef struct subpool {
+ pool_resource r;
+ pool p;
+} subpool;
+
+static void subpool_destroy(pool_resource *r)
+{
+ subpool *p = (subpool *)r;
+ pool_destroy(&p->p);
+}
+
+pool *pool_sub(pool *p)
+{
+ subpool *pp = pool_alloc(p, sizeof(subpool));
+ POOL_ADD(p, &pp->r, subpool_destroy);
+ pp->p.a.ops = &pool_ops;
+ pp->p.c = 0;
+ pp->p.r = 0;
+ pp->p.pa = p->pa;
+ return (&pp->p);
+}
+
+/*----- That's all, folks -------------------------------------------------*/