/src/ghostpdl/psi/iname.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 | | /* Name lookup for Ghostscript interpreter */ |
18 | | #include "memory_.h" |
19 | | #include "string_.h" |
20 | | #include "ghost.h" |
21 | | #include "gsstruct.h" |
22 | | #include "gxobj.h" /* for o_set_unmarked */ |
23 | | #include "ierrors.h" |
24 | | #include "inamedef.h" |
25 | | #include "imemory.h" /* for isave.h */ |
26 | | #include "isave.h" |
27 | | #include "store.h" |
28 | | |
29 | | /* Public values */ |
30 | | const uint name_max_string = max_name_string; |
31 | | |
32 | | /* Define the permutation table for name hashing. */ |
33 | | static const byte hash_permutation[256] = { |
34 | | NAME_HASH_PERMUTATION_DATA |
35 | | }; |
36 | | |
37 | | /* Define the data for the 1-character names. */ |
38 | | static const byte nt_1char_names[NT_1CHAR_SIZE] = { |
39 | | NT_1CHAR_NAMES_DATA |
40 | | }; |
41 | | |
42 | | static void gs_names_finalize(const gs_memory_t *, void *); |
43 | | |
44 | | /* Structure descriptors */ |
45 | | gs_private_st_simple(st_name_sub_table, name_sub_table, "name_sub_table"); |
46 | | gs_private_st_composite(st_name_string_sub_table, name_string_sub_table_t, |
47 | | "name_string_sub_table_t", |
48 | | name_string_sub_enum_ptrs, name_string_sub_reloc_ptrs); |
49 | | gs_private_st_composite_final(st_name_table, name_table, "name_table", |
50 | | name_table_enum_ptrs, name_table_reloc_ptrs,gs_names_finalize); |
51 | | |
52 | | /* Forward references */ |
53 | | static int name_alloc_sub(name_table *); |
54 | | static void name_free_sub(name_table *, uint, bool); |
55 | | static void name_scan_sub(name_table *, uint, bool, bool); |
56 | | |
57 | | /* Debugging printout */ |
58 | | #ifdef DEBUG |
59 | | static void |
60 | | name_print(const char *msg, const name_table *nt, uint nidx, const int *pflag) |
61 | | { |
62 | | const name_string_t *pnstr = names_index_string_inline(nt, nidx); |
63 | | const name *pname = names_index_ptr_inline(nt, nidx); |
64 | | const byte *str = pnstr->string_bytes; |
65 | | |
66 | | dmlprintf1(nt->memory, "[n]%s", msg); |
67 | | if (pflag) |
68 | | dmprintf1(nt->memory, "(%d)", *pflag); |
69 | | dmprintf2(nt->memory, " ("PRI_INTPTR"#%u)", (intptr_t)pname, nidx); |
70 | | debug_print_string(nt->memory, str, pnstr->string_size); |
71 | | dmprintf2(nt->memory, "("PRI_INTPTR",%u)\n", (intptr_t)str, pnstr->string_size); |
72 | | } |
73 | | # define if_debug_name(msg, nt, nidx, pflag)\ |
74 | | if ( gs_debug_c('n') ) name_print(msg, nt, nidx, pflag) |
75 | | #else |
76 | 1.30G | # define if_debug_name(msg, nt, nidx, pflag) DO_NOTHING |
77 | | #endif |
78 | | |
79 | | /* Initialize a name table */ |
80 | | name_table * |
81 | | names_init(ulong count, gs_ref_memory_t *imem) |
82 | 162k | { |
83 | 162k | gs_memory_t *mem = (gs_memory_t *)imem; |
84 | 162k | name_table *nt; |
85 | 162k | int i; |
86 | | |
87 | 162k | if (count == 0) |
88 | 162k | count = max_name_count + 1L; |
89 | 0 | else if (count - 1 > max_name_count) |
90 | 0 | return 0; |
91 | 162k | nt = gs_alloc_struct(mem, name_table, &st_name_table, "name_init(nt)"); |
92 | 162k | if (nt == 0) |
93 | 0 | return 0; |
94 | 162k | memset(nt, 0, sizeof(name_table)); |
95 | 162k | nt->max_sub_count = |
96 | 162k | ((count - 1) | nt_sub_index_mask) >> nt_log2_sub_size; |
97 | 162k | nt->name_string_attrs = imemory_space(imem) | a_readonly; |
98 | 162k | nt->memory = mem; |
99 | | /* Initialize the one-character names. */ |
100 | | /* Start by creating the necessary sub-tables. */ |
101 | 324k | for (i = 0; i < NT_1CHAR_FIRST + NT_1CHAR_SIZE; i += nt_sub_size) { |
102 | 162k | int code = name_alloc_sub(nt); |
103 | | |
104 | 162k | if (code < 0) { |
105 | 0 | names_free(nt); |
106 | 0 | return 0; |
107 | 0 | } |
108 | 162k | } |
109 | 21.1M | for (i = -1; i < NT_1CHAR_SIZE; i++) { |
110 | 20.9M | uint ncnt = NT_1CHAR_FIRST + i; |
111 | 20.9M | uint nidx = name_count_to_index(ncnt); |
112 | 20.9M | name *pname = names_index_ptr_inline(nt, nidx); |
113 | 20.9M | name_string_t *pnstr = names_index_string_inline(nt, nidx); |
114 | | |
115 | 20.9M | if (i < 0) |
116 | 162k | pnstr->string_bytes = nt_1char_names, |
117 | 162k | pnstr->string_size = 0; |
118 | 20.7M | else |
119 | 20.7M | pnstr->string_bytes = nt_1char_names + i, |
120 | 20.7M | pnstr->string_size = 1; |
121 | 20.9M | pnstr->foreign_string = 1; |
122 | 20.9M | pnstr->mark = 1; |
123 | 20.9M | pname->pvalue = pv_no_defn; |
124 | 20.9M | } |
125 | 162k | nt->perm_count = NT_1CHAR_FIRST + NT_1CHAR_SIZE; |
126 | | /* Reconstruct the free list. */ |
127 | 162k | nt->free = 0; |
128 | 162k | names_trace_finish(nt, NULL); |
129 | 162k | return nt; |
130 | 162k | } |
131 | | |
132 | | /* Free a name table */ |
133 | | void |
134 | | names_free(name_table *nt) |
135 | 0 | { |
136 | 0 | if (nt == NULL) return; |
137 | | |
138 | 0 | while (nt->sub_count > 0) |
139 | 0 | name_free_sub(nt, --(nt->sub_count), false); |
140 | 0 | gs_free_object(nt->memory, nt, "name_init(nt)"); |
141 | 0 | } |
142 | | |
143 | | static void |
144 | | gs_names_finalize(const gs_memory_t *cmem, void *vptr) |
145 | 162k | { |
146 | 162k | (void)vptr; /* unused */ |
147 | 162k | cmem->gs_lib_ctx->gs_name_table = NULL; |
148 | 162k | } |
149 | | |
150 | | /* Get the allocator for the name table. */ |
151 | | gs_memory_t * |
152 | | names_memory(const name_table * nt) |
153 | 13.0M | { |
154 | 13.0M | return nt->memory; |
155 | 13.0M | } |
156 | | |
157 | | /* Look up or enter a name in the table. */ |
158 | | /* Return 0 or an error code. */ |
159 | | /* The return may overlap the characters of the string! */ |
160 | | /* See iname.h for the meaning of enterflag. */ |
161 | | int |
162 | | names_ref(name_table *nt, const byte *ptr, uint size, ref *pref, int enterflag) |
163 | 10.8G | { |
164 | 10.8G | name *pname; |
165 | 10.8G | name_string_t *pnstr; |
166 | 10.8G | uint nidx; |
167 | 10.8G | uint *phash; |
168 | | |
169 | | /* Compute a hash for the string. */ |
170 | | /* Make a special check for 1-character names. */ |
171 | 10.8G | switch (size) { |
172 | 1.19M | case 0: |
173 | 1.19M | nidx = name_count_to_index(1); |
174 | 1.19M | pname = names_index_ptr_inline(nt, nidx); |
175 | 1.19M | goto mkn; |
176 | 1.55G | case 1: |
177 | 1.55G | if (*ptr < NT_1CHAR_SIZE) { |
178 | 1.55G | uint hash = *ptr + NT_1CHAR_FIRST; |
179 | | |
180 | 1.55G | nidx = name_count_to_index(hash); |
181 | 1.55G | pname = names_index_ptr_inline(nt, nidx); |
182 | 1.55G | goto mkn; |
183 | 1.55G | } |
184 | | /* falls through */ |
185 | 9.25G | default: { |
186 | 9.25G | uint hash; |
187 | | |
188 | 9.25G | NAME_HASH(hash, hash_permutation, ptr, size); |
189 | 9.25G | phash = nt->hash + (hash & (NT_HASH_SIZE - 1)); |
190 | 9.25G | } |
191 | 10.8G | } |
192 | | |
193 | 22.3G | for (nidx = *phash; nidx != 0; |
194 | 13.0G | nidx = name_next_index(nidx, pnstr) |
195 | 21.0G | ) { |
196 | 21.0G | pnstr = names_index_string_inline(nt, nidx); |
197 | 21.0G | if (pnstr->string_size == size && |
198 | 21.0G | !memcmp_inline(ptr, pnstr->string_bytes, size) |
199 | 21.0G | ) { |
200 | 7.94G | pname = name_index_ptr_inline(nt, nidx); |
201 | 7.94G | goto mkn; |
202 | 7.94G | } |
203 | 21.0G | } |
204 | | /* Name was not in the table. Make a new entry. */ |
205 | 1.30G | if (enterflag < 0) |
206 | 94.2M | return_error(gs_error_undefined); |
207 | 1.21G | if (size > max_name_string) |
208 | 16 | return_error(gs_error_limitcheck); |
209 | 1.21G | nidx = nt->free; |
210 | 1.21G | if (nidx == 0) { |
211 | 2.31M | int code = name_alloc_sub(nt); |
212 | | |
213 | 2.31M | if (code < 0) |
214 | 0 | return code; |
215 | 2.31M | nidx = nt->free; |
216 | 2.31M | } |
217 | 1.21G | pnstr = names_index_string_inline(nt, nidx); |
218 | 1.21G | if (enterflag == 1) { |
219 | 1.08G | byte *cptr = (byte *)gs_alloc_string(nt->memory, size, |
220 | 1.08G | "names_ref(string)"); |
221 | | |
222 | 1.08G | if (cptr == 0) |
223 | 0 | return_error(gs_error_VMerror); |
224 | 1.08G | memcpy(cptr, ptr, size); |
225 | 1.08G | pnstr->string_bytes = cptr; |
226 | 1.08G | pnstr->foreign_string = 0; |
227 | 1.08G | } else { |
228 | 129M | pnstr->string_bytes = ptr; |
229 | 129M | pnstr->foreign_string = (enterflag == 0 ? 1 : 0); |
230 | 129M | } |
231 | 1.21G | pnstr->string_size = size; |
232 | 1.21G | pname = name_index_ptr_inline(nt, nidx); |
233 | 1.21G | pname->pvalue = pv_no_defn; |
234 | 1.21G | nt->free = name_next_index(nidx, pnstr); |
235 | 1.21G | set_name_next_index(nidx, pnstr, *phash); |
236 | 1.21G | *phash = nidx; |
237 | 1.21G | if_debug_name("new name", nt, nidx, &enterflag); |
238 | 10.7G | mkn: |
239 | 10.7G | make_name(pref, nidx, pname); |
240 | 10.7G | return 0; |
241 | 1.21G | } |
242 | | |
243 | | /* Get the string for a name. */ |
244 | | void |
245 | | names_string_ref(const name_table * nt, const ref * pnref /* t_name */ , |
246 | | ref * psref /* result, t_string */ ) |
247 | 934M | { |
248 | 934M | const name_string_t *pnstr = names_string_inline(nt, pnref); |
249 | | |
250 | 934M | make_const_string(psref, |
251 | 934M | (pnstr->foreign_string ? avm_foreign | a_readonly : |
252 | 934M | nt->name_string_attrs), |
253 | 934M | pnstr->string_size, |
254 | 934M | (const byte *)pnstr->string_bytes); |
255 | 934M | } |
256 | | |
257 | | /* Convert a t_string object to a name. */ |
258 | | /* Copy the executable attribute. */ |
259 | | int |
260 | | names_from_string(name_table * nt, const ref * psref, ref * pnref) |
261 | 45.4M | { |
262 | 45.4M | int exec = r_has_attr(psref, a_executable); |
263 | 45.4M | int code = names_ref(nt, psref->value.bytes, r_size(psref), pnref, 1); |
264 | | |
265 | 45.4M | if (code < 0) |
266 | 12 | return code; |
267 | 45.4M | if (exec) |
268 | 0 | r_set_attrs(pnref, a_executable); |
269 | 45.4M | return code; |
270 | 45.4M | } |
271 | | |
272 | | /* Enter a (permanently allocated) C string as a name. */ |
273 | | int |
274 | | names_enter_string(name_table * nt, const char *str, ref * pref) |
275 | 16.3M | { |
276 | 16.3M | return names_ref(nt, (const byte *)str, strlen(str), pref, 0); |
277 | 16.3M | } |
278 | | |
279 | | /* Invalidate the value cache for a name. */ |
280 | | void |
281 | | names_invalidate_value_cache(name_table * nt, const ref * pnref) |
282 | 122M | { |
283 | 122M | pnref->value.pname->pvalue = pv_other; |
284 | 122M | } |
285 | | |
286 | | /* Convert between names and indices. */ |
287 | | #undef names_index |
288 | | name_index_t |
289 | | names_index(const name_table * nt, const ref * pnref) |
290 | 13.3G | { |
291 | 13.3G | return names_index_inline(nt, pnref); |
292 | 13.3G | } |
293 | | void |
294 | | names_index_ref(const name_table * nt, name_index_t index, ref * pnref) |
295 | 6.17G | { |
296 | 6.17G | names_index_ref_inline(nt, index, pnref); |
297 | 6.17G | } |
298 | | name * |
299 | | names_index_ptr(const name_table * nt, name_index_t index) |
300 | 0 | { |
301 | 0 | return names_index_ptr_inline(nt, index); |
302 | 0 | } |
303 | | |
304 | | /* Get the index of the next valid name. */ |
305 | | /* The argument is 0 or a valid index. */ |
306 | | /* Return 0 if there are no more. */ |
307 | | name_index_t |
308 | | names_next_valid_index(name_table * nt, name_index_t nidx) |
309 | 2.37G | { |
310 | 2.37G | const name_string_sub_table_t *ssub = |
311 | 2.37G | nt->sub[nidx >> nt_log2_sub_size].strings; |
312 | 2.37G | const name_string_t *pnstr; |
313 | | |
314 | 2.53G | do { |
315 | 2.53G | ++nidx; |
316 | 2.53G | if ((nidx & nt_sub_index_mask) == 0) |
317 | 4.95M | for (;; nidx += nt_sub_size) { |
318 | 4.95M | if ((nidx >> nt_log2_sub_size) >= nt->sub_count) |
319 | 325k | return 0; |
320 | 4.62M | ssub = nt->sub[nidx >> nt_log2_sub_size].strings; |
321 | 4.62M | if (ssub != 0) |
322 | 4.62M | break; |
323 | 4.62M | } |
324 | 2.53G | pnstr = &ssub->strings[nidx & nt_sub_index_mask]; |
325 | 2.53G | } |
326 | 2.53G | while (pnstr->string_bytes == 0); |
327 | 2.37G | return nidx; |
328 | 2.37G | } |
329 | | |
330 | | /* ------ Garbage collection ------ */ |
331 | | |
332 | | /* Unmark all non-permanent names before a garbage collection. */ |
333 | | void |
334 | | names_unmark_all(name_table * nt) |
335 | 325k | { |
336 | 325k | uint si; |
337 | 325k | name_string_sub_table_t *ssub; |
338 | | |
339 | 5.27M | for (si = 0; si < nt->sub_count; ++si) |
340 | 4.95M | if ((ssub = nt->sub[si].strings) != 0) { |
341 | 4.95M | uint i; |
342 | | |
343 | | /* We can make the test much more efficient if we want.... */ |
344 | 2.54G | for (i = 0; i < nt_sub_size; ++i) |
345 | 2.53G | if (name_index_to_count((si << nt_log2_sub_size) + i) >= |
346 | 2.53G | nt->perm_count) |
347 | 2.49G | ssub->strings[i].mark = 0; |
348 | 4.95M | } |
349 | 325k | } |
350 | | |
351 | | /* Mark a name. Return true if new mark. We export this so we can mark */ |
352 | | /* character names in the character cache. */ |
353 | | bool |
354 | | names_mark_index(name_table * nt, name_index_t nidx) |
355 | 7.26G | { |
356 | 7.26G | name_string_t *pnstr = names_index_string_inline(nt, nidx); |
357 | | |
358 | 7.26G | if (pnstr->mark) |
359 | 5.01G | return false; |
360 | 2.24G | pnstr->mark = 1; |
361 | 2.24G | return true; |
362 | 7.26G | } |
363 | | |
364 | | /* Get the object (sub-table) containing a name. */ |
365 | | /* The garbage collector needs this so it can relocate pointers to names. */ |
366 | | void /*obj_header_t */ * |
367 | | names_ref_sub_table(name_table * nt, const ref * pnref) |
368 | 5.32G | { |
369 | | /* When this procedure is called, the pointers from the name table */ |
370 | | /* to the sub-tables may or may not have been relocated already, */ |
371 | | /* so we can't use them. Instead, we have to work backwards from */ |
372 | | /* the name pointer itself. */ |
373 | 5.32G | return pnref->value.pname - (r_size(pnref) & nt_sub_index_mask); |
374 | 5.32G | } |
375 | | void /*obj_header_t */ * |
376 | | names_index_sub_table(name_table * nt, name_index_t index) |
377 | 2.28G | { |
378 | 2.28G | return nt->sub[index >> nt_log2_sub_size].names; |
379 | 2.28G | } |
380 | | void /*obj_header_t */ * |
381 | | names_index_string_sub_table(name_table * nt, name_index_t index) |
382 | 2.28G | { |
383 | 2.28G | return nt->sub[index >> nt_log2_sub_size].strings; |
384 | 2.28G | } |
385 | | |
386 | | /* |
387 | | * Clean up the name table after the trace/mark phase of a garbage |
388 | | * collection, by removing names that aren't marked. gcst == NULL indicates |
389 | | * we're doing this for initialization or restore rather than for a GC. |
390 | | */ |
391 | | void |
392 | | names_trace_finish(name_table * nt, gc_state_t * gcst) |
393 | 487k | { |
394 | 487k | uint *phash = &nt->hash[0]; |
395 | 487k | int i; |
396 | | |
397 | 1.99G | for (i = 0; i < NT_HASH_SIZE; phash++, i++) { |
398 | 1.99G | name_index_t prev = 0; |
399 | | /* |
400 | | * The following initialization is only to pacify compilers: |
401 | | * pnprev is only referenced if prev has been set in the loop, |
402 | | * in which case pnprev is also set. |
403 | | */ |
404 | 1.99G | name_string_t *pnprev = 0; |
405 | 1.99G | name_index_t nidx = *phash; |
406 | | |
407 | 4.33G | while (nidx != 0) { |
408 | 2.33G | name_string_t *pnstr = names_index_string_inline(nt, nidx); |
409 | 2.33G | name_index_t next = name_next_index(nidx, pnstr); |
410 | | |
411 | 2.33G | if (pnstr->mark) { |
412 | 2.24G | prev = nidx; |
413 | 2.24G | pnprev = pnstr; |
414 | 2.24G | } else { |
415 | 89.6M | if_debug_name("GC remove name", nt, nidx, NULL); |
416 | | /* Zero out the string data for the GC. */ |
417 | 89.6M | pnstr->string_bytes = 0; |
418 | 89.6M | pnstr->string_size = 0; |
419 | 89.6M | if (prev == 0) |
420 | 41.9M | *phash = next; |
421 | 47.7M | else |
422 | 47.7M | set_name_next_index(prev, pnprev, next); |
423 | 89.6M | } |
424 | 2.33G | nidx = next; |
425 | 2.33G | } |
426 | 1.99G | } |
427 | | /* Reconstruct the free list. */ |
428 | 487k | nt->free = 0; |
429 | 5.60M | for (i = nt->sub_count; --i >= 0;) { |
430 | 5.11M | name_sub_table *sub = nt->sub[i].names; |
431 | | |
432 | 5.11M | if (sub != 0) { |
433 | 5.11M | name_scan_sub(nt, i, true, true && (gcst != 0)); |
434 | 5.11M | } |
435 | 5.11M | } |
436 | 487k | nt->sub_next = 0; |
437 | 487k | } |
438 | | |
439 | | /* ------ Save/restore ------ */ |
440 | | |
441 | | /* Clean up the name table before a restore. */ |
442 | | /* Currently, this is never called, because the name table is allocated */ |
443 | | /* in system VM. However, for a Level 1 system, we might choose to */ |
444 | | /* allocate the name table in global VM; in this case, this routine */ |
445 | | /* would be called before doing the global part of a top-level restore. */ |
446 | | /* Currently we don't make any attempt to optimize this. */ |
447 | | void |
448 | | names_restore(name_table * nt, alloc_save_t * save) |
449 | 0 | { |
450 | | /* We simply mark all names older than the save, */ |
451 | | /* and let names_trace_finish sort everything out. */ |
452 | 0 | uint si; |
453 | |
|
454 | 0 | for (si = 0; si < nt->sub_count; ++si) |
455 | 0 | if (nt->sub[si].strings != 0) { |
456 | 0 | uint i; |
457 | |
|
458 | 0 | for (i = 0; i < nt_sub_size; ++i) { |
459 | 0 | name_string_t *pnstr = |
460 | 0 | names_index_string_inline(nt, (si << nt_log2_sub_size) + i); |
461 | |
|
462 | 0 | if (pnstr->string_bytes == 0) |
463 | 0 | pnstr->mark = 0; |
464 | 0 | else if (pnstr->foreign_string) { |
465 | | /* Avoid storing into a read-only name string. */ |
466 | 0 | if (!pnstr->mark) |
467 | 0 | pnstr->mark = 1; |
468 | 0 | } else |
469 | 0 | pnstr->mark = |
470 | 0 | !alloc_is_since_save(pnstr->string_bytes, save); |
471 | 0 | } |
472 | 0 | } |
473 | 0 | names_trace_finish(nt, NULL); |
474 | 0 | } |
475 | | |
476 | | /* ------ Internal procedures ------ */ |
477 | | |
478 | | /* Allocate the next sub-table. */ |
479 | | static int |
480 | | name_alloc_sub(name_table * nt) |
481 | 2.47M | { |
482 | 2.47M | gs_memory_t *mem = nt->memory; |
483 | 2.47M | uint sub_index = nt->sub_next; |
484 | 2.47M | name_sub_table *sub; |
485 | 2.47M | name_string_sub_table_t *ssub; |
486 | | |
487 | 2.63M | for (;; ++sub_index) { |
488 | 2.63M | if (sub_index > nt->max_sub_count) |
489 | 0 | return_error(gs_error_limitcheck); |
490 | 2.63M | if (nt->sub[sub_index].names == 0) |
491 | 2.47M | break; |
492 | 2.63M | } |
493 | 2.47M | nt->sub_next = sub_index + 1; |
494 | 2.47M | if (nt->sub_next > nt->sub_count) |
495 | 2.47M | nt->sub_count = nt->sub_next; |
496 | 2.47M | sub = gs_alloc_struct(mem, name_sub_table, &st_name_sub_table, |
497 | 2.47M | "name_alloc_sub(sub-table)"); |
498 | 2.47M | ssub = gs_alloc_struct(mem, name_string_sub_table_t, |
499 | 2.47M | &st_name_string_sub_table, |
500 | 2.47M | "name_alloc_sub(string sub-table)"); |
501 | 2.47M | if (sub == 0 || ssub == 0) { |
502 | 0 | gs_free_object(mem, ssub, "name_alloc_sub(string sub-table)"); |
503 | 0 | gs_free_object(mem, sub, "name_alloc_sub(sub-table)"); |
504 | 0 | return_error(gs_error_VMerror); |
505 | 0 | } |
506 | 2.47M | memset(sub, 0, sizeof(name_sub_table)); |
507 | 2.47M | memset(ssub, 0, sizeof(name_string_sub_table_t)); |
508 | | /* The following code is only used if EXTEND_NAMES is non-zero. */ |
509 | 2.47M | #if name_extension_bits > 0 |
510 | 2.47M | sub->high_index = (sub_index >> (16 - nt_log2_sub_size)) << 16; |
511 | 2.47M | #endif |
512 | 2.47M | nt->sub[sub_index].names = sub; |
513 | 2.47M | nt->sub[sub_index].strings = ssub; |
514 | | /* Add the newly allocated entries to the free list. */ |
515 | | /* Note that the free list will only be properly sorted if */ |
516 | | /* it was empty initially. */ |
517 | 2.47M | name_scan_sub(nt, sub_index, false, false); |
518 | | #ifdef DEBUG |
519 | | if (gs_debug_c('n')) { /* Print the lengths of the hash chains. */ |
520 | | int i0; |
521 | | |
522 | | for (i0 = 0; i0 < NT_HASH_SIZE; i0 += 16) { |
523 | | int i; |
524 | | |
525 | | dmlprintf1(mem, "[n]chain %d:", i0); |
526 | | for (i = i0; i < i0 + 16; i++) { |
527 | | int n = 0; |
528 | | uint nidx; |
529 | | |
530 | | for (nidx = nt->hash[i]; nidx != 0; |
531 | | nidx = name_next_index(nidx, |
532 | | names_index_string_inline(nt, nidx)) |
533 | | ) |
534 | | n++; |
535 | | dmprintf1(mem, " %d", n); |
536 | | } |
537 | | dmputc(mem, '\n'); |
538 | | } |
539 | | } |
540 | | #endif |
541 | 2.47M | return 0; |
542 | 2.47M | } |
543 | | |
544 | | /* Free a sub-table. */ |
545 | | static void |
546 | | name_free_sub(name_table * nt, uint sub_index, bool unmark) |
547 | 527 | { |
548 | | /* If the subtable is in a previous save level, gs_free_object() |
549 | | * may not actually free the memory, in case that happens, we need |
550 | | * to explicitly remove the gc mark if requested. |
551 | | */ |
552 | 527 | if (unmark) { |
553 | 527 | name_sub_table *sub = nt->sub[sub_index].names; |
554 | 527 | name_string_sub_table_t *ssub = nt->sub[sub_index].strings; |
555 | | |
556 | 527 | o_set_unmarked((obj_header_t *)sub - 1); |
557 | 527 | o_set_unmarked((obj_header_t *)ssub - 1); |
558 | 527 | } |
559 | | |
560 | 527 | gs_free_object(nt->memory, nt->sub[sub_index].strings, |
561 | 527 | "name_free_sub(string sub-table)"); |
562 | 527 | gs_free_object(nt->memory, nt->sub[sub_index].names, |
563 | 527 | "name_free_sub(sub-table)"); |
564 | 527 | nt->sub[sub_index].names = 0; |
565 | 527 | nt->sub[sub_index].strings = 0; |
566 | 527 | } |
567 | | |
568 | | /* Scan a sub-table and add unmarked entries to the free list. */ |
569 | | /* We add the entries in decreasing count order, so the free list */ |
570 | | /* will stay sorted. If all entries are unmarked and free_empty is true, */ |
571 | | /* free the sub-table. */ |
572 | | static void |
573 | | name_scan_sub(name_table * nt, uint sub_index, bool free_empty, bool unmark) |
574 | 7.58M | { |
575 | 7.58M | name_string_sub_table_t *ssub = nt->sub[sub_index].strings; |
576 | 7.58M | uint free = nt->free; |
577 | 7.58M | uint nbase = sub_index << nt_log2_sub_size; |
578 | 7.58M | uint ncnt = nbase + (nt_sub_size - 1); |
579 | 7.58M | bool keep = !free_empty; |
580 | | |
581 | 7.58M | if (ssub == 0) |
582 | 0 | return; |
583 | 7.58M | if (nbase == 0) |
584 | 650k | nbase = 1, keep = true; /* don't free name 0 */ |
585 | 3.88G | for (;; --ncnt) { |
586 | 3.88G | uint nidx = name_count_to_index(ncnt); |
587 | 3.88G | name_string_t *pnstr = &ssub->strings[nidx & nt_sub_index_mask]; |
588 | | |
589 | 3.88G | if (pnstr->mark) |
590 | 2.30G | keep = true; |
591 | 1.57G | else { |
592 | 1.57G | set_name_next_index(nidx, pnstr, free); |
593 | 1.57G | free = nidx; |
594 | 1.57G | } |
595 | 3.88G | if (ncnt == nbase) |
596 | 7.58M | break; |
597 | 3.88G | } |
598 | 7.58M | if (keep) |
599 | 7.58M | nt->free = free; |
600 | 527 | else { |
601 | | /* No marked entries, free the sub-table. */ |
602 | 527 | name_free_sub(nt, sub_index, unmark); |
603 | 527 | if (sub_index == nt->sub_count - 1) { |
604 | | /* Back up over a final run of deleted sub-tables. */ |
605 | 217 | do { |
606 | 217 | --sub_index; |
607 | 217 | } while (nt->sub[sub_index].names == 0); |
608 | 217 | nt->sub_count = sub_index + 1; |
609 | 217 | if (nt->sub_next > sub_index) |
610 | 217 | nt->sub_next = sub_index; |
611 | 310 | } else if (nt->sub_next == sub_index) |
612 | 0 | nt->sub_next--; |
613 | 527 | } |
614 | 7.58M | } |
615 | | |
616 | | /* Garbage collector enumeration and relocation procedures. */ |
617 | | static |
618 | | ENUM_PTRS_BEGIN_PROC(name_table_enum_ptrs) |
619 | 11.1M | { |
620 | 11.1M | EV_CONST name_table *const nt = vptr; |
621 | 11.1M | uint i = index >> 1; |
622 | | |
623 | 11.1M | if (i >= nt->sub_count) |
624 | 354k | return 0; |
625 | 10.8M | if (index & 1) |
626 | 5.40M | ENUM_RETURN(nt->sub[i].strings); |
627 | 5.40M | else |
628 | 5.40M | ENUM_RETURN(nt->sub[i].names); |
629 | 10.8M | } |
630 | | ENUM_PTRS_END_PROC |
631 | 339k | static RELOC_PTRS_WITH(name_table_reloc_ptrs, name_table *nt) |
632 | 339k | { |
633 | 339k | uint sub_count = nt->sub_count; |
634 | 339k | uint i; |
635 | | |
636 | | /* Now we can relocate the sub-table pointers. */ |
637 | 5.51M | for (i = 0; i < sub_count; i++) { |
638 | 5.17M | RELOC_VAR(nt->sub[i].names); |
639 | 5.17M | RELOC_VAR(nt->sub[i].strings); |
640 | 5.17M | } |
641 | | /* |
642 | | * We also need to relocate the cached value pointers. |
643 | | * We don't do this here, but in a separate scan over the |
644 | | * permanent dictionaries, at the very end of garbage collection. |
645 | | */ |
646 | 339k | } |
647 | 339k | RELOC_PTRS_END |
648 | | |
649 | | static ENUM_PTRS_BEGIN_PROC(name_string_sub_enum_ptrs) |
650 | 5.17M | { |
651 | 5.17M | return 0; |
652 | 5.17M | } |
653 | | ENUM_PTRS_END_PROC |
654 | 5.17M | static RELOC_PTRS_BEGIN(name_string_sub_reloc_ptrs) |
655 | 5.17M | { |
656 | 5.17M | name_string_t *pnstr = ((name_string_sub_table_t *)vptr)->strings; |
657 | 5.17M | uint i; |
658 | | |
659 | 2.65G | for (i = 0; i < nt_sub_size; ++pnstr, ++i) { |
660 | 2.65G | if (pnstr->string_bytes != 0 && !pnstr->foreign_string) { |
661 | 2.16G | gs_const_string nstr; |
662 | | |
663 | 2.16G | nstr.data = pnstr->string_bytes; |
664 | 2.16G | nstr.size = pnstr->string_size; |
665 | 2.16G | RELOC_CONST_STRING_VAR(nstr); |
666 | 2.16G | pnstr->string_bytes = nstr.data; |
667 | 2.16G | } |
668 | 2.65G | } |
669 | 5.17M | } |
670 | 5.17M | RELOC_PTRS_END |