Commit | Line | Data |
---|---|---|
28a0455d WJ |
1 | /* Copyright (C) 1991, 1992 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
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 */ | |
32 | extern void file_restore(P1(alloc_save *)); | |
33 | extern 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. */ | |
92 | int 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. */ | |
95 | int 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. */ | |
107 | struct 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. */ | |
135 | struct alloc_save_s { | |
136 | alloc_state state; | |
137 | alloc_state_ptr cap; | |
138 | uint name_cnt; | |
139 | }; | |
140 | ||
141 | /* Debugging printout */ | |
142 | #ifdef DEBUG | |
143 | private void | |
144 | alloc_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 */ | |
157 | private void save_set_new(P2(alloc_state_ptr, int)); | |
158 | ||
159 | /* Initialize the save/restore machinery. */ | |
160 | void | |
161 | alloc_save_init() | |
162 | { set_not_in_save(); | |
163 | } | |
164 | ||
165 | /* Save the state. */ | |
166 | alloc_save * | |
167 | alloc_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 | |
191 | if ( 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. */ | |
201 | ref * | |
202 | alloc_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 | |
220 | if ( 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 | |
240 | if ( 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. */ | |
250 | void | |
251 | alloc_free_refs(ref *ptr, uint num_refs, const char *client_name) | |
252 | { | |
253 | #ifdef DEBUG | |
254 | if ( 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. */ | |
265 | int | |
266 | alloc_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 | |
280 | if ( 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 */ | |
290 | int | |
291 | alloc_save_level() | |
292 | { return alloc_state_current->save_level; | |
293 | } | |
294 | ||
295 | /* Test whether a reference would be invalidated by a restore. */ | |
296 | int | |
297 | alloc_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 | |
308 | if ( 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 | |
317 | if ( 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 | |
331 | if ( 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 | |
349 | if ( 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. */ | |
362 | int | |
363 | alloc_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. */ | |
368 | int | |
369 | alloc_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. */ | |
381 | void | |
382 | alloc_restore_state(alloc_save *save) | |
383 | { register alloc_state_ptr ap = save->cap; | |
384 | alloc_save *sprev; | |
385 | ||
386 | #ifdef DEBUG | |
387 | if ( 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 | |
410 | if ( 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. */ | |
475 | private void | |
476 | save_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 | } |