]> git.saurik.com Git - apple/security.git/blob - Security/libsecurity_asn1/lib/plarena.c
Security-57031.30.12.tar.gz
[apple/security.git] / Security / libsecurity_asn1 / lib / plarena.c
1 /*
2 * Copyright (c) 2003-2006,2008,2010-2012 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * The contents of this file are subject to the Mozilla Public
25 * License Version 1.1 (the "License"); you may not use this file
26 * except in compliance with the License. You may obtain a copy of
27 * the License at http://www.mozilla.org/MPL/
28 *
29 * Software distributed under the License is distributed on an "AS
30 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
31 * implied. See the License for the specific language governing
32 * rights and limitations under the License.
33 *
34 * The Original Code is the Netscape Portable Runtime (NSPR).
35 *
36 * The Initial Developer of the Original Code is Netscape
37 * Communications Corporation. Portions created by Netscape are
38 * Copyright (C) 1998-2000 Netscape Communications Corporation. All
39 * Rights Reserved.
40 *
41 * Contributor(s):
42 *
43 * Alternatively, the contents of this file may be used under the
44 * terms of the GNU General Public License Version 2 or later (the
45 * "GPL"), in which case the provisions of the GPL are applicable
46 * instead of those above. If you wish to allow use of your
47 * version of this file only under the terms of the GPL and not to
48 * allow others to use your version of this file under the MPL,
49 * indicate your decision by deleting the provisions above and
50 * replace them with the notice and other provisions required by
51 * the GPL. If you do not delete the provisions above, a recipient
52 * may use your version of this file under either the MPL or the
53 * GPL.
54 */
55
56 /*
57 * Lifetime-based fast allocation, inspired by much prior art, including
58 * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
59 * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
60 */
61 #include <stdlib.h>
62 #include <string.h>
63 #include "plarena.h"
64 #include "prmem.h"
65 #include "prbit.h"
66 #include "prlog.h"
67 #include "prinit.h"
68
69 #ifdef PL_ARENAMETER
70 static PLArenaStats *arena_stats_list;
71
72 #define COUNT(pool,what) (pool)->stats.what++
73 #else
74 #define COUNT(pool,what) /* nothing */
75 #endif
76
77 #define PL_ARENA_DEFAULT_ALIGN sizeof(double)
78
79 PR_IMPLEMENT(void) PL_InitArenaPool(
80 PLArenaPool *pool, const char *name, PRUint32 size, PRUint32 align)
81 {
82 #if !defined (__GNUC__)
83 #pragma unused (name)
84 #endif
85
86 if (align == 0)
87 align = PL_ARENA_DEFAULT_ALIGN;
88 pool->mask = PR_BITMASK(PR_CeilingLog2(align));
89 pool->first.next = NULL;
90 pool->first.base = pool->first.avail = pool->first.limit =
91 (PRUword)PL_ARENA_ALIGN(pool, &pool->first + 1);
92 pool->current = &pool->first;
93 pool->arenasize = size;
94 #ifdef PL_ARENAMETER
95 memset(&pool->stats, 0, sizeof pool->stats);
96 pool->stats.name = strdup(name);
97 pool->stats.next = arena_stats_list;
98 arena_stats_list = &pool->stats;
99 #endif
100 }
101
102 #if __APPLE__
103 #define MAX_SIZE (PR_UINT32_MAX >> 1)
104 #endif
105
106 /*
107 ** PL_ArenaAllocate() -- allocate space from an arena pool
108 **
109 ** Description: PL_ArenaAllocate() allocates space from an arena
110 ** pool.
111 **
112 ** First, try to satisfy the request from arenas starting at
113 ** pool->current. Then try to allocate a new arena from the heap.
114 **
115 ** Returns: pointer to allocated space or NULL
116 **
117 ** Notes: The original implementation had some difficult to
118 ** solve bugs; the code was difficult to read. Sometimes it's
119 ** just easier to rewrite it. I did that. larryh.
120 **
121 ** See also: bugzilla: 45343.
122 **
123 */
124
125 PR_IMPLEMENT(void *) PL_ArenaAllocate(PLArenaPool *pool, PRUint32 nb)
126 {
127 PLArena *a;
128 char *rp; /* returned pointer */
129
130 PR_ASSERT((nb & pool->mask) == 0);
131 #ifdef __APPLE__
132 nb = PL_ARENA_ALIGN(pool, nb); /* force alignment, cast is useless/causes warning. */
133 #else
134 nb = (PRUword)PL_ARENA_ALIGN(pool, nb); /* force alignment */
135 #endif
136
137 /* attempt to allocate from arenas at pool->current */
138 {
139 a = pool->current;
140 do {
141 if ( nb <= a->limit - a->avail ) {
142 pool->current = a;
143 rp = (char *)a->avail;
144 a->avail += nb;
145 return rp;
146 }
147 } while( NULL != (a = a->next) );
148 }
149
150 /* attempt to allocate from the heap */
151 {
152 PRUint32 sz = PR_MAX(pool->arenasize, nb);
153 if (PR_UINT32_MAX - sz < sizeof *a + pool->mask) {
154 a = NULL;
155 } else {
156 sz += sizeof *a + pool->mask; /* header and alignment slop */
157 a = (PLArena*)PR_MALLOC(sz);
158 }
159 #ifdef __APPLE__
160 // Check for integer overflow on a->avail += nb
161 PRUword a_avail_tmp=(PRUword)PL_ARENA_ALIGN(pool, a + 1);
162 if (a_avail_tmp + nb < a_avail_tmp)
163 {
164 PR_FREEIF(a); // Set a back to NULL
165 }
166 #endif
167 if ( NULL != a ) {
168 a->limit = (PRUword)a + sz;
169 #ifdef __APPLE__
170 a->base = a->avail = a_avail_tmp;
171 #else
172 a->base = a->avail = (PRUword)PL_ARENA_ALIGN(pool, a + 1);
173 #endif
174 rp = (char *)a->avail;
175 a->avail += nb;
176 /* the newly allocated arena is linked after pool->current
177 * and becomes pool->current */
178 a->next = pool->current->next;
179 pool->current->next = a;
180 pool->current = a;
181 if ( NULL == pool->first.next )
182 pool->first.next = a;
183 PL_COUNT_ARENA(pool,++);
184 COUNT(pool, nmallocs);
185 return(rp);
186 }
187 }
188
189 /* we got to here, and there's no memory to allocate */
190 return(NULL);
191 } /* --- end PL_ArenaAllocate() --- */
192
193 /*
194 * Grow, a.k.a. realloc. The PL_ARENA_GROW macro has already handled
195 * the possible grow-in-place action in which the current PLArena is the
196 * source of the incoming pointer, and there is room in that arena for
197 * the requested size.
198 */
199 PR_IMPLEMENT(void *) PL_ArenaGrow(
200 PLArenaPool *pool, void *p, PRUint32 origSize, PRUint32 incr)
201 {
202 void *newp;
203 PLArena *thisArena;
204 PLArena *lastArena;
205 PRUint32 origAlignSize; // bytes currently reserved for caller
206 PRUint32 newSize; // bytes actually mallocd here
207
208 /* expand at least by 2x */
209 origAlignSize = PL_ARENA_ALIGN(pool, origSize);
210 newSize = PR_MAX(origAlignSize+incr, 2*origAlignSize);
211 newSize = PL_ARENA_ALIGN(pool, newSize);
212 #if __APPLE__
213 // Enforce maximal size before any potential implicit truncation
214 if (newSize>=MAX_SIZE || origSize>=MAX_SIZE || incr>=MAX_SIZE) {
215 return NULL;
216 }
217 #endif
218 PL_ARENA_ALLOCATE(newp, pool, newSize);
219 if (newp == NULL) {
220 return NULL;
221 }
222 /*
223 * Trim back the memory we just allocated to the amount our caller really
224 * needs, leaving the remainder for grow-in-place on subsequent calls
225 * to PL_ARENA_GROW.
226 */
227 PRUint32 newAlignSize = PL_ARENA_ALIGN(pool, origSize+incr);
228 PR_ASSERT(pool->current->avail == ((PRUword)newp + newSize));
229 pool->current->avail = (PRUword)newp + newAlignSize;
230 PR_ASSERT(pool->current->avail <= pool->current->limit);
231
232 /* "realloc" */
233 memcpy(newp, p, origSize);
234
235 /*
236 * Free old memory only if it's the entire outstanding allocated
237 * memory associated with one of our known PLArenas.
238 */
239 lastArena = &pool->first; /* pool->first always empty */
240 thisArena = lastArena->next; /* so, start here */
241
242 PRUword origPtr = (PRUword)p;
243 while(thisArena != NULL) {
244 if(origPtr == thisArena->base) {
245 if((origPtr + origAlignSize) == thisArena->avail) {
246 /* unlink */
247 lastArena->next = thisArena->next;
248
249 /* and free */
250 PL_CLEAR_ARENA(thisArena);
251 PL_COUNT_ARENA(pool,--);
252 PR_DELETE(thisArena);
253 break;
254 }
255 }
256 lastArena = thisArena;
257 thisArena = thisArena->next;
258 }
259 /*
260 * Note: inability to free is not an error; it just causes a temporary leak
261 * of the old buffer (until the arena pool is freed, of course).
262 */
263 return newp;
264 }
265
266 static void ClearArenaList(PLArena *a, PRInt32 pattern)
267 {
268
269 for (; a; a = a->next) {
270 PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
271 a->avail = a->base;
272 PL_CLEAR_UNUSED_PATTERN(a, pattern);
273 }
274 }
275
276 PR_IMPLEMENT(void) PL_ClearArenaPool(PLArenaPool *pool, PRInt32 pattern)
277 {
278 ClearArenaList(pool->first.next, pattern);
279 }
280
281 /*
282 * Free tail arenas linked after head, which may not be the true list head.
283 * Reset pool->current to point to head in case it pointed at a tail arena.
284 */
285 static void FreeArenaList(PLArenaPool *pool, PLArena *head, PRBool reallyFree)
286 {
287 PLArena **ap, *a;
288
289 ap = &head->next;
290 a = *ap;
291 if (!a)
292 return;
293
294 do {
295 *ap = a->next;
296 PL_CLEAR_ARENA(a);
297 PL_COUNT_ARENA(pool,--);
298 PR_DELETE(a);
299 } while ((a = *ap) != 0);
300
301 pool->current = head;
302 }
303
304 PR_IMPLEMENT(void) PL_ArenaRelease(PLArenaPool *pool, char *mark)
305 {
306 #if ARENA_MARK_ENABLE
307 PLArena *a;
308
309 for (a = pool->first.next; a; a = a->next) {
310 if (PR_UPTRDIFF(mark, a->base) < PR_UPTRDIFF(a->avail, a->base)) {
311 a->avail = (PRUword)PL_ARENA_ALIGN(pool, mark);
312 FreeArenaList(pool, a, PR_FALSE);
313 return;
314 }
315 }
316 #endif /* ARENA_MARK_ENABLE */
317 }
318
319 PR_IMPLEMENT(void) PL_FreeArenaPool(PLArenaPool *pool)
320 {
321 FreeArenaList(pool, &pool->first, PR_FALSE);
322 COUNT(pool, ndeallocs);
323 }
324
325 PR_IMPLEMENT(void) PL_FinishArenaPool(PLArenaPool *pool)
326 {
327 FreeArenaList(pool, &pool->first, PR_TRUE);
328 #ifdef PL_ARENAMETER
329 {
330 PLArenaStats *stats, **statsp;
331
332 if (pool->stats.name)
333 PR_DELETE(pool->stats.name);
334 for (statsp = &arena_stats_list; (stats = *statsp) != 0;
335 statsp = &stats->next) {
336 if (stats == &pool->stats) {
337 *statsp = stats->next;
338 return;
339 }
340 }
341 }
342 #endif
343 }
344
345 PR_IMPLEMENT(void) PL_CompactArenaPool(PLArenaPool *ap)
346 {
347 }
348
349 PR_IMPLEMENT(void) PL_ArenaFinish(void)
350 {
351 }
352
353 #ifdef PL_ARENAMETER
354 PR_IMPLEMENT(void) PL_ArenaCountAllocation(PLArenaPool *pool, PRUint32 nb)
355 {
356 pool->stats.nallocs++;
357 pool->stats.nbytes += nb;
358 if (nb > pool->stats.maxalloc)
359 pool->stats.maxalloc = nb;
360 pool->stats.variance += nb * nb;
361 }
362
363 PR_IMPLEMENT(void) PL_ArenaCountInplaceGrowth(
364 PLArenaPool *pool, PRUint32 size, PRUint32 incr)
365 {
366 pool->stats.ninplace++;
367 }
368
369 PR_IMPLEMENT(void) PL_ArenaCountGrowth(
370 PLArenaPool *pool, PRUint32 size, PRUint32 incr)
371 {
372 pool->stats.ngrows++;
373 pool->stats.nbytes += incr;
374 pool->stats.variance -= size * size;
375 size += incr;
376 if (size > pool->stats.maxalloc)
377 pool->stats.maxalloc = size;
378 pool->stats.variance += size * size;
379 }
380
381 PR_IMPLEMENT(void) PL_ArenaCountRelease(PLArenaPool *pool, char *mark)
382 {
383 pool->stats.nreleases++;
384 }
385
386 PR_IMPLEMENT(void) PL_ArenaCountRetract(PLArenaPool *pool, char *mark)
387 {
388 pool->stats.nfastrels++;
389 }
390
391 #include <math.h>
392 #include <stdio.h>
393
394 PR_IMPLEMENT(void) PL_DumpArenaStats(FILE *fp)
395 {
396 PLArenaStats *stats;
397 double mean, variance;
398
399 for (stats = arena_stats_list; stats; stats = stats->next) {
400 if (stats->nallocs != 0) {
401 mean = (double)stats->nbytes / stats->nallocs;
402 variance = fabs(stats->variance / stats->nallocs - mean * mean);
403 } else {
404 mean = variance = 0;
405 }
406
407 fprintf(fp, "\n%s allocation statistics:\n", stats->name);
408 fprintf(fp, " number of arenas: %u\n", stats->narenas);
409 fprintf(fp, " number of allocations: %u\n", stats->nallocs);
410 fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims);
411 fprintf(fp, " number of malloc calls: %u\n", stats->nmallocs);
412 fprintf(fp, " number of deallocations: %u\n", stats->ndeallocs);
413 fprintf(fp, " number of allocation growths: %u\n", stats->ngrows);
414 fprintf(fp, " number of in-place growths: %u\n", stats->ninplace);
415 fprintf(fp, "number of released allocations: %u\n", stats->nreleases);
416 fprintf(fp, " number of fast releases: %u\n", stats->nfastrels);
417 fprintf(fp, " total bytes allocated: %u\n", stats->nbytes);
418 fprintf(fp, " mean allocation size: %g\n", mean);
419 fprintf(fp, " standard deviation: %g\n", sqrt(variance));
420 fprintf(fp, " maximum allocation size: %u\n", stats->maxalloc);
421 }
422 }
423 #endif /* PL_ARENAMETER */