~ubuntu-branches/ubuntu/jaunty/ghostscript/jaunty-updates

« back to all changes in this revision

Viewing changes to src/isave.c

  • Committer: Bazaar Package Importer
  • Author(s): Till Kamppeter
  • Date: 2009-01-20 16:40:45 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20090120164045-lnfhi0n30o5lwhwa
Tags: 8.64.dfsg.1~svn9377-0ubuntu1
* New upstream release (SVN rev 9377)
   o Fixes many bugs concerning PDF rendering, to make the PDF printing
     workflow correctly working.
   o Fixes long-standing bugs in many drivers, like input paper tray and
     duplex options not working for the built-in PCL 4, 5, 5c, 5e, and
     6/XL drivers, PDF input not working for bjc600, bjc800, and cups
     output devices, several options not working and uninitialized
     memory with cups output device.
   o Merged nearly all patches of the Ubuntu and Debian packages upstream.
   o Fixes LP: #317810, LP: #314439, LP: #314018.
* debian/patches/03_libpaper_support.dpatch,
  debian/patches/11_gs-cjk_font_glyph_handling_fix.dpatch,
  debian/patches/12_gs-cjk_vertical_writing_metrics_fix.dpatch,
  debian/patches/13_gs-cjk_cjkps_examples.dpatch,
  debian/patches/20_bbox_segv_fix.dpatch,
  debian/patches/21_brother_7x0_gdi_fix.dpatch,
  debian/patches/22_epsn_margin_workaround.dpatch,
  debian/patches/24_gs_man_fix.dpatch,
  debian/patches/25_toolbin_insecure_tmp_usage_fix.dpatch,
  debian/patches/26_assorted_script_fixes.dpatch,
  debian/patches/29_gs_css_fix.dpatch,
  debian/patches/30_ps2pdf_man_improvement.dpatch,
  debian/patches/31_fix-gc-sigbus.dpatch,
  debian/patches/34_ftbfs-on-hurd-fix.dpatch,
  debian/patches/35_disable_libcairo.dpatch,
  debian/patches/38_pxl-duplex.dpatch,
  debian/patches/39_pxl-resolution.dpatch,
  debian/patches/42_gs-init-ps-delaybind-fix.dpatch,
  debian/patches/45_bjc600-bjc800-pdf-input.dpatch,
  debian/patches/48_cups-output-device-pdf-duplex-uninitialized-memory-fix.dpatch,
  debian/patches/50_lips4-floating-point-exception.dpatch,
  debian/patches/52_cups-device-logging.dpatch,
  debian/patches/55_pcl-input-slot-fix.dpatch,
  debian/patches/57_pxl-input-slot-fix.dpatch,
  debian/patches/60_pxl-cups-driver-pdf.dpatch,
  debian/patches/62_onebitcmyk-pdf.dpatch,
  debian/patches/65_too-big-temp-files-1.dpatch,
  debian/patches/67_too-big-temp-files-2.dpatch,
  debian/patches/70_take-into-account-data-in-stream-buffer-before-refill.dpatch:
  Removed, applied upstream.
* debian/patches/01_docdir_fix_for_debian.dpatch,
  debian/patches/02_gs_man_fix_debian.dpatch,
  debian/patches/01_docdir-fix-for-debian.dpatch,
  debian/patches/02_docdir-fix-for-debian.dpatch: Renamed patches to
  make merging with Debian easier.
* debian/patches/32_improve-handling-of-media-size-changes-from-gv.dpatch, 
  debian/patches/33_bad-params-to-xinitimage-on-large-bitmaps.dpatch:
  regenerated for new source directory structure.
* debian/rules: Corrected paths to remove cidfmap (it is in Resource/Init/
  in GS 8.64) and to install headers (source paths are psi/ and base/ now).
* debian/rules: Remove all fontmaps, as DeFoMa replaces them.
* debian/local/pdftoraster/pdftoraster.c,
  debian/local/pdftoraster/pdftoraster.convs, debian/rules: Removed
  added pdftoraster filter and use the one which comes with Ghostscript.
* debian/ghostscript.links: s/8.63/8.64/

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (C) 2001-2006 Artifex Software, Inc.
2
 
   All Rights Reserved.
3
 
  
4
 
   This software is provided AS-IS with no warranty, either express or
5
 
   implied.
6
 
 
7
 
   This software is distributed under license and may not be copied, modified
8
 
   or distributed except as expressly authorized under the terms of that
9
 
   license.  Refer to licensing information at http://www.artifex.com/
10
 
   or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11
 
   San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12
 
*/
13
 
 
14
 
/* $Id: isave.c 8496 2008-01-21 20:59:55Z leonardo $ */
15
 
/* Save/restore manager for Ghostscript interpreter */
16
 
#include "ghost.h"
17
 
#include "memory_.h"
18
 
#include "ierrors.h"
19
 
#include "gsexit.h"
20
 
#include "gsstruct.h"
21
 
#include "stream.h"             /* for linking for forgetsave */
22
 
#include "iastate.h"
23
 
#include "inamedef.h"
24
 
#include "iname.h"
25
 
#include "ipacked.h"
26
 
#include "isave.h"
27
 
#include "isstate.h"
28
 
#include "store.h"              /* for ref_assign */
29
 
#include "ivmspace.h"
30
 
#include "igc.h"
31
 
#include "gsutil.h"             /* gs_next_ids prototype */
32
 
 
33
 
/* Structure descriptor */
34
 
private_st_alloc_save();
35
 
 
36
 
/* Define the maximum amount of data we are willing to scan repeatedly -- */
37
 
/* see below for details. */
38
 
static const long max_repeated_scan = 100000;
39
 
 
40
 
/* Define the minimum space for creating an inner chunk. */
41
 
/* Must be at least sizeof(chunk_head_t). */
42
 
static const long min_inner_chunk_space = sizeof(chunk_head_t) + 500;
43
 
 
44
 
/*
45
 
 * The logic for saving and restoring the state is complex.
46
 
 * Both the changes to individual objects, and the overall state
47
 
 * of the memory manager, must be saved and restored.
48
 
 */
49
 
 
50
 
/*
51
 
 * To save the state of the memory manager:
52
 
 *      Save the state of the current chunk in which we are allocating.
53
 
 *      Shrink all chunks to their inner unallocated region.
54
 
 *      Save and reset the free block chains.
55
 
 * By doing this, we guarantee that no object older than the save
56
 
 * can be freed.
57
 
 *
58
 
 * To restore the state of the memory manager:
59
 
 *      Free all chunks newer than the save, and the descriptors for
60
 
 *        the inner chunks created by the save.
61
 
 *      Make current the chunk that was current at the time of the save.
62
 
 *      Restore the state of the current chunk.
63
 
 *
64
 
 * In addition to save ("start transaction") and restore ("abort transaction"),
65
 
 * we support forgetting a save ("commit transation").  To forget a save:
66
 
 *      Reassign to the next outer save all chunks newer than the save.
67
 
 *      Free the descriptors for the inners chunk, updating their outer
68
 
 *        chunks to reflect additional allocations in the inner chunks.
69
 
 *      Concatenate the free block chains with those of the outer save.
70
 
 */
71
 
 
72
 
/*
73
 
 * For saving changes to individual objects, we add an "attribute" bit
74
 
 * (l_new) that logically belongs to the slot where the ref is stored,
75
 
 * not to the ref itself.  The bit means "the contents of this slot
76
 
 * have been changed, or the slot was allocated, since the last save."
77
 
 * To keep track of changes since the save, we associate a chain of
78
 
 * <slot, old_contents> pairs that remembers the old contents of slots.
79
 
 *
80
 
 * When creating an object, if the save level is non-zero:
81
 
 *      Set l_new in all slots.
82
 
 *
83
 
 * When storing into a slot, if the save level is non-zero:
84
 
 *      If l_new isn't set, save the address and contents of the slot
85
 
 *        on the current contents chain.
86
 
 *      Set l_new after storing the new value.
87
 
 *
88
 
 * To do a save:
89
 
 *      If the save level is non-zero:
90
 
 *              Reset l_new in all slots on the contents chain, and in all
91
 
 *                objects created since the previous save.
92
 
 *      Push the head of the contents chain, and reset the chain to empty.
93
 
 *
94
 
 * To do a restore:
95
 
 *      Check all the stacks to make sure they don't contain references
96
 
 *        to objects created since the save.
97
 
 *      Restore all the slots on the contents chain.
98
 
 *      Pop the contents chain head.
99
 
 *      If the save level is now non-zero:
100
 
 *              Scan the newly restored contents chain, and set l_new in all
101
 
 *                the slots it references.
102
 
 *              Scan all objects created since the previous save, and set
103
 
 *                l_new in all the slots of each object.
104
 
 *
105
 
 * To forget a save:
106
 
 *      If the save level is greater than 1:
107
 
 *              Set l_new as for a restore, per the next outer save.
108
 
 *              Concatenate the next outer contents chain to the end of
109
 
 *                the current one.
110
 
 *      If the save level is 1:
111
 
 *              Reset l_new as for a save.
112
 
 *              Free the contents chain.
113
 
 */
