386BSD 0.1 development
[unix-history] / usr / othersrc / public / ghostscript-2.4.1 / isave.c
CommitLineData
28a0455d
WJ
1/* Copyright (C) 1991, 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/* isave.c */
21/* Save/restore machinery for Ghostscript */
22#include "ghost.h"
23#include "memory_.h"
24#include "alloc.h"
25#include "astate.h"
26#include "name.h"
27#include "packed.h"
28#include "save.h"
29#include "store.h" /* for ref_assign */
30
31/* Imported restore routines */
32extern void file_restore(P1(alloc_save *));
33extern void font_restore(P1(alloc_save *));
34
35/*
36 * The logic for saving and restore the state is rather subtle.
37 * Both the changes to individual objects, and the overall state
38 * of the memory manager, must be saved and restored.
39 */
40
41/*
42 * To save the state of the memory manager:
43 * Save the state of the current chunk in which we are allocating.
44 * Save the identity of the current chunk.
45 * Save and reset the malloc chain and the orphan block chains.
46 * By doing this, we guarantee that no object older than the save
47 * can be freed.
48 *
49 * To restore the state of the memory manager:
50 * Free all chunks newer than the save.
51 * Free all malloc'ed blocks newer than the save.
52 * Make current the chunk that was current at the time of the save.
53 * Free all objects allocated in the current chunk since the save.
54 */
55
56/*
57 * For saving changes to individual objects, we add an "attribute" bit
58 * (l_new) that logically belongs to the slot where the descriptor is stored,
59 * not to the descriptor itself. The bit means "the contents
60 * of this slot have been changed since the last save."
61 * To keep track of changes since the save, we associate a chain of
62 * <slot, old_contents> pairs that remembers the old contents of slots.
63 *
64 * When creating an object, if the save level is non-zero:
65 * Set the bit in all slots.
66 *
67 * When storing into a slot, if the save level is non-zero:
68 * If the bit isn't set, save the address and contents of the slot
69 * on the current contents chain.
70 * Set the bit after storing the new value.
71 *
72 * To do a save:
73 * If the save level is non-zero:
74 * Reset the bit in all slots on the contents chain, and in all
75 * objects created since the previous save.
76 * Push the head of contents chain, and reset the chain to empty.
77 *
78 * To do a restore:
79 * Check all the stacks to make sure they don't contain references
80 * to objects created since the save.
81 * Restore all the slots on the contents chain.
82 * Pop the contents chain head.
83 * If the save level is now non-zero:
84 * Scan the newly restored contents chain, and set the bit in all
85 * the slots it references.
86 * Scan all objects created since the previous save, and set the
87 * bit in all the slots of each object.
88 */
89
90/* Declare the mask for checking stores. */
91/* This is -1 if we are not in a save, 0 if we are in a save. */
92int alloc_save_test_mask;
93/* Declare the mask for tagging new objects. */
94/* This is 0 if we are not in a save, l_new if we are in a save. */
95int alloc_save_new_mask;
96#define set_in_save()\
97 (alloc_save_test_mask = 0, alloc_save_new_mask = l_new)
98#define set_not_in_save()\
99 (alloc_save_test_mask = -1, alloc_save_new_mask = 0)
100
101/* Structure for saved change chain for save/restore. */
102/* If where = 0, contents is a t_array that refers to */
103/* a newly allocated object; if contents.value.refs is 0, */
104/* this is a free record (possible since we allocate them two at a time.) */
105/* We merge adjacent objects to reduce */
106/* the need to allocate alloc_change records. */
107struct alloc_change_s {
108 alloc_change *next;
109 ref *where;
110 ref contents;
111};
112#define alloc_change_is_free(cp)\
113 ((cp)->where == 0 && (cp)->contents.value.refs == 0)
114
115/*
116 * Macro to allocate a pair of change records.
117 * Must be used in the following form:
118 * if_not_alloc_change_pair(cp, ap, cname)
119 * { ... failure code ...
120 * }
121 */
122#define if_not_alloc_change_pair(cp, ap, cname)\
123 cp = (alloc_change *)alloc(2, sizeof(alloc_change), cname);\
124 if ( cp != 0 )\
125 { cp->next = ap->changes;\
126 cp[1].next = cp;\
127 cp[1].where = 0;\
128 cp[1].contents.value.refs = 0;\
129 r_set_size(&cp[1].contents, 0);\
130 ap->changes = cp + 1;\
131 }\
132 else
133
134/* Saved state of allocator and other things as needed. */
135struct alloc_save_s {
136 alloc_state state;
137 alloc_state_ptr cap;
138 uint name_cnt;
139};
140
141/* Debugging printout */
142#ifdef DEBUG
143private void
144alloc_save_print(alloc_change *cp)
145{ dprintf1(" %lx:", (ulong)cp);
146 if ( cp->where )
147 dprintf4(" %lx: %x %x %lx\n", (ulong)cp->where,
148 r_type_attrs(&cp->contents), r_size(&cp->contents),
149 (ulong)cp->contents.value.intval);
150 else
151 dprintf2(" %lx(%u)\n", (ulong)cp->contents.value.refs,
152 r_size(&cp->contents));
153}
154#endif
155
156/* Forward references */
157private void save_set_new(P2(alloc_state_ptr, int));
158
159/* Initialize the save/restore machinery. */
160void
161alloc_save_init()
162{ set_not_in_save();
163}
164
165/* Save the state. */
166alloc_save *
167alloc_save_state()
168{ register alloc_state_ptr ap = alloc_state_current;
169 alloc_save *save =
170 (alloc_save *)alloc(1, sizeof(alloc_save),
171 "alloc_save_state");
172 if ( save == 0 ) return 0;
173 save->state = *ap;
174 save->cap = ap;
175 save->name_cnt = name_count();
176 /* Reset the l_new attribute in all slots. The only slots that */
177 /* can have the attribute set are the ones on the changes chain. */
178 save_set_new(ap, 0);
179 /* Clear the free chains, to prevent old objects from being freed. */
180 memset(&ap->free[0], 0, num_free_chains * sizeof(char *));
181 ap->malloc_chain = 0;
182 ap->saved = save;
183 ap->save_level++;
184 ap->changes = 0;
185 ap->saved_cbot = ap->cbot;
186 ap->saved_ctop = ap->ctop;
187 /* Clear the last_freed cache, because the cache pointer */
188 /* must point to a chunk at the current save level. */
189 ap->last_freed = 0;
190#ifdef DEBUG
191if ( gs_debug['u'] )
192 { dprintf3("[u]save at %lx: cbot=%lx ctop=%lx\n", (ulong)save,
193 (ulong)ap->cbot, (ulong)ap->ctop);
194 }
195#endif
196 set_in_save();
197 return save;
198}
199
200/* Allocate a ref-containing object and record it as new. */
201ref *
202alloc_refs(uint num_refs, const char *client_name)
203{ register alloc_state_ptr ap = alloc_state_current;
204 register alloc_change *cp;
205 register ref *obj = (ref *)alloc(num_refs, sizeof(ref), client_name);
206 if ( obj == 0 ) return 0;
207 if ( ap->save_level == 0 ) /* no saving */
208 return obj;
209 cp = ap->changes;
210 if ( cp != 0 && cp->where == 0 && cp->contents.value.refs != 0 &&
211 obj == cp->contents.value.refs + r_size(&cp->contents) &&
212 /* Don't create a single block that is large enough to */
213 /* mislead the allocator into thinking it was allocated */
214 /* with a single malloc. */
215 r_size(&cp->contents) + num_refs < ap->big_size / sizeof(ref)
216 )
217 { /* Merge adjacent allocations. */
218 r_inc_size(&cp->contents, num_refs);
219#ifdef DEBUG
220if ( gs_debug['u'] )
221 { dprintf1("[u]alloc_refs %s merge", client_name);
222 alloc_save_print(cp);
223 }
224#endif
225 }
226 else
227 { if ( cp == 0 || !alloc_change_is_free(cp) )
228 { /* Allocate a pair of entries. */
229 if_not_alloc_change_pair(cp, ap, "alloc_refs")
230 {
231 alloc_free((char *)obj, num_refs, sizeof(ref),
232 client_name);
233 return 0;
234 }
235 }
236 cp->where = 0;
237 r_set_size(&cp->contents, num_refs);
238 cp->contents.value.refs = obj;
239#ifdef DEBUG
240if ( gs_debug['u'] )
241 { dprintf1("[u]alloc_refs %s", client_name);
242 alloc_save_print(cp);
243 }
244#endif
245 }
246 return obj;
247}
248
249/* Deallocate a ref-containing object. This is a dummy for now. */
250void
251alloc_free_refs(ref *ptr, uint num_refs, const char *client_name)
252{
253#ifdef DEBUG
254if ( gs_debug['u'] )
255 { dprintf3("[u]alloc_free_refs (%lx,%d) %s\n",
256 (ulong)ptr, num_refs, client_name);
257 }
258#endif
259}
260
261
262/* Record a state change that must be undone for restore, */
263/* and mark it as having been saved. */
264/* This can only be called if we are in a save. */
265int
266alloc_save_change(ref *where, const char *client_name)
267{ register alloc_state_ptr ap = alloc_state_current;
268 register alloc_change *cp;
269 if ( ap->save_level == 0 ) return 0; /* no saving */
270 cp = ap->changes;
271 if ( cp == 0 || !alloc_change_is_free(cp) )
272 { /* Allocate a pair of entries. */
273 if_not_alloc_change_pair(cp, ap, "alloc_save_change")
274 { return -1;
275 }
276 }
277 cp->where = where;
278 ref_assign(&cp->contents, where);
279#ifdef DEBUG
280if ( gs_debug['u'] )
281 { dprintf1("[u]save %s", client_name);
282 alloc_save_print(cp);
283 }
284#endif
285 if ( !r_is_packed(where) ) r_set_attrs(where, l_new);
286 return 0;
287}
288
289/* Return the current save level */
290int
291alloc_save_level()
292{ return alloc_state_current->save_level;
293}
294
295/* Test whether a reference would be invalidated by a restore. */
296int
297alloc_is_since_save(char *ptr, alloc_save *save)
298{
299 /* A reference can postdate a save in one of three ways: */
300 /* - It is in the chunk that was current at the time */
301 /* of the save, and allocated more recently. */
302 /* - It is in a chunk allocated since the save; */
303 /* - It was malloc'ed since the save; */
304
305 register alloc_state_ptr ap = save->cap;
306
307#ifdef DEBUG
308if ( gs_debug['U'] )
309 dprintf2("[U]is_since_save %lx, %lx:\n", (ulong)ptr, (ulong)save);
310#endif
311
312 /* Check against current chunk at the time of the save */
313 if ( ptr_is_in_chunk(ptr, &save->state.current) )
314 { /* In the chunk, check against allocation pointers */
315 /* at the time of the save */
316#ifdef DEBUG
317if ( gs_debug['U'] )
318 dprintf2("[U?] current chunk %lx, %lx\n",
319 (ulong)save->state.cbot, (ulong)save->state.ctop);
320#endif
321 return ( (ptr_ord_t)ptr >= (ptr_ord_t)save->state.cbot &&
322 (ptr_ord_t)ptr < (ptr_ord_t)save->state.ctop );
323 }
324
325 /* Check against chunks allocated since the save */
326 { alloc_chunk *chunk = &ap->current;
327 while ( chunk->save_level > save->state.save_level )
328 { if ( ptr_is_in_chunk(ptr, chunk) )
329 {
330#ifdef DEBUG
331if ( gs_debug['U'] )
332 dprintf3("[U+] new chunk %lx: %lx, %lx\n", chunk,
333 (ulong)chunk->base, (ulong)chunk->limit);
334#endif
335 return 1;
336 }
337 chunk = chunk->next;
338 }
339 }
340
341 /* Check the malloc chains since the save */
342 { alloc_state *asp = ap;
343 for ( ; asp != &save->state; asp = &asp->saved->state )
344 { alloc_block *mblk = asp->malloc_chain;
345 for ( ; mblk != 0; mblk = mblk->next )
346 if ( alloc_block_size + (char *)mblk == ptr )
347 {
348#ifdef DEBUG
349if ( gs_debug['U'] )
350 dprintf("[U+] malloc'ed\n");
351#endif
352 return 1;
353 }
354 }
355 }
356
357 /* Not in any of those places, must be OK. */
358 return 0;
359}
360
361/* Test whether a name would be invalidated by a restore. */
362int
363alloc_name_is_since_save(ref *pnref, alloc_save *save)
364{ return name_is_since_count(pnref, save->name_cnt);
365}
366
367/* Validate a saved state pointer. */
368int
369alloc_restore_state_check(alloc_save *save)
370{ alloc_save *sprev = save->cap->saved;
371 while ( sprev != save )
372 { if ( sprev == 0 ) return -1; /* not on chain */
373 sprev = sprev->state.saved;
374 }
375 return 0;
376}
377
378/* Restore the state. The client is responsible for calling */
379/* alloc_restore_state_check first, and for ensuring that */
380/* there are no surviving pointers for which alloc_is_since_save is true. */
381void
382alloc_restore_state(alloc_save *save)
383{ register alloc_state_ptr ap = save->cap;
384 alloc_save *sprev;
385
386#ifdef DEBUG
387if ( gs_debug['u'] )
388 { dprintf1("[u]restore from %lx\n", (ulong)save);
389 }
390#endif
391
392 /* Iteratively restore the state */
393 do
394 { sprev = ap->saved;
395
396 /* Close inaccessible files. */
397 file_restore(save);
398
399 /* Remove entries from font and character caches. */
400 font_restore(save);
401
402 /* Adjust the name table. */
403 name_restore(sprev->name_cnt);
404
405 /* Undo changes since the save. */
406 { alloc_change *cp = ap->changes;
407 while ( cp )
408 {
409#ifdef DEBUG
410if ( gs_debug['u'] )
411 { dprintf("[u]restore");
412 alloc_save_print(cp);
413 }
414#endif
415 if ( cp->where )
416 ref_assign(cp->where, &cp->contents);
417 else if ( cp->contents.value.refs != 0 ) /* might be an unfilled save record */
418 { alloc_free((char *)cp->contents.value.refs,
419 r_size(&cp->contents), sizeof(ref),
420 "alloc_restore_state");
421 }
422 cp = cp->next;
423 }
424 }
425
426 /* Free chunks allocated since the save. */
427 { alloc_chunk *cp = ap->current_ptr;
428 *cp = ap->current; /* update in memory */
429 }
430 while ( ap->current.save_level == ap->save_level )
431 { byte *cp = (byte *)ap->current_ptr;
432 uint csize = ap->climit - cp;
433 ap->current_ptr = ap->current.next;
434 ap->current = *ap->current_ptr;
435 (*ap->pfree)((char *)cp, 1, csize, "alloc_restore_state(chunk)");
436 }
437
438 /* Free blocks allocated with malloc since the save. */
439 /* Since we reset the chain when we did the save, */
440 /* we just free all the objects on the current chain. */
441 { while ( ap->malloc_chain != 0 )
442 { alloc_block *mblock = ap->malloc_chain;
443 ap->malloc_chain = mblock->next;
444 (*ap->pfree)((char *)mblock,
445 1, alloc_block_size + mblock->size,
446 "alloc_restore_state(malloc'ed)");
447 }
448 }
449
450 /* Restore the allocator state. */
451 *ap = sprev->state;
452 alloc_free((char *)sprev, 1, sizeof(alloc_save),
453 "alloc_restore_state");
454
455 }
456 while ( sprev != save );
457
458 /* Clean up */
459 if ( sprev != 0 )
460 ap->saved_cbot = sprev->state.cbot,
461 ap->saved_ctop = sprev->state.ctop;
462 /* Clear the last_freed cache, because the cache pointer */
463 /* must point to a chunk at the current save level. */
464 ap->last_freed = 0;
465 if ( ap->save_level == 0 )
466 set_not_in_save();
467 /* Set the l_new attribute in all slots that have been saved. */
468 save_set_new(ap, l_new);
469}
470
471/* ------ Internal routines ------ */
472
473/* Set or reset the l_new attribute in every slot on the current */
474/* change chain. */
475private void
476save_set_new(alloc_state_ptr ap, int new) /* l_new or 0 */
477{ register alloc_change *cp = ap->changes;
478 while ( cp )
479 { ref *rp = cp->where;
480 if ( rp != 0 )
481 { if ( !r_is_packed(rp) )
482 rp->tas.type_attrs =
483 (rp->tas.type_attrs & ~l_new) + new;
484 }
485 else
486 { register ushort size = r_size(&cp->contents);
487 register ref *ep = cp->contents.value.refs;
488 while ( size-- )
489 { if ( !r_is_packed(ep) )
490 ep->tas.type_attrs =
491 (ep->tas.type_attrs & ~l_new) + new,
492 ep++;
493 }
494 }
495 cp = cp->next;
496 }
497}