/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 | } |