• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
MHD_pool_create(size_t max)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
MHD_pool_destroy(struct MemoryPool * pool)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 *
MHD_pool_allocate(struct MemoryPool * pool,size_t size,int from_end)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 *
MHD_pool_reallocate(struct MemoryPool * pool,void * old,size_t old_size,size_t new_size)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 *
MHD_pool_reset(struct MemoryPool * pool,void * keep,size_t size)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