/src/ghostpdl/psi/idict.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 | | /* Dictionary implementation */ |
18 | | #include "math_.h" /* for frexp */ |
19 | | #include "string_.h" /* for strlen */ |
20 | | #include "ghost.h" |
21 | | #include "gxalloc.h" /* for accessing masks */ |
22 | | #include "ierrors.h" |
23 | | #include "imemory.h" |
24 | | #include "idebug.h" /* for debug_print_name */ |
25 | | #include "inamedef.h" |
26 | | #include "iname.h" |
27 | | #include "ipacked.h" |
28 | | #include "isave.h" /* for value cache in names */ |
29 | | #include "store.h" |
30 | | #include "idict.h" /* interface definition */ |
31 | | #include "idictdef.h" |
32 | | #include "iutil.h" |
33 | | #include "ivmspace.h" /* for store check */ |
34 | | /* |
35 | | #include "idicttpl.h" - Do not remove this comment. |
36 | | "idicttpl.h" is included below. |
37 | | */ |
38 | | |
39 | | /* |
40 | | * Dictionaries per se aren't supposed to know anything about the |
41 | | * dictionary stack, let alone the interpreter's dictionary stack. |
42 | | * Unfortunately, there is are two design couplings between them: |
43 | | * dictionary stacks cache some of the elements of their top dictionary |
44 | | * (requiring updating when that dictionary grows or is unpacked), |
45 | | * and names may cache a pointer to their definition (requiring a |
46 | | * check whether a dictionary appears on the dictionary stack). |
47 | | * Therefore, we need iddstack.h here. |
48 | | * We'd really like to fix this, but we don't see how. |
49 | | */ |
50 | | #include "iddstack.h" |
51 | | |
52 | | /* |
53 | | * Define the size of the largest valid dictionary. |
54 | | * This is limited by the size field of the keys and values refs, |
55 | | * and by the enumeration interface, which requires the size to |
56 | | * fit in an int. As it happens, max_array_size will always be |
57 | | * smaller than max_int. |
58 | | */ |
59 | | const uint dict_max_size = max_array_size - 1; |
60 | | |
61 | | /* Define whether dictionaries are packed by default. */ |
62 | | enum { |
63 | | dict_default_pack = true |
64 | | }; |
65 | | |
66 | | /* |
67 | | * Define the check for whether we can set the 1-element cache. |
68 | | * We only set the cache if we aren't inside a save. |
69 | | * This way, we never have to undo setting the cache. |
70 | | */ |
71 | | #define CAN_SET_PVALUE_CACHE(pds, pdref, mem)\ |
72 | 1.11G | (pds && dstack_dict_is_permanent(pds, pdref) && !ref_saving_in(mem)) |
73 | | |
74 | | /* Forward references */ |
75 | | static int dict_create_contents(uint size, const ref * pdref, bool pack); |
76 | | |
77 | | /* Debugging statistics - uses a static, so not threadsafe. */ |
78 | | /* #define COLLECT_STATS_IDICT */ |
79 | | |
80 | | #ifdef COLLECT_STATS_IDICT |
81 | | struct stats_dict_s { |
82 | | long lookups; /* total lookups */ |
83 | | long probe1; /* successful lookups on only 1 probe */ |
84 | | long probe2; /* successful lookups on 2 probes */ |
85 | | } stats_dict; |
86 | | |
87 | | /* Wrapper for dict_find */ |
88 | | int real_dict_find(const ref * pdref, const ref * key, ref ** ppvalue); |
89 | | int |
90 | | dict_find(const ref * pdref, const ref * pkey, ref ** ppvalue) |
91 | | { |
92 | | dict *pdict = pdref->value.pdict; |
93 | | int code = real_dict_find(pdref, pkey, ppvalue); |
94 | | |
95 | | stats_dict.lookups++; |
96 | | if (r_has_type(pkey, t_name) && dict_is_packed(pdict)) { |
97 | | uint nidx = name_index(dict_mem(pdict), pkey); |
98 | | uint hash = |
99 | | dict_hash_mod(dict_name_index_hash(nidx), npairs(pdict)) + 1; |
100 | | |
101 | | if (pdict->keys.value.packed[hash] == |
102 | | pt_tag(pt_literal_name) + nidx |
103 | | ) |
104 | | stats_dict.probe1++; |
105 | | else if (pdict->keys.value.packed[hash - 1] == |
106 | | pt_tag(pt_literal_name) + nidx |
107 | | ) |
108 | | stats_dict.probe2++; |
109 | | } |
110 | | /* Do the cheap flag test before the expensive remainder test. */ |
111 | | if (gs_debug_c('d') && !(stats_dict.lookups % 1000)) |
112 | | dlprintf3("[d]lookups=%ld probe1=%ld probe2=%ld\n", |
113 | | stats_dict.lookups, stats_dict.probe1, stats_dict.probe2); |
114 | | return code; |
115 | | } |
116 | | #define dict_find real_dict_find |
117 | | #endif |
118 | | |
119 | | /* Round up the size of a dictionary. Return 0 if too large. */ |
120 | | uint |
121 | | dict_round_size_small(uint rsize) |
122 | 0 | { |
123 | 0 | return (rsize > dict_max_size ? 0 : rsize); |
124 | 0 | } |
125 | | uint |
126 | | dict_round_size_large(uint rsize) |
127 | 109M | { /* Round up to a power of 2 if not huge. */ |
128 | | /* If the addition overflows, the new rsize will be zero, */ |
129 | | /* which will (correctly) be interpreted as a limitcheck. */ |
130 | 109M | if (rsize > dict_max_non_huge) |
131 | 104 | return (rsize > dict_max_size ? 0 : rsize); |
132 | 229M | while (rsize & (rsize - 1)) |
133 | 119M | rsize = (rsize | (rsize - 1)) + 1; |
134 | 109M | return (rsize <= dict_max_size ? rsize : dict_max_non_huge); |
135 | 109M | } |
136 | | |
137 | | /* Create a dictionary using the given allocator. */ |
138 | | int |
139 | | dict_alloc(gs_ref_memory_t * mem, uint size, ref * pdref) |
140 | 94.3M | { |
141 | 94.3M | ref arr; |
142 | 94.3M | int code = |
143 | 94.3M | gs_alloc_ref_array(mem, &arr, a_all, sizeof(dict) / sizeof(ref), |
144 | 94.3M | "dict_alloc"); |
145 | 94.3M | dict *pdict; |
146 | 94.3M | ref dref; |
147 | | |
148 | 94.3M | if (code < 0) |
149 | 0 | return code; |
150 | 94.3M | pdict = (dict *) arr.value.refs; |
151 | 94.3M | make_tav(&dref, t_dictionary, |
152 | 94.3M | r_space(&arr) | imemory_new_mask(mem) | a_all, |
153 | 94.3M | pdict, pdict); |
154 | 94.3M | make_struct(&pdict->memory, avm_foreign, mem); |
155 | 94.3M | code = dict_create_contents(size, &dref, dict_default_pack); |
156 | 94.3M | if (code < 0) { |
157 | 87 | gs_free_ref_array(mem, &arr, "dict_alloc"); |
158 | 87 | return code; |
159 | 87 | } |
160 | 94.3M | *pdref = dref; |
161 | 94.3M | return 0; |
162 | 94.3M | } |
163 | | /* Create unpacked keys for a dictionary. */ |
164 | | /* The keys are allocated using the same allocator as the dictionary. */ |
165 | | static int |
166 | | dict_create_unpacked_keys(uint asize, const ref * pdref) |
167 | 60.8M | { |
168 | 60.8M | dict *pdict = pdref->value.pdict; |
169 | 60.8M | gs_ref_memory_t *mem = dict_memory(pdict); |
170 | 60.8M | int code; |
171 | | |
172 | 60.8M | code = gs_alloc_ref_array(mem, &pdict->keys, a_all, asize, |
173 | 60.8M | "dict_create_unpacked_keys"); |
174 | 60.8M | if (code >= 0) { |
175 | 60.8M | uint new_mask = imemory_new_mask(mem); |
176 | 60.8M | ref *kp = pdict->keys.value.refs; |
177 | | |
178 | 60.8M | r_set_attrs(&pdict->keys, new_mask); |
179 | 60.8M | refset_null_new(kp, asize, new_mask); |
180 | 60.8M | r_set_attrs(kp, a_executable); /* wraparound entry */ |
181 | 60.8M | } |
182 | 60.8M | return code; |
183 | 60.8M | } |
184 | | /* Create the contents (keys and values) of a newly allocated dictionary. */ |
185 | | /* Allocate in the current VM space, which is assumed to be the same as */ |
186 | | /* the VM space where the dictionary is allocated. */ |
187 | | static int |
188 | | dict_create_contents(uint size, const ref * pdref, bool pack) |
189 | 109M | { |
190 | 109M | dict *pdict = pdref->value.pdict; |
191 | 109M | gs_ref_memory_t *mem = dict_memory(pdict); |
192 | 109M | uint new_mask = imemory_new_mask(mem); |
193 | 109M | uint asize = dict_round_size((size == 0 ? 1 : size)); |
194 | 109M | int code; |
195 | 109M | register uint i; |
196 | | |
197 | 109M | if (asize == 0 || asize > max_array_size - 1) /* too large */ |
198 | 87 | return_error(gs_error_limitcheck); |
199 | 109M | asize++; /* allow room for wraparound entry */ |
200 | 109M | code = gs_alloc_ref_array(mem, &pdict->values, a_all, asize, |
201 | 109M | "dict_create_contents(values)"); |
202 | 109M | if (code < 0) |
203 | 0 | return code; |
204 | 109M | r_set_attrs(&pdict->values, new_mask); |
205 | 109M | refset_null_new(pdict->values.value.refs, asize, new_mask); |
206 | 109M | if (pack) { |
207 | 98.5M | uint ksize = (asize + packed_per_ref - 1) / packed_per_ref; |
208 | 98.5M | ref arr; |
209 | 98.5M | ref_packed *pkp; |
210 | 98.5M | ref_packed *pzp; |
211 | | |
212 | 98.5M | code = gs_alloc_ref_array(mem, &arr, a_all, ksize, |
213 | 98.5M | "dict_create_contents(packed keys)"); |
214 | 98.5M | if (code < 0) |
215 | 0 | return code; |
216 | 98.5M | pkp = (ref_packed *) arr.value.refs; |
217 | 98.5M | make_tasv(&pdict->keys, t_shortarray, |
218 | 98.5M | r_space(&arr) | a_all | new_mask, |
219 | 98.5M | asize, packed, pkp); |
220 | 6.20G | for (pzp = pkp, i = 0; i < asize || i % packed_per_ref; pzp++, i++) |
221 | 6.10G | *pzp = packed_key_empty; |
222 | 98.5M | *pkp = packed_key_deleted; /* wraparound entry */ |
223 | 98.5M | } else { /* not packed */ |
224 | 11.4M | int code = dict_create_unpacked_keys(asize, pdref); |
225 | | |
226 | 11.4M | if (code < 0) |
227 | 0 | return code; |
228 | 11.4M | } |
229 | 109M | make_tav(&pdict->count, t_integer, new_mask, intval, 0); |
230 | 109M | make_tav(&pdict->maxlength, t_integer, new_mask, intval, size); |
231 | 109M | return 0; |
232 | 109M | } |
233 | | |
234 | | /* |
235 | | * Ensure that a dictionary uses the unpacked representation for keys. |
236 | | * We can't just use dict_resize, because the values slots mustn't move. |
237 | | */ |
238 | | int |
239 | | dict_unpack(ref * pdref, dict_stack_t *pds) |
240 | 49.4M | { |
241 | 49.4M | dict *pdict = pdref->value.pdict; |
242 | | |
243 | 49.4M | if (!dict_is_packed(pdict)) |
244 | 0 | return 0; /* nothing to do */ |
245 | 49.4M | { |
246 | 49.4M | gs_ref_memory_t *mem = dict_memory(pdict); |
247 | 49.4M | uint count = nslots(pdict); |
248 | 49.4M | const ref_packed *okp = pdict->keys.value.packed; |
249 | 49.4M | ref old_keys; |
250 | 49.4M | int code; |
251 | 49.4M | ref *nkp; |
252 | | |
253 | 49.4M | old_keys = pdict->keys; |
254 | 49.4M | if (ref_must_save_in(mem, &old_keys)) |
255 | 471 | ref_do_save_in(mem, pdref, &pdict->keys, "dict_unpack(keys)"); |
256 | 49.4M | code = dict_create_unpacked_keys(count, pdref); |
257 | 49.4M | if (code < 0) |
258 | 0 | return code; |
259 | 4.60G | for (nkp = pdict->keys.value.refs; count--; okp++, nkp++) |
260 | 4.55G | if (r_packed_is_name(okp)) { |
261 | 216M | packed_get((const gs_memory_t *)mem, okp, nkp); |
262 | 216M | ref_mark_new_in(mem, nkp); |
263 | 4.34G | } else if (*okp == packed_key_deleted) |
264 | 61.6M | r_set_attrs(nkp, a_executable); |
265 | 49.4M | if (!ref_must_save_in(mem, &old_keys)) |
266 | 49.4M | gs_free_ref_array(mem, &old_keys, "dict_unpack(old keys)"); |
267 | 49.4M | if (pds) |
268 | 46.0M | dstack_set_top(pds); /* just in case */ |
269 | 49.4M | } |
270 | 0 | return 0; |
271 | 49.4M | } |
272 | | |
273 | | /* |
274 | | * Look up a key in a dictionary. Store a pointer to the value slot |
275 | | * where found, or to the (value) slot for inserting. |
276 | | * See idict.h for the possible return values. |
277 | | */ |
278 | | int |
279 | | dict_find(const ref * pdref, const ref * pkey, |
280 | | ref ** ppvalue /* result is stored here */ ) |
281 | 9.77G | { |
282 | 9.77G | dict *pdict = pdref->value.pdict; |
283 | 9.77G | uint size = npairs(pdict); |
284 | 9.77G | register int etype; |
285 | 9.77G | uint nidx; |
286 | 9.77G | ref_packed kpack; |
287 | 9.77G | uint hash; |
288 | 9.77G | int ktype; |
289 | 9.77G | const gs_memory_t *mem = dict_mem(pdict); |
290 | | |
291 | | /* Compute hash. The only types we bother with are strings, */ |
292 | | /* names, and (unlikely, but worth checking for) integers. */ |
293 | 9.77G | switch (r_type(pkey)) { |
294 | 20.9M | case t_string: /* convert to a name first */ |
295 | 20.9M | { |
296 | 20.9M | ref nref; |
297 | 20.9M | int code; |
298 | | |
299 | 20.9M | if (!r_has_attr(pkey, a_read)) |
300 | 0 | return_error(gs_error_invalidaccess); |
301 | 20.9M | code = name_ref(mem, pkey->value.bytes, r_size(pkey), &nref, 1); |
302 | 20.9M | if (code < 0) |
303 | 4 | return code; |
304 | 20.9M | nidx = name_index(mem, &nref); |
305 | 20.9M | } |
306 | 0 | goto nh; |
307 | 9.06G | case t_name: |
308 | 9.06G | nidx = name_index(mem, pkey); |
309 | 9.09G | nh: |
310 | 9.09G | hash = dict_name_index_hash(nidx); |
311 | 9.09G | kpack = packed_name_key(nidx); |
312 | 9.09G | ktype = t_name; |
313 | 9.09G | break; |
314 | 152k | case t_real: |
315 | | /* |
316 | | * Make sure that equal reals and integers hash the same. |
317 | | */ |
318 | 152k | { |
319 | 152k | int expt, i; |
320 | 152k | double mant = frexp(pkey->value.realval, &expt); |
321 | | /* |
322 | | * The value is mant * 2^expt, where 0.5 <= mant < 1, |
323 | | * or else expt == mant == 0. |
324 | | */ |
325 | | |
326 | 152k | if (expt < sizeof(long) * 8 || pkey->value.realval == min_long) |
327 | 111k | i = (int)pkey->value.realval; |
328 | 41.0k | else |
329 | 41.0k | i = (int)(mant * min_long); /* MSVC 6.00.8168.0 cannot compile this */ |
330 | 152k | hash = (uint)i * 30503; /* with -O2 as a single expression */ |
331 | 152k | } |
332 | 152k | goto ih; |
333 | 678M | case t_integer: |
334 | 678M | hash = (uint)pkey->value.intval * 30503; |
335 | 678M | ih: |
336 | 678M | kpack = packed_key_impossible; |
337 | 678M | ktype = -1; |
338 | 678M | nidx = 0; /* only to pacify gcc */ |
339 | 678M | break; |
340 | 107 | case t_null: /* not allowed as a key */ |
341 | 107 | return_error(gs_error_typecheck); |
342 | 3.81M | default: |
343 | 3.81M | hash = r_btype(pkey) * 99; /* yech */ |
344 | 3.81M | kpack = packed_key_impossible; |
345 | 3.81M | ktype = -1; |
346 | 3.81M | nidx = 0; /* only to pacify gcc */ |
347 | 9.77G | } |
348 | | /* Search the dictionary */ |
349 | 9.77G | if (dict_is_packed(pdict)) { |
350 | 1.69G | const ref_packed *pslot = 0; |
351 | | |
352 | 1.69G | # define found *ppvalue = packed_search_value_pointer; return 1 |
353 | 1.69G | # define deleted if (pslot == 0) pslot = kp |
354 | 1.69G | # define missing goto miss |
355 | 1.69G | # include "idicttpl.h" |
356 | 200M | # undef missing |
357 | 200M | # undef deleted |
358 | 200M | # undef found |
359 | | /* |
360 | | * Double wraparound, dict is full. |
361 | | * Note that even if there was an empty slot (pslot != 0), |
362 | | * we must return dictfull if length = maxlength. |
363 | | */ |
364 | 200M | if (pslot == 0 || d_length(pdict) == d_maxlength(pdict)) |
365 | 200M | return_error(gs_error_dictfull); |
366 | 0 | *ppvalue = pdict->values.value.refs + (pslot - kbot); |
367 | 0 | return 0; |
368 | 941M | miss: /* Key is missing, not double wrap. See above re dictfull. */ |
369 | 941M | if (d_length(pdict) == d_maxlength(pdict)) |
370 | 53.7M | return_error(gs_error_dictfull); |
371 | 887M | if (pslot == 0) |
372 | 887M | pslot = kp; |
373 | 887M | *ppvalue = pdict->values.value.refs + (pslot - kbot); |
374 | 887M | return 0; |
375 | 8.08G | } else { |
376 | 8.08G | ref *kbot = pdict->keys.value.refs; |
377 | 8.08G | register ref *kp; |
378 | 8.08G | ref *pslot = 0; |
379 | 8.08G | int wrap = 0; |
380 | | |
381 | 29.1G | for (kp = kbot + dict_hash_mod(hash, size) + 2;;) { |
382 | 29.1G | --kp; |
383 | 29.1G | if ((etype = r_type(kp)) == ktype) { /* Fast comparison if both keys are names */ |
384 | 21.5G | if (name_index(mem, kp) == nidx) { |
385 | 3.84G | *ppvalue = pdict->values.value.refs + (kp - kbot); |
386 | 3.84G | return 1; |
387 | 3.84G | } |
388 | 21.5G | } else if (etype == t_null) { /* Empty, deleted, or wraparound. */ |
389 | | /* Figure out which. */ |
390 | 5.24G | if (kp == kbot) { /* wrap */ |
391 | 199M | if (wrap++) { /* wrapped twice */ |
392 | 67.3M | if (pslot == 0) |
393 | 67.1M | return_error(gs_error_dictfull); |
394 | 162k | break; |
395 | 67.3M | } |
396 | 132M | kp += size + 1; |
397 | 5.04G | } else if (r_has_attr(kp, a_executable)) { /* Deleted entry, save the slot. */ |
398 | 891M | if (pslot == 0) |
399 | 476M | pslot = kp; |
400 | 891M | } else /* key not found */ |
401 | 4.15G | break; |
402 | 5.24G | } else { |
403 | 2.36G | if (obj_eq(mem, kp, pkey)) { |
404 | 12.6M | *ppvalue = pdict->values.value.refs + (kp - kbot); |
405 | 12.6M | return 1; |
406 | 12.6M | } |
407 | 2.36G | } |
408 | 29.1G | } |
409 | 4.15G | if (d_length(pdict) == d_maxlength(pdict)) |
410 | 314M | return_error(gs_error_dictfull); |
411 | 3.84G | *ppvalue = pdict->values.value.refs + |
412 | 3.84G | ((pslot != 0 ? pslot : kp) - kbot); |
413 | 3.84G | return 0; |
414 | 4.15G | } |
415 | 9.77G | } |
416 | | |
417 | | /* |
418 | | * Look up a (constant) C string in a dictionary. |
419 | | * Return 1 if found, <= 0 if not. |
420 | | */ |
421 | | int |
422 | | dict_find_string(const ref * pdref, const char *kstr, ref ** ppvalue) |
423 | 740M | { |
424 | 740M | int code; |
425 | 740M | ref kname; |
426 | 740M | if ( pdref != 0 ) { |
427 | 740M | dict *pdict = pdref->value.pdict; |
428 | | |
429 | 740M | if ((code = name_ref(dict_mem(pdict), |
430 | 740M | (const byte *)kstr, strlen(kstr), &kname, -1)) < 0) |
431 | 93.2M | return code; |
432 | 646M | code = dict_find(pdref, &kname, ppvalue); |
433 | 646M | if (code == gs_error_dictfull) |
434 | 7.41M | return_error(gs_error_undefined); |
435 | 639M | return code; |
436 | 646M | } |
437 | 162k | return 0; |
438 | 740M | } |
439 | | |
440 | | /* |
441 | | * Enter a key-value pair in a dictionary. |
442 | | * See idict.h for the possible return values. |
443 | | */ |
444 | | int |
445 | | dict_put(ref * pdref /* t_dictionary */ , const ref * pkey, const ref * pvalue, |
446 | | dict_stack_t *pds) |
447 | 4.85G | { |
448 | 4.85G | dict *pdict = pdref->value.pdict; |
449 | 4.85G | gs_ref_memory_t *mem = dict_memory(pdict); |
450 | 4.85G | gs_memory_t *pmem = dict_mem(pdict); |
451 | 4.85G | int rcode = 0; |
452 | 4.85G | int code; |
453 | 4.85G | ref *pvslot, kname; |
454 | | |
455 | | /* Check the value. */ |
456 | 4.85G | store_check_dest(pdref, pvalue); |
457 | 4.90G | top:if ((code = dict_find(pdref, pkey, &pvslot)) <= 0) { /* not found *//* Check for overflow */ |
458 | 3.62G | uint index; |
459 | | |
460 | 3.62G | switch (code) { |
461 | 3.62G | case 0: |
462 | 3.62G | break; |
463 | 4.42M | case gs_error_dictfull: |
464 | 4.42M | if (!pmem->gs_lib_ctx->dict_auto_expand) |
465 | 0 | return_error(gs_error_dictfull); |
466 | 4.42M | code = dict_grow(pdref, pds); |
467 | 4.42M | if (code < 0) |
468 | 0 | return code; |
469 | 4.42M | goto top; /* keep things simple */ |
470 | 4.42M | default: /* gs_error_typecheck */ |
471 | 7 | return code; |
472 | 3.62G | } |
473 | 3.62G | index = pvslot - pdict->values.value.refs; |
474 | | /* If the key is a string, convert it to a name. */ |
475 | 3.62G | if (r_has_type(pkey, t_string)) { |
476 | 11.8M | int code; |
477 | | |
478 | 11.8M | if (!r_has_attr(pkey, a_read)) |
479 | 0 | return_error(gs_error_invalidaccess); |
480 | 11.8M | code = name_from_string(pmem, pkey, &kname); |
481 | 11.8M | if (code < 0) |
482 | 0 | return code; |
483 | 11.8M | pkey = &kname; |
484 | 11.8M | } |
485 | 3.62G | if (dict_is_packed(pdict)) { |
486 | 504M | ref_packed *kp; |
487 | | |
488 | 504M | if (!r_has_type(pkey, t_name) || |
489 | 504M | name_index(pmem, pkey) > packed_name_max_index |
490 | 504M | ) { /* Change to unpacked representation. */ |
491 | 49.4M | int code = dict_unpack(pdref, pds); |
492 | | |
493 | 49.4M | if (code < 0) |
494 | 0 | return code; |
495 | 49.4M | goto top; |
496 | 49.4M | } |
497 | 454M | kp = pdict->keys.value.writable_packed + index; |
498 | 454M | if (ref_must_save_in(mem, &pdict->keys)) { /* See initial comment for why it is safe */ |
499 | | /* not to save the change if the keys */ |
500 | | /* array itself is new. */ |
501 | 441k | ref_do_save_in(mem, &pdict->keys, kp, "dict_put(key)"); |
502 | 441k | } |
503 | 454M | *kp = pt_tag(pt_literal_name) + name_index(pmem, pkey); |
504 | 3.11G | } else { |
505 | 3.11G | ref *kp = pdict->keys.value.refs + index; |
506 | | |
507 | 3.11G | if_debug2m('d', (const gs_memory_t *)mem, "[d]"PRI_INTPTR": fill key at "PRI_INTPTR"\n", |
508 | 3.11G | (intptr_t)pdict, (intptr_t)kp); |
509 | 3.11G | store_check_dest(pdref, pkey); |
510 | 3.11G | ref_assign_old_in(mem, &pdict->keys, kp, pkey, |
511 | 3.11G | "dict_put(key)"); /* set key of pair */ |
512 | 3.11G | } |
513 | 3.57G | ref_save_in(mem, pdref, &pdict->count, "dict_put(count)"); |
514 | 3.57G | pdict->count.value.intval++; |
515 | | /* If the key is a name, update its 1-element cache. */ |
516 | 3.57G | if (r_has_type(pkey, t_name)) { |
517 | 2.92G | name *pname = pkey->value.pname; |
518 | | |
519 | 2.92G | if (pname->pvalue == pv_no_defn && |
520 | 2.92G | CAN_SET_PVALUE_CACHE(pds, pdref, mem) |
521 | 2.92G | ) { /* Set the cache. */ |
522 | 161M | if_debug0m('d', (const gs_memory_t *)mem, "[d]set cache\n"); |
523 | 161M | pname->pvalue = pvslot; |
524 | 2.75G | } else { /* The cache can't be used. */ |
525 | 2.75G | if_debug0m('d', (const gs_memory_t *)mem, "[d]no cache\n"); |
526 | 2.75G | pname->pvalue = pv_other; |
527 | 2.75G | } |
528 | 2.92G | } |
529 | 3.57G | rcode = 1; |
530 | 3.57G | } |
531 | 4.90G | if_debug8m('d', (const gs_memory_t *)mem, |
532 | 4.85G | "[d]"PRI_INTPTR": put key 0x%lx 0x%lx\n value at "PRI_INTPTR": old 0x%lx 0x%lx, new 0x%lx 0x%lx\n", |
533 | 4.85G | (intptr_t) pdref->value.pdict, |
534 | 4.85G | ((const ulong *)pkey)[0], ((const ulong *)pkey)[1], |
535 | 4.85G | (intptr_t) pvslot, |
536 | 4.85G | ((const ulong *)pvslot)[0], ((const ulong *)pvslot)[1], |
537 | 4.85G | ((const ulong *)pvalue)[0], ((const ulong *)pvalue)[1]); |
538 | 4.85G | ref_assign_old_in(mem, &pdref->value.pdict->values, pvslot, pvalue, |
539 | 4.85G | "dict_put(value)"); |
540 | 4.85G | return rcode; |
541 | 4.90G | } |
542 | | |
543 | | /* |
544 | | * Enter a key-value pair where the key is a (constant) C string. |
545 | | */ |
546 | | int |
547 | | dict_put_string(ref * pdref, const char *kstr, const ref * pvalue, |
548 | | dict_stack_t *pds) |
549 | 98.0M | { |
550 | 98.0M | int code; |
551 | 98.0M | ref kname; |
552 | 98.0M | dict *pdict = pdref->value.pdict; |
553 | | |
554 | 98.0M | if ((code = name_ref(dict_mem(pdict), |
555 | 98.0M | (const byte *)kstr, strlen(kstr), &kname, 0)) < 0) |
556 | 0 | return code; |
557 | 98.0M | return dict_put(pdref, &kname, pvalue, pds); |
558 | 98.0M | } |
559 | | |
560 | | /* |
561 | | * Enter a key-value pair where the key is a C string that must be copied. |
562 | | */ |
563 | | int |
564 | | dict_put_string_copy(ref * pdref, const char *kstr, const ref * pvalue, |
565 | | dict_stack_t *pds) |
566 | 2.43M | { |
567 | 2.43M | int code; |
568 | 2.43M | ref kname; |
569 | 2.43M | dict *pdict = pdref->value.pdict; |
570 | | |
571 | 2.43M | if ((code = name_ref(dict_mem(pdict), |
572 | 2.43M | (const byte *)kstr, strlen(kstr), &kname, 1)) < 0) |
573 | 0 | return code; |
574 | 2.43M | return dict_put(pdref, &kname, pvalue, pds); |
575 | 2.43M | } |
576 | | |
577 | | /* Remove an element from a dictionary. */ |
578 | | int |
579 | | dict_undef(ref * pdref, const ref * pkey, dict_stack_t *pds) |
580 | 410M | { |
581 | 410M | gs_ref_memory_t *mem; |
582 | 410M | ref *pvslot; |
583 | 410M | dict *pdict; |
584 | 410M | uint index; |
585 | 410M | int code = dict_find(pdref, pkey, &pvslot); |
586 | | |
587 | 410M | switch (code) { |
588 | 18.8M | case 0: |
589 | 18.8M | case gs_error_dictfull: |
590 | 18.8M | return_error(gs_error_undefined); |
591 | 391M | case 1: |
592 | 391M | break; |
593 | 0 | default: /* other error */ |
594 | 0 | return code; |
595 | 410M | } |
596 | | /* Remove the entry from the dictionary. */ |
597 | 391M | pdict = pdref->value.pdict; |
598 | 391M | index = pvslot - pdict->values.value.refs; |
599 | 391M | mem = dict_memory(pdict); |
600 | 391M | if (dict_is_packed(pdict)) { |
601 | 22.4M | ref_packed *pkp = pdict->keys.value.writable_packed + index; |
602 | 22.4M | bool must_save = ref_must_save_in(mem, &pdict->keys); |
603 | | |
604 | 22.4M | if_debug3m('d', (const gs_memory_t *)mem, |
605 | 22.4M | "[d]"PRI_INTPTR": removing key at "PRI_INTPTR": 0x%x\n", |
606 | 22.4M | (intptr_t)pdict, (intptr_t)pkp, (uint)*pkp); |
607 | | /* See the initial comment for why it is safe not to save */ |
608 | | /* the change if the keys array itself is new. */ |
609 | 22.4M | if (must_save) |
610 | 141k | ref_do_save_in(mem, &pdict->keys, pkp, "dict_undef(key)"); |
611 | | /* |
612 | | * Accumulating deleted entries slows down lookup. |
613 | | * Detect the easy case where we can use an empty entry |
614 | | * rather than a deleted one, namely, when the next entry |
615 | | * in the probe order is empty. |
616 | | */ |
617 | 22.4M | if (pkp[-1] == packed_key_empty) { |
618 | | /* |
619 | | * In this case we can replace any preceding deleted keys with |
620 | | * empty ones as well. |
621 | | */ |
622 | 9.89M | uint end = nslots(pdict); |
623 | | |
624 | 9.89M | *pkp = packed_key_empty; |
625 | 9.89M | if (must_save) { |
626 | 141k | while (++index < end && *++pkp == packed_key_deleted) { |
627 | 0 | ref_do_save_in(mem, &pdict->keys, pkp, "dict_undef(key)"); |
628 | 0 | *pkp = packed_key_empty; |
629 | 0 | } |
630 | 9.74M | } else { |
631 | 9.74M | while (++index < end && *++pkp == packed_key_deleted) |
632 | 0 | *pkp = packed_key_empty; |
633 | 9.74M | } |
634 | 9.89M | } else |
635 | 12.5M | *pkp = packed_key_deleted; |
636 | 369M | } else { /* not packed */ |
637 | 369M | ref *kp = pdict->keys.value.refs + index; |
638 | | |
639 | 369M | if_debug4m('d', (const gs_memory_t *)mem, |
640 | 369M | "[d]"PRI_INTPTR": removing key at "PRI_INTPTR": 0x%lx 0x%lx\n", |
641 | 369M | (intptr_t)pdict, (intptr_t)kp, ((ulong *)kp)[0], ((ulong *)kp)[1]); |
642 | 369M | make_null_old_in(mem, &pdict->keys, kp, "dict_undef(key)"); |
643 | | /* |
644 | | * Accumulating deleted entries slows down lookup. |
645 | | * Detect the easy case where we can use an empty entry |
646 | | * rather than a deleted one, namely, when the next entry |
647 | | * in the probe order is empty. |
648 | | */ |
649 | 369M | if (!r_has_type(kp - 1, t_null) || /* full entry */ |
650 | 369M | r_has_attr(kp - 1, a_executable) /* deleted or wraparound */ |
651 | 369M | ) |
652 | 244M | r_set_attrs(kp, a_executable); /* mark as deleted */ |
653 | 369M | } |
654 | 391M | ref_save_in(mem, pdref, &pdict->count, "dict_undef(count)"); |
655 | 391M | pdict->count.value.intval--; |
656 | | /* If the key is a name, update its 1-element cache. */ |
657 | 391M | if (r_has_type(pkey, t_name)) { |
658 | 391M | name *pname = pkey->value.pname; |
659 | | |
660 | 391M | if (pv_valid(pname->pvalue)) { |
661 | | #ifdef DEBUG |
662 | | /* Check the the cache is correct. */ |
663 | | if (!(pds && dstack_dict_is_permanent(pds, pdref))) |
664 | | lprintf1("dict_undef: cached name value pointer " PRI_INTPTR " is incorrect!\n", |
665 | | (intptr_t) pname->pvalue); |
666 | | #endif |
667 | | /* Clear the cache */ |
668 | 25.0M | pname->pvalue = pv_no_defn; |
669 | 25.0M | } |
670 | 391M | } |
671 | 391M | make_null_old_in(mem, &pdict->values, pvslot, "dict_undef(value)"); |
672 | 391M | return 0; |
673 | 410M | } |
674 | | |
675 | | /* Return the number of elements in a dictionary. */ |
676 | | uint |
677 | | dict_length(const ref * pdref /* t_dictionary */ ) |
678 | 134M | { |
679 | 134M | return d_length(pdref->value.pdict); |
680 | 134M | } |
681 | | |
682 | | /* Return the capacity of a dictionary. */ |
683 | | uint |
684 | | dict_maxlength(const ref * pdref /* t_dictionary */ ) |
685 | 92.9M | { |
686 | 92.9M | return d_maxlength(pdref->value.pdict); |
687 | 92.9M | } |
688 | | |
689 | | /* Return the maximum index of a slot within a dictionary. */ |
690 | | uint |
691 | | dict_max_index(const ref * pdref /* t_dictionary */ ) |
692 | 5.36M | { |
693 | 5.36M | return npairs(pdref->value.pdict) - 1; |
694 | 5.36M | } |
695 | | |
696 | | /* |
697 | | * Copy one dictionary into another. |
698 | | * If COPY_NEW_ONLY is set, only copy entries whose keys |
699 | | * aren't already present in the destination. |
700 | | * If COPY_FOR_RESIZE is set, reset any valid name cache entries to |
701 | | * pv_no_defn before doing the dict_put. |
702 | | */ |
703 | 359M | #define COPY_NEW_ONLY 1 |
704 | 338M | #define COPY_FOR_RESIZE 2 |
705 | | static int |
706 | | dict_copy_elements(const ref * pdrfrom /* t_dictionary */ , |
707 | | ref * pdrto /* t_dictionary */ , int options, |
708 | | dict_stack_t *pds) |
709 | 18.1M | { |
710 | 18.1M | int space = r_space(pdrto); |
711 | 18.1M | int index; |
712 | 18.1M | ref elt[2]; |
713 | 18.1M | ref *pvslot; |
714 | 18.1M | int code; |
715 | | |
716 | 18.1M | if (space != avm_max) { |
717 | | /* Do the store check before starting the copy. */ |
718 | 2.29M | index = dict_first(pdrfrom); |
719 | 22.7M | while ((index = dict_next(pdrfrom, index, elt)) >= 0) |
720 | 20.4M | if (!(options & COPY_NEW_ONLY) || |
721 | 20.4M | dict_find(pdrto, &elt[0], &pvslot) <= 0 |
722 | 20.4M | ) { |
723 | 20.4M | store_check_space(space, &elt[0]); |
724 | 20.4M | store_check_space(space, &elt[1]); |
725 | 20.4M | } |
726 | 2.29M | } |
727 | | /* Now copy the contents. */ |
728 | 18.1M | index = dict_first(pdrfrom); |
729 | 356M | while ((index = dict_next(pdrfrom, index, elt)) >= 0) { |
730 | 338M | ref *pvalue = pv_no_defn; |
731 | | |
732 | 338M | if ((options & COPY_NEW_ONLY) && |
733 | 338M | dict_find(pdrto, &elt[0], &pvslot) > 0 |
734 | 338M | ) |
735 | 147k | continue; |
736 | 338M | if ((options & COPY_FOR_RESIZE) && |
737 | 338M | r_has_type(&elt[0], t_name) && |
738 | 338M | (pvalue = elt[0].value.pname->pvalue, pv_valid(pvalue)) |
739 | 338M | ) |
740 | 0 | elt[0].value.pname->pvalue = pv_no_defn; |
741 | 338M | if ((code = dict_put(pdrto, &elt[0], &elt[1], pds)) < 0) { |
742 | | /* |
743 | | * If COPY_FOR_RESIZE is set, the dict_put isn't supposed to |
744 | | * be able to fail, but we don't want to depend on this. |
745 | | */ |
746 | 0 | if (pvalue != pv_no_defn) |
747 | 0 | elt[0].value.pname->pvalue = pvalue; |
748 | 0 | return code; |
749 | 0 | } |
750 | 338M | } |
751 | 18.1M | return 0; |
752 | 18.1M | } |
753 | | int |
754 | | dict_copy_entries(const ref *pdrfrom, ref *pdrto, bool new_only, |
755 | | dict_stack_t *pds) |
756 | 2.49M | { |
757 | 2.49M | return dict_copy_elements(pdrfrom, pdrto, (new_only ? COPY_NEW_ONLY : 0), |
758 | 2.49M | pds); |
759 | 2.49M | } |
760 | | |
761 | | /* Resize a dictionary. */ |
762 | | int |
763 | | dict_resize(ref * pdref, uint new_size, dict_stack_t *pds) |
764 | 15.6M | { |
765 | 15.6M | dict *pdict = pdref->value.pdict; |
766 | 15.6M | gs_ref_memory_t *mem = dict_memory(pdict); |
767 | 15.6M | uint new_mask = imemory_new_mask(mem); |
768 | 15.6M | ushort orig_attrs = r_type_attrs(&pdict->values) & (a_all | a_executable); |
769 | 15.6M | dict dnew; |
770 | 15.6M | ref drto; |
771 | 15.6M | int code; |
772 | | |
773 | 15.6M | if (new_size < d_length(pdict)) { |
774 | 0 | if (!mem->gs_lib_ctx->dict_auto_expand) |
775 | 0 | return_error(gs_error_dictfull); |
776 | 0 | new_size = d_length(pdict); |
777 | 0 | } |
778 | 15.6M | make_tav(&drto, t_dictionary, r_space(pdref) | a_all | new_mask, |
779 | 15.6M | pdict, &dnew); |
780 | 15.6M | dnew.memory = pdict->memory; |
781 | 15.6M | if ((code = dict_create_contents(new_size, &drto, dict_is_packed(pdict))) < 0) |
782 | 0 | return code; |
783 | | /* |
784 | | * We must suppress the store check, in case we are expanding |
785 | | * systemdict or another global dictionary that is allowed |
786 | | * to reference local objects. |
787 | | */ |
788 | 15.6M | r_set_space(&drto, avm_local); |
789 | | /* |
790 | | * If we are expanding a permanent dictionary, we must make sure that |
791 | | * dict_put doesn't think this is a second definition for any |
792 | | * single-definition names. This in turn requires that |
793 | | * dstack_dict_is_permanent must be true for the second ("to") |
794 | | * argument of dict_copy_elements, which requires temporarily |
795 | | * setting *pdref = drto. |
796 | | */ |
797 | 15.6M | if (CAN_SET_PVALUE_CACHE(pds, pdref, mem)) { |
798 | 162k | ref drfrom; |
799 | | |
800 | 162k | drfrom = *pdref; |
801 | 162k | *pdref = drto; |
802 | 162k | dict_copy_elements(&drfrom, pdref, COPY_FOR_RESIZE, pds); |
803 | 162k | *pdref = drfrom; |
804 | 15.4M | } else { |
805 | 15.4M | dict_copy_elements(pdref, &drto, 0, pds); |
806 | 15.4M | } |
807 | | /* Save or free the old dictionary. */ |
808 | 15.6M | if (ref_must_save_in(mem, &pdict->values)) |
809 | 2.58k | ref_do_save_in(mem, pdref, &pdict->values, "dict_resize(values)"); |
810 | 15.6M | else |
811 | 15.6M | gs_free_ref_array(mem, &pdict->values, "dict_resize(old values)"); |
812 | 15.6M | if (ref_must_save_in(mem, &pdict->keys)) |
813 | 2.35k | ref_do_save_in(mem, pdref, &pdict->keys, "dict_resize(keys)"); |
814 | 15.6M | else |
815 | 15.6M | gs_free_ref_array(mem, &pdict->keys, "dict_resize(old keys)"); |
816 | 15.6M | ref_assign(&pdict->keys, &dnew.keys); |
817 | 15.6M | ref_assign(&pdict->values, &dnew.values); |
818 | 15.6M | r_store_attrs(&pdict->values, a_all | a_executable, orig_attrs); |
819 | 15.6M | ref_save_in(dict_memory(pdict), pdref, &pdict->maxlength, |
820 | 15.6M | "dict_resize(maxlength)"); |
821 | 15.6M | d_set_maxlength(pdict, new_size); |
822 | 15.6M | if (pds) |
823 | 15.6M | dstack_set_top(pds); /* just in case this is the top dict */ |
824 | 15.6M | return 0; |
825 | 15.6M | } |
826 | | |
827 | | /* Grow a dictionary for dict_put. */ |
828 | | int |
829 | | dict_grow(ref * pdref, dict_stack_t *pds) |
830 | 4.42M | { |
831 | 4.42M | dict *pdict = pdref->value.pdict; |
832 | | /* We might have maxlength < npairs, if */ |
833 | | /* dict_round_size increased the size. */ |
834 | 4.42M | ulong new_size = (ulong) d_maxlength(pdict); |
835 | | /* Adobe does this */ |
836 | 4.42M | if (new_size < 20) |
837 | 3.30M | new_size += 10; |
838 | 1.12M | else if (new_size < 200) |
839 | 959k | new_size *= 2; |
840 | 162k | else |
841 | 162k | new_size += new_size / 2; |
842 | 4.42M | #if ARCH_SIZEOF_INT < ARCH_SIZEOF_LONG |
843 | 4.42M | if (new_size > max_uint) |
844 | 0 | new_size = max_uint; |
845 | 4.42M | #endif |
846 | 4.42M | if (new_size > npairs(pdict)) { |
847 | 4.25M | int code = dict_resize(pdref, (uint) new_size, pds); |
848 | | |
849 | 4.25M | if (code >= 0) |
850 | 4.25M | return code; |
851 | | /* new_size was too big. */ |
852 | 0 | if (npairs(pdict) < dict_max_size) { |
853 | 0 | code = dict_resize(pdref, dict_max_size, pds); |
854 | 0 | if (code >= 0) |
855 | 0 | return code; |
856 | 0 | } |
857 | 0 | if (npairs(pdict) == d_maxlength(pdict)) { /* Can't do it. */ |
858 | 0 | return code; |
859 | 0 | } |
860 | | /* We can't grow to new_size, but we can grow to npairs. */ |
861 | 0 | new_size = npairs(pdict); |
862 | 0 | } |
863 | | /* maxlength < npairs, we can grow in place */ |
864 | 169k | ref_save_in(dict_memory(pdict), pdref, &pdict->maxlength, |
865 | 169k | "dict_put(maxlength)"); |
866 | 169k | d_set_maxlength(pdict, new_size); |
867 | 169k | return 0; |
868 | 4.42M | } |
869 | | |
870 | | /* Prepare to enumerate a dictionary. */ |
871 | | int |
872 | | dict_first(const ref * pdref) |
873 | 70.3M | { |
874 | 70.3M | return (int)nslots(pdref->value.pdict); |
875 | 70.3M | } |
876 | | |
877 | | /* Enumerate the next element of a dictionary. */ |
878 | | int |
879 | | dict_next(const ref * pdref, int index, ref * eltp /* ref eltp[2] */ ) |
880 | 2.98G | { |
881 | 2.98G | dict *pdict = pdref->value.pdict; |
882 | 2.98G | ref *vp = pdict->values.value.refs + index; |
883 | | |
884 | 7.33G | while (vp--, --index >= 0) { |
885 | 7.26G | array_get(dict_mem(pdict), &pdict->keys, (long)index, eltp); |
886 | | /* Make sure this is a valid entry. */ |
887 | 7.26G | if (r_has_type(eltp, t_name) || |
888 | 7.26G | (!dict_is_packed(pdict) && !r_has_type(eltp, t_null)) |
889 | 7.26G | ) { |
890 | 2.91G | eltp[1] = *vp; |
891 | 2.91G | if_debug6m('d', dict_mem(pdict), "[d]0x%lx: index %d: %lx %lx, %lx %lx\n", |
892 | 2.91G | (intptr_t)pdict, index, |
893 | 2.91G | ((ulong *) eltp)[0], ((ulong *) eltp)[1], |
894 | 2.91G | ((ulong *) vp)[0], ((ulong *) vp)[1]); |
895 | 2.91G | return index; |
896 | 2.91G | } |
897 | 7.26G | } |
898 | 69.8M | return -1; /* no more elements */ |
899 | 2.98G | } |
900 | | |
901 | | /* Return the index of a value within a dictionary. */ |
902 | | int |
903 | | dict_value_index(const ref * pdref, const ref * pvalue) |
904 | 20.0M | { |
905 | 20.0M | return (int)(pvalue - pdref->value.pdict->values.value.refs - 1); |
906 | 20.0M | } |
907 | | |
908 | | /* Return the entry at a given index within a dictionary. */ |
909 | | /* If the index designates an unoccupied entry, return gs_error_undefined. */ |
910 | | int |
911 | | dict_index_entry(const ref * pdref, int index, ref * eltp /* ref eltp[2] */ ) |
912 | 0 | { |
913 | 0 | const dict *pdict = pdref->value.pdict; |
914 | |
|
915 | 0 | array_get(dict_mem(pdict), &pdict->keys, (long)(index + 1), eltp); |
916 | 0 | if (r_has_type(eltp, t_name) || |
917 | 0 | (!dict_is_packed(pdict) && !r_has_type(eltp, t_null)) |
918 | 0 | ) { |
919 | 0 | eltp[1] = pdict->values.value.refs[index + 1]; |
920 | 0 | return 0; |
921 | 0 | } |
922 | 0 | return gs_error_undefined; |
923 | 0 | } |