Coverage Report

Created: 2026-04-04 08:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/binutils-gdb/libiberty/objalloc.c
Line
Count
Source
1
/* objalloc.c -- routines to allocate memory for objects
2
   Copyright (C) 1997-2026 Free Software Foundation, Inc.
3
   Written by Ian Lance Taylor, Cygnus Solutions.
4
5
This program is free software; you can redistribute it and/or modify it
6
under the terms of the GNU General Public License as published by the
7
Free Software Foundation; either version 2, or (at your option) any
8
later version.
9
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, 51 Franklin Street - Fifth Floor,
18
Boston, MA 02110-1301, USA.  */
19
20
#include "config.h"
21
#include "ansidecl.h"
22
23
#include "objalloc.h"
24
25
/* Get a definition for NULL.  */
26
#include <stdio.h>
27
28
#if VMS
29
#include <stdlib.h>
30
#include <unixlib.h>
31
#else
32
33
/* Get a definition for size_t.  */
34
#include <stddef.h>
35
36
#ifdef HAVE_STDLIB_H
37
#include <stdlib.h>
38
#else
39
/* For systems with larger pointers than ints, this must be declared.  */
40
extern void *malloc (size_t);
41
extern void free (void *);
42
#endif
43
44
#endif
45
46
/* These routines allocate space for an object.  Freeing allocated
47
   space may or may not free all more recently allocated space.
48
49
   We handle large and small allocation requests differently.  If we
50
   don't have enough space in the current block, and the allocation
51
   request is for more than 512 bytes, we simply pass it through to
52
   malloc.  */
53
54
/* The objalloc structure is defined in objalloc.h.  */
55
56
/* This structure appears at the start of each chunk.  */
57
58
struct objalloc_chunk
59
{
60
  /* Next chunk.  */
61
  struct objalloc_chunk *next;
62
  /* If this chunk contains large objects, this is the value of
63
     current_ptr when this chunk was allocated.  If this chunk
64
     contains small objects, this is NULL.  */
65
  char *current_ptr;
66
};
67
68
/* The aligned size of objalloc_chunk.  */
69
70
#define CHUNK_HEADER_SIZE         \
71
87.1M
  ((sizeof (struct objalloc_chunk) + OBJALLOC_ALIGN - 1) \
72
87.1M
   &~ (OBJALLOC_ALIGN - 1))
73
74
/* We ask for this much memory each time we create a chunk which is to
75
   hold small objects.  */
76
77
170M
#define CHUNK_SIZE (4096 - 32)
78
79
/* A request for this amount or more is just passed through to malloc.  */
80
81
10.5M
#define BIG_REQUEST (512)
82
83
/* Create an objalloc structure.  */
84
85
struct objalloc *
86
objalloc_create (void)
87
26.4M
{
88
26.4M
  struct objalloc *ret;
89
26.4M
  struct objalloc_chunk *chunk;
90
91
26.4M
  ret = (struct objalloc *) malloc (sizeof *ret);
92
26.4M
  if (ret == NULL)
93
0
    return NULL;
94
95
26.4M
  ret->chunks = (void *) malloc (CHUNK_SIZE);
96
26.4M
  if (ret->chunks == NULL)
97
0
    {
98
0
      free (ret);
99
0
      return NULL;
100
0
    }
101
102
26.4M
  chunk = (struct objalloc_chunk *) ret->chunks;
103
26.4M
  chunk->next = NULL;
104
26.4M
  chunk->current_ptr = NULL;
105
106
26.4M
  ret->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE;
107
26.4M
  ret->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE;
108
109
26.4M
  return ret;
110
26.4M
}
111
112
/* Allocate space from an objalloc structure.  */
113
114
void *
115
_objalloc_alloc (struct objalloc *o, unsigned long original_len)
116
10.5M
{
117
10.5M
  unsigned long len = original_len;
118
119
  /* We avoid confusion from zero sized objects by always allocating
120
     at least 1 byte.  */
121
10.5M
  if (len == 0)
122
0
    len = 1;
123
124
10.5M
  len = (len + OBJALLOC_ALIGN - 1) &~ (OBJALLOC_ALIGN - 1);
125
126
  /* Check for overflow in the alignment operation above and the
127
     malloc argument below. */
128
10.5M
  if (len + CHUNK_HEADER_SIZE < original_len)
129
0
    return NULL;
130
131
10.5M
  if (len <= o->current_space)
132
0
    {
133
0
      o->current_ptr += len;
134
0
      o->current_space -= len;
135
0
      return (void *) (o->current_ptr - len);
136
0
    }
137
138
10.5M
  if (len >= BIG_REQUEST)
139
9.84M
    {
140
9.84M
      char *ret;
141
9.84M
      struct objalloc_chunk *chunk;
142
143
9.84M
      ret = (char *) malloc (CHUNK_HEADER_SIZE + len);
144
9.84M
      if (ret == NULL)
145
0
  return NULL;
146
147
9.84M
      chunk = (struct objalloc_chunk *) ret;
148
9.84M
      chunk->next = (struct objalloc_chunk *) o->chunks;
149
9.84M
      chunk->current_ptr = o->current_ptr;
150
151
9.84M
      o->chunks = (void *) chunk;
152
153
9.84M
      return (void *) (ret + CHUNK_HEADER_SIZE);
154
9.84M
    }
155
664k
  else
156
664k
    {
157
664k
      struct objalloc_chunk *chunk;
158
159
664k
      chunk = (struct objalloc_chunk *) malloc (CHUNK_SIZE);
160
664k
      if (chunk == NULL)
161
0
  return NULL;
162
664k
      chunk->next = (struct objalloc_chunk *) o->chunks;
163
664k
      chunk->current_ptr = NULL;
164
165
664k
      o->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE;
166
664k
      o->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE;
167
168
664k
      o->chunks = (void *) chunk;
169
170
664k
      return objalloc_alloc (o, len);
171
664k
    }
172
10.5M
}
173
174
/* Free an entire objalloc structure.  */
175
176
void
177
objalloc_free (struct objalloc *o)
178
34.9M
{
179
34.9M
  struct objalloc_chunk *l;
180
181
  /* Handle a nullptr as being a no-op. */
182
34.9M
  if (o == NULL)
183
8.50M
    return;
184
185
26.4M
  l = (struct objalloc_chunk *) o->chunks;
186
62.4M
  while (l != NULL)
187
36.0M
    {
188
36.0M
      struct objalloc_chunk *next;
189
190
36.0M
      next = l->next;
191
36.0M
      free (l);
192
36.0M
      l = next;
193
36.0M
    }
194
195
26.4M
  free (o);
196
26.4M
}
197
198
/* Free a block from an objalloc structure.  This also frees all more
199
   recently allocated blocks.  */