114
 
 
115
 
/*
116
 
 * A consequence of the foregoing algorithms is that the cost of a save is
117
 
 * proportional to the total amount of data allocated since the previous
118
 
 * save.  If a PostScript program reads in a large amount of setup code and
119
 
 * then uses save/restore heavily, each save/restore will be expensive.  To
120
 
 * mitigate this, we check to see how much data we have scanned at this save
121
 
 * level: if it is large, we do a second, invisible save.  This greatly
122
 
 * reduces the cost of inner saves, at the expense of possibly saving some
123
 
 * changes twice that otherwise would only have to be saved once.
124
 
 */
125
 
 
126
 
/*
127
 
 * The presence of global and local VM complicates the situation further.
128
 
 * There is a separate save chain and contents chain for each VM space.
129
 
 * When multiple contexts are fully implemented, save and restore will have
130
 
 * the following effects, according to the privacy status of the current
131
 
 * context's global and local VM:
132
 
 *      Private global, private local:
133
 
 *              The outermost save saves both global and local VM;
134
 
 *                otherwise, save only saves local VM.
135
 
 *      Shared global, private local:
136
 
 *              Save only saves local VM.
137
 
 *      Shared global, shared local:
138
 
 *              Save only saves local VM, and suspends all other contexts
139
 
 *                sharing the same local VM until the matching restore.
140
 
 * Since we do not currently implement multiple contexts, only the first
141
 
 * case is relevant.
142
 
 *
143
 
 * Note that when saving the contents of a slot, the choice of chain
144
 
 * is determined by the VM space in which the slot is allocated,
145
 
 * not by the current allocation mode.
146
 
 */
147
 
 
148
 
/* Tracing printout */
149
 
static void
150
 
print_save(const char *str, uint spacen, const alloc_save_t *sav)
151
 
{
152
 
  if_debug5('u', "[u]%s space %u 0x%lx: cdata = 0x%lx, id = %lu\n",\
153
 
            str, spacen, (ulong)sav, (ulong)sav->client_data, (ulong)sav->id);
154
 
}
155
 
 
156
 
/* A link to igcref.c . */
157
 
ptr_proc_reloc(igc_reloc_ref_ptr_nocheck, ref_packed);
158
 
 
159
 
/*
160
 
 * Structure for saved change chain for save/restore.  Because of the
161
 
 * garbage collector, we need to distinguish the cases where the change
162
 
 * is in a static object, a dynamic ref, or a dynamic struct.
163
 
 */
164
 
typedef struct alloc_change_s alloc_change_t;
165
 
struct alloc_change_s {
166
 
    alloc_change_t *next;
167
 
    ref_packed *where;
168
 
    ref contents;
169
 
#define AC_OFFSET_STATIC (-2)   /* static object */
170
 
#define AC_OFFSET_REF (-1)      /* dynamic ref */
171
 
#define AC_OFFSET_ALLOCATED (-3) /* a newly allocated ref array */
172
 
    short offset;               /* if >= 0, offset within struct */
173
 
};
174
 
 
175
 
static 
176
 
CLEAR_MARKS_PROC(change_clear_marks)
177
 
{
178
 
    alloc_change_t *const ptr = (alloc_change_t *)vptr;
179
 
 
180
 
    if (r_is_packed(&ptr->contents))
181
 
        r_clear_pmark((ref_packed *) & ptr->contents);
182
 
    else
183
 
        r_clear_attrs(&ptr->contents, l_mark);
184
 
}
185
 
static 
186
 
ENUM_PTRS_WITH(change_enum_ptrs, alloc_change_t *ptr) return 0;
187
 
ENUM_PTR(0, alloc_change_t, next);
188
 
case 1:
189
 
    if (ptr->offset >= 0)
190
 
        ENUM_RETURN((byte *) ptr->where - ptr->offset);
191
 
    else
192
 
        if (ptr->offset != AC_OFFSET_ALLOCATED)
193
 
            ENUM_RETURN_REF(ptr->where);
194
 
        else {
195
 
            /* Don't enumerate ptr->where, because it 
196
 
               needs a special processing with 
197
 
               alloc_save__filter_changes. */
198
 
            ENUM_RETURN(0);
199
 
        }
200
 
case 2:
201
 
    ENUM_RETURN_REF(&ptr->contents);
202
 
ENUM_PTRS_END
203
 
static RELOC_PTRS_WITH(change_reloc_ptrs, alloc_change_t *ptr)
204
 
{
205
 
    RELOC_VAR(ptr->next);
206
 
    switch (ptr->offset) {
207
 
        case AC_OFFSET_STATIC:
208
 
            break;
209
 
        case AC_OFFSET_REF:
210
 
            RELOC_REF_PTR_VAR(ptr->where);
211
 
            break;
212
 
        case AC_OFFSET_ALLOCATED:
213
 
            /* We know that ptr->where may point to an unmarked object
214
 
               because change_enum_ptrs skipped it,
215
 
               and we know it always points to same space 
216
 
               because we took a special care when calling alloc_save_change_alloc.
217
 
               Therefore we must skip the check for the mark,
218
 
               which would happen if we call the regular relocation function
219
 
               igc_reloc_ref_ptr from RELOC_REF_PTR_VAR. 
220
 
               Calling igc_reloc_ref_ptr_nocheck instead. */
221
 
            {   /* A sanity check. */
222
 
                obj_header_t *pre = (obj_header_t *)ptr->where - 1, *pre1 = 0;
223
 
 
224
 
                if (pre->o_type != &st_refs)
225
 
                    pre1->o_type = 0; /* issue a segfault. */
226
 
            }
227
 
            if (ptr->where != 0 && !gcst->relocating_untraced)
228
 
                ptr->where = igc_reloc_ref_ptr_nocheck(ptr->where, gcst);
229
 
            break;
230
 
        default:
231
 
            {
232
 
                byte *obj = (byte *) ptr->where - ptr->offset;
233
 
 
234
 
                RELOC_VAR(obj);
235
 
                ptr->where = (ref_packed *) (obj + ptr->offset);
236
 
            }
237
 
            break;
238
 
    }
239
 
    if (r_is_packed(&ptr->contents))
240
 
        r_clear_pmark((ref_packed *) & ptr->contents);
241
 
    else {
242
 
        RELOC_REF_VAR(ptr->contents);
243
 
        r_clear_attrs(&ptr->contents, l_mark);
244
 
    }
245
 
}
246
 
RELOC_PTRS_END
247
 
gs_private_st_complex_only(st_alloc_change, alloc_change_t, "alloc_change",
248
 
                change_clear_marks, change_enum_ptrs, change_reloc_ptrs, 0);
249
 
 
250
 
/* Debugging printout */
251
 
#ifdef DEBUG
252
 
static void
253
 
alloc_save_print(alloc_change_t * cp, bool print_current)
254
 
{
255
 
    dprintf2(" 0x%lx: 0x%lx: ", (ulong) cp, (ulong) cp->where);
256
 
    if (r_is_packed(&cp->contents)) {
257
 
        if (print_current)
258
 
            dprintf2("saved=%x cur=%x\n", *(ref_packed *) & cp->contents,
259
 
                     *cp->where);
260
 
        else
261
 
            dprintf1("%x\n", *(ref_packed *) & cp->contents);
262
 
    } else {
263
 
        if (print_current)
264
 
            dprintf6("saved=%x %x %lx cur=%x %x %lx\n",
265
 
                     r_type_attrs(&cp->contents), r_size(&cp->contents),
266
 
                     (ulong) cp->contents.value.intval,
267
 
                     r_type_attrs((ref *) cp->where),
268
 
                     r_size((ref *) cp->where),
269
 
                     (ulong) ((ref *) cp->where)->value.intval);
270
 
        else
271
 
            dprintf3("%x %x %lx\n",
272
 
                     r_type_attrs(&cp->contents), r_size(&cp->contents),
273
 
                     (ulong) cp->contents.value.intval);
274
 
    }
275
 
}
276
 
