Coverage Report

Created: 2025-12-31 07:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/psi/ialloc.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 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
/* Memory allocator for Ghostscript interpreter */
18
#include "gx.h"
19
#include "memory_.h"
20
#include "gsexit.h"
21
#include "ierrors.h"
22
#include "gsstruct.h"
23
#include "iref.h"   /* must precede iastate.h */
24
#include "iastate.h"
25
#include "igc.h"    /* for gs_gc_reclaim */
26
#include "ipacked.h"
27
#include "iutil.h"
28
#include "ivmspace.h"
29
#include "store.h"
30
31
/*
32
 * Define global and local instances.
33
 */
34
public_st_gs_dual_memory();
35
36
/* Initialize the allocator */
37
int
38
ialloc_init(gs_dual_memory_t *dmem, gs_memory_t * rmem, uint clump_size,
39
            bool level2)
40
182k
{
41
182k
    gs_ref_memory_t *ilmem = ialloc_alloc_state(rmem, clump_size);
42
182k
    gs_ref_memory_t *ilmem_stable = ialloc_alloc_state(rmem, clump_size);
43
182k
    gs_ref_memory_t *igmem = 0;
44
182k
    gs_ref_memory_t *igmem_stable = 0;
45
182k
    gs_ref_memory_t *ismem = ialloc_alloc_state(rmem, clump_size);
46
182k
    int i;
47
48
182k
    if (ilmem == 0 || ilmem_stable == 0 || ismem == 0)
49
0
        goto fail;
50
182k
    ilmem->stable_memory = (gs_memory_t *)ilmem_stable;
51
182k
    if (level2) {
52
182k
        igmem = ialloc_alloc_state(rmem, clump_size);
53
182k
        igmem_stable = ialloc_alloc_state(rmem, clump_size);
54
182k
        if (igmem == 0 || igmem_stable == 0)
55
0
            goto fail;
56
182k
        igmem->stable_memory = (gs_memory_t *)igmem_stable;
57
182k
    } else
58
0
        igmem = ilmem, igmem_stable = ilmem_stable;
59
914k
    for (i = 0; i < countof(dmem->spaces_indexed); i++)
60
731k
        dmem->spaces_indexed[i] = 0;
61
182k
    dmem->space_local = ilmem;
62
182k
    dmem->space_global = igmem;
63
182k
    dmem->space_system = ismem;
64
182k
    dmem->spaces.vm_reclaim = gs_gc_reclaim; /* real GC */
65
182k
    dmem->reclaim = 0;    /* no interpreter GC yet */
66
    /* Level 1 systems have only local VM. */
67
182k
    igmem->space = avm_global;
68
182k
    igmem_stable->space = avm_global;
69
182k
    ilmem->space = avm_local; /* overrides if ilmem == igmem */
70
182k
    ilmem_stable->space = avm_local; /* ditto */
71
182k
    ismem->space = avm_system;
72
#   if IGC_PTR_STABILITY_CHECK
73
    igmem->space_id = (i_vm_global << 1) + 1;
74
    igmem_stable->space_id = i_vm_global << 1;
75
    ilmem->space_id = (i_vm_local << 1) + 1;  /* overrides if ilmem == igmem */
76
    ilmem_stable->space_id = i_vm_local << 1; /* ditto */
77
    ismem->space_id = (i_vm_system << 1);
78
#   endif
79
182k
    ialloc_set_space(dmem, avm_global);
80
182k
    return 0;
81
0
 fail:
82
0
    ialloc_free_state(igmem_stable);
83
0
    ialloc_free_state(igmem);
84
0
    ialloc_free_state(ismem);
85
0
    ialloc_free_state(ilmem_stable);
86
0
    ialloc_free_state(ilmem);
87
0
    return_error(gs_error_VMerror);
88
182k
}
89
90
/* Free the allocator */
91
void
92
ialloc_finit(gs_dual_memory_t *mem)
93
182k
{
94
182k
    if (mem != NULL) {
95
182k
        gs_ref_memory_t *ilmem = mem->space_local;
96
182k
        gs_ref_memory_t *igmem = mem->space_global;
97
182k
        gs_ref_memory_t *ismem = mem->space_system;
98
99
182k
        if (ilmem != NULL) {
100
182k
            gs_ref_memory_t *ilmem_stable = (gs_ref_memory_t *)(ilmem->stable_memory);
101
182k
            gs_memory_free_all((gs_memory_t *)ilmem_stable, FREE_ALL_EVERYTHING, "ialloc_finit");
102
182k
            gs_memory_free_all((gs_memory_t *)ilmem, FREE_ALL_EVERYTHING, "ialloc_finit");
103
182k
        }
104
105
182k
        if (igmem != NULL) {
106
182k
            gs_ref_memory_t *igmem_stable = (gs_ref_memory_t *)(igmem->stable_memory);
107
182k
            gs_memory_free_all((gs_memory_t *)igmem_stable, FREE_ALL_EVERYTHING, "ialloc_finit");
108
182k
            gs_memory_free_all((gs_memory_t *)igmem, FREE_ALL_EVERYTHING, "ialloc_finit");
109
182k
        }
110
111
182k
        if (ismem != NULL)
112
182k
            gs_memory_free_all((gs_memory_t *)ismem, FREE_ALL_EVERYTHING, "ialloc_finit");
113
182k
     }
114
182k
}
115
116
/* ================ Local/global VM ================ */
117
118
/* Get the space attribute of an allocator */
119
uint
120
imemory_space(const gs_ref_memory_t * iimem)
121
1.12G
{
122
1.12G
    return iimem->space;
123
1.12G
}
124
125
/* Select the allocation space. */
126
void
127
ialloc_set_space(gs_dual_memory_t * dmem, uint space)
128
584M
{
129
584M
    gs_ref_memory_t *mem = dmem->spaces_indexed[space >> r_space_shift];
130
131
584M
    dmem->current = mem;
132
584M
    dmem->current_space = mem->space;
133
584M
}
134
135
/* Get the l_new attribute of a current allocator. */
136
/* (A copy of the new_mask in the gs_dual_memory_t.) */
137
uint
138
imemory_new_mask(const gs_ref_memory_t *imem)
139
1.22G
{
140
1.22G
    return imem->new_mask;
141
1.22G
}
142
143
/* Get the save level of an allocator. */
144
int
145
imemory_save_level(const gs_ref_memory_t *imem)
146
19.7M
{
147
19.7M
    return imem->save_level;
148
19.7M
}
149
150
/* Reset the requests. */
151
void
152
ialloc_reset_requested(gs_dual_memory_t * dmem)
153
3.67M
{
154
3.67M
    dmem->space_system->gc_status.requested = 0;
155
3.67M
    dmem->space_global->gc_status.requested = 0;
156
3.67M
    dmem->space_local->gc_status.requested = 0;
157
3.67M
}
158
159
/* ================ Refs ================ */
160
161
#ifdef DEBUG
162
static int
163
ialloc_trace_space(const gs_ref_memory_t *imem)
164
{
165
    return imem->space + (imem->stable_memory == (const gs_memory_t *)imem);
166
}
167
#endif
168
169
/* Register a ref root. */
170
int
171
gs_register_ref_root(gs_memory_t *mem, gs_gc_root_t **root,
172
                     void **pp, client_name_t cname)
