xref: /plan9/sys/src/cmd/gs/src/idictdef.h (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998 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: idictdef.h,v 1.4 2002/02/21 22:24:53 giles Exp $ */
18 /* Internals of dictionary implementation */
19 
20 #ifndef idictdef_INCLUDED
21 #  define idictdef_INCLUDED
22 
23 /*
24  * A dictionary of capacity M is a structure containing the following
25  * elements (refs):
26  *
27  *      keys - a t_shortarray or t_array of M+1 elements, containing
28  *      the keys.
29  *
30  *      values - a t_array of M+1 elements, containing the values.
31  *
32  *      count - a t_integer whose value tells how many entries are
33  *      occupied (N).
34  *
35  *      maxlength - a t_integer whose value gives the client's view of
36  *      the capacity (C).  C may be less than M (see below).
37  *
38  *      memory - a foreign t_struct referencing the allocator used to
39  *      create this dictionary, which will also be used to expand or
40  *      unpack it if necessary.
41  *
42  * C < M is possible because on large-memory systems, we usually round up M
43  * so that M is a power of 2 (see idict.h for details); this allows us to
44  * use masking rather than division for computing the initial hash probe.
45  * However, C is always the maxlength specified by the client, so clients
46  * get a consistent story.
47  *
48  * As noted above, the keys may be either in packed or unpacked form.
49  * The markers for unused and deleted entries are different in the two forms.
50  * In the packed form:
51  *      unused entries contain packed_key_empty;
52  *      deleted entries contain packed_key_deleted.
53  * In the unpacked form:
54  *      unused entries contain a literal null;
55  *      deleted entries contain an executable null.
56  *
57  * The first entry is always marked deleted, to reduce the cost of the
58  * wrap-around check.
59  *
60  * Note that if the keys slot in the dictionary is new,
61  * all the key slots are new (more recent than the last save).
62  * We use this fact to avoid saving stores into packed keys
63  * for newly created dictionaries.
64  *
65  * Note that name keys with indices above packed_name_max_index require using
66  * the unpacked form.  */
67 #define dict_is_packed(dct) r_has_type(&(dct)->keys, t_shortarray)
68 #define packed_key_empty (pt_tag(pt_integer) + 0)
69 #define packed_key_deleted (pt_tag(pt_integer) + 1)
70 #define packed_key_impossible pt_tag(pt_full_ref)	/* never matches */
71 #define packed_name_key(nidx)\
72   ((nidx) <= packed_name_max_index ? pt_tag(pt_literal_name) + (nidx) :\
73    packed_key_impossible)
74 /*
75  * Using a special mark for deleted entries causes lookup time to degrade
76  * as entries are inserted and deleted.  This is not a problem, because
77  * entries are almost never deleted.
78  */
79 #define d_maxlength(dct) ((uint)((dct)->maxlength.value.intval))
80 #define d_set_maxlength(dct,siz) ((dct)->maxlength.value.intval = (siz))
81 #define nslots(dct) r_size(&(dct)->values)
82 #define npairs(dct) (nslots(dct) - 1)
83 #define d_length(dct) ((uint)((dct)->count.value.intval))
84 
85 /*
86  * Define macros for searching a packed dictionary.  Free variables:
87  *      ref_packed kpack - holds the packed key.
88  *      uint hash - holds the hash of the name.
89  *      dict *pdict - points to the dictionary.
90  *      uint size - holds npairs(pdict).
91  * Note that the macro is *not* enclosed in {}, so that we can access
92  * the values of kbot and kp after leaving the loop.
93  *
94  * We break the macro into two to avoid overflowing some preprocessors.
95  */
96 /* packed_search_body also uses kp and kbot as free variables. */
97 #define packed_search_value_pointer (pdict->values.value.refs + (kp - kbot))
98 #define packed_search_body(found1,found2,del,miss)\
99     { if_debug2('D', "[D]probe 0x%lx: 0x%x\n", (ulong)kp, *kp);\
100       if ( *kp == kpack )\
101        { found1;\
102 	 found2;\
103        }\
104       else if ( !r_packed_is_name(kp) )\
105        { /* Empty, deleted, or wraparound. Figure out which. */\
106 	 if ( *kp == packed_key_empty ) miss;\
107 	 if ( kp == kbot ) break;	/* wrap */\
108 	 else { del; }\
109        }\
110     }
111 #define packed_search_1(found1,found2,del,miss)\
112    const ref_packed *kbot = pdict->keys.value.packed;\
113    register const ref_packed *kp;\
114    for ( kp = kbot + dict_hash_mod(hash, size) + 1; ; kp-- )\
115      packed_search_body(found1,found2,del,miss)
116 #define packed_search_2(found1,found2,del,miss)\
117    for ( kp += size; ; kp-- )\
118      packed_search_body(found1,found2,del,miss)
119 
120 #endif /* idictdef_INCLUDED */
121