#endif
277
 
 
278
 
/* Forward references */
279
 
static int  restore_resources(alloc_save_t *, gs_ref_memory_t *);
280
 
static void restore_free(gs_ref_memory_t *);
281
 
static int  save_set_new(gs_ref_memory_t * mem, bool to_new, bool set_limit, ulong *pscanned);
282
 
static int  save_set_new_changes(gs_ref_memory_t *, bool, bool);
283
 
static bool check_l_mark(void *obj);
284
 
 
285
 
/* Initialize the save/restore machinery. */
286
 
void
287
 
alloc_save_init(gs_dual_memory_t * dmem)
288
 
{
289
 
    alloc_set_not_in_save(dmem);
290
 
}
291
 
 
292
 
/* Record that we are in a save. */
293
 
static void
294
 
alloc_set_masks(gs_dual_memory_t *dmem, uint new_mask, uint test_mask)
295
 
{
296
 
    int i;
297
 
    gs_ref_memory_t *mem;
298
 
 
299
 
    dmem->new_mask = new_mask;
300
 
    dmem->test_mask = test_mask;
301
 
    for (i = 0; i < countof(dmem->spaces.memories.indexed); ++i)
302
 
        if ((mem = dmem->spaces.memories.indexed[i]) != 0) {
303
 
            mem->new_mask = new_mask, mem->test_mask = test_mask;
304
 
            if (mem->stable_memory != (gs_memory_t *)mem) {
305
 
                mem = (gs_ref_memory_t *)mem->stable_memory;
306
 
                mem->new_mask = new_mask, mem->test_mask = test_mask;
307
 
            }
308
 
        }
309
 
}
310
 
void
311
 
alloc_set_in_save(gs_dual_memory_t *dmem)
312
 
{
313
 
    alloc_set_masks(dmem, l_new, l_new);
314
 
}
315
 
 
316
 
/* Record that we are not in a save. */
317
 
void
318
 
alloc_set_not_in_save(gs_dual_memory_t *dmem)
319
 
{
320
 
    alloc_set_masks(dmem, 0, ~0);
321
 
}
322
 
 
323
 
/* Save the state. */
324
 
static alloc_save_t *alloc_save_space(gs_ref_memory_t *mem,
325
 
                                       gs_dual_memory_t *dmem,
326
 
                                       ulong sid);
327
 
static void
328
 
alloc_free_save(gs_ref_memory_t *mem, alloc_save_t *save, const char *scn)
329
 
{
330
 
    gs_free_object((gs_memory_t *)mem, save, scn);
331
 
    /* Free any inner chunk structures.  This is the easiest way to do it. */
332
 
    restore_free(mem);
333
 
}
334
 
int
335
 
alloc_save_state(gs_dual_memory_t * dmem, void *cdata, ulong *psid)
336
 
{
337
 
    gs_ref_memory_t *lmem = dmem->space_local;
338
 
    gs_ref_memory_t *gmem = dmem->space_global;
339
 
    ulong sid = gs_next_ids((const gs_memory_t *)lmem->stable_memory, 2);
340
 
    bool global =
341
 
        lmem->save_level == 0 && gmem != lmem &&
342
 
        gmem->num_contexts == 1;
343
 
    alloc_save_t *gsave =
344
 
        (global ? alloc_save_space(gmem, dmem, sid + 1) : (alloc_save_t *) 0);
345
 
    alloc_save_t *lsave = alloc_save_space(lmem, dmem, sid);
346
 
 
347
 
    if (lsave == 0 || (global && gsave == 0)) {
348
 
        if (lsave != 0)
349
 
            alloc_free_save(lmem, lsave, "alloc_save_state(local save)");
350
 
        if (gsave != 0)
351
 
            alloc_free_save(gmem, gsave, "alloc_save_state(global save)");
352
 
        return 0;
353
 
    }
354
 
    if (gsave != 0) {
355
 
        gsave->client_data = 0;
356
 
        print_save("save", gmem->space, gsave);
357
 
        /* Restore names when we do the local restore. */
358
 
        lsave->restore_names = gsave->restore_names;
359
 
        gsave->restore_names = false;
360
 
    }
361
 
    lsave->id = sid;
362
 
    lsave->client_data = cdata;
363
 
    print_save("save", lmem->space, lsave);
364
 
    /* Reset the l_new attribute in all slots.  The only slots that */
365
 
    /* can have the attribute set are the ones on the changes chain, */
366
 
    /* and ones in objects allocated since the last save. */
367
 
    if (lmem->save_level > 1) {
368
 
        ulong scanned;
369
 
        int code = save_set_new(&lsave->state, false, true, &scanned);
370
 
 
371
 
        if (code < 0)
372
 
            return code;
373
 
#if 0 /* Disable invisible save levels. */
374
 
        if ((lsave->state.total_scanned += scanned) > max_repeated_scan) {
375
 
            /* Do a second, invisible save. */
376
 
            alloc_save_t *rsave;
377
 
 
378
 
            rsave = alloc_save_space(lmem, dmem, 0L);
379
 
            if (rsave != 0) {
380
 
                rsave->client_data = cdata;
381
 
#if 0 /* Bug 688153 */
382
 
                rsave->id = lsave->id;
383
 
                print_save("save", lmem->space, rsave);
384
 
                lsave->id = 0;  /* mark as invisible */
385
 
                rsave->state.save_level--; /* ditto */
386
 
                lsave->client_data = 0;
387
 
#else
388
 
                rsave->id = 0;  /* mark as invisible */
389
 
                print_save("save", lmem->space, rsave);
390
 
                rsave->state.save_level--; /* ditto */
391
 
                rsave->client_data = 0;
392
 
#endif
393
 
                /* Inherit the allocated space count -- */
394
 
                /* we need this for triggering a GC. */
395
 
                print_save("save", lmem->space, lsave);
396
 
            }
397
 
        }
398
 
#endif
399
 
    }
400
 
    alloc_set_in_save(dmem);
401
 
    *psid = sid;
402
 
    return 0;
403
 
}
404
 
/* Save the state of one space (global or local). */
405
 
static alloc_save_t *
406
 
alloc_save_space(gs_ref_memory_t * mem, gs_dual_memory_t * dmem, ulong sid)
407
 
{
408
 
    gs_ref_memory_t save_mem;
409
 
    alloc_save_t *save;
410
 
    chunk_t *cp;
411
 
    chunk_t *new_pcc = 0;
412
 
 
413
 
    save_mem = *mem;
414
 
    alloc_close_chunk(mem);
415
 
    mem->pcc = 0;
416
 
    gs_memory_status((gs_memory_t *) mem, &mem->previous_status);
417
 
    ialloc_reset(mem);
418
 
 
419
 
    /* Create inner chunks wherever it's worthwhile. */
420
 
 
421
 
    for (cp = save_mem.cfirst; cp != 0; cp = cp->cnext) {
422
 
        if (cp->ctop - cp->cbot > min_inner_chunk_space) {
423
 
            /* Create an inner chunk to cover only the unallocated part. */
424
 
            chunk_t *inner =
425
 
                gs_raw_alloc_struct_immovable(mem->non_gc_memory, &st_chunk,
426
 
                                              "alloc_save_space(inner)");
427
 
 
428
 
            if (inner == 0)
429
 
                break;          /* maybe should fail */
430
 
            alloc_init_chunk(inner, cp->cbot, cp->ctop, cp->sreloc != 0, cp);
431
 
            alloc_link_chunk(inner, mem);
432
 
            if_debug2('u', "[u]inner chunk: cbot=0x%lx ctop=0x%lx\n",
433
 
                      (ulong) inner->cbot, (ulong) inner->ctop);
434
 
            if (cp == save_mem.pcc)
435
 
                new_pcc = inner;
436
 
        }
437
 
    }
438
 
    mem->pcc = new_pcc;
439
 
    alloc_open_chunk(mem);
440
 
 
441
 
    save = gs_alloc_struct((gs_memory_t *) mem, alloc_save_t,
442
 
                           &st_alloc_save, "alloc_save_space(save)");
443
 
    if_debug2('u', "[u]save space %u at 0x%lx\n",
444
 
              mem->space, (ulong) save);
445
 
    if (save == 0) {
446
 
        /* Free the inner chunk structures.  This is the easiest way. */
447
 
        restore_free(mem);
448
 
        *mem = save_mem;
449
 
        return 0;
450
 
    }
451
 
    save->state = save_mem;
452
 
    save->spaces = dmem->spaces;
453
 
    save->restore_names = (name_memory(mem) == (gs_memory_t *) mem);
454
 
    save->is_current = (dmem->current == mem);
455
 
    save->id = sid;
456
 
    mem->saved = save;
457
 
    if_debug2('u', "[u%u]file_save 0x%lx\n",
458
 
              mem->space, (ulong) mem->streams);
459
 
    mem->streams = 0;
460
 
    mem->total_scanned = 0;
461
 
    mem->total_scanned_after_compacting = 0;
462
 
    if (sid)
463
 
        mem->save_level++;
464
 
    return save;
465
 
}
466
 
 
467
 
