Coverage Report

Created: 2026-07-24 07:44

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