1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2009, 2010 Daniel Pittman and Christian Grothoff 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 /** 21 * @file memorypool.c 22 * @brief memory pool 23 * @author Christian Grothoff 24 */ 25 #include "memorypool.h" 26 27 /* define MAP_ANONYMOUS for Mac OS X */ 28 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) 29 #define MAP_ANONYMOUS MAP_ANON 30 #endif 31 #ifndef MAP_FAILED 32 #define MAP_FAILED ((void*)-1) 33 #endif 34 35 /** 36 * Align to 2x word size (as GNU libc does). 37 */ 38 #define ALIGN_SIZE (2 * sizeof(void*)) 39 40 /** 41 * Round up 'n' to a multiple of ALIGN_SIZE. 42 */ 43 #define ROUND_TO_ALIGN(n) ((n+(ALIGN_SIZE-1)) & (~(ALIGN_SIZE-1))) 44 45 46 /** 47 * Handle for a memory pool. Pools are not reentrant and must not be 48 * used by multiple threads. 49 */ 50 struct MemoryPool 51 { 52 53 /** 54 * Pointer to the pool's memory 55 */ 56 char *memory; 57 58 /** 59 * Size of the pool. 60 */ 61 size_t size; 62 63 /** 64 * Offset of the first unallocated byte. 65 */ 66 size_t pos; 67 68 /** 69 * Offset of the last unallocated byte. 70 */ 71 size_t end; 72 73 /** 74 * #MHD_NO if pool was malloc'ed, #MHD_YES if mmapped (VirtualAlloc'ed for W32). 75 */ 76 int is_mmap; 77 }; 78 79 80 /** 81 * Create a memory pool. 82 * 83 * @param max maximum size of the pool 84 * @return NULL on error 85 */ 86 struct MemoryPool * 87 MHD_pool_create (size_t max) 88 { 89 struct MemoryPool *pool; 90 91 pool = malloc (sizeof (struct MemoryPool)); 92 if (NULL == pool) 93 return NULL; 94 #if defined(MAP_ANONYMOUS) || defined(_WIN32) 95 if (max <= 32 * 1024) 96 pool->memory = MAP_FAILED; 97 else 98 #if defined(MAP_ANONYMOUS) && !defined(_WIN32) 99 pool->memory = mmap (NULL, max, PROT_READ | PROT_WRITE, 100 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 101 #elif defined(_WIN32) 102 pool->memory = VirtualAlloc(NULL, max, MEM_COMMIT | MEM_RESERVE, 103 PAGE_READWRITE); 104 #endif 105 #else 106 pool->memory = MAP_FAILED; 107 #endif 108 if ((pool->memory == MAP_FAILED) || (pool->memory == NULL)) 109 { 110 pool->memory = malloc (max); 111 if (pool->memory == NULL) 112 { 113 free (pool); 114 return NULL; 115 } 116 pool->is_mmap = MHD_NO; 117 } 118 else 119 { 120 pool->is_mmap = MHD_YES; 121 } 122 pool->pos = 0; 123 pool->end = max; 124 pool->size = max; 125 return pool; 126 } 127 128 129 /** 130 * Destroy a memory pool. 131 * 132 * @param pool memory pool to destroy 133 */ 134 void 135 MHD_pool_destroy (struct MemoryPool *pool) 136 { 137 if (pool == NULL) 138 return; 139 if (pool->is_mmap == MHD_NO) 140 free (pool->memory); 141 else 142 #if defined(MAP_ANONYMOUS) && !defined(_WIN32) 143 munmap (pool->memory, pool->size); 144 #elif defined(_WIN32) 145 VirtualFree(pool->memory, 0, MEM_RELEASE); 146 #else 147 abort(); 148 #endif 149 free (pool); 150 } 151 152 153 /** 154 * Allocate size bytes from the pool. 155 * 156 * @param pool memory pool to use for the operation 157 * @param size number of bytes to allocate 158 * @param from_end allocate from end of pool (set to #MHD_YES); 159 * use this for small, persistent allocations that 160 * will never be reallocated 161 * @return NULL if the pool cannot support size more 162 * bytes 163 */ 164 void * 165 MHD_pool_allocate (struct MemoryPool *pool, 166 size_t size, int from_end) 167 { 168 void *ret; 169 size_t asize; 170 171 asize = ROUND_TO_ALIGN (size); 172 if ( (0 == asize) && (0 != size) ) 173 return NULL; /* size too close to SIZE_MAX */ 174 if ((pool->pos + asize > pool->end) || (pool->pos + asize < pool->pos)) 175 return NULL; 176 if (from_end == MHD_YES) 177 { 178 ret = &pool->memory[pool->end - asize]; 179 pool->end -= asize; 180 } 181 else 182 { 183 ret = &pool->memory[pool->pos]; 184 pool->pos += asize; 185 } 186 return ret; 187 } 188 189 190 /** 191 * Reallocate a block of memory obtained from the pool. 192 * This is particularly efficient when growing or 193 * shrinking the block that was last (re)allocated. 194 * If the given block is not the most recenlty 195 * (re)allocated block, the memory of the previous 196 * allocation may be leaked until the pool is 197 * destroyed (and copying the data maybe required). 198 * 199 * @param pool memory pool to use for the operation 200 * @param old the existing block 201 * @param old_size the size of the existing block 202 * @param new_size the new size of the block 203 * @return new address of the block, or 204 * NULL if the pool cannot support @a new_size 205 * bytes (old continues to be valid for @a old_size) 206 */ 207 void * 208 MHD_pool_reallocate (struct MemoryPool *pool, 209 void *old, 210 size_t old_size, 211 size_t new_size) 212 { 213 void *ret; 214 size_t asize; 215 216 asize = ROUND_TO_ALIGN (new_size); 217 if ( (0 == asize) && (0 != new_size) ) 218 return NULL; /* new_size too close to SIZE_MAX */ 219 if ((pool->end < old_size) || (pool->end < asize)) 220 return NULL; /* unsatisfiable or bogus request */ 221 222 if ((pool->pos >= old_size) && (&pool->memory[pool->pos - old_size] == old)) 223 { 224 /* was the previous allocation - optimize! */ 225 if (pool->pos + asize - old_size <= pool->end) 226 { 227 /* fits */ 228 pool->pos += asize - old_size; 229 if (asize < old_size) /* shrinking - zero again! */ 230 memset (&pool->memory[pool->pos], 0, old_size - asize); 231 return old; 232 } 233 /* does not fit */ 234 return NULL; 235 } 236 if (asize <= old_size) 237 return old; /* cannot shrink, no need to move */ 238 if ((pool->pos + asize >= pool->pos) && 239 (pool->pos + asize <= pool->end)) 240 { 241 /* fits */ 242 ret = &pool->memory[pool->pos]; 243 memcpy (ret, old, old_size); 244 pool->pos += asize; 245 return ret; 246 } 247 /* does not fit */ 248 return NULL; 249 } 250 251 252 /** 253 * Clear all entries from the memory pool except 254 * for @a keep of the given @a size. 255 * 256 * @param pool memory pool to use for the operation 257 * @param keep pointer to the entry to keep (maybe NULL) 258 * @param size how many bytes need to be kept at this address 259 * @return addr new address of @a keep (if it had to change) 260 */ 261 void * 262 MHD_pool_reset (struct MemoryPool *pool, 263 void *keep, 264 size_t size) 265 { 266 if (NULL != keep) 267 { 268 if (keep != pool->memory) 269 { 270 memmove (pool->memory, keep, size); 271 keep = pool->memory; 272 } 273 } 274 pool->end = pool->size; 275 memset (&pool->memory[size], 276 0, 277 pool->size - size); 278 if (NULL != keep) 279 pool->pos = ROUND_TO_ALIGN(size); 280 return keep; 281 } 282 283 284 /* end of memorypool.c */ 285