/src/binutils-gdb/libiberty/objalloc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* objalloc.c -- routines to allocate memory for objects |
2 | | Copyright (C) 1997-2025 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 | 321M | ((sizeof (struct objalloc_chunk) + OBJALLOC_ALIGN - 1) \ |
72 | 321M | &~ (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 | 2.22G | #define CHUNK_SIZE (4096 - 32) |
78 | | |
79 | | /* A request for this amount or more is just passed through to malloc. */ |
80 | | |
81 | 81.0M | #define BIG_REQUEST (512) |
82 | | |
83 | | /* Create an objalloc structure. */ |
84 | | |
85 | | struct objalloc * |
86 | | objalloc_create (void) |
87 | 13.4M | { |
88 | 13.4M | struct objalloc *ret; |
89 | 13.4M | struct objalloc_chunk *chunk; |
90 | | |
91 | 13.4M | ret = (struct objalloc *) malloc (sizeof *ret); |
92 | 13.4M | if (ret == NULL) |
93 | 0 | return NULL; |
94 | | |
95 | 13.4M | ret->chunks = (void *) malloc (CHUNK_SIZE); |
96 | 13.4M | if (ret->chunks == NULL) |
97 | 0 | { |
98 | 0 | free (ret); |
99 | 0 | return NULL; |
100 | 0 | } |
101 | | |
102 | 13.4M | chunk = (struct objalloc_chunk *) ret->chunks; |
103 | 13.4M | chunk->next = NULL; |
104 | 13.4M | chunk->current_ptr = NULL; |
105 | | |
106 | 13.4M | ret->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE; |
107 | 13.4M | ret->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE; |
108 | | |
109 | 13.4M | return ret; |
110 | 13.4M | } |
111 | | |
112 | | /* Allocate space from an objalloc structure. */ |
113 | | |
114 | | void * |
115 | | _objalloc_alloc (struct objalloc *o, unsigned long original_len) |
116 | 81.0M | { |
117 | 81.0M | unsigned long len = original_len; |
118 | | |
119 | | /* We avoid confusion from zero sized objects by always allocating |
120 | | at least 1 byte. */ |
121 | 81.0M | if (len == 0) |
122 | 0 | len = 1; |
123 | | |
124 | 81.0M | 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 | 81.0M | if (len + CHUNK_HEADER_SIZE < original_len) |
129 | 0 | return NULL; |
130 | | |
131 | 81.0M | 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 | 81.0M | if (len >= BIG_REQUEST) |
139 | 49.9M | { |
140 | 49.9M | char *ret; |
141 | 49.9M | struct objalloc_chunk *chunk; |
142 | | |
143 | 49.9M | ret = (char *) malloc (CHUNK_HEADER_SIZE + len); |
144 | 49.9M | if (ret == NULL) |
145 | 0 | return NULL; |
146 | | |
147 | 49.9M | chunk = (struct objalloc_chunk *) ret; |
148 | 49.9M | chunk->next = (struct objalloc_chunk *) o->chunks; |
149 | 49.9M | chunk->current_ptr = o->current_ptr; |
150 | | |
151 | 49.9M | o->chunks = (void *) chunk; |
152 | | |
153 | 49.9M | return (void *) (ret + CHUNK_HEADER_SIZE); |
154 | 49.9M | } |
155 | 31.1M | else |
156 | 31.1M | { |
157 | 31.1M | struct objalloc_chunk *chunk; |
158 | | |
159 | 31.1M | chunk = (struct objalloc_chunk *) malloc (CHUNK_SIZE); |
160 | 31.1M | if (chunk == NULL) |
161 | 0 | return NULL; |
162 | 31.1M | chunk->next = (struct objalloc_chunk *) o->chunks; |
163 | 31.1M | chunk->current_ptr = NULL; |
164 | | |
165 | 31.1M | o->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE; |
166 | 31.1M | o->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE; |
167 | | |
168 | 31.1M | o->chunks = (void *) chunk; |
169 | | |
170 | 31.1M | return objalloc_alloc (o, len); |
171 | 31.1M | } |
172 | 81.0M | } |
173 | | |
174 | | /* Free an entire objalloc structure. */ |
175 | | |
176 | | void |
177 | | objalloc_free (struct objalloc *o) |
178 | 13.4M | { |
179 | 13.4M | struct objalloc_chunk *l; |
180 | | |
181 | 13.4M | l = (struct objalloc_chunk *) o->chunks; |
182 | 79.0M | while (l != NULL) |
183 | 65.5M | { |
184 | 65.5M | struct objalloc_chunk *next; |
185 | | |
186 | 65.5M | next = l->next; |
187 | 65.5M | free (l); |
188 | 65.5M | l = next; |
189 | 65.5M | } |
190 | | |
191 | 13.4M | free (o); |
192 | 13.4M | } |
193 | | |
194 | | /* Free a block from an objalloc structure. This also frees all more |
195 | | recently allocated blocks. */ |
196 | | |
197 | | void |
198 | | objalloc_free_block (struct objalloc *o, void *block) |
199 | 1.06G | { |
200 | 1.06G | struct objalloc_chunk *p, *small; |
201 | 1.06G | char *b = (char *) block; |
202 | | |
203 | | /* First set P to the chunk which contains the block we are freeing, |
204 | | and set Q to the last small object chunk we see before P. */ |
205 | 1.06G | small = NULL; |
206 | 1.12G | for (p = (struct objalloc_chunk *) o->chunks; p != NULL; p = p->next) |
207 | 1.12G | { |
208 | 1.12G | if (p->current_ptr == NULL) |
209 | 1.07G | { |
210 | 1.07G | if (b > (char *) p && b < (char *) p + CHUNK_SIZE) |
211 | 1.06G | break; |
212 | 6.07M | small = p; |
213 | 6.07M | } |
214 | 50.8M | else |
215 | 50.8M | { |
216 | 50.8M | if (b == (char *) p + CHUNK_HEADER_SIZE) |
217 | 55.5k | break; |
218 | 50.8M | } |
219 | 1.12G | } |
220 | | |
221 | | /* If we can't find the chunk, the caller has made a mistake. */ |
222 | 1.06G | if (p == NULL) |
223 | 0 | abort (); |
224 | | |
225 | 1.06G | if (p->current_ptr == NULL) |
226 | 1.06G | { |
227 | 1.06G | struct objalloc_chunk *q; |
228 | 1.06G | struct objalloc_chunk *first; |
229 | | |
230 | | /* The block is in a chunk containing small objects. We can |
231 | | free every chunk through SMALL, because they have certainly |
232 | | been allocated more recently. After SMALL, we will not see |
233 | | any chunks containing small objects; we can free any big |
234 | | chunk if the current_ptr is greater than or equal to B. We |
235 | | can then reset the new current_ptr to B. */ |
236 | | |
237 | 1.06G | first = NULL; |
238 | 1.06G | q = (struct objalloc_chunk *) o->chunks; |
239 | 1.12G | while (q != p) |
240 | 56.7M | { |
241 | 56.7M | struct objalloc_chunk *next; |
242 | | |
243 | 56.7M | next = q->next; |
244 | 56.7M | if (small != NULL) |
245 | 12.2M | { |
246 | 12.2M | if (small == q) |
247 | 1.13M | small = NULL; |
248 | 12.2M | free (q); |
249 | 12.2M | } |
250 | 44.5M | else if (q->current_ptr > b) |
251 | 16.6M | free (q); |
252 | 27.9M | else if (first == NULL) |
253 | 20.4M | first = q; |
254 | | |
255 | 56.7M | q = next; |
256 | 56.7M | } |
257 | | |
258 | 1.06G | if (first == NULL) |
259 | 1.04G | first = p; |
260 | 1.06G | o->chunks = (void *) first; |
261 | | |
262 | | /* Now start allocating from this small block again. */ |
263 | 1.06G | o->current_ptr = b; |
264 | 1.06G | o->current_space = ((char *) p + CHUNK_SIZE) - b; |
265 | 1.06G | } |
266 | 55.5k | else |
267 | 55.5k | { |
268 | 55.5k | struct objalloc_chunk *q; |
269 | 55.5k | char *current_ptr; |
270 | | |
271 | | /* This block is in a large chunk by itself. We can free |
272 | | everything on the list up to and including this block. We |
273 | | then start allocating from the next chunk containing small |
274 | | objects, setting current_ptr from the value stored with the |
275 | | large chunk we are freeing. */ |
276 | | |
277 | 55.5k | current_ptr = p->current_ptr; |
278 | 55.5k | p = p->next; |
279 | | |
280 | 55.5k | q = (struct objalloc_chunk *) o->chunks; |
281 | 240k | while (q != p) |
282 | 184k | { |
283 | 184k | struct objalloc_chunk *next; |
284 | | |
285 | 184k | next = q->next; |
286 | 184k | free (q); |
287 | 184k | q = next; |
288 | 184k | } |
289 | | |
290 | 55.5k | o->chunks = (void *) p; |
291 | | |
292 | 100k | while (p->current_ptr != NULL) |
293 | 45.0k | p = p->next; |
294 | | |
295 | 55.5k | o->current_ptr = current_ptr; |
296 | 55.5k | o->current_space = ((char *) p + CHUNK_SIZE) - current_ptr; |
297 | 55.5k | } |
298 | 1.06G | } |