Coverage Report

Created: 2026-04-04 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nspr/lib/ds/plarena.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
/*
6
 * Lifetime-based fast allocation, inspired by much prior art, including
7
 * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
8
 * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
9
 */
10
#include <stdlib.h>
11
#include <string.h>
12
#include "plarena.h"
13
#include "prmem.h"
14
#include "prbit.h"
15
#include "prlog.h"
16
#include "prlock.h"
17
#include "prinit.h"
18
19
#ifdef PL_ARENAMETER
20
static PLArenaStats* arena_stats_list;
21
22
#  define COUNT(pool, what) (pool)->stats.what++
23
#else
24
#  define COUNT(pool, what) /* nothing */
25
#endif
26
27
0
#define PL_ARENA_DEFAULT_ALIGN sizeof(double)
28
29
PR_IMPLEMENT(void)
30
PL_InitArenaPool(PLArenaPool* pool, const char* name, PRUint32 size,
31
4.21M
                 PRUint32 align) {
32
  /*
33
   * Look-up table of PR_BITMASK(PR_CeilingLog2(align)) values for
34
   * align = 1 to 32.
35
   */
36
4.21M
  static const PRUint8 pmasks[33] = {
37
4.21M
      0, /*  not used */
38
4.21M
      0,  1,  3,  3,  7,  7,  7,  7,
39
4.21M
      15, 15, 15, 15, 15, 15, 15, 15, /*  1 ... 16 */
40
4.21M
      31, 31, 31, 31, 31, 31, 31, 31,
41
4.21M
      31, 31, 31, 31, 31, 31, 31, 31 /* 17 ... 32 */
42
4.21M
  };
43
44
4.21M
  if (align == 0) {
45
0
    align = PL_ARENA_DEFAULT_ALIGN;
46
0
  }
47
48
4.21M
  if (align < sizeof(pmasks) / sizeof(pmasks[0])) {
49
4.21M
    pool->mask = pmasks[align];
50
4.21M
  } else {
51
0
    pool->mask = PR_BITMASK(PR_CeilingLog2(align));
52
0
  }
53
54
4.21M
  pool->first.next = NULL;
55
  /* Set all three addresses in pool->first to the same dummy value.
56
   * These addresses are only compared with each other, but never
57
   * dereferenced. */
58
4.21M
  pool->first.base = pool->first.avail = pool->first.limit =
59
4.21M
      (PRUword)PL_ARENA_ALIGN(pool, &pool->first + 1);
60
4.21M
  pool->current = &pool->first;
61
  /*
62
   * Compute the net size so that each arena's gross size is |size|.
63
   * sizeof(PLArena) + pool->mask is the header and alignment slop
64
   * that PL_ArenaAllocate adds to the net size.
65
   */
66
4.21M
  if (size > sizeof(PLArena) + pool->mask) {
67
4.21M
    pool->arenasize = size - (sizeof(PLArena) + pool->mask);
68
4.21M
  } else {
69
0
    pool->arenasize = size;
70
0
  }
71
#ifdef PL_ARENAMETER
72
  memset(&pool->stats, 0, sizeof pool->stats);
73
  pool->stats.name = strdup(name);
74
  pool->stats.next = arena_stats_list;
75
  arena_stats_list = &pool->stats;
76
#endif
77
4.21M
}
78
79
/*
80
** PL_ArenaAllocate() -- allocate space from an arena pool
81
**
82
** Description: PL_ArenaAllocate() allocates space from an arena
83
** pool.
84
**
85
** First, try to satisfy the request from arenas starting at
86
** pool->current. Then try to allocate a new arena from the heap.
87
**
88
** Returns: pointer to allocated space or NULL
89
**
90
** Notes: The original implementation had some difficult to
91
** solve bugs; the code was difficult to read. Sometimes it's
92
** just easier to rewrite it. I did that. larryh.
93
**
94
** See also: bugzilla: 45343.
95
**
96
*/
97
98
6.49M
PR_IMPLEMENT(void*) PL_ArenaAllocate(PLArenaPool* pool, PRUint32 nb) {
99
6.49M
  PLArena* a;
100
6.49M
  char* rp; /* returned pointer */
101
6.49M
  PRUint32 nbOld;
102
103
6.49M
  PR_ASSERT((nb & pool->mask) == 0);
104
105
6.49M
  nbOld = nb;
106
6.49M
  nb = (PRUword)PL_ARENA_ALIGN(pool, nb); /* force alignment */
107
6.49M
  if (nb < nbOld) {
108
0
    return NULL;
109
0
  }
110
111
  /* attempt to allocate from arenas at pool->current */
112
6.49M
  {
113
6.49M
    a = pool->current;
114
6.49M
    do {
115
6.49M
      if (nb <= a->limit - a->avail) {
116
0
        pool->current = a;
117
0
        rp = (char*)a->avail;
118
0
        a->avail += nb;
119
0
        return rp;
120
0
      }
121
6.49M
    } while (NULL != (a = a->next));
122
6.49M
  }
123
124
  /* attempt to allocate from the heap */
125
6.49M
  {
126
6.49M
    PRUint32 sz = PR_MAX(pool->arenasize, nb);
127
6.49M
    if (PR_UINT32_MAX - sz < sizeof *a + pool->mask) {
128
0
      a = NULL;
129
6.49M
    } else {
130
6.49M
      sz += sizeof *a + pool->mask; /* header and alignment slop */
131
6.49M
      a = (PLArena*)PR_MALLOC(sz);
132
6.49M
    }
133
6.49M
    if (NULL != a) {
134
6.49M
      a->limit = (PRUword)a + sz;
135
6.49M
      a->base = a->avail = (PRUword)PL_ARENA_ALIGN(pool, a + 1);
136
6.49M
      PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
137
6.49M
      rp = (char*)a->avail;
138
6.49M
      a->avail += nb;
139
6.49M
      PR_ASSERT(a->avail <= a->limit);
140
      /* the newly allocated arena is linked after pool->current
141
       *  and becomes pool->current */
142
6.49M
      a->next = pool->current->next;
143
6.49M
      pool->current->next = a;
144
6.49M
      pool->current = a;
145
6.49M
      if (NULL == pool->first.next) {
146
0
        pool->first.next = a;
147
0
      }
148
6.49M
      PL_COUNT_ARENA(pool, ++);
149
6.49M
      COUNT(pool, nmallocs);
150
6.49M
      return (rp);
151
6.49M
    }
152
6.49M
  }
153
154
  /* we got to here, and there's no memory to allocate */
155
0
  return (NULL);
156
6.49M
} /* --- end PL_ArenaAllocate() --- */
157
158
PR_IMPLEMENT(void*)
159
248k
PL_ArenaGrow(PLArenaPool* pool, void* p, PRUint32 size, PRUint32 incr) {
160
248k
  void* newp;
161
162
248k
  if (PR_UINT32_MAX - size < incr) {
163
0
    return NULL;
164
0
  }
165
248k
  PL_ARENA_ALLOCATE(newp, pool, size + incr);
166
248k
  if (newp) {
167
248k
    memcpy(newp, p, size);
168
248k
  }
169
248k
  return newp;
170
248k
}
171
172
1.30M
PR_IMPLEMENT(void) PL_ClearArenaPool(PLArenaPool* pool, PRInt32 pattern) {
173
1.30M
  PLArena* a;
174
175
4.25M
  for (a = pool->first.next; a; a = a->next) {
176
2.95M
    PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
177
2.95M
    a->avail = a->base;
178
2.95M
    PL_CLEAR_UNUSED_PATTERN(a, pattern);
179
2.95M
    PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
180
2.95M
  }
181
1.30M
}
182
183
/*
184
 * Free tail arenas linked after head, which may not be the true list head.
185
 * Reset pool->current to point to head in case it pointed at a tail arena.
186
 */
