/src/ghostpdl/pcl/pl/pldict.c
Line | Count | Source |
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 | | /* pldict.h */ |
18 | | /* Dictionary implementation for PCL parsers */ |
19 | | |
20 | | #include "memory_.h" |
21 | | #include "gstypes.h" |
22 | | #include "gsmemory.h" |
23 | | #include "gsstruct.h" |
24 | | #include "pldict.h" |
25 | | |
26 | | /* |
27 | | * The current implementation of dictionaries is a simple linked list. The |
28 | | * only concession to efficiency is that we store keys of up to |
29 | | * pl_dict_max_short_key characters in the node itself rather than a separately |
30 | | * allocated string. |
31 | | */ |
32 | | struct pl_dict_entry_s |
33 | | { |
34 | | gs_const_string key; /* data pointer = 0 if short key */ |
35 | | void *value; |
36 | | pl_dict_entry_t *next; |
37 | | pl_dict_entry_t *link; /* a link to the actual entry (supports aliases). */ |
38 | | byte short_key[pl_dict_max_short_key]; |
39 | | }; |
40 | | |
41 | | #define entry_key_data(pde)\ |
42 | 1.45G | ((pde)->key.size <= pl_dict_max_short_key ? (pde)->short_key : (pde)->key.data) |
43 | | |
44 | | /* GC descriptors */ |
45 | | public_st_pl_dict(); |
46 | | gs_private_st_composite(st_pl_dict_entry, pl_dict_entry_t, "pl_dict_entry_t", |
47 | | pl_dict_entry_enum_ptrs, pl_dict_entry_reloc_ptrs); |
48 | | #define pde ((pl_dict_entry_t *)vptr) |
49 | | static |
50 | 0 | ENUM_PTRS_BEGIN(pl_dict_entry_enum_ptrs) |
51 | 0 | return 0; |
52 | | |
53 | | /* ENUM_CONST_STRING_PTR(0, pl_dict_entry_t, key); */ |
54 | 0 | ENUM_PTR(1, pl_dict_entry_t, value); |
55 | 0 | ENUM_PTR(2, pl_dict_entry_t, next); |
56 | 0 | ENUM_PTR(3, pl_dict_entry_t, link); |
57 | 0 | ENUM_PTRS_END static RELOC_PTRS_BEGIN(pl_dict_entry_reloc_ptrs) |
58 | 0 | { |
59 | | /* RELOC_CONST_STRING_PTR(pl_dict_entry_t, key); */ |
60 | 0 | RELOC_PTR(pl_dict_entry_t, value); |
61 | 0 | RELOC_PTR(pl_dict_entry_t, next); |
62 | 0 | RELOC_PTR(pl_dict_entry_t, link); |
63 | 0 | } RELOC_PTRS_END |
64 | | #undef pde |
65 | | /* ---------------- Utilities ---------------- */ |
66 | | /* Provide a standard procedure for freeing a value. */ |
67 | | static void |
68 | | pl_dict_value_free(gs_memory_t * mem, void *value, client_name_t cname) |
69 | 0 | { |
70 | 0 | gs_free_object(mem, value, cname); |
71 | 0 | } |
72 | | |
73 | | /* |
74 | | * Look up an entry in a dictionary. Return a pointer to the pointer to the |
75 | | * entry. |
76 | | */ |
77 | | static pl_dict_entry_t ** |
78 | | pl_dict_lookup_entry(pl_dict_t * pdict, const byte * kdata, uint ksize) |
79 | 63.3M | { |
80 | 63.3M | pl_dict_entry_t **ppde = &pdict->entries; |
81 | 63.3M | pl_dict_entry_t *pde; |
82 | | |
83 | 1.45G | for (; (pde = *ppde) != 0; ppde = &pde->next) { |
84 | 1.41G | if (pde->key.size == ksize && |
85 | 1.41G | !memcmp(entry_key_data(pde), kdata, ksize) |
86 | 1.41G | ) |
87 | 25.3M | return ppde; |
88 | 1.41G | } |
89 | 37.9M | return 0; |
90 | 63.3M | } |
91 | | |
92 | | /* Delete a dictionary entry. */ |
93 | | static void |
94 | | pl_dict_free(pl_dict_t * pdict, pl_dict_entry_t ** ppde, client_name_t cname) |
95 | 1.53M | { |
96 | 1.53M | pl_dict_entry_t *pde = *ppde; |
97 | 1.53M | gs_memory_t *mem = pdict->memory; |
98 | | |
99 | 1.53M | *ppde = pde->next; |
100 | 1.53M | if (!pde->link) /* values are not freed for links */ |
101 | 1.53M | (*pdict->free_proc) (mem, pde->value, cname); |
102 | 1.53M | if (pde->key.size > pl_dict_max_short_key) |
103 | 260k | gs_free_string(mem, (byte *) pde->key.data, pde->key.size, cname); |
104 | 1.53M | gs_free_object(mem, pde, cname); |
105 | 1.53M | pdict->entry_count--; |
106 | 1.53M | } |
107 | | |
108 | | /* ---------------- API procedures ---------------- */ |
109 | | |
110 | | /* Initialize a dictionary. */ |
111 | | void |
112 | | pl_dict_init(pl_dict_t * pdict, gs_memory_t * mem, |
113 | | pl_dict_value_free_proc_t free_proc) |
114 | 528k | { |
115 | 528k | pdict->memory = mem; |
116 | 528k | pdict->free_proc = (free_proc ? free_proc : pl_dict_value_free); |
117 | 528k | pdict->entries = 0; |
118 | 528k | pdict->entry_count = 0; |
119 | 528k | pdict->parent = 0; |
120 | 528k | } |
121 | | |
122 | | /* |
123 | | * Look up an entry in a dictionary, optionally searching the stack, and |
124 | | * optionally returning a pointer to the actual dictionary where the |
125 | | * entry was found. Return true, setting *pvalue (and, if ppdict is not |
126 | | * NULL, *ppdict), if found. Note that this is the only routine that |
127 | | * searches the stack. |
128 | | */ |
129 | | bool |
130 | | pl_dict_lookup(pl_dict_t * pdict, const byte * kdata, uint ksize, |
131 | | void **pvalue, bool with_stack, pl_dict_t ** ppdict) |
132 | 61.1M | { |
133 | 61.1M | pl_dict_t *pdcur = pdict; |
134 | 61.1M | pl_dict_entry_t **ppde; |
135 | | |
136 | 61.1M | while ((ppde = pl_dict_lookup_entry(pdcur, kdata, ksize)) == 0) { |
137 | 35.8M | if (!with_stack || (pdcur = pdcur->parent) == 0) |
138 | 35.8M | return false; |
139 | 35.8M | } |
140 | 25.3M | *pvalue = (*ppde)->value; |
141 | 25.3M | if (ppdict) |
142 | 0 | *ppdict = pdcur; |
143 | 25.3M | return true; |
144 | 61.1M | } |
145 | | |
146 | | /* |
147 | | * make a new dictionary entry. |
148 | | */ |
149 | | static int |
150 | | pl_dict_build_new_entry(pl_dict_t * pdict, const byte * kdata, uint ksize, |
151 | | void *value, pl_dict_entry_t * link) |
152 | 1.53M | { /* Make a new entry. */ |
153 | 1.53M | byte *kstr; |
154 | 1.53M | gs_memory_t *mem = pdict->memory; |
155 | 1.53M | pl_dict_entry_t *pde; |
156 | | |
157 | 1.53M | pde = gs_alloc_struct(mem, pl_dict_entry_t, &st_pl_dict_entry, |
158 | 1.53M | "pl_dict_put(entry)"); |
159 | 1.53M | kstr = (ksize <= pl_dict_max_short_key ? pde->short_key : |
160 | 1.53M | gs_alloc_string(mem, ksize, "pl_dict_put(key)")); |
161 | 1.53M | if (pde == 0 || kstr == 0) { |
162 | 0 | if (kstr && kstr != pde->short_key) |
163 | 0 | gs_free_string(mem, kstr, ksize, "pl_dict_put(key)"); |
164 | 0 | gs_free_object(mem, pde, "pl_dict_put(entry)"); |
165 | 0 | return -1; |
166 | 0 | } |
167 | 1.53M | memcpy(kstr, kdata, ksize); |
168 | 1.53M | pde->key.data = (ksize <= pl_dict_max_short_key ? 0 : kstr); |
169 | 1.53M | pde->key.size = ksize; |
170 | 1.53M | pde->link = link; |
171 | 1.53M | pde->value = value; |
172 | 1.53M | pde->next = pdict->entries; |
173 | 1.53M | pdict->entries = pde; |
174 | 1.53M | pdict->entry_count++; |
175 | 1.53M | return 0; |
176 | 1.53M | } |
177 | | |
178 | | /* |
179 | | * Add an entry to a dictionary. Return 1 if it replaces an existing entry. |
180 | | * Return -1 if we couldn't allocate memory. pl_dict_put copies the key |
181 | | * string, but takes ownership of the value object (i.e., it will free it |
182 | | * when the entry is deleted, using the free_proc). |
183 | | */ |
184 | | int |
185 | | pl_dict_put(pl_dict_t * pdict, const byte * kdata, uint ksize, void *value) |
186 | 1.56M | { |
187 | 1.56M | pl_dict_entry_t **ppde = pl_dict_lookup_entry(pdict, kdata, ksize); |
188 | | |
189 | 1.56M | if (!ppde) { |
190 | 1.53M | void *link = 0; |
191 | 1.53M | int code = pl_dict_build_new_entry(pdict, kdata, ksize, value, link); |
192 | | |
193 | 1.53M | if (code < 0) |
194 | 0 | (*pdict->free_proc) (pdict->memory, value, "pl_dict_put(new value)"); |
195 | | |
196 | 1.53M | return code; |
197 | 1.53M | } else { /* Replace the value in an existing entry. */ |
198 | 31.9k | pl_dict_entry_t *pde; |
199 | | |
200 | 31.9k | pde = *ppde; |
201 | 31.9k | (*pdict->free_proc) (pdict->memory, pde->value, |
202 | 31.9k | "pl_dict_put(old value)"); |
203 | 31.9k | pde->value = value; |
204 | 31.9k | return 1; |
205 | 31.9k | } |
206 | 1.56M | } |
207 | | |
208 | | /* |
209 | | * link entry or alias |
210 | | */ |
211 | | int |
212 | | pl_dict_put_synonym(pl_dict_t * pdict, const byte * old_kdata, uint old_ksize, |
213 | | const byte * new_kdata, uint new_ksize) |
214 | 0 | { |
215 | 0 | pl_dict_entry_t **old_ppde = |
216 | 0 | pl_dict_lookup_entry(pdict, old_kdata, old_ksize); |
217 | 0 | pl_dict_entry_t *old_pde; |
218 | 0 | pl_dict_entry_t **new_ppde = |
219 | 0 | pl_dict_lookup_entry(pdict, new_kdata, new_ksize); |
220 | | /* old value doesn't exist or new value does exist */ |
221 | 0 | if (!old_ppde || new_ppde) |
222 | 0 | return -1; |
223 | | /* find the original data if this is a link to a link */ |
224 | 0 | old_pde = *old_ppde; |
225 | 0 | if (old_pde->link != 0) |
226 | 0 | old_pde = old_pde->link; |
227 | |
|
228 | 0 | return pl_dict_build_new_entry(pdict, new_kdata, new_ksize, |
229 | 0 | old_pde->value, old_pde); |
230 | 0 | } |
231 | | |
232 | | /* |
233 | | * Purge alias entries. A bit tricky but this doesn't fowl the |
234 | | * enumeration code since links are always prior to their entries. We |
235 | | * insert at the head of the list and a real entry must be present to |
236 | | * insert a link. Also deleting an entry deletes *all* associated |
237 | | * links and deleting a link deletes the corresponding entry. |
238 | | */ |
239 | | void |
240 | | pl_dict_undef_purge_synonyms(pl_dict_t * pdict, const byte * kdata, |
241 | | uint ksize) |
242 | 36 | { |
243 | 36 | pl_dict_entry_t **ppde = &pdict->entries; |
244 | 36 | pl_dict_entry_t **pptarget = pl_dict_lookup_entry(pdict, kdata, ksize); |
245 | 36 | pl_dict_entry_t *pde; |
246 | 36 | pl_dict_entry_t *ptarget; |
247 | | |
248 | 36 | if (!pptarget) |
249 | 0 | return; |
250 | 36 | ptarget = *pptarget; |
251 | | /* get the real entry if this is a link. */ |
252 | 36 | if (ptarget->link) |
253 | 0 | ptarget = ptarget->link; |
254 | 36 | #define dict_get_key_data(entry) ((entry)->key.size > pl_dict_max_short_key ?\ |
255 | 36 | (entry)->key.data : (entry)->short_key) |
256 | 36 | pl_dict_undef(pdict, dict_get_key_data(ptarget), ptarget->key.size); |
257 | | /* delete links to the target */ |
258 | 36 | pde = *ppde; |
259 | 36 | while (pde) { |
260 | 0 | pl_dict_entry_t *npde = pde->next; /* next entry */ |
261 | |
|
262 | 0 | if (pde->link && pde->link == ptarget) |
263 | 0 | pl_dict_undef(pdict, dict_get_key_data(pde), pde->key.size); |
264 | 0 | pde = npde; |
265 | 0 | } |
266 | 36 | #undef dict_get_key_data |
267 | 36 | } |
268 | | |
269 | | /* |
270 | | * Remove an entry from a dictionary. Return true if the entry was present. |
271 | | */ |
272 | | bool |
273 | | pl_dict_undef(pl_dict_t * pdict, const byte * kdata, uint ksize) |
274 | 584k | { |
275 | 584k | pl_dict_entry_t **ppde = pl_dict_lookup_entry(pdict, kdata, ksize); |
276 | | |
277 | 584k | if (!ppde) |
278 | 581k | return false; |
279 | 2.59k | pl_dict_free(pdict, ppde, "pl_dict_undef"); |
280 | 2.59k | return true; |
281 | 584k | } |
282 | | |
283 | | /* |
284 | | * Return the number of entries in a dictionary. |
285 | | */ |
286 | | uint |
287 | | pl_dict_length(const pl_dict_t * pdict, bool with_stack) |
288 | 116k | { |
289 | 116k | uint count = pdict->entry_count; |
290 | | |
291 | 116k | if (with_stack) { |
292 | 116k | const pl_dict_t *pdcur; |
293 | | |
294 | 116k | for (pdcur = pdict->parent; pdcur != 0; pdcur = pdcur->parent) |
295 | 0 | count += pdcur->entry_count; |
296 | 116k | } |
297 | 116k | return count; |
298 | 116k | } |
299 | | |
300 | | /* |
301 | | * Enumerate a dictionary with or without its stack. |
302 | | * See pldict.h for details. |
303 | | */ |
304 | | void |
305 | | pl_dict_enum_stack_begin(const pl_dict_t * pdict, pl_dict_enum_t * penum, |
306 | | bool with_stack) |
307 | 575k | { |
308 | 575k | penum->pdict = pdict; |
309 | 575k | penum->next = 0; |
310 | 575k | penum->first = true; |
311 | 575k | penum->next_dict = (with_stack ? pdict->parent : 0); |
312 | 575k | } |
313 | | |
314 | | bool |
315 | | pl_dict_enum_next(pl_dict_enum_t * penum, gs_const_string * pkey, |
316 | | void **pvalue) |
317 | 36.0M | { |
318 | 36.0M | pl_dict_entry_t *pde; |
319 | | |
320 | 36.4M | while ((pde = (penum->first ? penum->pdict->entries : penum->next)) == 0) { |
321 | 769k | if (penum->next_dict == 0) |
322 | 410k | return false; |
323 | 359k | penum->next_dict = (penum->pdict = penum->next_dict)->parent; |
324 | 359k | penum->first = true; |
325 | 359k | } |
326 | 35.6M | pkey->data = entry_key_data(pde); |
327 | 35.6M | pkey->size = pde->key.size; |
328 | 35.6M | *pvalue = pde->value; |
329 | 35.6M | penum->next = pde->next; |
330 | 35.6M | penum->first = false; |
331 | 35.6M | return true; |
332 | 36.0M | } |
333 | | |
334 | | /* |
335 | | * Release a dictionary by freeing all keys, values, and other storage. |
336 | | */ |
337 | | void |
338 | | pl_dict_release(pl_dict_t * pdict) |
339 | 477k | { |
340 | 2.01M | while (pdict->entries) |
341 | 1.53M | pl_dict_free(pdict, &pdict->entries, "pl_dict_release"); |
342 | 477k | } |