200
201
void
202
objalloc_free_block (struct objalloc *o, void *block)
203
57.9M
{
204
57.9M
  struct objalloc_chunk *p, *small;
205
57.9M
  char *b = (char *) block;
206
207
  /* First set P to the chunk which contains the block we are freeing,
208
     and set Q to the last small object chunk we see before P.  */
209
57.9M
  small = NULL;
210
60.9M
  for (p = (struct objalloc_chunk *) o->chunks; p != NULL; p = p->next)
211
60.9M
    {
212
60.9M
      if (p->current_ptr == NULL)
213
58.2M
  {
214
58.2M
    if (b > (char *) p && b < (char *) p + CHUNK_SIZE)
215
57.9M
      break;
216
226k
    small = p;
217
226k
  }
218
2.77M
      else
219
2.77M
  {
220
2.77M
    if (b == (char *) p + CHUNK_HEADER_SIZE)
221
9.87k
      break;
222
2.77M
  }
223
60.9M
    }
224
225
  /* If we can't find the chunk, the caller has made a mistake.  */
226
57.9M
  if (p == NULL)
227
0
    abort ();
228
229
57.9M
  if (p->current_ptr == NULL)
230
57.9M
    {
231
57.9M
      struct objalloc_chunk *q;
232
57.9M
      struct objalloc_chunk *first;
233
234
      /* The block is in a chunk containing small objects.  We can
235
   free every chunk through SMALL, because they have certainly
236
   been allocated more recently.  After SMALL, we will not see
237
   any chunks containing small objects; we can free any big
238
   chunk if the current_ptr is greater than or equal to B.  We
239
   can then reset the new current_ptr to B.  */
240
241
57.9M
      first = NULL;
242
57.9M
      q = (struct objalloc_chunk *) o->chunks;
243
60.9M
      while (q != p)
244
2.97M
  {
245
2.97M
    struct objalloc_chunk *next;
246
247
2.97M
    next = q->next;
248
2.97M
    if (small != NULL)
249
359k
      {
250
359k
        if (small == q)
251
74.1k
    small = NULL;
252
359k
        free (q);
253
359k
      }
254
2.61M
    else if (q->current_ptr > b)
255
502k
      free (q);
256
2.11M
    else if (first == NULL)
257
1.12M
      first = q;
258
259
2.97M
    q = next;
260
2.97M
  }
261
262
57.9M
      if (first == NULL)
263
56.8M
  first = p;
264
57.9M
      o->chunks = (void *) first;
265
266
      /* Now start allocating from this small block again.  */
267
57.9M
      o->current_ptr = b;
268
57.9M
      o->current_space = ((char *) p + CHUNK_SIZE) - b;
269
57.9M
    }
270
9.87k
  else
271
9.87k
    {
272
9.87k
      struct objalloc_chunk *q;
273
9.87k
      char *current_ptr;
274
275
      /* This block is in a large chunk by itself.  We can free
276
         everything on the list up to and including this block.  We
277
         then start allocating from the next chunk containing small
278
         objects, setting current_ptr from the value stored with the
279
         large chunk we are freeing.  */
280
281
9.87k
      current_ptr = p->current_ptr;
282
9.87k
      p = p->next;
283
284
9.87k
      q = (struct objalloc_chunk *) o->chunks;
285
36.5k
      while (q != p)
286
26.6k
  {
287
26.6k
    struct objalloc_chunk *next;
288
289
26.6k
    next = q->next;
290
26.6k
    free (q);
291
26.6k
    q = next;
292
26.6k
  }
293
294
9.87k
      o->chunks = (void *) p;
295
296
13.4k
      while (p->current_ptr != NULL)
297
3.62k
  p = p->next;
298
299
9.87k
      o->current_ptr = current_ptr;
300
9.87k
      o->current_space = ((char *) p + CHUNK_SIZE) - current_ptr;
301
9.87k
    }
302
57.9M
}