Coverage Report

Created: 2026-10-06 06:17

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
5.46M
                 PRUint32 align) {
32
  /*
33
   * Look-up table of PR_BITMASK(PR_CeilingLog2(align)) values for
34
   * align = 1 to 32.
35
   */
36
5.46M
  static const PRUint8 pmasks[33] = {
37
5.46M
      0, /*  not used */
38
5.46M
      0,  1,  3,  3,  7,  7,  7,  7,
39
5.46M
      15, 15, 15, 15, 15, 15, 15, 15, /*  1 ... 16 */
40
5.46M
      31, 31, 31, 31, 31, 31, 31, 31,
41
5.46M
      31, 31, 31, 31, 31, 31, 31, 31 /* 17 ... 32 */
42
5.46M
  };
43
44
5.46M
  if (align == 0) {
45
0
    align = PL_ARENA_DEFAULT_ALIGN;
46
0
  }
47
48
5.46M
  if (align < sizeof(pmasks) / sizeof(pmasks[0])) {
49
5.46M
    pool->mask = pmasks[align];
50
5.46M
  } else {
51
0
    pool->mask = PR_BITMASK(PR_CeilingLog2(align));
52
0
  }
53
54
5.46M
  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
5.46M
  pool->first.base = pool->first.avail = pool->first.limit =
59
5.46M
      (PRUword)PL_ARENA_ALIGN(pool, &pool->first + 1);
60
5.46M
  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
5.46M
  if (size > sizeof(PLArena) + pool->mask) {
67
5.46M
    pool->arenasize = size - (sizeof(PLArena) + pool->mask);
68
5.46M
  } 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
5.46M
}
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
8.06M
PR_IMPLEMENT(void*) PL_ArenaAllocate(PLArenaPool* pool, PRUint32 nb) {
99
8.06M
  PLArena* a;
100
8.06M
  char* rp; /* returned pointer */
101
8.06M
  PRUint32 nbOld;
102
103
8.06M
  PR_ASSERT((nb & pool->mask) == 0);
104
105
8.06M
  nbOld = nb;
106
8.06M
  nb = (PRUword)PL_ARENA_ALIGN(pool, nb); /* force alignment */
107
8.06M
  if (nb < nbOld) {
108
0
    return NULL;
109
0
  }
110
111
  /* attempt to allocate from arenas at pool->current */
112
8.06M
  {
113
8.06M
    a = pool->current;
114
8.06M
    do {
115
8.06M
      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
8.06M
    } while (NULL != (a = a->next));
122
8.06M
  }
123
124
  /* attempt to allocate from the heap */
125
8.06M
  {
126
8.06M
    PRUint32 sz = PR_MAX(pool->arenasize, nb);
127
8.06M
    if (PR_UINT32_MAX - sz < sizeof *a + pool->mask) {
128
0
      a = NULL;
129
8.06M
    } else {
130
8.06M
      sz += sizeof *a + pool->mask; /* header and alignment slop */
131
8.06M
      a = (PLArena*)PR_MALLOC(sz);
132
8.06M
    }
133
8.06M
    if (NULL != a) {
134
8.06M
      a->limit = (PRUword)a + sz;
135
8.06M
      a->base = a->avail = (PRUword)PL_ARENA_ALIGN(pool, a + 1);
136
8.06M
      PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
137
8.06M
      rp = (char*)a->avail;
138
8.06M
      a->avail += nb;
139
8.06M
      PR_ASSERT(a->avail <= a->limit);
140
      /* the newly allocated arena is linked after pool->current
141
       *  and becomes pool->current */
142
8.06M
      a->next = pool->current->next;
143
8.06M
      pool->current->next = a;
144
8.06M
      pool->current = a;
145
8.06M
      if (NULL == pool->first.next) {
146
0
        pool->first.next = a;
147
0
      }
148
8.06M
      PL_COUNT_ARENA(pool, ++);
149
8.06M
      COUNT(pool, nmallocs);
150
8.06M
      return (rp);
151
8.06M
    }
152
8.06M
  }
153
154
  /* we got to here, and there's no memory to allocate */
155
0
  return (NULL);
156
8.06M
} /* --- end PL_ArenaAllocate() --- */
157
158
PR_IMPLEMENT(void*)
159
363k
PL_ArenaGrow(PLArenaPool* pool, void* p, PRUint32 size, PRUint32 incr) {
160
363k
  void* newp;
161
162
363k
  if (PR_UINT32_MAX - size < incr) {
163
0
    return NULL;
164
0
  }
165
363k
  PL_ARENA_ALLOCATE(newp, pool, size + incr);
166
363k
  if (newp) {
167
363k
    memcpy(newp, p, size);
168
363k
    PL_MAKE_MEM_NOACCESS(p, size);
169
363k
  }
170
363k
  return newp;
171
363k
}
172
173
1.24M
PR_IMPLEMENT(void) PL_ClearArenaPool(PLArenaPool* pool, PRInt32 pattern) {
174
1.24M
  PLArena* a;
175
176
4.90M
  for (a = pool->first.next; a; a = a->next) {
177
3.66M
    PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
178
3.66M
    a->avail = a->base;
179
3.66M
    PL_CLEAR_UNUSED_PATTERN(a, pattern);
180
3.66M
    PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
181
3.66M
  }
182
1.24M
}
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
5.64M
static void FreeArenaList(PLArenaPool* pool, PLArena* head) {
189
5.64M
  PLArena* a = head->next;
190
5.64M
  if (!a) {
191
2.33M
    return;
192
2.33M
  }
193
194
3.30M
  head->next = NULL;
195
196
8.06M
  do {
197
8.06M
    PLArena* tmp = a;
198
8.06M
    a = a->next;
199
8.06M
    PL_CLEAR_ARENA(tmp);
200
8.06M
    PL_COUNT_ARENA(pool, --);
201
8.06M
    PR_DELETE(tmp);
202
8.06M
  } while (a);
203
204
3.30M
  pool->current = head;
205
3.30M
}
206
207
179k
PR_IMPLEMENT(void) PL_ArenaRelease(PLArenaPool* pool, char* mark) {
208
179k
  PLArena* a;
209
210
135M
  for (a = &pool->first; a; a = a->next) {
211
135M
    if (PR_UPTRDIFF(mark, a->base) <= PR_UPTRDIFF(a->avail, a->base)) {
212
179k
      a->avail = (PRUword)PL_ARENA_ALIGN(pool, mark);
213
179k
      PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
214
179k
      FreeArenaList(pool, a);
215
179k
      return;
216
179k
    }
217
135M
  }
218
179k
}
219
220
5.23M
PR_IMPLEMENT(void) PL_FreeArenaPool(PLArenaPool* pool) {
221
5.23M
  FreeArenaList(pool, &pool->first);
222
5.23M
  COUNT(pool, ndeallocs);
223
5.23M
}
224
225
225k
PR_IMPLEMENT(void) PL_FinishArenaPool(PLArenaPool* pool) {
226
225k
  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
225k
}
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 */