Coverage Report

Created: 2025-06-10 07:17

/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
609M
# 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
64.0M
{
85
64.0M
    dict *pdict = pdref->value.pdict;
86
64.0M
    int i;
87
88
64.0M
    if (pds->stack.extension_size == 0) { /* Only one block of d-stack. */
89
227M
        for (i = 0; i < pds->min_size; ++i)
90
173M
            if (pds->stack.bot[i].value.pdict == pdict)
91
9.49M
                return true;
92
64.0M
    } else {     /* More than one block of d-stack. */
93
153
        uint count = ref_stack_count(&pds->stack);
94
95
612
        for (i = count - pds->min_size; i < count; ++i)
96
459
            if (ref_stack_index(&pds->stack, i)->value.pdict == pdict)
97
0
                return true;
98
153
    }
99
54.5M
    return false;
100
64.0M
}
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
635M
{
109
635M
    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
635M
#define hash dict_name_index_hash(nidx)
114
635M
    ref_packed kpack = packed_name_key(nidx);
115
116
1.06G
    do {
117
1.06G
        dict *pdict = pdref->value.pdict;
118
1.06G
        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
1.06G
#define INCR_DEPTH(pdref)\
133
1.06G
  INCR(depth[min(MAX_STATS_DEPTH, pds->stack.p - pdref)])
134
1.06G
        if (dict_is_packed(pdict)) {
135
175M
#     define found INCR_DEPTH(pdref); return packed_search_value_pointer
136
175M
#     define deleted
137
175M
#     define missing break;
138
177M
#     include "idicttpl.h"
139
177M
#     undef missing
140
177M
#     undef deleted
141
177M
#     undef found
142
888M
        } 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
888M
            ref *kbot = pdict->keys.value.refs;
150
888M
            register ref *kp;
151
888M
            int wrap = 0;
152
153
            /* Search the dictionary */
154
2.14G
            for (kp = kbot + dict_hash_mod(hash, size) + 2;;) {
155
2.14G
                --kp;
156
2.14G
                if (r_has_type(kp, t_name)) {
157
1.75G
                    if (name_index(_mem_not_used, kp) == nidx) {
158
555M
                        INCR_DEPTH(pdref);
159
555M
                        return pdict->values.value.refs + (kp - kbot);
160
555M
                    }
161
1.75G
                } else if (r_has_type(kp, t_null)) { /* Empty, deleted, or wraparound. */
162
                    /* Figure out which. */
163
369M
                    if (!r_has_attr(kp, a_executable))
164
332M
                        break;
165
36.9M
                    if (kp == kbot) { /* wrap */
166
4.76M
                        if (wrap++)
167
489k
                            break;  /* 2 wraps */
168
4.28M
                        kp += size + 1;
169
4.28M
                    }
170
36.9M
                }
171
2.14G
            }
172
888M
        }
173
1.06G
#undef INCR_DEPTH
174
1.06G
    }
175
635M
    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
25.4M
    if (!pds->stack.extension_size)  /* no more blocks */
179
25.4M
        return (ref *) 0;
180
6.93k
    {       /* We could use the STACK_LOOP macros, but for now, */
181
        /* we'll do things the simplest way. */
182
6.93k
        ref key;
183
6.93k
        uint i = pds->stack.p + 1 - pds->stack.bot;
184
6.93k
        uint size = ref_stack_count(&pds->stack);
185
6.93k
        ref *pvalue;
186
187
6.93k
        dict *pdict = pds->stack.p->value.pdict;
188
6.93k
        const gs_memory_t *mem = dict_mem(pdict);
189
190
6.93k
        name_index_ref(mem, nidx, &key);
191
684k
        for (; i < size; i++) {
192
684k
            if (dict_find(ref_stack_index(&pds->stack, i),
193
684k
                          &key, &pvalue) > 0
194
684k
                ) {
195
6.93k
                INCR(depth[min(MAX_STATS_DEPTH, i)]);
196
6.93k
                return pvalue;
197
6.93k
            }
198
684k
        }
199
6.93k
    }
200
4
    return (ref *) 0;
201
6.93k
#undef hash
202
6.93k
}
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
43.5M
{
211
43.5M
    ds_ptr dsp = pds->stack.p;
212
43.5M
    dict *pdict = dsp->value.pdict;
213
214
43.5M
    if_debug3('d', "[d]dsp = "PRI_INTPTR" -> "PRI_INTPTR", key array type = %d\n",
215
43.5M
              (intptr_t)dsp, (intptr_t)pdict, r_type(&pdict->keys));
216
43.5M
    if (dict_is_packed(pdict) &&
217
43.5M
        r_has_attr(dict_access_ref(dsp), a_read)
218
43.5M
        ) {
219
4.14M
        pds->top_keys = pdict->keys.value.packed;
220
4.14M
        pds->top_npairs = npairs(pdict);
221
4.14M
        pds->top_values = pdict->values.value.refs;
222
39.3M
    } else {
223
39.3M
        pds->top_keys = no_packed_keys;
224
39.3M
        pds->top_npairs = 1;
225
39.3M
    }
226
43.5M
    if (!r_has_attr(dict_access_ref(dsp), a_write))
227
18.1M
        pds->def_space = -1;
228
25.3M
    else
229
25.3M
        pds->def_space = r_space(dsp);
230
43.5M
}
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
20.2k
{
237
20.2k
    uint count = ref_stack_count(&pds->stack);
238
20.2k
    uint dsi;
239
240
80.9k
    for (dsi = pds->min_size; dsi > 0; --dsi) {
241
60.6k
        const dict *pdict =
242
60.6k
        ref_stack_index(&pds->stack, count - dsi)->value.pdict;
243
60.6k
        uint size = 0;
244
60.6k
        ref *pvalue = NULL;
245
60.6k
        uint i;
246
247
60.6k
        if (pdict == NULL)
248
0
            continue;
249
250
60.6k
        size = nslots(pdict);
251
60.6k
        pvalue = pdict->values.value.refs;
252
7.20M
        for (i = 0; i < size; ++i, ++pvalue) {
253
7.16M
            ref key;
254
7.16M
            ref *old_pvalue;
255
256
7.16M
            array_get(dict_mem(pdict), &pdict->keys, (long)i, &key);
257
7.16M
            if (r_has_type(&key, t_name) &&
258
7.16M
                pv_valid(old_pvalue = key.value.pname->pvalue)
259
7.16M
                ) {   /*
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
202k
                if (old_pvalue == pvalue) {
266
21.2k
                    if_debug1('d', "[d]skipping dstack entry %d\n",
267
21.2k
                              dsi - 1);
268
21.2k
                    break;
269
21.2k
                }
270
                /* Update the value pointer. */
271
181k
                key.value.pname->pvalue = pvalue;
272
181k
            }
273
7.16M
        }
274
60.6k
    }
275
20.2k
}