Coverage Report

Created: 2022-04-16 11:23

/src/ghostpdl/psi/idstack.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2022 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.,  1305 Grant Avenue - Suite 200, Novato,
13
   CA 94945, U.S.A., +1(415)492-9861, 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
57.0M
# 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
5.03M
{
85
5.03M
    dict *pdict = pdref->value.pdict;
86
5.03M
    int i;
87
88
5.03M
    if (pds->stack.extension_size == 0) { /* Only one block of d-stack. */
89
18.1M
        for (i = 0; i < pds->min_size; ++i)
90
13.7M
            if (pds->stack.bot[i].value.pdict == pdict)
91
683k
                return true;
92
5.03M
    } else {     /* More than one block of d-stack. */
93
0
        uint count = ref_stack_count(&pds->stack);
94
95
0
        for (i = count - pds->min_size; i < count; ++i)
96
0
            if (ref_stack_index(&pds->stack, i)->value.pdict == pdict)
97
0
                return true;
98
0
    }
99
4.35M
    return false;
100
5.03M
}
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
60.0M
{
109
60.0M
    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
60.0M
#define hash dict_name_index_hash(nidx)
114
60.0M
    ref_packed kpack = packed_name_key(nidx);
115
116
117M
    do {
117
117M
        dict *pdict = pdref->value.pdict;
118
117M
        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
117M
#define INCR_DEPTH(pdref)\
133
117M
  INCR(depth[min(MAX_STATS_DEPTH, pds->stack.p - pdref)])
134
117M
        if (dict_is_packed(pdict)) {
135
10.5M
#     define found INCR_DEPTH(pdref); return packed_search_value_pointer
136
10.5M
#     define deleted
137
10.5M
#     define missing break;
138
10.6M
#     include "idicttpl.h"
139
10.6M
#     undef missing
140
10.6M
#     undef deleted
141
10.6M
#     undef found
142
106M
        } 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
106M
            ref *kbot = pdict->keys.value.refs;
150
106M
            register ref *kp;
151
106M
            int wrap = 0;
152
153
            /* Search the dictionary */
154
160M
            for (kp = kbot + dict_hash_mod(hash, size) + 2;;) {
155
160M
                --kp;
156
160M
                if (r_has_type(kp, t_name)) {
157
106M
                    if (name_index(_mem_not_used, kp) == nidx) {
158
53.2M
                        INCR_DEPTH(pdref);
159
53.2M
                        return pdict->values.value.refs + (kp - kbot);
160
53.2M
                    }
161
106M
                } else if (r_has_type(kp, t_null)) { /* Empty, deleted, or wraparound. */
162
                    /* Figure out which. */
163
54.6M
                    if (!r_has_attr(kp, a_executable))
164
53.2M
                        break;
165
1.40M
                    if (kp == kbot) { /* wrap */
166
468k
                        if (wrap++)
167
40.3k
                            break;  /* 2 wraps */
168
428k
                        kp += size + 1;
169
428k
                    }
170
1.40M
                }
171
160M
            }
172
106M
        }
173
117M
#undef INCR_DEPTH
174
117M
    }
175
60.0M
    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
3.04M
    if (!pds->stack.extension_size) /* no more blocks */
179
3.04M
        return (ref *) 0;
180
0
    {       /* We could use the STACK_LOOP macros, but for now, */
181
        /* we'll do things the simplest way. */
182
0
        ref key;
183
0
        uint i = pds->stack.p + 1 - pds->stack.bot;
184
0
        uint size = ref_stack_count(&pds->stack);
185
0
        ref *pvalue;
186
187
0
        dict *pdict = pds->stack.p->value.pdict;
188
0
        const gs_memory_t *mem = dict_mem(pdict);
189
190
0
        name_index_ref(mem, nidx, &key);
191
0
        for (; i < size; i++) {
192
0
            if (dict_find(ref_stack_index(&pds->stack, i),
193
0
                          &key, &pvalue) > 0
194
0
                ) {
195
0
                INCR(depth[min(MAX_STATS_DEPTH, i)]);
196
0
                return pvalue;
197
0
            }
198
0
        }
199
0
    }
200
0
    return (ref *) 0;
201
0
#undef hash
202
0
}
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
3.16M
{
211
3.16M
    ds_ptr dsp = pds->stack.p;
212
3.16M
    dict *pdict = dsp->value.pdict;
213
214
3.16M
    if_debug3('d', "[d]dsp = "PRI_INTPTR" -> "PRI_INTPTR", key array type = %d\n",
215
3.16M
              (intptr_t)dsp, (intptr_t)pdict, r_type(&pdict->keys));
216
3.16M
    if (dict_is_packed(pdict) &&
217
3.16M
        r_has_attr(dict_access_ref(dsp), a_read)
218
3.16M
        ) {
219
110k
        pds->top_keys = pdict->keys.value.packed;
220
110k
        pds->top_npairs = npairs(pdict);
221
110k
        pds->top_values = pdict->values.value.refs;
222
3.05M
    } else {
223
3.05M
        pds->top_keys = no_packed_keys;
224
3.05M
        pds->top_npairs = 1;
225
3.05M
    }
226
3.16M
    if (!r_has_attr(dict_access_ref(dsp), a_write))
227
1.30M
        pds->def_space = -1;
228
1.85M
    else
229
1.85M
        pds->def_space = r_space(dsp);
230
3.16M
}
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
1.40k
{
237
1.40k
    uint count = ref_stack_count(&pds->stack);
238
1.40k
    uint dsi;
239
240
5.60k
    for (dsi = pds->min_size; dsi > 0; --dsi) {
241
4.20k
        const dict *pdict =
242
4.20k
        ref_stack_index(&pds->stack, count - dsi)->value.pdict;
243
4.20k
        uint size = nslots(pdict);
244
4.20k
        ref *pvalue = pdict->values.value.refs;
245
4.20k
        uint i;
246
247
407k
        for (i = 0; i < size; ++i, ++pvalue) {
248
404k
            ref key;
249
404k
            ref *old_pvalue;
250
251
404k
            array_get(dict_mem(pdict), &pdict->keys, (long)i, &key);
252
404k
            if (r_has_type(&key, t_name) &&
253
404k
                pv_valid(old_pvalue = key.value.pname->pvalue)
254
404k
                ) {   /*
255
                                 * The name only has a single definition,
256
                                 * so it must be this one.  Check to see if
257
                                 * no relocation is actually needed; if so,
258
                                 * we can skip the entire dictionary.
259
                                 */
260
14.4k
                if (old_pvalue == pvalue) {
261
1.43k
                    if_debug1('d', "[d]skipping dstack entry %d\n",
262
1.43k
                              dsi - 1);
263
1.43k
                    break;
264
1.43k
                }
265
                /* Update the value pointer. */
266
12.9k
                key.value.pname->pvalue = pvalue;
267
12.9k
            }
268
404k
        }
269
4.20k
    }
270
1.40k
}