aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/memorypool.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd/memorypool.c')
-rw-r--r--src/microhttpd/memorypool.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/src/microhttpd/memorypool.c b/src/microhttpd/memorypool.c
new file mode 100644
index 00000000..2f399319
--- /dev/null
+++ b/src/microhttpd/memorypool.c
@@ -0,0 +1,284 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007, 2009, 2010 Daniel Pittman and Christian Grothoff
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+ * @file memorypool.c
+ * @brief memory pool
+ * @author Christian Grothoff
+ */
+#include "memorypool.h"
+
+/* define MAP_ANONYMOUS for Mac OS X */
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void*)-1)
+#endif
+
+/**
+ * Align to 2x word size (as GNU libc does).
+ */
+#define ALIGN_SIZE (2 * sizeof(void*))
+
+/**
+ * Round up 'n' to a multiple of ALIGN_SIZE.
+ */
+#define ROUND_TO_ALIGN(n) ((n+(ALIGN_SIZE-1)) & (~(ALIGN_SIZE-1)))
+
+
+/**
+ * Handle for a memory pool. Pools are not reentrant and must not be
+ * used by multiple threads.
+ */
+struct MemoryPool
+{
+
+ /**
+ * Pointer to the pool's memory
+ */
+ char *memory;
+
+ /**
+ * Size of the pool.
+ */
+ size_t size;
+
+ /**
+ * Offset of the first unallocated byte.
+ */
+ size_t pos;
+
+ /**
+ * Offset of the last unallocated byte.
+ */
+ size_t end;
+
+ /**
+ * #MHD_NO if pool was malloc'ed, #MHD_YES if mmapped (VirtualAlloc'ed for W32).
+ */
+ int is_mmap;
+};
+
+
+/**
+ * Create a memory pool.
+ *
+ * @param max maximum size of the pool
+ * @return NULL on error
+ */
+struct MemoryPool *
+MHD_pool_create (size_t max)
+{
+ struct MemoryPool *pool;
+
+ pool = malloc (sizeof (struct MemoryPool));
+ if (NULL == pool)
+ return NULL;
+#if defined(MAP_ANONYMOUS) || defined(_WIN32)
+ if (max <= 32 * 1024)
+ pool->memory = MAP_FAILED;
+ else
+#if defined(MAP_ANONYMOUS) && !defined(_WIN32)
+ pool->memory = mmap (NULL, max, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+#elif defined(_WIN32)
+ pool->memory = VirtualAlloc(NULL, max, MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE);
+#endif
+#else
+ pool->memory = MAP_FAILED;
+#endif
+ if ((pool->memory == MAP_FAILED) || (pool->memory == NULL))
+ {
+ pool->memory = malloc (max);
+ if (pool->memory == NULL)
+ {
+ free (pool);
+ return NULL;
+ }
+ pool->is_mmap = MHD_NO;
+ }
+ else
+ {
+ pool->is_mmap = MHD_YES;
+ }
+ pool->pos = 0;
+ pool->end = max;
+ pool->size = max;
+ return pool;
+}
+
+
+/**
+ * Destroy a memory pool.
+ *
+ * @param pool memory pool to destroy
+ */
+void
+MHD_pool_destroy (struct MemoryPool *pool)
+{
+ if (pool == NULL)
+ return;
+ if (pool->is_mmap == MHD_NO)
+ free (pool->memory);
+ else
+#if defined(MAP_ANONYMOUS) && !defined(_WIN32)
+ munmap (pool->memory, pool->size);
+#elif defined(_WIN32)
+ VirtualFree(pool->memory, 0, MEM_RELEASE);
+#else
+ abort();
+#endif
+ free (pool);
+}
+
+
+/**
+ * Allocate size bytes from the pool.
+ *
+ * @param pool memory pool to use for the operation
+ * @param size number of bytes to allocate
+ * @param from_end allocate from end of pool (set to #MHD_YES);
+ * use this for small, persistent allocations that
+ * will never be reallocated
+ * @return NULL if the pool cannot support size more
+ * bytes
+ */
+void *
+MHD_pool_allocate (struct MemoryPool *pool,
+ size_t size, int from_end)
+{
+ void *ret;
+ size_t asize;
+
+ asize = ROUND_TO_ALIGN (size);
+ if ( (0 == asize) && (0 != size) )
+ return NULL; /* size too close to SIZE_MAX */
+ if ((pool->pos + asize > pool->end) || (pool->pos + asize < pool->pos))
+ return NULL;
+ if (from_end == MHD_YES)
+ {
+ ret = &pool->memory[pool->end - asize];
+ pool->end -= asize;
+ }
+ else
+ {
+ ret = &pool->memory[pool->pos];
+ pool->pos += asize;
+ }
+ return ret;
+}
+
+
+/**
+ * Reallocate a block of memory obtained from the pool.
+ * This is particularly efficient when growing or
+ * shrinking the block that was last (re)allocated.
+ * If the given block is not the most recenlty
+ * (re)allocated block, the memory of the previous
+ * allocation may be leaked until the pool is
+ * destroyed (and copying the data maybe required).
+ *
+ * @param pool memory pool to use for the operation
+ * @param old the existing block
+ * @param old_size the size of the existing block
+ * @param new_size the new size of the block
+ * @return new address of the block, or
+ * NULL if the pool cannot support @a new_size
+ * bytes (old continues to be valid for @a old_size)
+ */
+void *
+MHD_pool_reallocate (struct MemoryPool *pool,
+ void *old,
+ size_t old_size,
+ size_t new_size)
+{
+ void *ret;
+ size_t asize;
+
+ asize = ROUND_TO_ALIGN (new_size);
+ if ( (0 == asize) && (0 != new_size) )
+ return NULL; /* new_size too close to SIZE_MAX */
+ if ((pool->end < old_size) || (pool->end < asize))
+ return NULL; /* unsatisfiable or bogus request */
+
+ if ((pool->pos >= old_size) && (&pool->memory[pool->pos - old_size] == old))
+ {
+ /* was the previous allocation - optimize! */
+ if (pool->pos + asize - old_size <= pool->end)
+ {
+ /* fits */
+ pool->pos += asize - old_size;
+ if (asize < old_size) /* shrinking - zero again! */
+ memset (&pool->memory[pool->pos], 0, old_size - asize);
+ return old;
+ }
+ /* does not fit */
+ return NULL;
+ }
+ if (asize <= old_size)
+ return old; /* cannot shrink, no need to move */
+ if ((pool->pos + asize >= pool->pos) &&
+ (pool->pos + asize <= pool->end))
+ {
+ /* fits */
+ ret = &pool->memory[pool->pos];
+ memcpy (ret, old, old_size);
+ pool->pos += asize;
+ return ret;
+ }
+ /* does not fit */
+ return NULL;
+}
+
+
+/**
+ * Clear all entries from the memory pool except
+ * for @a keep of the given @a size.
+ *
+ * @param pool memory pool to use for the operation
+ * @param keep pointer to the entry to keep (maybe NULL)
+ * @param size how many bytes need to be kept at this address
+ * @return addr new address of @a keep (if it had to change)
+ */
+void *
+MHD_pool_reset (struct MemoryPool *pool,
+ void *keep,
+ size_t size)
+{
+ if (NULL != keep)
+ {
+ if (keep != pool->memory)
+ {
+ memmove (pool->memory, keep, size);
+ keep = pool->memory;
+ }
+ }
+ pool->end = pool->size;
+ memset (&pool->memory[size],
+ 0,
+ pool->size - size);
+ if (NULL != keep)
+ pool->pos = ROUND_TO_ALIGN(size);
+ return keep;
+}
+
+
+/* end of memorypool.c */