/* Record a state change that must be undone for restore, */
468
 
/* and mark it as having been saved. */
469
 
int
470
 
alloc_save_change_in(gs_ref_memory_t *mem, const ref * pcont,
471
 
                  ref_packed * where, client_name_t cname)
472
 
{
473
 
    register alloc_change_t *cp;
474
 
 
475
 
    if (mem->new_mask == 0)
476
 
        return 0;               /* no saving */
477
 
    cp = gs_alloc_struct((gs_memory_t *)mem, alloc_change_t,
478
 
                         &st_alloc_change, "alloc_save_change");
479
 
    if (cp == 0)
480
 
        return -1;
481
 
    cp->next = mem->changes;
482
 
    cp->where = where;
483
 
    if (pcont == NULL)
484
 
        cp->offset = AC_OFFSET_STATIC;
485
 
    else if (r_is_array(pcont) || r_has_type(pcont, t_dictionary))
486
 
        cp->offset = AC_OFFSET_REF;
487
 
    else if (r_is_struct(pcont))
488
 
        cp->offset = (byte *) where - (byte *) pcont->value.pstruct;
489
 
    else {
490
 
        lprintf3("Bad type %u for save!  pcont = 0x%lx, where = 0x%lx\n",
491
 
                 r_type(pcont), (ulong) pcont, (ulong) where);
492
 
        gs_abort((const gs_memory_t *)mem);
493
 
    }
494
 
    if (r_is_packed(where))
495
 
        *(ref_packed *)&cp->contents = *where;
496
 
    else {
497
 
        ref_assign_inline(&cp->contents, (ref *) where);
498
 
        r_set_attrs((ref *) where, l_new);
499
 
    }
500
 
    mem->changes = cp;
501
 
#ifdef DEBUG
502
 
    if (gs_debug_c('U')) {
503
 
        dlprintf1("[U]save(%s)", client_name_string(cname));
504
 
        alloc_save_print(cp, false);
505
 
    }
506
 
#endif
507
 
    return 0;
508
 
}
509
 
int
510
 
alloc_save_change(gs_dual_memory_t * dmem, const ref * pcont,
511
 
                  ref_packed * where, client_name_t cname)
512
 
{
513
 
    gs_ref_memory_t *mem =
514
 
        (pcont == NULL ? dmem->space_local :
515
 
         dmem->spaces_indexed[r_space(pcont) >> r_space_shift]);
516
 
 
517
 
    return alloc_save_change_in(mem, pcont, where, cname);
518
 
}
519
 
 
520
 
/* Allocate a structure for recording an allocation event. */
521
 
int
522
 
alloc_save_change_alloc(gs_ref_memory_t *mem, client_name_t cname, ref_packed ***ppr)
523
 
{
524
 
    register alloc_change_t *cp;
525
 
 
526
 
    if (mem->new_mask == 0)
527
 
        return 0;               /* no saving */
528
 
    cp = gs_alloc_struct((gs_memory_t *)mem, alloc_change_t,
529
 
                         &st_alloc_change, "alloc_save_change");
530
 
    if (cp == 0)
531
 
        return_error(e_VMerror);
532
 
    cp->next = mem->changes;
533
 
    cp->where = 0;
534
 
    cp->offset = AC_OFFSET_ALLOCATED;
535
 
    make_null(&cp->contents);
536
 
    mem->changes = cp;
537
 
    *ppr = &cp->where;
538
 
    return 1;
539
 
}
540
 
 
541
 
/* Remove an AC_OFFSET_ALLOCATED element. */
542
 
void
543
 
alloc_save_remove(gs_ref_memory_t *mem, ref_packed *obj, client_name_t cname)
544
 
{
545
 
    alloc_change_t **cpp = &mem->changes;
546
 
    
547
 
    for (; *cpp != NULL;) {
548
 
        alloc_change_t *cp = *cpp;
549
 
 
550
 
        if (cp->offset == AC_OFFSET_ALLOCATED && cp->where == obj) {
551
 
            if (mem->scan_limit == cp)
552
 
                mem->scan_limit = cp->next;
553
 
            *cpp = cp->next;
554
 
            gs_free_object((gs_memory_t *)mem, cp, "alloc_save_remove");
555
 
        } else
556
 
            cpp = &(*cpp)->next;
557
 
    }
558
 
}
559
 
 
560
 
/* Filter save change lists. */
561
 
static inline void
562
 
alloc_save__filter_changes_in_space(gs_ref_memory_t *mem)
563
 
{
564
 
    /* This is a special function, which is called
565
 
       from the garbager after setting marks and before collecting
566
 
       unused space. Therefore it just resets marks for
567
 
       elements being released instead releasing them really. */
568
 
    alloc_change_t **cpp = &mem->changes;
569
 
    
570
 
    for (; *cpp != NULL; ) {
571
 
        alloc_change_t *cp = *cpp;
572
 
 
573
 
        if (cp->offset == AC_OFFSET_ALLOCATED && !check_l_mark(cp->where)) {
574
 
            obj_header_t *pre = (obj_header_t *)cp - 1;
575
 
 
576
 
            *cpp = cp->next;
577
 
            cp->where = 0;
578
 
            if (mem->scan_limit == cp)
579
 
                mem->scan_limit = cp->next;
580
 
            o_set_unmarked(pre);
581
 
        } else
582
 
            cpp = &(*cpp)->next;
583
 
    }
584
 
}
585
 
 
586
 
/* Filter save change lists. */
587
 
void
588
 
alloc_save__filter_changes(gs_ref_memory_t *memory)
589
 
{
590
 
    gs_ref_memory_t *mem = memory;
591
 
 
592
 
    for  (; mem; mem = &mem->saved->state)
593
 
        alloc_save__filter_changes_in_space(mem);
594
 
}
595
 
 
596
 
/* Return (the id of) the innermost externally visible save object, */
597
 
/* i.e., the innermost save with a non-zero ID. */
598
 
ulong
599
 
alloc_save_current_id(const gs_dual_memory_t * dmem)
600
 
{
601
 
    const alloc_save_t *save = dmem->space_local->saved;
602
 
 
603
 
    while (save != 0 && save->id == 0)
604
 
        save = save->state.saved;
605
 
    return save->id;
606
 
}
607
 
alloc_save_t *
608
 
alloc_save_current(const gs_dual_memory_t * dmem)
609
 
{
610
 
    return alloc_find_save(dmem, alloc_save_current_id(dmem));
611
 
}
612
 
 
613
 
/* Test whether a reference would be invalidated by a restore. */
614
 
bool
615
 
alloc_is_since_save(const void *vptr, const alloc_save_t * save)
616
 
