xref: /plan9/sys/src/cmd/gs/src/idict.h (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 1995, 1997, 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: idict.h,v 1.6 2004/08/04 19:36:12 stefan Exp $ */
18 /* Interfaces for Ghostscript dictionary package */
19 
20 #ifndef idict_INCLUDED
21 #  define idict_INCLUDED
22 
23 #include "iddstack.h"
24 
25 /*
26  * Contrary to our usual practice, we expose the (first-level)
27  * representation of a dictionary in the interface file,
28  * because it is so important that access checking go fast.
29  * The access attributes for the dictionary are stored in
30  * the values ref.
31  */
32 struct dict_s {
33     ref values;			/* t_array, values */
34     ref keys;			/* t_shortarray or t_array, keys */
35     ref count;			/* t_integer, count of occupied entries */
36     /* (length) */
37     ref maxlength;		/* t_integer, maxlength as seen by client. */
38     ref memory;			/* foreign t_struct, the allocator that */
39     /* created this dictionary */
40 #define dict_memory(pdict) r_ptr(&(pdict)->memory, gs_ref_memory_t)
41 #define dict_mem(pdict) r_ptr(&(pdict)->memory, gs_memory_t)
42 };
43 
44 /*
45  * Define the maximum size of a dictionary.
46  */
47 extern const uint dict_max_size;
48 
49 /*
50  * Define whether dictionaries expand automatically when full.  Note that
51  * if dict_auto_expand is true, dict_put, dict_copy, dict_resize, and
52  * dict_grow cannot return e_dictfull; however, they can return e_VMerror.
53  * (dict_find can return e_dictfull even if dict_auto_expand is true.)
54  */
55 extern bool dict_auto_expand;
56 
57 /*
58  * Create a dictionary.
59  */
60 #ifndef gs_ref_memory_DEFINED
61 #  define gs_ref_memory_DEFINED
62 typedef struct gs_ref_memory_s gs_ref_memory_t;
63 #endif
64 int dict_alloc(gs_ref_memory_t *, uint maxlength, ref * pdref);
65 
66 #define dict_create(maxlen, pdref)\
67   dict_alloc(iimemory, maxlen, pdref)
68 
69 /*
70  * Return a pointer to a ref that holds the access attributes
71  * for a dictionary.
72  */
73 #define dict_access_ref(pdref) (&(pdref)->value.pdict->values)
74 /*
75  * Check a dictionary for read or write permission.
76  * Note: this does NOT check the type of its operand!
77  */
78 #define check_dict_read(dref) check_read(*dict_access_ref(&dref))
79 #define check_dict_write(dref) check_write(*dict_access_ref(&dref))
80 
81 /*
82  * Look up a key in a dictionary.  Store a pointer to the value slot
83  * where found, or to the (value) slot for inserting.
84  * The caller is responsible for checking that the dictionary is readable.
85  * Return 1 if found, 0 if not and there is room for a new entry,
86  * Failure returns:
87  *      e_typecheck if the key is null;
88  *      e_invalidaccess if the key is a string lacking read access;
89  *      e_VMerror or e_limitcheck if the key is a string and the corresponding
90  *        error occurs from attempting to convert it to a name;
91  *      e_dictfull if the dictionary is full and the key is missing.
92  */
93 int dict_find(const ref * pdref, const ref * key, ref ** ppvalue);
94 
95 /*
96  * Look up a (constant) C string in a dictionary.
97  * Return 1 if found, <= 0 if not.
98  */
99 int dict_find_string(const ref * pdref, const char *kstr, ref ** ppvalue);
100 
101 /*
102  * Enter a key-value pair in a dictionary.
103  * The caller is responsible for checking that the dictionary is writable.
104  * Return 1 if this was a new entry, 0 if this replaced an existing entry.
105  * Failure returns are as for dict_find, except that e_dictfull doesn't
106  * occur if the dictionary is full but expandable, plus:
107  *      e_invalidaccess for an attempt to store a younger key or value into
108  *        an older dictionary, or as described just below;
109  *      e_VMerror if a VMerror occurred while trying to expand the
110  *        dictionary.
111  * Note that this procedure, and all procedures that may change the
112  * contents of a dictionary, take a pointer to a dictionary stack,
113  * so they can update the cached 'top' values and also update the cached
114  * value pointer in names.  A NULL pointer for the dictionary stack is
115  * allowed, but in this case, if the dictionary is present on any dictionary
116  * stack, an e_invalidaccess error will occur if cached values need updating.
117  * THIS ERROR CHECK IS NOT IMPLEMENTED YET.
118  */
119 int dict_put(ref * pdref, const ref * key, const ref * pvalue,
120 	     dict_stack_t *pds);
121 
122 /*
123  * Enter a key-value pair where the key is a (constant) C string.
124  */
125 int dict_put_string(ref * pdref, const char *kstr, const ref * pvalue,
126 		    dict_stack_t *pds);
127 
128 /*
129  * Remove a key-value pair from a dictionary.
130  * Return 0 or e_undefined.
131  */
132 int dict_undef(ref * pdref, const ref * key, dict_stack_t *pds);
133 
134 /*
135  * Return the number of elements in a dictionary.
136  */
137 uint dict_length(const ref * pdref);
138 
139 /*
140  * Return the capacity of a dictionary.
141  */
142 uint dict_maxlength(const ref * pdref);
143 
144 /*
145  * Return the maximum index of a slot within a dictionary.
146  * Note that this may be greater than maxlength.
147  */
148 uint dict_max_index(const ref * pdref);
149 
150 /*
151  * Copy one dictionary into another.
152  * Return 0 or e_dictfull.
153  * If new_only is true, only copy entries whose keys
154  * aren't already present in the destination.
155  */
156 int dict_copy_entries(const ref * dfrom, ref * dto, bool new_only,
157 		      dict_stack_t *pds);
158 
159 #define dict_copy(dfrom, dto, pds) dict_copy_entries(dfrom, dto, false, pds)
160 #define dict_copy_new(dfrom, dto, pds) dict_copy_entries(dfrom, dto, true, pds)
161 
162 /*
163  * Grow or shrink a dictionary.
164  * Return 0, e_dictfull, or e_VMerror.
165  */
166 int dict_resize(ref * pdref, uint newmaxlength, dict_stack_t *pds);
167 
168 /*
169  * Grow a dictionary in the same way as dict_put does.
170  * We export this for some special-case code in zop_def.
171  */
172 int dict_grow(ref * pdref, dict_stack_t *pds);
173 
174 /*
175  * Ensure that a dictionary uses the unpacked representation for keys.
176  * (This is of no interest to ordinary clients.)
177  */
178 int dict_unpack(ref * pdref, dict_stack_t *pds);
179 
180 /*
181  * Prepare to enumerate a dictionary.
182  * Return an integer suitable for the first call to dict_next.
183  */
184 int dict_first(const ref * pdref);
185 
186 /*
187  * Enumerate the next element of a dictionary.
188  * index is initially the result of a call on dict_first.
189  * Either store a key and value at eltp[0] and eltp[1]
190  * and return an updated index, or return -1
191  * to signal that there are no more elements in the dictionary.
192  */
193 int dict_next(const ref * pdref, int index, ref * eltp);
194 
195 /*
196  * Given a value pointer return by dict_find, return an index that
197  * identifies the entry within the dictionary. (This may, but need not,
198  * be the same as the index returned by dict_next.)
199  * The index is in the range [0..max_index-1].
200  */
201 int dict_value_index(const ref * pdref, const ref * pvalue);
202 
203 /*
204  * Given an index in [0..max_index-1], as returned by dict_value_index,
205  * return the key and value, as returned by dict_next.
206  * If the index designates an unoccupied entry, return e_undefined.
207  */
208 int dict_index_entry(const ref * pdref, int index, ref * eltp);
209 
210 /*
211  * The following are some internal details that are used in both the
212  * implementation and some high-performance clients.
213  */
214 
215 /* On machines with reasonable amounts of memory, we round up dictionary
216  * sizes to the next power of 2 whenever possible, to allow us to use
217  * masking rather than division for computing the hash index.
218  * Unfortunately, if we required this, it would cut the maximum size of a
219  * dictionary in half.  Therefore, on such machines, we distinguish
220  * "huge" dictionaries (dictionaries whose size is larger than the largest
221  * power of 2 less than dict_max_size) as a special case:
222  *
223  *      - If the top dictionary on the stack is huge, we set the dtop
224  *      parameters so that the fast inline lookup will always fail.
225  *
226  *      - For general lookup, we use the slower hash_mod algorithm for
227  *      huge dictionaries.
228  */
229 #define dict_max_non_huge ((uint)(max_array_size / 2 + 1))
230 
231 /* Define the hashing function for names. */
232 /* We don't have to scramble the index, because */
233 /* indices are assigned in a scattered order (see name_ref in iname.c). */
234 #define dict_name_index_hash(nidx) (nidx)
235 
236 /* Hash an arbitrary non-negative or unsigned integer into a dictionary. */
237 #define dict_hash_mod_rem(hash, size) ((hash) % (size))
238 #define dict_hash_mod_mask(hash, size) ((hash) & ((size) - 1))
239 #define dict_hash_mod_small(hash, size) dict_hash_mod_rem(hash, size)
240 #define dict_hash_mod_inline_small(hash, size) dict_hash_mod_rem(hash, size)
241 #define dict_hash_mod_large(hash, size)\
242   (size > dict_max_non_huge ? dict_hash_mod_rem(hash, size) :\
243    dict_hash_mod_mask(hash, size))
244 #define dict_hash_mod_inline_large(hash, size) dict_hash_mod_mask(hash, size)
245 /* Round up the requested size of a dictionary.  Return 0 if too big. */
246 uint dict_round_size_small(uint rsize);
247 uint dict_round_size_large(uint rsize);
248 
249 /* Choose the algorithms depending on the size of memory. */
250 #if arch_small_memory
251 #  define dict_hash_mod(h, s) dict_hash_mod_small(h, s)
252 #  define dict_hash_mod_inline(h, s) dict_hash_mod_inline_small(h, s)
253 #  define dict_round_size(s) dict_round_size_small(s)
254 #else
255 #  ifdef DEBUG
256 #    define dict_hash_mod(h, s)\
257        (gs_debug_c('.') ? dict_hash_mod_small(h, s) :\
258 	dict_hash_mod_large(h, s))
259 #    define dict_hash_mod_inline(h, s)\
260        (gs_debug_c('.') ? dict_hash_mod_inline_small(h, s) :\
261 	dict_hash_mod_inline_large(h, s))
262 #    define dict_round_size(s)\
263        (gs_debug_c('.') ? dict_round_size_small(s) :\
264 	dict_round_size_large(s))
265 #  else
266 #    define dict_hash_mod(h, s) dict_hash_mod_large(h, s)
267 #    define dict_hash_mod_inline(h, s) dict_hash_mod_inline_large(h, s)
268 #    define dict_round_size(s) dict_round_size_large(s)
269 #  endif
270 #endif
271 
272 #endif /* idict_INCLUDED */
273