Coverage Report

Created: 2025-06-24 07:01

/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