{
617
 
    /* A reference postdates a save iff it is in a chunk allocated */
618
 
    /* since the save (including any carried-over inner chunks). */
619
 
 
620
 
    const char *const ptr = (const char *)vptr;
621
 
    register const gs_ref_memory_t *mem = save->space_local;
622
 
 
623
 
    if_debug2('U', "[U]is_since_save 0x%lx, 0x%lx:\n",
624
 
              (ulong) ptr, (ulong) save);
625
 
    if (mem->saved == 0) {      /* This is a special case, the final 'restore' from */
626
 
        /* alloc_restore_all. */
627
 
        return true;
628
 
    }
629
 
    /* Check against chunks allocated since the save. */
630
 
    /* (There may have been intermediate saves as well.) */
631
 
    for (;; mem = &mem->saved->state) {
632
 
        const chunk_t *cp;
633
 
 
634
 
        if_debug1('U', "[U]checking mem=0x%lx\n", (ulong) mem);
635
 
        for (cp = mem->cfirst; cp != 0; cp = cp->cnext) {
636
 
            if (ptr_is_within_chunk(ptr, cp)) {
637
 
                if_debug3('U', "[U+]in new chunk 0x%lx: 0x%lx, 0x%lx\n",
638
 
                          (ulong) cp,
639
 
                          (ulong) cp->cbase, (ulong) cp->cend);
640
 
                return true;
641
 
            }
642
 
            if_debug1('U', "[U-]not in 0x%lx\n", (ulong) cp);
643
 
        }
644
 
        if (mem->saved == save) {       /* We've checked all the more recent saves, */
645
 
            /* must be OK. */
646
 
            break;
647
 
        }
648
 
    }
649
 
 
650
 
    /*
651
 
     * If we're about to do a global restore (a restore to the level 0),
652
 
     * and there is only one context using this global VM
653
 
     * (the normal case, in which global VM is saved by the
654
 
     * outermost save), we also have to check the global save.
655
 
     * Global saves can't be nested, which makes things easy.
656
 
     */
657
 
    if (save->state.save_level == 0 /* Restoring to save level 0 - see bug 688157, 688161 */ &&
658
 
        (mem = save->space_global) != save->space_local &&
659
 
        save->space_global->num_contexts == 1
660
 
        ) {
661
 
        const chunk_t *cp;
662
 
 
663
 
        if_debug1('U', "[U]checking global mem=0x%lx\n", (ulong) mem);
664
 
        for (cp = mem->cfirst; cp != 0; cp = cp->cnext)
665
 
            if (ptr_is_within_chunk(ptr, cp)) {
666
 
                if_debug3('U', "[U+]  new chunk 0x%lx: 0x%lx, 0x%lx\n",
667
 
                          (ulong) cp, (ulong) cp->cbase, (ulong) cp->cend);
668
 
                return true;
669
 
            }
670
 
    }
671
 
    return false;
672
 
 
673
 
#undef ptr
674
 
}
675
 
 
676
 
/* Test whether a name would be invalidated by a restore. */
677
 
bool
678
 
alloc_name_is_since_save(const gs_memory_t *mem,
679
 
                         const ref * pnref, const alloc_save_t * save)
680
 
{
681
 
    const name_string_t *pnstr;
682
 
 
683
 
    if (!save->restore_names)
684
 
        return false;
685
 
    pnstr = names_string_inline(mem->gs_lib_ctx->gs_name_table, pnref);
686
 
    if (pnstr->foreign_string)
687
 
        return false;
688
 
    return alloc_is_since_save(pnstr->string_bytes, save);
689
 
}
690
 
bool
691
 
alloc_name_index_is_since_save(const gs_memory_t *mem,
692
 
                               uint nidx, const alloc_save_t *save)
693
 
{
694
 
    const name_string_t *pnstr;
695
 
 
696
 
    if (!save->restore_names)
697
 
        return false;
698
 
    pnstr = names_index_string_inline(mem->gs_lib_ctx->gs_name_table, nidx);
699
 
    if (pnstr->foreign_string)
700
 
        return false;
701
 
    return alloc_is_since_save(pnstr->string_bytes, save);
702
 
}
703
 
 
704
 
/* Check whether any names have been created since a given save */
705
 
/* that might be released by the restore. */
706
 
bool
707
 
alloc_any_names_since_save(const alloc_save_t * save)
708
 
{
709
 
    return save->restore_names;
710
 
}
711
 
 
712
 
/* Get the saved state with a given ID. */
713
 
alloc_save_t *
714
 
alloc_find_save(const gs_dual_memory_t * dmem, ulong sid)
715
 
{
716
 
    alloc_save_t *sprev = dmem->space_local->saved;
717
 
 
718
 
    if (sid == 0)
719
 
        return 0;               /* invalid id */
720
 
    while (sprev != 0) {
721
 
        if (sprev->id == sid)
722
 
            return sprev;
723
 
        sprev = sprev->state.saved;
724
 
    }
725
 
    return 0;
726
 
}
727
 
 
728
 
/* Get the client data from a saved state. */
729
 
void *
730
 
alloc_save_client_data(const alloc_save_t * save)
731
 
{
732
 
    return save->client_data;
733
 
}
734
 
 
735
 
/*
736
 
 * Do one step of restoring the state.  The client is responsible for
737
 
 * calling alloc_find_save to get the save object, and for ensuring that
738
 
 * there are no surviving pointers for which alloc_is_since_save is true.
739
 
 * Return true if the argument was the innermost save, in which case
740
 
 * this is the last (or only) step.
741
 
 * Note that "one step" may involve multiple internal steps,
742
 
 * if this is the outermost restore (which requires restoring both local
743
 
 * and global VM) or if we created extra save levels to reduce scanning.
744
 
 */
745
 
static void restore_finalize(gs_ref_memory_t *);
746
 
static void restore_space(gs_ref_memory_t *, gs_dual_memory_t *);
747
 
 
748
 
int
749
 
alloc_restore_step_in(gs_dual_memory_t *dmem, alloc_save_t * save)
750
 
{
751
 
    /* Get save->space_* now, because the save object will be freed. */
752
 
    gs_ref_memory_t *lmem = save->space_local;
753
 
    gs_ref_memory_t *gmem = save->space_global;
754
 
    gs_ref_memory_t *mem = lmem;
755
 
    alloc_save_t *sprev;
756
 
    int code;
757
 
 
758
 
    /* Finalize all objects before releasing resources or undoing changes. */
759
 
    do {
760
 
        ulong sid;
761
 
 
762
 
        sprev = mem->saved;
763
 
        sid = sprev->id;
764
 
        restore_finalize(mem);  /* finalize objects */
765
 
        mem = &sprev->state;
766
 
        if (sid != 0)
767
 
            break;
768
 
    }
769
 
    while (sprev != save);
770
 
    if (mem->save_level == 0) {
771
 
        /* This is the outermost save, which might also */
772
 
        /* need to restore global VM. */
773
 
        mem = gmem;
774
 
        if (mem != lmem && mem->saved != 0)
775
 
            restore_finalize(mem);
776
 
    }
777
 
 
778
 
    /* Do one (externally visible) step of restoring the state. */
779
 
    mem = lmem;
780
 
    do {
781
 
        ulong sid;
782
 
 
783
 
        sprev = mem->saved;
784
 
        sid = sprev->id;
785
 
        code = restore_resources(sprev, mem);   /* release other resources */
786
 
        if (code < 0)
787
 
            return code;
788
 
        restore_space(mem, dmem);       /* release memory */
789
 
        if (sid != 0)
790
 
            break;
791
 
    }
792
 
    while (sprev != save);
793
 
 
794
 
    if (mem->save_level == 0) {
795
 
        /* This is the outermost save, which might also */
796
 
        /* need to restore global VM. */
797
 
        mem = gmem;
798
 
        if (mem != lmem && mem->saved != 0) {
799
 
            code = restore_resources(mem->saved, mem);
800
 
            if (code < 0)
801
 
                return code;
802
 
            restore_space(mem, dmem);
803
 
        }
804
 
        alloc_set_not_in_save(dmem);
805
 
    } else {                    /* Set the l_new attribute in all slots that are now new. */
806
 
        ulong scanned;
807
 
 
808
 
        code = save_set_new(mem, true, false, &scanned);
809
 
        if (code < 0)
810
 
            return code;
811
 
    }
812
 
 
813
 
    return sprev == save;
814
 
}
815
 
/* Restore the memory of one space, by undoing changes and freeing */
816
 
/* memory allocated since the save. */
817
 
static void
818
 
restore_space(gs_ref_memory_t * mem, gs_dual_memory_t *dmem)
819
 