187
4.79M
static void FreeArenaList(PLArenaPool* pool, PLArena* head) {
188
4.79M
  PLArena* a = head->next;
189
4.79M
  if (!a) {
190
1.53M
    return;
191
1.53M
  }
192
193
3.26M
  head->next = NULL;
194
195
6.49M
  do {
196
6.49M
    PLArena* tmp = a;
197
6.49M
    a = a->next;
198
6.49M
    PL_CLEAR_ARENA(tmp);
199
6.49M
    PL_COUNT_ARENA(pool, --);
200
6.49M
    PR_DELETE(tmp);
201
6.49M
  } while (a);
202
203
3.26M
  pool->current = head;
204
3.26M
}
205
206
587k
PR_IMPLEMENT(void) PL_ArenaRelease(PLArenaPool* pool, char* mark) {
207
587k
  PLArena* a;
208
209
411M
  for (a = &pool->first; a; a = a->next) {
210
411M
    if (PR_UPTRDIFF(mark, a->base) <= PR_UPTRDIFF(a->avail, a->base)) {
211
587k
      a->avail = (PRUword)PL_ARENA_ALIGN(pool, mark);
212
587k
      FreeArenaList(pool, a);
213
587k
      return;
214
587k
    }
215
411M
  }
216
587k
}
217
218
4.00M
PR_IMPLEMENT(void) PL_FreeArenaPool(PLArenaPool* pool) {
219
4.00M
  FreeArenaList(pool, &pool->first);
220
4.00M
  COUNT(pool, ndeallocs);
221
4.00M
}
222
223
205k
PR_IMPLEMENT(void) PL_FinishArenaPool(PLArenaPool* pool) {
224
205k
  FreeArenaList(pool, &pool->first);
225
#ifdef PL_ARENAMETER
226
  {
227
    PLArenaStats *stats, **statsp;
228
229
    if (pool->stats.name) {
230
      PR_DELETE(pool->stats.name);
231
    }
232
    for (statsp = &arena_stats_list; (stats = *statsp) != 0;
233
         statsp = &stats->next) {
234
      if (stats == &pool->stats) {
235
        *statsp = stats->next;
236
        return;
237
      }
238
    }
239
  }
240
#endif
241
205k
}
242
243
0
PR_IMPLEMENT(void) PL_CompactArenaPool(PLArenaPool* ap) {}
244
245
0
PR_IMPLEMENT(void) PL_ArenaFinish(void) {}
246
247
PR_IMPLEMENT(size_t)
248
PL_SizeOfArenaPoolExcludingPool(const PLArenaPool* pool,
249
0
                                PLMallocSizeFn mallocSizeOf) {
250
  /*
251
   * The first PLArena is within |pool|, so don't measure it.  Subsequent
252
   * PLArenas are separate and must be measured.
253
   */
254
0
  size_t size = 0;
255
0
  const PLArena* arena = pool->first.next;
256
0
  while (arena) {
257
0
    size += mallocSizeOf(arena);
258
0
    arena = arena->next;
259
0
  }
260
0
  return size;
261
0
}
262
263
#ifdef PL_ARENAMETER
264
PR_IMPLEMENT(void) PL_ArenaCountAllocation(PLArenaPool* pool, PRUint32 nb) {
265
  pool->stats.nallocs++;
266
  pool->stats.nbytes += nb;
267
  if (nb > pool->stats.maxalloc) {
268
    pool->stats.maxalloc = nb;
269
  }
270
  pool->stats.variance += nb * nb;
271
}
272
273
PR_IMPLEMENT(void)
274
PL_ArenaCountInplaceGrowth(PLArenaPool* pool, PRUint32 size, PRUint32 incr) {
275
  pool->stats.ninplace++;
276
}
277
278
PR_IMPLEMENT(void)
279
PL_ArenaCountGrowth(PLArenaPool* pool, PRUint32 size, PRUint32 incr) {
280
  pool->stats.ngrows++;
281
  pool->stats.nbytes += incr;
282
  pool->stats.variance -= size * size;
283
  size += incr;
284
  if (size > pool->stats.maxalloc) {
285
    pool->stats.maxalloc = size;
286
  }
287
  pool->stats.variance += size * size;
288
}
289
290
PR_IMPLEMENT(void) PL_ArenaCountRelease(PLArenaPool* pool, char* mark) {
291
  pool->stats.nreleases++;
292
}
293
294
PR_IMPLEMENT(void) PL_ArenaCountRetract(PLArenaPool* pool, char* mark) {
295
  pool->stats.nfastrels++;
296
}
297
298
#  include <math.h>
299
#  include <stdio.h>
300
301
PR_IMPLEMENT(void) PL_DumpArenaStats(FILE* fp) {
302
  PLArenaStats* stats;
303
  double mean, variance;
304
305
  for (stats = arena_stats_list; stats; stats = stats->next) {
306
    if (stats->nallocs != 0) {
307
      mean = (double)stats->nbytes / stats->nallocs;
308
      variance = fabs(stats->variance / stats->nallocs - mean * mean);
309
    } else {
310
      mean = variance = 0;
311
    }
312
313
    fprintf(fp, "\n%s allocation statistics:\n", stats->name);
314
    fprintf(fp, "              number of arenas: %u\n", stats->narenas);
315
    fprintf(fp, "         number of allocations: %u\n", stats->nallocs);
316
    fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims);
317
    fprintf(fp, "        number of malloc calls: %u\n", stats->nmallocs);
318
    fprintf(fp, "       number of deallocations: %u\n", stats->ndeallocs);
319
    fprintf(fp, "  number of allocation growths: %u\n", stats->ngrows);
320
    fprintf(fp, "    number of in-place growths: %u\n", stats->ninplace);
321
    fprintf(fp, "number of released allocations: %u\n", stats->nreleases);
322
    fprintf(fp, "       number of fast releases: %u\n", stats->nfastrels);
323
    fprintf(fp, "         total bytes allocated: %u\n", stats->nbytes);
324
    fprintf(fp, "          mean allocation size: %g\n", mean);
325
    fprintf(fp, "            standard deviation: %g\n", sqrt(variance));
326
    fprintf(fp, "       maximum allocation size: %u\n", stats->maxalloc);
327
  }
328
}
329
#endif /* PL_ARENAMETER */