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

« back to all changes in this revision

Viewing changes to psi/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 9043 2008-08-28 22:48:19Z giles $ */
 
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
}