{
820
 
    alloc_save_t *save = mem->saved;
821
 
    alloc_save_t saved;
822
 
 
823
 
    print_save("restore", mem->space, save);
824
 
 
825
 
    /* Undo changes since the save. */
826
 
    {
827
 
        register alloc_change_t *cp = mem->changes;
828
 
 
829
 
        while (cp) {
830
 
#ifdef DEBUG
831
 
            if (gs_debug_c('U')) {
832
 
                dlputs("[U]restore");
833
 
                alloc_save_print(cp, true);
834
 
            }
835
 
#endif
836
 
            if (cp->offset == AC_OFFSET_ALLOCATED)
837
 
                DO_NOTHING;
838
 
            else
839
 
            if (r_is_packed(&cp->contents))
840
 
                *cp->where = *(ref_packed *) & cp->contents;
841
 
            else
842
 
                ref_assign_inline((ref *) cp->where, &cp->contents);
843
 
            cp = cp->next;
844
 
        }
845
 
    }
846
 
 
847
 
    /* Free memory allocated since the save. */
848
 
    /* Note that this frees all chunks except the inner ones */
849
 
    /* belonging to this level. */
850
 
    saved = *save;
851
 
    restore_free(mem);
852
 
 
853
 
    /* Restore the allocator state. */
854
 
    {
855
 
        int num_contexts = mem->num_contexts;   /* don't restore */
856
 
 
857
 
        *mem = saved.state;
858
 
        mem->num_contexts = num_contexts;
859
 
    }
860
 
    alloc_open_chunk(mem);
861
 
 
862
 
    /* Make the allocator current if it was current before the save. */
863
 
    if (saved.is_current) {
864
 
        dmem->current = mem;
865
 
        dmem->current_space = mem->space;
866
 
    }
867
 
}
868
 
 
869
 
/* Restore to the initial state, releasing all resources. */
870
 
/* The allocator is no longer usable after calling this routine! */
871
 
int
872
 
alloc_restore_all(gs_dual_memory_t * dmem)
873
 
{
874
 
    /*
875
 
     * Save the memory pointers, since freeing space_local will also
876
 
     * free dmem itself.
877
 
     */
878
 
    gs_ref_memory_t *lmem = dmem->space_local;
879
 
    gs_ref_memory_t *gmem = dmem->space_global;
880
 
    gs_ref_memory_t *smem = dmem->space_system;
881
 
    gs_ref_memory_t *mem;
882
 
    int code;
883
 
 
884
 
    /* Restore to a state outside any saves. */
885
 
    while (lmem->save_level != 0) {
886
 
        code = alloc_restore_step_in(dmem, lmem->saved);
887
 
        if (code < 0)
888
 
            return code;
889
 
    }
890
 
 
891
 
    /* Finalize memory. */
892
 
    restore_finalize(lmem);
893
 
    if ((mem = (gs_ref_memory_t *)lmem->stable_memory) != lmem)
894
 
        restore_finalize(mem);
895
 
    if (gmem != lmem && gmem->num_contexts == 1) {
896
 
        restore_finalize(gmem);
897
 
        if ((mem = (gs_ref_memory_t *)gmem->stable_memory) != gmem)
898
 
            restore_finalize(mem);
899
 
    }
900
 
    restore_finalize(smem);
901
 
 
902
 
    /* Release resources other than memory, using fake */
903
 
    /* save and memory objects. */
904
 
    {
905
 
        alloc_save_t empty_save;
906
 
 
907
 
        empty_save.spaces = dmem->spaces;
908
 
        empty_save.restore_names = false;       /* don't bother to release */
909
 
        code = restore_resources(&empty_save, NULL);
910
 
        if (code < 0)
911
 
            return code;
912
 
    }
913
 
 
914
 
    /* Finally, release memory. */
915
 
    restore_free(lmem);
916
 
    if ((mem = (gs_ref_memory_t *)lmem->stable_memory) != lmem)
917
 
        restore_free(mem);
918
 
    if (gmem != lmem) {
919
 
        if (!--(gmem->num_contexts)) {
920
 
            restore_free(gmem);
921
 
            if ((mem = (gs_ref_memory_t *)gmem->stable_memory) != gmem)
922
 
                restore_free(mem);
923
 
        }
924
 
    }
925
 
    restore_free(smem);
926
 
    return 0;
927
 
}
928
 
 
929
 
/*
930
 
 * Finalize objects that will be freed by a restore.
931
 
 * Note that we must temporarily disable the freeing operations
932
 
 * of the allocator while doing this.
933
 
 */
934
 
static void
935
 
restore_finalize(gs_ref_memory_t * mem)
936
 
{
937
 
    chunk_t *cp;
938
 
 
939
 
    alloc_close_chunk(mem);
940
 
    gs_enable_free((gs_memory_t *) mem, false);
941
 
    for (cp = mem->clast; cp != 0; cp = cp->cprev) {
942
 
        SCAN_CHUNK_OBJECTS(cp)
943
 
            DO_ALL
944
 
            struct_proc_finalize((*finalize)) =
945
 
            pre->o_type->finalize;
946
 
        if (finalize != 0) {
947
 
            if_debug2('u', "[u]restore finalizing %s 0x%lx\n",
948
 
                      struct_type_name_string(pre->o_type),
949
 
                      (ulong) (pre + 1));
950
 
            (*finalize) (pre + 1);
951
 
        }
952
 
        END_OBJECTS_SCAN
953
 
    }
954
 
    gs_enable_free((gs_memory_t *) mem, true);
955
 
}
956
 
 
957
 
/* Release resources for a restore */
958
 
static int
959
 
restore_resources(alloc_save_t * sprev, gs_ref_memory_t * mem)
960
 
{
961
 
    int code;
962
 
#ifdef DEBUG
963
 
    if (mem) {
964
 
        /* Note restoring of the file list. */
965
 
        if_debug4('u', "[u%u]file_restore 0x%lx => 0x%lx for 0x%lx\n",
966
 
                  mem->space, (ulong)mem->streams,
967
 
                  (ulong)sprev->state.streams, (ulong) sprev);
968
 
    }
969
 
#endif
970
 
 
971
 
    /* Remove entries from font and character caches. */
972
 
    code = font_restore(sprev);
973
 
    if (code < 0)
974
 
        return code;
975
 
 
976
 
    /* Adjust the name table. */
977
 
    if (sprev->restore_names)
978
 
        names_restore(mem->gs_lib_ctx->gs_name_table, sprev);
979
 
    return 0;
980
 
}
981
 
 
982
 
/* Release memory for a restore. */
983
 
static void
984
 
restore_free(gs_ref_memory_t * mem)
985
 
{
986
 
    /* Free chunks allocated since the save. */
987
 
    gs_free_all((gs_memory_t *) mem);
988
 
}
989
 
 
990
 
/* Forget a save, by merging this level with the next outer one. */
991
 
static void file_forget_save(gs_ref_memory_t *);
992
 
static void combine_space(gs_ref_memory_t *);
993
 
static void forget_changes(gs_ref_memory_t *);
994
 
int
995
 
alloc_forget_save_in(gs_dual_memory_t *dmem, alloc_save_t * save)
996
 
{
997
 
    gs_ref_memory_t *mem = save->space_local;
998
 
    alloc_save_t *sprev;
999
 
    ulong scanned;
1000
 
    int code;
1001
 
 
1002
 
    print_save("forget_save", mem->space, save);
1003
 
 
1004
 
    /* Iteratively combine the current level with the previous one. */
1005
 
    do {
1006
 
        sprev = mem->saved;
1007
 
        if (sprev->id != 0)
1008
 
            mem->save_level--;
1009
 
        if (mem->save_level != 0) {
1010
 
            alloc_change_t *chp = mem->changes;
1011
 
 
1012
 
            code = save_set_new(&sprev->state, true, false, &scanned);
1013
 
            if (code < 0)
1014
 
                return code;
1015
 
            /* Concatenate the changes chains. */
1016
 
            if (chp == 0)
1017
 
                mem->changes = sprev->state.changes;
1018
 
            else {
1019
 
                while (chp->next != 0)
1020
 
                    chp = chp->next;
1021
 
                chp->next = sprev->state.changes;
1022
 
            }
1023
 
            file_forget_save(mem);
1024
 
            combine_space(mem); /* combine memory */
1025
 
        } else {
1026
 
            forget_changes(mem);
1027
 
            code = save_set_new(mem, false, false, &scanned);
1028
 
            if (code < 0)
1029
 
                return code;
1030
 
            file_forget_save(mem);
1031
 
            combine_space(mem); /* combine memory */
1032
 
            /* This is the outermost save, which might also */
1033
 
            /* need to combine global VM. */
1034
 
            mem = save->space_global;
1035
 
            if (mem != save->space_local && mem->saved != 0) {
1036
 
                forget_changes(mem);
1037
 
                code = save_set_new(mem, false, false, &scanned);
1038
 
                if (code < 0)
1039
 
                    return code;
1040
 
                file_forget_save(mem);
1041
 
                combine_space(mem);
1042
 
            }
1043
 
            alloc_set_not_in_save(dmem);
1044
 
            break;              /* must be outermost */
1045
 
        }
1046
 
    }
1047
 
    while (sprev != save);
1048
 
    return 0;
1049
 
}
1050
 
