Coverage Report

Created: 2026-05-19 06:33

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.17M
                 PRUint32 align) {
32
  /*
33
   * Look-up table of PR_BITMASK(PR_CeilingLog2(align)) values for
34
   * align = 1 to 32.
35
   */
36
4.17M
  static const PRUint8 pmasks[33] = {
37
4.17M
      0, /*  not used */
38
4.17M
      0,  1,  3,  3,  7,  7,  7,  7,
39
4.17M
      15, 15, 15, 15, 15, 15, 15, 15, /*  1 ... 16 */
40
4.17M
      31, 31, 31, 31, 31, 31, 31, 31,
41
4.17M
      31, 31, 31, 31, 31, 31, 31, 31 /* 17 ... 32 */
42
4.17M
  };
43
44
4.17M
  if (align == 0) {
45
0
    align = PL_ARENA_DEFAULT_ALIGN;
46
0
  }
47
48
4.17M
  if (align < sizeof(pmasks) / sizeof(pmasks[0])) {
49
4.17M
    pool->mask = pmasks[align];
50
4.17M
  } else {
51
0
    pool->mask = PR_BITMASK(PR_CeilingLog2(align));
52
0
  }
53
54
4.17M
  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.17M
  pool->first.base = pool->first.avail = pool->first.limit =
59
4.17M
      (PRUword)PL_ARENA_ALIGN(pool, &pool->first + 1);
60
4.17M
  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.17M
  if (size > sizeof(PLArena) + pool->mask) {
67
4.17M
    pool->arenasize = size - (sizeof(PLArena) + pool->mask);
68
4.17M
  } 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.17M
}
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.48M
PR_IMPLEMENT(void*) PL_ArenaAllocate(PLArenaPool* pool, PRUint32 nb) {
99
6.48M
  PLArena* a;
100
6.48M
  char* rp; /* returned pointer */
101
6.48M
  PRUint32 nbOld;
102
103
6.48M
  PR_ASSERT((nb & pool->mask) == 0);
104
105
6.48M
  nbOld = nb;
106
6.48M
  nb = (PRUword)PL_ARENA_ALIGN(pool, nb); /* force alignment */
107
6.48M
  if (nb < nbOld) {
108
0
    return NULL;
109
0
  }
110
111
  /* attempt to allocate from arenas at pool->current */
112
6.48M
  {
113
6.48M
    a = pool->current;
114
6.48M
    do {
115
6.48M
      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.48M
    } while (NULL != (a = a->next));
122
6.48M
  }
123
124
  /* attempt to allocate from the heap */
125
6.48M
  {
126
6.48M
    PRUint32 sz = PR_MAX(pool->arenasize, nb);
127
6.48M
    if (PR_UINT32_MAX - sz < sizeof *a + pool->mask) {
128
0
      a = NULL;
129
6.48M
    } else {
130
6.48M
      sz += sizeof *a + pool->mask; /* header and alignment slop */
131
6.48M
      a = (PLArena*)PR_MALLOC(sz);
132
6.48M
    }
133
6.48M
    if (NULL != a) {
134
6.48M
      a->limit = (PRUword)a + sz;
135
6.48M
      a->base = a->avail = (PRUword)PL_ARENA_ALIGN(pool, a + 1);
136
6.48M
      PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
137
6.48M
      rp = (char*)a->avail;
138
6.48M
      a->avail += nb;
139
6.48M
      PR_ASSERT(a->avail <= a->limit);
140
      /* the newly allocated arena is linked after pool->current
141
       *  and becomes pool->current */
142
6.48M
      a->next = pool->current->next;
143
6.48M
      pool->current->next = a;
144
6.48M
      pool->current = a;
145
6.48M
      if (NULL == pool->first.next) {
146
0
        pool->first.next = a;
147
0
      }
148
6.48M
      PL_COUNT_ARENA(pool, ++);
149
6.48M
      COUNT(pool, nmallocs);
150
6.48M
      return (rp);
151
6.48M
    }
152
6.48M
  }
153
154
  /* we got to here, and there's no memory to allocate */
155
0
  return (NULL);
156
6.48M
} /* --- end PL_ArenaAllocate() --- */
157
158
PR_IMPLEMENT(void*)
159
252k
PL_ArenaGrow(PLArenaPool* pool, void* p, PRUint32 size, PRUint32 incr) {
160
252k
  void* newp;
161
162
252k
  if (PR_UINT32_MAX - size < incr) {
163
0
    return NULL;
164
0
  }
165
252k
  PL_ARENA_ALLOCATE(newp, pool, size + incr);
166
252k
  if (newp) {
167
252k
    memcpy(newp, p, size);
168
252k
    PL_MAKE_MEM_NOACCESS(p, size);
169
252k
  }
170
252k
  return newp;
171
252k
}
172
173
1.30M
PR_IMPLEMENT(void) PL_ClearArenaPool(PLArenaPool* pool, PRInt32 pattern) {
174
1.30M
  PLArena* a;
175
176
4.28M
  for (a = pool->first.next; a; a = a->next) {
177
2.98M
    PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
178
2.98M
    a->avail = a->base;
179
2.98M
    PL_CLEAR_UNUSED_PATTERN(a, pattern);
180
2.98M
    PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
181
2.98M
  }
182
1.30M
}
183
184
/*
185
 * Free tail arenas linked after head, which may not be the true list head.
186
 * Reset pool->current to point to head in case it pointed at a tail arena.
187
 */
