]> git.saurik.com Git - apple/security.git/blob - Security/libsecurity_asn1/lib/plarena.c
Security-57031.1.35.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
103 /*
104 ** PL_ArenaAllocate() -- allocate space from an arena pool
105 **
106 ** Description: PL_ArenaAllocate() allocates space from an arena
107 ** pool.
108 **
109 ** First, try to satisfy the request from arenas starting at
110 ** pool->current. Then try to allocate a new arena from the heap.
111 **
112 ** Returns: pointer to allocated space or NULL
113 **
114 ** Notes: The original implementation had some difficult to
115 ** solve bugs; the code was difficult to read. Sometimes it's
116 ** just easier to rewrite it. I did that. larryh.
117 **
118 ** See also: bugzilla: 45343.
119 **
120 */
121
122 PR_IMPLEMENT(void *) PL_ArenaAllocate(PLArenaPool *pool, PRUint32 nb)
123 {
124 PLArena *a;
125 char *rp; /* returned pointer */
126
127 PR_ASSERT((nb & pool->mask) == 0);
128
129 nb = (PRUint32)PL_ARENA_ALIGN(pool, nb); /* force alignment */
130
131 /* attempt to allocate from arenas at pool->current */
132 {
133 a = pool->current;
134 do {
135 if ( a->avail +nb <= a->limit ) {
136 pool->current = a;
137 rp = (char *)a->avail;
138 a->avail += nb;
139 return rp;
140 }
141 } while( NULL != (a = a->next) );
142 }
143
144 /* attempt to allocate from the heap */
145 {
146 PRSize sz = PR_MAX(pool->arenasize, nb);
147 sz += sizeof(*a) + pool->mask; /* header and alignment slop */
148 a = (PLArena*)PR_MALLOC(sz);
149 if ( NULL != a ) {
150 a->limit = (PRUword)a + sz;
151 a->base = a->avail = (PRUword)PL_ARENA_ALIGN(pool, a + 1);
152 rp = (char *)a->avail;
153 a->avail += nb;
154 /* the newly allocated arena is linked after pool->current
155 * and becomes pool->current */
156 a->next = pool->current->next;
157 pool->current->next = a;
158 pool->current = a;
159 if ( NULL == pool->first.next )
160 pool->first.next = a;
161 PL_COUNT_ARENA(pool,++);
162 COUNT(pool, nmallocs);
163 return(rp);
164 }
165 }
166
167 /* we got to here, and there's no memory to allocate */
168 return(NULL);
169 } /* --- end PL_ArenaAllocate() --- */
170
171 /*
172 * Grow, a.k.a. realloc. The PL_ARENA_GROW macro has already handled
173 * the possible grow-in-place action in which the current PLArena is the
174 * source of the incoming pointer, and there is room in that arena for
175 * the requested size.
176 */
177 PR_IMPLEMENT(void *) PL_ArenaGrow(
178 PLArenaPool *pool, void *p, PRUint32 origSize, PRUint32 incr)
179 {
180 void *newp;
181 PLArena *thisArena;
182 PLArena *lastArena;
183 PRUint32 origAlignSize; // bytes currently reserved for caller
184 PRUint32 newSize; // bytes actually mallocd here
185
186 /* expand at least by 2x */
187 origAlignSize = PL_ARENA_ALIGN(pool, origSize);
188 newSize = PR_MAX(origAlignSize+incr, 2*origAlignSize);
189 newSize = PL_ARENA_ALIGN(pool, newSize);
190 PL_ARENA_ALLOCATE(newp, pool, newSize);
191 if (newp == NULL) {
192 return NULL;
193 }
194
195 /*
196 * Trim back the memory we just allocated to the amount our caller really
197 * needs, leaving the remainder for grow-in-place on subsequent calls
198 * to PL_ARENA_GROW.
199 */
200 PRUint32 newAlignSize = PL_ARENA_ALIGN(pool, origSize+incr);
201 PR_ASSERT(pool->current->avail == ((PRUword)newp + newSize));
202 pool->current->avail = (PRUword)newp + newAlignSize;
203 PR_ASSERT(pool->current->avail <= pool->current->limit);
204
205 /* "realloc" */
206 memcpy(newp, p, origSize);
207
208 /*
209 * Free old memory only if it's the entire outstanding allocated
210 * memory associated with one of our known PLArenas.
211 */
212 lastArena = &pool->first; /* pool->first always empty */
213 thisArena = lastArena->next; /* so, start here */
214
215 PRUword origPtr = (PRUword)p;
216 while(thisArena != NULL) {
217 if(origPtr == thisArena->base) {
218 if((origPtr + origAlignSize) == thisArena->avail) {
219 /* unlink */
220 lastArena->next = thisArena->next;
221
222 /* and free */
223 PL_CLEAR_ARENA(thisArena);
224 PL_COUNT_ARENA(pool,--);
225 PR_DELETE(thisArena);
226 break;
227 }
228 }
229 lastArena = thisArena;
230 thisArena = thisArena->next;
231 }
232 /*
233 * Note: inability to free is not an error; it just causes a temporary leak
234 * of the old buffer (until the arena pool is freed, of course).
235 */
236 return newp;
237 }
238
239 /*
240 * Free tail arenas linked after head, which may not be the true list head.
241 * Reset pool->current to point to head in case it pointed at a tail arena.
242 */
243 static void FreeArenaList(PLArenaPool *pool, PLArena *head, PRBool reallyFree)
244 {
245 PLArena **ap, *a;
246
247 ap = &head->next;
248 a = *ap;
249 if (!a)
250 return;
251
252 do {
253 *ap = a->next;
254 PL_CLEAR_ARENA(a);
255 PL_COUNT_ARENA(pool,--);
256 PR_DELETE(a);
257 } while ((a = *ap) != 0);
258
259 pool->current = head;
260 }
261
262 PR_IMPLEMENT(void) PL_ArenaRelease(PLArenaPool *pool, char *mark)
263 {
264 #if ARENA_MARK_ENABLE
265 PLArena *a;
266
267 for (a = pool->first.next; a; a = a->next) {
268 if (PR_UPTRDIFF(mark, a->base) < PR_UPTRDIFF(a->avail, a->base)) {
269 a->avail = (PRUword)PL_ARENA_ALIGN(pool, mark);
270 FreeArenaList(pool, a, PR_FALSE);
271 return;
272 }
273 }
274 #endif /* ARENA_MARK_ENABLE */
275 }
276
277 PR_IMPLEMENT(void) PL_FreeArenaPool(PLArenaPool *pool)
278 {
279 FreeArenaList(pool, &pool->first, PR_FALSE);
280 COUNT(pool, ndeallocs);
281 }
282
283 PR_IMPLEMENT(void) PL_FinishArenaPool(PLArenaPool *pool)
284 {
285 FreeArenaList(pool, &pool->first, PR_TRUE);
286 #ifdef PL_ARENAMETER
287 {
288 PLArenaStats *stats, **statsp;
289
290 if (pool->stats.name)
291 PR_DELETE(pool->stats.name);
292 for (statsp = &arena_stats_list; (stats = *statsp) != 0;
293 statsp = &stats->next) {
294 if (stats == &pool->stats) {
295 *statsp = stats->next;
296 return;
297 }
298 }
299 }
300 #endif
301 }
302
303 PR_IMPLEMENT(void) PL_CompactArenaPool(PLArenaPool *ap)
304 {
305 }
306
307 PR_IMPLEMENT(void) PL_ArenaFinish(void)
308 {
309 }
310
311 #ifdef PL_ARENAMETER
312 PR_IMPLEMENT(void) PL_ArenaCountAllocation(PLArenaPool *pool, PRUint32 nb)
313 {
314 pool->stats.nallocs++;
315 pool->stats.nbytes += nb;
316 if (nb > pool->stats.maxalloc)
317 pool->stats.maxalloc = nb;
318 pool->stats.variance += nb * nb;
319 }
320
321 PR_IMPLEMENT(void) PL_ArenaCountInplaceGrowth(
322 PLArenaPool *pool, PRUint32 size, PRUint32 incr)
323 {
324 pool->stats.ninplace++;
325 }
326
327 PR_IMPLEMENT(void) PL_ArenaCountGrowth(
328 PLArenaPool *pool, PRUint32 size, PRUint32 incr)
329 {
330 pool->stats.ngrows++;
331 pool->stats.nbytes += incr;
332 pool->stats.variance -= size * size;
333 size += incr;
334 if (size > pool->stats.maxalloc)
335 pool->stats.maxalloc = size;
336 pool->stats.variance += size * size;
337 }
338
339 PR_IMPLEMENT(void) PL_ArenaCountRelease(PLArenaPool *pool, char *mark)
340 {
341 pool->stats.nreleases++;
342 }
343
344 PR_IMPLEMENT(void) PL_ArenaCountRetract(PLArenaPool *pool, char *mark)
345 {
346 pool->stats.nfastrels++;
347 }
348
349 #include <math.h>
350 #include <stdio.h>
351
352 PR_IMPLEMENT(void) PL_DumpArenaStats(FILE *fp)
353 {
354 PLArenaStats *stats;
355 double mean, variance;
356
357 for (stats = arena_stats_list; stats; stats = stats->next) {
358 if (stats->nallocs != 0) {
359 mean = (double)stats->nbytes / stats->nallocs;
360 variance = fabs(stats->variance / stats->nallocs - mean * mean);
361 } else {
362 mean = variance = 0;
363 }
364
365 fprintf(fp, "\n%s allocation statistics:\n", stats->name);
366 fprintf(fp, " number of arenas: %u\n", stats->narenas);
367 fprintf(fp, " number of allocations: %u\n", stats->nallocs);
368 fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims);
369 fprintf(fp, " number of malloc calls: %u\n", stats->nmallocs);
370 fprintf(fp, " number of deallocations: %u\n", stats->ndeallocs);
371 fprintf(fp, " number of allocation growths: %u\n", stats->ngrows);
372 fprintf(fp, " number of in-place growths: %u\n", stats->ninplace);
373 fprintf(fp, "number of released allocations: %u\n", stats->nreleases);
374 fprintf(fp, " number of fast releases: %u\n", stats->nfastrels);
375 fprintf(fp, " total bytes allocated: %u\n", stats->nbytes);
376 fprintf(fp, " mean allocation size: %g\n", mean);
377 fprintf(fp, " standard deviation: %g\n", sqrt(variance));
378 fprintf(fp, " maximum allocation size: %u\n", stats->maxalloc);
379 }
380 }
381 #endif /* PL_ARENAMETER */