/* Combine the chunks of the next outer level with those of the current one, */
1051
 
/* and free the bookkeeping structures. */
1052
 
static void
1053
 
combine_space(gs_ref_memory_t * mem)
1054
 
{
1055
 
    alloc_save_t *saved = mem->saved;
1056
 
    gs_ref_memory_t *omem = &saved->state;
1057
 
    chunk_t *cp;
1058
 
    chunk_t *csucc;
1059
 
 
1060
 
    alloc_close_chunk(mem);
1061
 
    for (cp = mem->cfirst; cp != 0; cp = csucc) {
1062
 
        csucc = cp->cnext;      /* save before relinking */
1063
 
        if (cp->outer == 0)
1064
 
            alloc_link_chunk(cp, omem);
1065
 
        else {
1066
 
            chunk_t *outer = cp->outer;
1067
 
 
1068
 
            outer->inner_count--;
1069
 
            if (mem->pcc == cp)
1070
 
                mem->pcc = outer;
1071
 
            if (mem->cfreed.cp == cp)
1072
 
                mem->cfreed.cp = outer;
1073
 
            /* "Free" the header of the inner chunk, */
1074
 
            /* and any immediately preceding gap left by */
1075
 
            /* the GC having compacted the outer chunk. */
1076
 
            {
1077
 
                obj_header_t *hp = (obj_header_t *) outer->cbot;
1078
 
 
1079
 
                hp->o_alone = 0;
1080
 
                hp->o_size = (char *)(cp->chead + 1)
1081
 
                    - (char *)(hp + 1);
1082
 
                hp->o_type = &st_bytes;
1083
 
                /* The following call is probably not safe. */
1084
 
#if 0                           /* **************** */
1085
 
                gs_free_object((gs_memory_t *) mem,
1086
 
                               hp + 1, "combine_space(header)");
1087
 
#endif /* **************** */
1088
 
            }
1089
 
            /* Update the outer chunk's allocation pointers. */
1090
 
            outer->cbot = cp->cbot;
1091
 
            outer->rcur = cp->rcur;
1092
 
            outer->rtop = cp->rtop;
1093
 
            outer->ctop = cp->ctop;
1094
 
            outer->has_refs |= cp->has_refs;
1095
 
            gs_free_object(mem->non_gc_memory, cp,
1096
 
                           "combine_space(inner)");
1097
 
        }
1098
 
    }
1099
 
    /* Update relevant parts of allocator state. */
1100
 
    mem->cfirst = omem->cfirst;
1101
 
    mem->clast = omem->clast;
1102
 
    mem->allocated += omem->allocated;
1103
 
    mem->gc_allocated += omem->allocated;
1104
 
    mem->lost.objects += omem->lost.objects;
1105
 
    mem->lost.refs += omem->lost.refs;
1106
 
    mem->lost.strings += omem->lost.strings;
1107
 
    mem->saved = omem->saved;
1108
 
    mem->previous_status = omem->previous_status;
1109
 
    {                           /* Concatenate free lists. */
1110
 
        int i;
1111
 
 
1112
 
        for (i = 0; i < num_freelists; i++) {
1113
 
            obj_header_t *olist = omem->freelists[i];
1114
 
            obj_header_t *list = mem->freelists[i];
1115
 
 
1116
 
            if (olist == 0);
1117
 
            else if (list == 0)
1118
 
                mem->freelists[i] = olist;
1119
 
            else {
1120
 
                while (*(obj_header_t **) list != 0)
1121
 
                    list = *(obj_header_t **) list;
1122
 
                *(obj_header_t **) list = olist;
1123
 
            }
1124
 
        }
1125
 
        if (omem->largest_free_size > mem->largest_free_size)
1126
 
            mem->largest_free_size = omem->largest_free_size;
1127
 
    }
1128
 
    gs_free_object((gs_memory_t *) mem, saved, "combine_space(saved)");
1129
 
    alloc_open_chunk(mem);
1130
 
}
1131
 
/* Free the changes chain for a level 0 .forgetsave, */
1132
 
/* resetting the l_new flag in the changed refs. */
1133
 
static void
1134
 
forget_changes(gs_ref_memory_t * mem)
1135
 
{
1136
 
    register alloc_change_t *chp = mem->changes;
1137
 
    alloc_change_t *next;
1138
 
 
1139
 
    for (; chp; chp = next) {
1140
 
        ref_packed *prp = chp->where;
1141
 
 
1142
 
        if_debug1('U', "[U]forgetting change 0x%lx\n", (ulong) chp);
1143
 
        if (chp->offset == AC_OFFSET_ALLOCATED)
1144
 
            DO_NOTHING;
1145
 
        else
1146
 
        if (!r_is_packed(prp))
1147
 
            r_clear_attrs((ref *) prp, l_new);
1148
 
        next = chp->next;
1149
 
        gs_free_object((gs_memory_t *) mem, chp, "forget_changes");
1150
 
    }
1151
 
    mem->changes = 0;
1152
 
}
1153
 
/* Update the streams list when forgetting a save. */
1154
 
static void
1155
 
file_forget_save(gs_ref_memory_t * mem)
1156
 
{
1157
 
    const alloc_save_t *save = mem->saved;
1158
 
    stream *streams = mem->streams;
1159
 
    stream *saved_streams = save->state.streams;
1160
 
 
1161
 
    if_debug4('u', "[u%d]file_forget_save 0x%lx + 0x%lx for 0x%lx\n",
1162
 
              mem->space, (ulong) streams, (ulong) saved_streams,
1163
 
              (ulong) save);
1164
 
    if (streams == 0)
1165
 
        mem->streams = saved_streams;
1166
 
    else if (saved_streams != 0) {
1167
 
        while (streams->next != 0)
1168
 
            streams = streams->next;
1169
 
        streams->next = saved_streams;
1170
 
        saved_streams->prev = streams;
1171
 
    }
1172
 
}
1173
 
 
1174
 
static inline int
1175
 
mark_allocated(void *obj, bool to_new, uint *psize)
1176
 
{   
1177
 
    obj_header_t *pre = (obj_header_t *)obj - 1;
1178
 
    uint size = pre_obj_contents_size(pre);
1179
 
    ref_packed *prp = (ref_packed *) (pre + 1);
1180
 
    ref_packed *next = (ref_packed *) ((char *)prp + size);
1181
 
#ifdef ALIGNMENT_ALIASING_BUG
1182
 
                ref *rpref;
1183
 
# define RP_REF(rp) (rpref = (ref *)rp, rpref)
1184
 
#else
1185
 
# define RP_REF(rp) ((ref *)rp)
1186
 
#endif
1187
 
 
1188
 
    if (pre->o_type != &st_refs) {
1189
 
        /* Must not happen. */
1190
 
        if_debug0('u', "Wrong object type when expected a ref.\n");
1191
 
        return_error(e_Fatal);
1192
 
    }
1193
 
    /* We know that every block of refs ends with */
1194
 
    /* a full-size ref, so we only need the end check */
1195
 
    /* when we encounter one of those. */
1196
 
    if (to_new)
1197
 
        while (1) {
1198
 
            if (r_is_packed(prp))
1199
 
                prp++;
1200
 
            else {
1201
 
                RP_REF(prp)->tas.type_attrs |= l_new;
1202
 
                prp += packed_per_ref;
1203
 
                if (prp >= next)
1204
 
                    break;
1205
 
            }
1206
 
    } else
1207
 
        while (1) {
1208
 
            if (r_is_packed(prp))
1209
 
                prp++;
1210
 
            else {
1211
 
                RP_REF(prp)->tas.type_attrs &= ~l_new;
1212
 
                prp += packed_per_ref;
1213
 
                if (prp >= next)
1214
 
                    break;
1215
 
            }
1216
 
        }
1217
 
#undef RP_REF
1218
 
    *psize = size;
1219
 
    return 0;
1220
 
}
1221
 
 
1222
 
