Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/psi/idstack.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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
/* Implementation of dictionary stacks */
18
#include "ghost.h"
19
#include "idict.h"
20
#include "idictdef.h"
21
#include "idstack.h"
22
#include "inamedef.h"
23
#include "iname.h"
24
#include "ipacked.h"
25
#include "iutil.h"
26
#include "ivmspace.h"
27
#include "idebug.h"   /* for debug_print_name */
28
29
/*
30
#include "idicttpl.h" - Do not remove this comment.
31
                        "idicttpl.h" is included below.
32
*/
33
34
/* Debugging statistics */
35
/* #define COLLECT_STATS_IDSTACK */
36
37
#ifdef COLLECT_STATS_IDSTACK
38
#define MAX_STATS_DEPTH 6
39
struct stats_dstack_s {
40
    long lookups;   /* total lookups */
41
    long probes[2];   /* successful lookups on 1 or 2 probes */
42
    long depth[MAX_STATS_DEPTH + 1]; /* stack depth of lookups requiring search */
43
} stats_dstack;
44
# define INCR(v) (++stats_dstack.v)
45
#else
46
9.98G
# define INCR(v) DO_NOTHING
47
#endif
48
49
#ifdef COLLECT_STATS_IDSTACK
50
/* Wrapper for dstack_find_name_by_index */
51
ref *real_dstack_find_name_by_index(dict_stack_t * pds, uint nidx);
52
ref *
53
dstack_find_name_by_index(dict_stack_t * pds, uint nidx)
54
{
55
    ref *pvalue = real_dstack_find_name_by_index(pds, nidx);
56
    dict *pdict = pds->stack.p->value.pdict;
57
58
    INCR(lookups);
59
    if (dict_is_packed(pdict)) {
60
        uint hash =
61
        dict_hash_mod(dict_name_index_hash(nidx), npairs(pdict)) + 1;
62
63
        if (pdict->keys.value.packed[hash] ==
64
            pt_tag(pt_literal_name) + nidx
65
            )
66
            INCR(probes[0]);
67
        else if (pdict->keys.value.packed[hash - 1] ==
68
                 pt_tag(pt_literal_name) + nidx
69
            )
70
            INCR(probes[1]);
71
    }
72
    if (gs_debug_c('d') && !(stats_dstack.lookups % 1000))
73
        dlprintf3("[d]lookups=%ld probe1=%ld probe2=%ld\n",
74
                  stats_dstack.lookups, stats_dstack.probes[0],
75
                  stats_dstack.probes[1]);
76
    return pvalue;
77
}
78
#define dstack_find_name_by_index real_dstack_find_name_by_index
79
#endif
80
81
/* Check whether a dictionary is one of the permanent ones on the d-stack. */
82
bool
83
dstack_dict_is_permanent(const dict_stack_t * pds, const ref * pdref)
84
1.11G
{
85
1.11G
    dict *pdict = pdref->value.pdict;
86
1.11G
    int i;
87
88
1.11G
    if (pds->stack.extension_size == 0) { /* Only one block of d-stack. */
89
3.97G
        for (i = 0; i < pds->min_size; ++i)
90
3.02G
            if (pds->stack.bot[i].value.pdict == pdict)
91
161M
                return true;
92
1.11G
    } else {     /* More than one block of d-stack. */
93
3.50k
        uint count = ref_stack_count(&pds->stack);
94
95
14.0k
        for (i = count - pds->min_size; i < count; ++i)
96
10.5k
            if (ref_stack_index(&pds->stack, i)->value.pdict == pdict)
97
0
                return true;
98
3.50k
    }
99
953M
    return false;
100
1.11G
}
101
102
/*
103
 * Look up a name on the dictionary stack.
104
 * Return the pointer to the value if found, 0 if not.
105
 */
