386BSD 0.1 development
[unix-history] / usr / othersrc / public / ghostscript-2.4.1 / ialloc.c
CommitLineData
9f298f4f
WJ
1/* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved.
2 Distributed by Free Software Foundation, Inc.
3
4This file is part of Ghostscript.
5
6Ghostscript is distributed in the hope that it will be useful, but
7WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
8to anyone for the consequences of using it or for whether it serves any
9particular purpose or works at all, unless he says so in writing. Refer
10to the Ghostscript General Public License for full details.
11
12Everyone is granted permission to copy, modify and redistribute
13Ghostscript, but only under the conditions described in the Ghostscript
14General Public License. A copy of this license is supposed to have been
15given to you along with Ghostscript so you can know your rights and
16responsibilities. It should be in a file named COPYING. Among other
17things, the copyright notice and this notice must be preserved on all
18copies. */
19
20/* ialloc.c */
21/* Memory allocator for Ghostscript interpreter */
22#include <stdio.h> /* for NULL */
23#include "gs.h"
24#include "memory_.h"
25#include "alloc.h"
26#include "astate.h"
27
28#ifdef DEBUG
29extern char gs_debug[128];
30#endif
31
32/* Forward references */
33private int alloc_add_chunk(P2(alloc_state_ptr, uint));
34private char *alloc_large(P3(alloc_state_ptr, uint, const char *));
35private void alloc_free_large(P3(char *, uint, const char *));
36
37/* The only allocator instance (for now). */
38private alloc_state as_current;
39alloc_state_ptr alloc_state_current = &as_current;
40
41/* Debugging printout */
42#ifdef DEBUG
43# define alloc_print(rtag, tag, blk, sz)\
44 if ( gs_debug['A'] )\
45 fprintf(gs_debug_out, "[a:%c:%c:%s] %lx(%u)\n", rtag, tag,\
46 client_name, (ulong)blk, sz)
47# define alloc_print_large(rtag, tag, blk, sz)\
48 if ( gs_debug['A'] | gs_debug['a'] )\
49 fprintf(gs_debug_out, "[a:%c:%c:%s] %lx(%u)\n", rtag, tag,\
50 client_name, (ulong)blk, sz)
51#else
52# define alloc_print(rtag, tag, blk, sz)
53# define alloc_print_large(rtag, tag, blk, sz)
54#endif
55
56/* ------ Initialize/status ------ */
57
58/* Initialize the allocator */
59void
60alloc_init(proc_alloc_t palloc, proc_free_t pfree, uint chunk_size)
61{ register alloc_state_ptr ap = &as_current;
62 memset(ap, 0, sizeof(alloc_state)); /* do it all at once */
63 ap->chunk_size = chunk_size;
64 ap->big_size = chunk_size / 4;
65 ap->palloc = palloc;
66 ap->pfree = pfree;
67 ap->last_freed = 0;
68 { extern void alloc_save_init(P0());
69 alloc_save_init();
70 }
71}
72
73/* Return the status of the allocator: space used, total space. */
74void
75alloc_status(long *pused, long *ptotal)
76{ register alloc_state_ptr ap = &as_current;
77 *pused = (ap->cbot - ap->cbase) + (ap->climit - ap->ctop) + ap->used;
78 *ptotal = ap->total;
79}
80
81/* ------ Allocation and freeing ------ */
82
83/* Allocate an object. Return 0 if not enough room. */
84char *
85alloc(uint num_elts, uint elt_size, const char *client_name)
86{ register alloc_state_ptr ap = &as_current;
87 uint size = num_elts * elt_size;
88 uint block_size;
89 uint left;
90 if ( size >= ap->big_size )
91 { /* Large object, do a separate malloc. */
92 char *block = alloc_large(ap, size, client_name);
93 if ( block != NULL ) return block;
94 if ( size > ap->chunk_size )
95 return 0; /* can't alloc */
96 }
97 block_size = align_round(size);
98 if ( block_size <= max_chain_size )
99 { /* See if we can use a freed block. */
100 char **fptr = &ap->free[block_size >> log2_align_mod];
101 char *block = *fptr;
102 if ( block != 0 )
103 { *fptr = *(char **)block;
104 alloc_print('+', '#', block, size);
105 return block;
106 }
107 }
108 left = ap->ctop - ap->cbot;
109 if ( block_size > left )
110 { uint csize = ap->chunk_size;
111 while ( !alloc_add_chunk(ap, csize) )
112 { alloc_print('+', '?', (ulong)0, size);
113 /* Things are desperate, but perhaps not hopeless. */
114 if ( (csize >>= 1) < block_size )
115 return 0; /* no hope */
116 }
117 }
118 if ( elt_size == 1 )
119 { /* Unaligned block */
120 ap->ctop -= size;
121 alloc_print('+', '>', ap->ctop, size);
122 return (char *)ap->ctop;
123 }
124 else
125 { /* Aligned block */
126 char *block = (char *)ap->cbot;
127 ap->cbot += block_size;
128 alloc_print('+', '<', block, size);
129 return block;
130 }
131}
132
133/* Free an object, if possible. */
134/* Note that if a save is in effect, objects in chunks older than */
135/* the save, and objects allocated with malloc before the save, */
136/* must not be freed. */
137void
138alloc_free(char *cobj, uint num_elts, uint elt_size, const char *client_name)
139{ register alloc_state_ptr ap = &as_current;
140 uint size = num_elts * elt_size;
141 uint block_size;
142 if ( size >= ap->big_size )
143 { /* Object was allocated with malloc. */
144 alloc_free_large(cobj, size, client_name);
145 return;
146 }
147#define obj ((byte *)cobj)
148 else if ( obj == ap->ctop )
149 { /* Don't free the object if we're in a save and */
150 /* this object wasn't allocated since the save. */
151 if ( ap->save_level == 0 ||
152 ap->current.save_level >= ap->save_level ||
153 /* We know the current chunk is the same as */
154 /* the one in as->saved->state */
155 obj < ap->saved_ctop
156 )
157 ap->ctop += size;
158 alloc_print('-', '>', obj, size);
159 return;
160 }
161 else if ( obj + (block_size = align_round(size)) == ap->cbot )
162 { /* Freeing an aligned object. Same check. */
163 if ( ap->save_level == 0 ||
164 ap->current.save_level >= ap->save_level ||
165 obj >= ap->saved_cbot
166 )
167 ap->cbot = obj;
168 alloc_print('-', '<', obj, size);
169 return;
170 }
171 else if ( !ptr_is_in_chunk(obj, &ap->current) )
172 { /* In another segment, check its save level. */
173 int level = ap->save_level;
174 alloc_chunk *cp = ap->last_freed;
175 if ( cp != 0 && ptr_is_in_chunk(obj, cp) ) /* cache hit */
176 { if ptr_lt(obj, cp->bot) goto pxf;
177 else goto pnf;
178 }
179 for ( cp = ap->current.next; cp != 0; cp = cp->next )
180 { switch ( cp->save_level - level )
181 {
182 case 0:
183 if ( ptr_is_in_chunk(obj, cp) )
184 { if ( ptr_lt(obj, cp->bot) ) goto pbf;
185 else goto pnf;
186 }
187 else continue;
188 case -1:
189 /* Might be alloc'ed since the save, */
190 /* or might not be aligned. */
191 if ( ptr_lt(obj, ap->saved_cbot) ) goto pbf;
192 }
193 }
194pnf: /* Older save level or unaligned, not freeable. */
195 alloc_print('-', '\\', obj, size);
196 return;
197pbf: /* If we get here, OK to put the block on a free list. */
198 ap->last_freed = cp;
199pxf: ;
200 }
201 else if ( obj >= ap->cbot ) /* not aligned object, punt */
202 { alloc_print('-', '~', obj, size);
203 return;
204 }
205 /* Put on a free list if small enough */
206 alloc_print('-', '#', obj, size);
207 if ( block_size <= max_chain_size && block_size >= sizeof(char **) )
208 { char **fptr = &ap->free[block_size >> log2_align_mod];
209 *(char **)cobj = *fptr;
210 *fptr = cobj;
211 }
212#undef obj
213}
214
215/* Grow an object. This may require allocating a new copy. */
216/* Return 0 if not enough room. */
217/****** Note: the object must have been allocated at
218 ****** the current save level. */
219byte *
220alloc_grow(byte *obj, uint old_num, uint new_num, uint elt_size,
221 const char *client_name)
222{ register alloc_state_ptr ap = &as_current;
223 uint old_size = old_num * elt_size;
224 uint new_size = new_num * elt_size;
225 byte *nobj;
226 if ( new_size == old_size ) return obj;
227 if ( new_size < ap->big_size ) /* try to grow in place */
228 { uint old_block_size;
229 uint new_block_size;
230 if ( obj == ap->ctop )
231 { /* Might be able to grow in place */
232 uint diff = new_size - old_size;
233 if ( diff <= ap->ctop - ap->cbot )
234 { alloc_print('>', '>', obj, new_size);
235 ap->ctop -= diff;
236 memcpy(ap->ctop, obj, old_size);
237 return ap->ctop;
238 }
239 }
240 old_block_size = align_round(old_size);
241 new_block_size = align_round(new_size);
242 if ( obj + old_block_size == ap->cbot )
243 { /* Might be able to grow in place */
244 uint diff = new_block_size - old_block_size;
245 if ( diff <= ap->ctop - ap->cbot )
246 { alloc_print('>', '<', obj, new_size);
247 ap->cbot += diff;
248 return obj;
249 }
250 }
251 }
252 /* Can't grow in place. Allocate a new object and copy. */
253 nobj = (byte *)alloc(new_num, elt_size, client_name);
254 if ( nobj == 0 )
255 return 0;
256 memcpy(nobj, obj, old_size);
257 alloc_free((char *)obj, old_num, elt_size, client_name);
258 alloc_print('>', '&', obj, new_size);
259 return nobj;
260}
261
262/* Shrink an object. */
263/****** Note: the object must have been allocated at
264 ****** the current save level. */
265byte *
266alloc_shrink(byte *obj, uint old_num, uint new_num, uint elt_size,
267 const char *client_name)
268{ register alloc_state_ptr ap = &as_current;
269 uint old_size = old_num * elt_size;
270 uint new_size = new_num * elt_size;
271 if ( new_size == old_size ) return obj;
272 if ( old_size >= ap->big_size )
273 { /* Allocate a new block. */
274 byte *nobj = (byte *)alloc(new_num, elt_size, client_name);
275 if ( nobj == 0 ) return obj; /* can't shrink, leave as is */
276 memcpy(nobj, obj, new_size);
277 alloc_free((char *)obj, old_num, elt_size, client_name);
278 alloc_print('<', '&', obj, new_size);
279 return nobj;
280 }
281 else if ( obj == ap->ctop )
282 { /* Move the object up in place. */
283 /* memcpy doesn't do this properly. */
284 register byte *from = obj + new_size;
285 register byte *to = obj + old_size;
286 while ( from > obj ) *--to = *--from;
287 obj = ap->ctop = to;
288 }
289 else
290 { uint new_block_size = align_round(new_size);
291 alloc_free((char *)(obj + new_block_size),
292 1, align_round(old_size) - new_block_size,
293 "alloc_shrink");
294 }
295 alloc_print('<', ' ', obj, new_size);
296 return obj;
297}
298
299/* ------ Private routines ------ */
300
301/* Allocate (with malloc) an object too large to be put in a chunk. */
302/* Return NULL on failure. */
303private char *
304alloc_large(alloc_state_ptr ap, uint size, const char *client_name)
305{ alloc_block *mblock = (alloc_block *)
306 (*ap->palloc)(1, alloc_block_size + size, client_name);
307 char *block;
308 if ( mblock == NULL ) return NULL;
309 block = (char *)mblock + alloc_block_size;
310 alloc_print_large('+', '*', block, size);
311 mblock->next = ap->malloc_chain;
312 mblock->size = size;
313 mblock->save_level = ap->save_level;
314 mblock->cap = ap;
315 ap->malloc_chain = mblock;
316 ap->used += size;
317 ap->total += size;
318 return block;
319}
320
321/* Allocate a new chunk. Return true if successful. */
322#define chunk_head_size align_round(sizeof(alloc_chunk))
323private int
324alloc_add_chunk(register alloc_state_ptr ap, uint csize)
325{ char *space =
326 (*ap->palloc)(1, chunk_head_size + csize, "alloc chunk");
327 long discard;
328 if ( space == NULL )
329 return 0;
330 ap->num_chunks++;
331 /* Accumulate statistics */
332 ap->total += csize;
333 alloc_status(&ap->used, &discard);
334 /* Stash the state of the old chunk */
335 if ( ap->current_ptr != 0 ) /* check for very first chunk */
336 *ap->current_ptr = ap->current;
337 /* Initialize the new chunk */
338 ap->cbase = ap->cbot = (byte *)space + chunk_head_size;
339 ap->climit = ap->ctop = ap->cbot + csize;
340 ap->current.next = ap->current_ptr;
341 ap->current.save_level = ap->save_level;
342 ap->current_ptr = (alloc_chunk *)space;
343 return 1;
344}
345#undef chunk_head_size
346
347/* Free a large object (allocated with malloc). */
348private void
349alloc_free_large(char *cobj, uint size, const char *client_name)
350{ alloc_block **prev;
351 alloc_block *mblock = (alloc_block *)(cobj - alloc_block_size);
352 alloc_state_ptr ap = mblock->cap;
353 if ( mblock->save_level == ap->save_level )
354 for ( prev = &ap->malloc_chain; *prev != 0; prev = &mblock->next )
355 { mblock = *prev;
356 if ( (char *)mblock + alloc_block_size == cobj )
357 { *prev = mblock->next;
358 ap->used -= size;
359 ap->total -= size;
360 (*ap->pfree)((char *)mblock,
361 1, size + alloc_block_size,
362 "large object");
363 alloc_print_large('-', '*', cobj, size);
364 return;
365 }
366 }
367 alloc_print('-', '?', cobj, size);
368}