173
3.29M
{
174
3.29M
    return gs_register_root(mem, root, ptr_ref_type, pp, cname);
175
3.29M
}
176
177
/*
178
 * As noted in iastate.h, every run of refs has an extra ref at the end
179
 * to hold relocation information for the garbage collector;
180
 * since sizeof(ref) % obj_align_mod == 0, we never need to
181
 * allocate any additional padding space at the end of the block.
182
 */
183
184
/* Allocate an array of refs. */
185
int
186
gs_alloc_ref_array(gs_ref_memory_t * mem, ref * parr, uint attrs,
187
                   uint num_refs, client_name_t cname)
188
2.13G
{
189
2.13G
    ref *obj;
190
2.13G
    int i;
191
192
    /* If we're allocating a run of refs already, */
193
    /* and we aren't about to overflow the maximum run length, use it. */
194
2.13G
    if (mem->cc && mem->cc->has_refs == true && mem->cc->rtop == mem->cc->cbot &&
195
2.11G
        num_refs < (mem->cc->ctop - mem->cc->cbot) / sizeof(ref) &&
196
2.09G
        mem->cc->rtop - (byte *) mem->cc->rcur + num_refs * sizeof(ref) <
197
2.09G
        max_size_st_refs
198
2.13G
        ) {
199
1.87G
        ref *end;
200
201
1.87G
        obj = (ref *) mem->cc->rtop - 1;    /* back up over last ref */
202
1.87G
        if_debug4m('A', (const gs_memory_t *)mem, "[a%d:+$ ]%s(%u) = "PRI_INTPTR"\n",
203
1.87G
                   ialloc_trace_space(mem), client_name_string(cname),
204
1.87G
                   num_refs, (intptr_t)obj);
205
1.87G
        mem->cc->rcur[-1].o_size += num_refs * sizeof(ref);
206
1.87G
        end = (ref *) (mem->cc->rtop = mem->cc->cbot +=
207
1.87G
                       num_refs * sizeof(ref));
208
1.87G
        make_mark(end - 1);
209
1.87G
    } else {
210
        /*
211
         * Allocate a new run.  We have to distinguish 3 cases:
212
         *      - Same clump: cc unchanged, end == cc->cbot.
213
         *      - Large clump: cc unchanged, end != cc->cbot.
214
         *      - New clump: cc changed.
215
         */
216
258M
        clump_t *cc;
217
258M
        ref *end;
218
258M
        alloc_change_t *cp = 0;
219
258M
        int code = 0;
220
221
258M
        if ((gs_memory_t *)mem != mem->stable_memory) {
222
251M
            code = alloc_save_change_alloc(mem, "gs_alloc_ref_array", &cp);
223
251M
            if (code < 0)
224
0
                return code;
225
251M
        }
226
        /* The save state allocation above may have moved mem->cc */
227
258M
        cc = mem->cc;
228
258M
        obj = gs_alloc_struct_array((gs_memory_t *) mem, num_refs + 1,
229
258M
                                    ref, &st_refs, cname);
230
258M
        if (obj == 0) {
231
            /* We don't have to alloc_save_remove() because the change
232
               object hasn't been attached to the allocator yet.
233
             */
234
349
            gs_free_object((gs_memory_t *) mem, cp, "gs_alloc_ref_array");
235
349
            return_error(gs_error_VMerror);
236
349
        }
237
        /* Set the terminating ref now. */
238
258M
        end = (ref *) obj + num_refs;
239
258M
        make_mark(end);
240
        /* Set has_refs in the clump. */
241
258M
        if (mem->cc && (mem->cc != cc || mem->cc->cbot == (byte *) (end + 1))) {
242
            /* Ordinary clump. */
243
249M
            mem->cc->rcur = (obj_header_t *) obj;
244
249M
            mem->cc->rtop = (byte *) (end + 1);
245
249M
            mem->cc->has_refs = true;
246
249M
        } else {
247
            /* Large clump. */
248
            /* This happens only for very large arrays, */
249
            /* so it doesn't need to be cheap. */
250
9.16M
            clump_locator_t cl;
251
252
9.16M
            cl.memory = mem;
253
9.16M
            cl.cp = mem->root;
254
            /* clump_locate_ptr() should *never* fail here */
255
9.16M
            if (clump_locate_ptr(obj, &cl)) {
256
9.16M
                cl.cp->has_refs = true;
257
9.16M
            }
258
0
            else {
259
0
                gs_abort((gs_memory_t *) mem);
260
0
            }
261
9.16M
        }
262
258M
        if (cp) {
263
35.1M
            mem->changes = cp;
264
35.1M
            cp->where = (ref_packed *)obj;
265
35.1M
        }
266
258M
    }
267
63.5G
    for (i = 0; i < num_refs; i++) {
268
61.4G
        make_null(&(obj[i]));
269
61.4G
    }
270
2.13G
    make_array(parr, attrs | mem->space, num_refs, obj);
271
2.13G
    return 0;
272
2.13G
}
273
274
/* Resize an array of refs.  Currently this is only implemented */
275
/* for shrinking, not for growing. */
276
int
277
gs_resize_ref_array(gs_ref_memory_t * mem, ref * parr,
278
                    uint new_num_refs, client_name_t cname)