188
4.75M
static void FreeArenaList(PLArenaPool* pool, PLArena* head) {
189
4.75M
  PLArena* a = head->next;
190
4.75M
  if (!a) {
191
1.51M
    return;
192
1.51M
  }
193
194
3.23M
  head->next = NULL;
195
196
6.48M
  do {
197
6.48M
    PLArena* tmp = a;
198
6.48M
    a = a->next;
199
6.48M
    PL_CLEAR_ARENA(tmp);
200
6.48M
    PL_COUNT_ARENA(pool, --);
201
6.48M
    PR_DELETE(tmp);
202
6.48M
  } while (a);
203
204
3.23M
  pool->current = head;
205
3.23M
}
206
207
580k
PR_IMPLEMENT(void) PL_ArenaRelease(PLArenaPool* pool, char* mark) {
208
580k
  PLArena* a;
209
210
412M
  for (a = &pool->first; a; a = a->next) {
211
412M
    if (PR_UPTRDIFF(mark, a->base) <= PR_UPTRDIFF(a->avail, a->base)) {
212
580k
      a->avail = (PRUword)PL_ARENA_ALIGN(pool, mark);
213
580k
      PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
214
580k
      FreeArenaList(pool, a);
215
580k
      return;
216
580k
    }
217
412M
  }
218
580k
}
219
220
3.97M
PR_IMPLEMENT(void) PL_FreeArenaPool(PLArenaPool* pool) {
221
3.97M
  FreeArenaList(pool, &pool->first);
222
3.97M
  COUNT(pool, ndeallocs);
223
3.97M
}
224
225
203k
PR_IMPLEMENT(void) PL_FinishArenaPool(PLArenaPool* pool) {
226
203k
  FreeArenaList(pool, &pool->first);
227
#ifdef PL_ARENAMETER
228
  {
229
    PLArenaStats *stats, **statsp;
230
231
    if (pool->stats.name) {
232
      PR_DELETE(pool->stats.name);
233
    }
234
    for (statsp = &arena_stats_list; (stats = *statsp) != 0;
235
         statsp = &stats->next) {
236
      if (stats == &pool->stats) {
237
        *statsp = stats->next;
238
        return;
239
      }
240
    }
241
  }
242
#endif
243
203k
}
244
245
0
PR_IMPLEMENT(void) PL_CompactArenaPool(PLArenaPool* ap) {}
246
247
0
PR_IMPLEMENT(void) PL_ArenaFinish(void) {}
248
249
PR_IMPLEMENT(size_t)
250
PL_SizeOfArenaPoolExcludingPool(const PLArenaPool* pool,
251
0
                                PLMallocSizeFn mallocSizeOf) {
252
  /*
253
   * The first PLArena is within |pool|, so don't measure it.  Subsequent
254
   * PLArenas are separate and must be measured.
255
   */
256
0
  size_t size = 0;
257
0
  const PLArena* arena = pool->first.next;
258
0
  while (arena) {
259
0
    size += mallocSizeOf(arena);
260
0
    arena = arena->next;
261
0
  }
262
0
  return size;
263
0
}
264
265
#ifdef PL_ARENAMETER
266
PR_IMPLEMENT(void) PL_ArenaCountAllocation(PLArenaPool* pool, PRUint32 nb) {
267
  pool->stats.nallocs++;
268
  pool->stats.nbytes += nb;
269
  if (nb > pool->stats.maxalloc) {
270
    pool->stats.maxalloc = nb;
271
  }
272
  pool->stats.variance += nb * nb;
273
}
274
275
PR_IMPLEMENT(void)
276
PL_ArenaCountInplaceGrowth(PLArenaPool* pool, PRUint32 size, PRUint32 incr) {
277
  pool->stats.ninplace++;
278
}
279
280
PR_IMPLEMENT(void)
281
PL_ArenaCountGrowth(PLArenaPool* pool, PRUint32 size, PRUint32 incr) {
282
  pool->stats.ngrows++;
283
  pool->stats.nbytes += incr;
284
  pool->stats.variance -= size * size;
285
  size += incr;
286
  if (size > pool->stats.maxalloc) {
287
    pool->stats.maxalloc = size;
288
  }
289
  pool->stats.variance += size * size;
290
}
291
292
PR_IMPLEMENT(void) PL_ArenaCountRelease(PLArenaPool* pool, char* mark) {
293
  pool->stats.nreleases++;
294
}
295
296
PR_IMPLEMENT(void) PL_ArenaCountRetract(PLArenaPool* pool, char* mark) {
297
  pool->stats.nfastrels++;
298
}
299
300
#  include <math.h>
301
#  include <stdio.h>
302
303
PR_IMPLEMENT(void) PL_DumpArenaStats(FILE* fp) {
304
  PLArenaStats* stats;
305
  double mean, variance;
306
307
  for (stats = arena_stats_list; stats; stats = stats->next) {
308
    if (stats->nallocs != 0) {
309
      mean = (double)stats->nbytes / stats->nallocs;
310
      variance = fabs(stats->variance / stats->nallocs - mean * mean);
311
    } else {
312
      mean = variance = 0;
313
    }
314
315
    fprintf(fp, "\n%s allocation statistics:\n", stats->name);
316
    fprintf(fp, "              number of arenas: %u\n", stats->narenas);
317
    fprintf(fp, "         number of allocations: %u\n", stats->nallocs);
318
    fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims);
319
    fprintf(fp, "        number of malloc calls: %u\n", stats->nmallocs);
320
    fprintf(fp, "       number of deallocations: %u\n", stats->ndeallocs);
321
    fprintf(fp, "  number of allocation growths: %u\n", stats->ngrows);
322
    fprintf(fp, "    number of in-place growths: %u\n", stats->ninplace);
323
    fprintf(fp, "number of released allocations: %u\n", stats->nreleases);
324
    fprintf(fp, "       number of fast releases: %u\n", stats->nfastrels);
325
    fprintf(fp, "         total bytes allocated: %u\n", stats->nbytes);
326
    fprintf(fp, "          mean allocation size: %g\n", mean);
327
    fprintf(fp, "            standard deviation: %g\n", sqrt(variance));
328
    fprintf(fp, "       maximum allocation size: %u\n", stats->maxalloc);
329
  }
330
}
331
#endif /* PL_ARENAMETER */