106
ref *
107
dstack_find_name_by_index(dict_stack_t * pds, uint nidx)
108
10.4G
{
109
10.4G
    ds_ptr pdref = pds->stack.p;
110
111
/* Since we know the hash function is the identity function, */
112
/* there's no point in allocating a separate variable for it. */
113
10.4G
#define hash dict_name_index_hash(nidx)
114
10.4G
    ref_packed kpack = packed_name_key(nidx);
115
116
16.9G
    do {
117
16.9G
        dict *pdict = pdref->value.pdict;
118
16.9G
        uint size = npairs(pdict);
119
#ifdef DEBUG
120
        if (gs_debug_c('D')) {
121
            const gs_memory_t *mem = dict_mem(pdict);
122
            ref dnref;
123
124
            name_index_ref(mem, nidx, &dnref);
125
            dmlputs(mem, "[D]lookup ");
126
            debug_print_name(mem, &dnref);
127
            dmprintf3(mem," in "PRI_INTPTR"(%u/%u)\n",
128
                      (intptr_t)pdict, dict_length(pdref),
129
                      dict_maxlength(pdref));
130
        }
131
#endif
132
16.9G
#define INCR_DEPTH(pdref)\
133
16.9G
  INCR(depth[min(MAX_STATS_DEPTH, pds->stack.p - pdref)])
134
16.9G
        if (dict_is_packed(pdict)) {
135
2.61G
#     define found INCR_DEPTH(pdref); return packed_search_value_pointer
136
2.61G
#     define deleted
137
2.61G
#     define missing break;
138
2.65G
#     include "idicttpl.h"
139
2.65G
#     undef missing
140
2.65G
#     undef deleted
141
2.65G
#     undef found
142
14.2G
        } else {
143
            /*
144
             * The name_index macro takes mem as its first argument, but
145
             * does not actually use it.  The following is a little ugly,
146
             * but it avoids a compiler warning.
147
             */
148
            /*const gs_memory_t *_mem_not_used = dict_mem(pdict);*/
149
14.2G
            ref *kbot = pdict->keys.value.refs;
150
14.2G
            register ref *kp;
151
14.2G
            int wrap = 0;
152
153
            /* Search the dictionary */
154
33.9G
            for (kp = kbot + dict_hash_mod(hash, size) + 2;;) {
155
33.9G
                --kp;
156
33.9G
                if (r_has_type(kp, t_name)) {
157
28.1G
                    if (name_index(_mem_not_used, kp) == nidx) {
158
9.06G
                        INCR_DEPTH(pdref);
159
9.06G
                        return pdict->values.value.refs + (kp - kbot);
160
9.06G
                    }
161
28.1G
                } else if (r_has_type(kp, t_null)) { /* Empty, deleted, or wraparound. */
162
                    /* Figure out which. */
163
5.72G
                    if (!r_has_attr(kp, a_executable))
164
5.21G
                        break;
165
511M
                    if (kp == kbot) { /* wrap */
166
76.7M
                        if (wrap++)
167
8.30M
                            break;  /* 2 wraps */
168
68.4M
                        kp += size + 1;
169
68.4M
                    }
170
511M
                }
171
33.9G
            }
172
14.2G
        }
173
16.9G
#undef INCR_DEPTH
174
16.9G
    }
175
10.4G
    while (pdref-- > pds->stack.bot);
176
    /* The name isn't in the top dictionary block. */
177
    /* If there are other blocks, search them now (more slowly). */
178
425M
    if (!pds->stack.extension_size)  /* no more blocks */
179
425M
        return (ref *) 0;
180
140k
    {       /* We could use the STACK_LOOP macros, but for now, */
181
        /* we'll do things the simplest way. */
182
140k
        ref key;
183
140k
        uint i = pds->stack.p + 1 - pds->stack.bot;
184
140k
        uint size = ref_stack_count(&pds->stack);
185
140k
        ref *pvalue;
186
187
140k
        dict *pdict = pds->stack.p->value.pdict;
188
140k
        const gs_memory_t *mem = dict_mem(pdict);
189
190
140k
        name_index_ref(mem, nidx, &key);
191
21.6M
        for (; i < size; i++) {
192
21.6M
            if (dict_find(ref_stack_index(&pds->stack, i),
193
21.6M
                          &key, &pvalue) > 0
194
21.6M
                ) {
195
140k
                INCR(depth[min(MAX_STATS_DEPTH, i)]);
196
140k
                return pvalue;
197
140k
            }
198
21.6M
        }
199
140k
    }
200
81
    return (ref *) 0;
201
140k
#undef hash
202
140k
}
203
204
/* Set the cached values computed from the top entry on the dstack. */
205
/* See idstack.h for details. */
206
static const ref_packed no_packed_keys[2] =
207
{packed_key_deleted, packed_key_empty};
208
void
209
dstack_set_top(dict_stack_t * pds)
210
721M
{
211
721M
    ds_ptr dsp = pds->stack.p;
212
721M
    dict *pdict = dsp->value.pdict;
213
214
721M
    if_debug3('d', "[d]dsp = "PRI_INTPTR" -> "PRI_INTPTR", key array type = %d\n",
215
721M
              (intptr_t)dsp, (intptr_t)pdict, r_type(&pdict->keys));
216
721M
    if (dict_is_packed(pdict) &&
217
721M
        r_has_attr(dict_access_ref(dsp), a_read)
218
721M
        ) {
219
48.2M
        pds->top_keys = pdict->keys.value.packed;
220
48.2M
        pds->top_npairs = npairs(pdict);
221
48.2M
        pds->top_values = pdict->values.value.refs;
222
673M
    } else {
223
673M
        pds->top_keys = no_packed_keys;
224
673M
        pds->top_npairs = 1;
225
673M
    }
226
721M
    if (!r_has_attr(dict_access_ref(dsp), a_write))
227
308M
        pds->def_space = -1;
228
412M
    else
229
412M
        pds->def_space = r_space(dsp);
230
721M
}
231
232
/* After a garbage collection, scan the permanent dictionaries and */
233
/* update the cached value pointers in names. */
234
void
235
dstack_gc_cleanup(dict_stack_t * pds)
236
339k
{
237
339k
    uint count = ref_stack_count(&pds->stack);
238
339k
    uint dsi;
239
240
1.35M
    for (dsi = pds->min_size; dsi > 0; --dsi) {
241
1.01M
        const dict *pdict =
242
1.01M
        ref_stack_index(&pds->stack, count - dsi)->value.pdict;
243
1.01M
        uint size = 0;
244
1.01M
        ref *pvalue = NULL;
245
1.01M
        uint i;
246
247
1.01M
        if (pdict == NULL)
248
0
            continue;
249
250
1.01M
        size = nslots(pdict);
251
1.01M
        pvalue = pdict->values.value.refs;
252
121M
        for (i = 0; i < size; ++i, ++pvalue) {
253
120M
            ref key;
254
120M
            ref *old_pvalue;
255
256
120M
            array_get(dict_mem(pdict), &pdict->keys, (long)i, &key);
257
120M
            if (r_has_type(&key, t_name) &&
258
120M
                pv_valid(old_pvalue = key.value.pname->pvalue)
259
120M
                ) {   /*
260
                                 * The name only has a single definition,
261
                                 * so it must be this one.  Check to see if
262
                                 * no relocation is actually needed; if so,
263
                                 * we can skip the entire dictionary.
264
                                 */
265
3.42M
                if (old_pvalue == pvalue) {
266
354k
                    if_debug1('d', "[d]skipping dstack entry %d\n",
267
354k
                              dsi - 1);
268
354k
                    break;
269
354k
                }
270
                /* Update the value pointer. */
271
3.06M
                key.value.pname->pvalue = pvalue;
272
3.06M
            }
273
120M
        }
274
1.01M
    }
275
339k
}