279
27.2k
{
280
27.2k
    uint old_num_refs = r_size(parr);
281
27.2k
    uint diff;
282
27.2k
    ref *obj = parr->value.refs;
283
284
27.2k
    if (new_num_refs > old_num_refs || !r_has_type(parr, t_array))
285
0
        return_error(gs_error_Fatal);
286
27.2k
    diff = old_num_refs - new_num_refs;
287
    /* Check for LIFO.  See gs_free_ref_array for more details. */
288
27.2k
    if (mem->cc && mem->cc->rtop == mem->cc->cbot &&
289
721
        (byte *) (obj + (old_num_refs + 1)) == mem->cc->rtop
290
27.2k
        ) {
291
        /* Shorten the refs object. */
292
721
        ref *end = (ref *) (mem->cc->cbot = mem->cc->rtop -=
293
721
                            diff * sizeof(ref));
294
295
721
        if_debug4m('A', (const gs_memory_t *)mem, "[a%d:<$ ]%s(%u) "PRI_INTPTR"\n",
296
721
                   ialloc_trace_space(mem), client_name_string(cname), diff,
297
721
                   (intptr_t)obj);
298
721
        mem->cc->rcur[-1].o_size -= diff * sizeof(ref);
299
721
        make_mark(end - 1);
300
26.5k
    } else {
301
        /* Punt. */
302
26.5k
        if_debug4m('A', (const gs_memory_t *)mem, "[a%d:<$#]%s(%u) "PRI_INTPTR"\n",
303
26.5k
                   ialloc_trace_space(mem), client_name_string(cname), diff,
304
26.5k
                   (intptr_t)obj);
305
26.5k
        mem->lost.refs += diff * sizeof(ref);
306
26.5k
    }
307
27.2k
    r_set_size(parr, new_num_refs);
308
27.2k
    return 0;
309
27.2k
}
310
311
/* Deallocate an array of refs.  Only do this if LIFO, or if */
312
/* the array occupies an entire clump by itself. */
313
void
314
gs_free_ref_array(gs_ref_memory_t * mem, ref * parr, client_name_t cname)
315
97.0M
{
316
97.0M
    uint num_refs = r_size(parr);
317
97.0M
    ref *obj = parr->value.refs;
318
319
    /*
320
     * Compute the storage size of the array, and check for LIFO
321
     * freeing or a separate clump.  Note that the array might be packed;
322
     * for the moment, if it's anything but a t_array, punt.
323
     * The +1s are for the extra ref for the GC.
324
     */
325
97.0M
    if (!r_has_type(parr, t_array))
326
97.0M
        DO_NOTHING;    /* don't look for special cases */
327
36.1M
    else if (mem->cc && mem->cc->rtop == mem->cc->cbot &&
328
29.8M
             (byte *) (obj + (num_refs + 1)) == mem->cc->rtop
329
36.1M
        ) {
330
239
        if ((obj_header_t *) obj == mem->cc->rcur) {
331
            /* Deallocate the entire refs object. */
332
227
            if ((gs_memory_t *)mem != mem->stable_memory)
333
192
                alloc_save_remove(mem, (ref_packed *)obj, "gs_free_ref_array");
334
227
            gs_free_object((gs_memory_t *) mem, obj, cname);
335
227
            mem->cc->rcur = 0;
336
227
            mem->cc->rtop = 0;
337
227
        } else {
338
            /* Deallocate it at the end of the refs object. */
339
12
            if_debug4m('A', (const gs_memory_t *)mem, "[a%d:-$ ]%s(%u) "PRI_INTPTR"\n",
340
12
                       ialloc_trace_space(mem), client_name_string(cname),
341
12
                       num_refs, (intptr_t)obj);
342
12
            mem->cc->rcur[-1].o_size -= num_refs * sizeof(ref);
343
12
            mem->cc->rtop = mem->cc->cbot = (byte *) (obj + 1);
344
12
            make_mark(obj);
345
12
        }
346
239
        return;
347
36.1M
    } else if (num_refs >= (mem->large_size / ARCH_SIZEOF_REF - 1)) {
348
        /* See if this array has a clump all to itself. */
349
        /* We only make this check when freeing very large objects, */
350
        /* so it doesn't need to be cheap. */
351
5.83M
        clump_locator_t cl;
352
353
5.83M
        cl.memory = mem;
354
5.83M
        cl.cp = mem->root;
355
5.83M
        if (clump_locate_ptr(obj, &cl) &&
356
5.83M
            obj == (ref *) ((obj_header_t *) (cl.cp->cbase) + 1) &&
357
5.83M
            (byte *) (obj + (num_refs + 1)) == cl.cp->cend
358
5.83M
            ) {
359
            /* Free the clump. */
360
5.83M
            if_debug4m('a', (const gs_memory_t *)mem, "[a%d:-$L]%s(%u) "PRI_INTPTR"\n",
361
5.83M
                       ialloc_trace_space(mem), client_name_string(cname),
362
5.83M
                       num_refs, (intptr_t)obj);
363
5.83M
            if ((gs_memory_t *)mem != mem->stable_memory) {
364
113k
                alloc_save_remove(mem, (ref_packed *)obj, "gs_free_ref_array");
365
113k
            }
366
5.83M
            alloc_free_clump(cl.cp, mem);
367
5.83M
            return;
368
5.83M
        }
369
5.83M
    }
370
    /* Punt, but fill the array with nulls so that there won't be */
371
    /* dangling references to confuse the garbage collector. */
372
97.0M
    if_debug4m('A', (const gs_memory_t *)mem, "[a%d:-$#]%s(%u) "PRI_INTPTR"\n",
373
91.1M
               ialloc_trace_space(mem), client_name_string(cname), num_refs,
374
91.1M
               (intptr_t)obj);
375
91.1M
    {
376
91.1M
        uint size;
377
378
91.1M
        switch (r_type(parr)) {
379
60.8M
            case t_shortarray:
380
60.8M
                size = num_refs * sizeof(ref_packed);
381
60.8M
                break;
382
0
            case t_mixedarray:{
383
                /* We have to parse the array to compute the storage size. */
384
0
                uint i = 0;
385
0
                const ref_packed *p = parr->value.packed;
386
387
0
                for (; i < num_refs; ++i)
388
0
                    p = packed_next(p);
389
0
                size = (const byte *)p - (const byte *)parr->value.packed;
390
0
                break;
391
0
            }
392
30.3M
            case t_array:
393
30.3M
                size = num_refs * sizeof(ref);
394
30.3M
                break;
395
0
            default:
396
0
                if_debug3('A', "Unknown type 0x%x in free_ref_array(%u,"PRI_INTPTR")!",
397
0
                         r_type(parr), num_refs, (intptr_t)obj);
398
0
                return;
399
91.1M
        }
400
        /*
401
         * If there are any leftover packed elements, we don't
402
         * worry about them, since they can't be dangling references.
403
         */
404
91.1M
        refset_null_new(obj, size / sizeof(ref), 0);
405
91.1M
        mem->lost.refs += size;
406
91.1M
    }
407
91.1M
}
408
409
/* Allocate a string ref. */
410
int
411
gs_alloc_string_ref(gs_ref_memory_t * mem, ref * psref,
412
                    uint attrs, uint nbytes, client_name_t cname)
413
53.5k
{
414
53.5k
    byte *str = gs_alloc_string((gs_memory_t *) mem, nbytes, cname);
415
416
53.5k
    if (str == 0)
417
0
        return_error(gs_error_VMerror);
418
53.5k
    make_string(psref, attrs | mem->space, nbytes, str);
419
53.5k
    return 0;
420
53.5k
}