diff options
Diffstat (limited to 'src/microhttpd/memorypool.c')
-rw-r--r-- | src/microhttpd/memorypool.c | 284 |
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 */ |