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