/* Check if a block contains refs marked by garbager. */
1223
 
static bool
1224
 
check_l_mark(void *obj)
1225
 
{   
1226
 
    obj_header_t *pre = (obj_header_t *)obj - 1;
1227
 
    uint size = pre_obj_contents_size(pre);
1228
 
    ref_packed *prp = (ref_packed *) (pre + 1);
1229
 
    ref_packed *next = (ref_packed *) ((char *)prp + size);
1230
 
#ifdef ALIGNMENT_ALIASING_BUG
1231
 
                ref *rpref;
1232
 
# define RP_REF(rp) (rpref = (ref *)rp, rpref)
1233
 
#else
1234
 
# define RP_REF(rp) ((ref *)rp)
1235
 
#endif
1236
 
 
1237
 
    /* We know that every block of refs ends with */
1238
 
    /* a full-size ref, so we only need the end check */
1239
 
    /* when we encounter one of those. */
1240
 
    while (1) {
1241
 
        if (r_is_packed(prp)) {
1242
 
            if (r_has_pmark(prp))
1243
 
                return true;
1244
 
            prp++;
1245
 
        } else {
1246
 
            if (r_has_attr(RP_REF(prp), l_mark))
1247
 
                return true;
1248
 
            prp += packed_per_ref;
1249
 
            if (prp >= next)
1250
 
                return false;
1251
 
        }
1252
 
    }
1253
 
#undef RP_REF
1254
 
}
1255
 
 
1256
 
/* Set or reset the l_new attribute in every relevant slot. */
1257
 
/* This includes every slot on the current change chain, */
1258
 
/* and every (ref) slot allocated at this save level. */
1259
 
/* Return the number of bytes of data scanned. */
1260
 
static int
1261
 
save_set_new(gs_ref_memory_t * mem, bool to_new, bool set_limit, ulong *pscanned)
1262
 
{
1263
 
    ulong scanned = 0;
1264
 
    int code;
1265
 
 
1266
 
    /* Handle the change chain. */
1267
 
    code = save_set_new_changes(mem, to_new, set_limit);
1268
 
    if (code < 0)
1269
 
        return code;
1270
 
 
1271
 
    /* Handle newly allocated ref objects. */
1272
 
    SCAN_MEM_CHUNKS(mem, cp) {
1273
 
        if (cp->has_refs) {
1274
 
            bool has_refs = false;
1275
 
 
1276
 
            SCAN_CHUNK_OBJECTS(cp)
1277
 
                DO_ALL
1278
 
                if_debug3('U', "[U]set_new scan(0x%lx(%u), %d)\n",
1279
 
                          (ulong) pre, size, to_new);
1280
 
            if (pre->o_type == &st_refs) {
1281
 
                /* These are refs, scan them. */
1282
 
                ref_packed *prp = (ref_packed *) (pre + 1);
1283
 
                uint size;
1284
 
                
1285
 
                code = mark_allocated(prp, to_new, &size);
1286
 
                if (code < 0)
1287
 
                    return code;
1288
 
                scanned += size;
1289
 
            } else
1290
 
                scanned += sizeof(obj_header_t);
1291
 
            END_OBJECTS_SCAN
1292
 
                cp->has_refs = has_refs;
1293
 
        }
1294
 
    }
1295
 
    END_CHUNKS_SCAN
1296
 
        if_debug2('u', "[u]set_new (%s) scanned %ld\n",
1297
 
                  (to_new ? "restore" : "save"), scanned);
1298
 
    *pscanned = scanned;
1299
 
    return 0;
1300
 
}
1301
 
 
1302
 
/* Drop redundant elements from the changes list and set l_new. */
1303
 
static void
1304
 
drop_redundant_changes(gs_ref_memory_t * mem)
1305
 
{
1306
 
    register alloc_change_t *chp = mem->changes, *chp_back = NULL, *chp_forth;
1307
 
 
1308
 
    /* First reverse the list and set all. */
1309
 
    for (; chp; chp = chp_forth) {
1310
 
        chp_forth = chp->next;
1311
 
        if (chp->offset != AC_OFFSET_ALLOCATED) {
1312
 
            ref_packed *prp = chp->where;
1313
 
 
1314
 
            if (!r_is_packed(prp)) {
1315
 
                ref *const rp = (ref *)prp;
1316
 
 
1317
 
                rp->tas.type_attrs |= l_new;
1318
 
            }
1319
 
        }
1320
 
        chp->next = chp_back;
1321
 
        chp_back = chp;
1322
 
    }
1323
 
    mem->changes = chp_back;
1324
 
    chp_back = NULL;
1325
 
    /* Then filter, reset and reverse again. */
1326
 
    for (chp = mem->changes; chp; chp = chp_forth) {
1327
 
        chp_forth = chp->next;
1328
 
        if (chp->offset != AC_OFFSET_ALLOCATED) {
1329
 
            ref_packed *prp = chp->where;
1330
 
 
1331
 
            if (!r_is_packed(prp)) {
1332
 
                ref *const rp = (ref *)prp;
1333
 
 
1334
 
                if ((rp->tas.type_attrs & l_new) == 0) {
1335
 
                    if (mem->scan_limit == chp)
1336
 
                        mem->scan_limit = chp_back;
1337
 
                    if (mem->changes == chp)
1338
 
                        mem->changes = chp_back;
1339
 
                    gs_free_object((gs_memory_t *)mem, chp, "alloc_save_remove");
1340
 
                    continue;
1341
 
                } else
1342
 
                    rp->tas.type_attrs &= ~l_new;
1343
 
            }
1344
 
        }
1345
 
        chp->next = chp_back;
1346
 
        chp_back = chp;
1347
 
    }
1348
 
    mem->changes = chp_back;
1349
 
}
1350
 
 
1351
 
 
1352
 
/* Set or reset the l_new attribute on the changes chain. */
1353
 
static int
1354
 
save_set_new_changes(gs_ref_memory_t * mem, bool to_new, bool set_limit)
1355
 
{
1356
 
    register alloc_change_t *chp;
1357
 
    register uint new = (to_new ? l_new : 0);
1358
 
    ulong scanned = 0;
1359
 
 
1360
 
    if (!to_new && mem->total_scanned_after_compacting > max_repeated_scan * 16) {
1361
 
        mem->total_scanned_after_compacting = 0;
1362
 
        drop_redundant_changes(mem);
1363
 
    } 
1364
 
    for (chp = mem->changes; chp; chp = chp->next) {
1365
 
        if (chp->offset == AC_OFFSET_ALLOCATED) {
1366
 
            if (chp->where != 0) {
1367
 
                uint size;
1368
 
                int code = mark_allocated((void *)chp->where, to_new, &size);
1369
 
 
1370
 
                if (code < 0)
1371
 
                    return code;
1372
 
                scanned += size;
1373
 
            }
1374
 
        } else {
1375
 
            ref_packed *prp = chp->where;
1376
 
 
1377
 
            if_debug3('U', "[U]set_new 0x%lx: (0x%lx, %d)\n",
1378
 
                    (ulong)chp, (ulong)prp, new);
1379
 
            if (!r_is_packed(prp)) {
1380
 
                ref *const rp = (ref *) prp;
1381
 
 
1382
 
                rp->tas.type_attrs =
1383
 
                    (rp->tas.type_attrs & ~l_new) + new;
1384
 
            }
1385
 
        }
1386
 
        if (mem->scan_limit == chp)
1387
 
            break;
1388
 
    }
1389
 
    if (set_limit) {
1390
 
        mem->total_scanned_after_compacting += scanned;
1391
 
        if (scanned  + mem->total_scanned >= max_repeated_scan) {
1392
 
            mem->scan_limit = mem->changes;
1393
 
            mem->total_scanned = 0;
1394
 
        } else
1395
 
            mem->total_scanned += scanned;
1396
 
    }
1397
 
    return 0;
1398
 
}