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