Coverage Report

Created: 2025-06-10 07:26

/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
73.0M
#  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
9.22k
{
83
9.22k
    gs_memory_t *mem = (gs_memory_t *)imem;
84
9.22k
    name_table *nt;
85
9.22k
    int i;
86
87
9.22k
    if (count == 0)
88
9.22k
        count = max_name_count + 1L;
89
0
    else if (count - 1 > max_name_count)
90
0
        return 0;
91
9.22k
    nt = gs_alloc_struct(mem, name_table, &st_name_table, "name_init(nt)");
92
9.22k
    if (nt == 0)
93
0
        return 0;
94
9.22k
    memset(nt, 0, sizeof(name_table));
95
9.22k
    nt->max_sub_count =
96
9.22k
        ((count - 1) | nt_sub_index_mask) >> nt_log2_sub_size;
97
9.22k
    nt->name_string_attrs = imemory_space(imem) | a_readonly;
98
9.22k
    nt->memory = mem;
99
    /* Initialize the one-character names. */
100
    /* Start by creating the necessary sub-tables. */
101
18.4k
    for (i = 0; i < NT_1CHAR_FIRST + NT_1CHAR_SIZE; i += nt_sub_size) {
102
9.22k
        int code = name_alloc_sub(nt);
103
104
9.22k
        if (code < 0) {
105
0
            names_free(nt);
106
0
            return 0;
107
0
        }
108
9.22k
    }
109
1.19M
    for (i = -1; i < NT_1CHAR_SIZE; i++) {
110
1.18M
        uint ncnt = NT_1CHAR_FIRST + i;
111
1.18M
        uint nidx = name_count_to_index(ncnt);
112
1.18M
        name *pname = names_index_ptr_inline(nt, nidx);
113
1.18M
        name_string_t *pnstr = names_index_string_inline(nt, nidx);
114
115
1.18M
        if (i < 0)
116
9.22k
            pnstr->string_bytes = nt_1char_names,
117
9.22k
                pnstr->string_size = 0;
118
1.18M
        else
119
1.18M
            pnstr->string_bytes = nt_1char_names + i,
120
1.18M
                pnstr->string_size = 1;
121
1.18M
        pnstr->foreign_string = 1;
122
1.18M
        pnstr->mark = 1;
123
1.18M
        pname->pvalue = pv_no_defn;
124
1.18M
    }
125
9.22k
    nt->perm_count = NT_1CHAR_FIRST + NT_1CHAR_SIZE;
126
    /* Reconstruct the free list. */
127
9.22k
    nt->free = 0;
128
9.22k
    names_trace_finish(nt, NULL);
129
9.22k
    return nt;
130
9.22k
}
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
9.22k
{
146
9.22k
    (void)vptr; /* unused */
147
9.22k
    cmem->gs_lib_ctx->gs_name_table = NULL;
148
9.22k
}
149
150
/* Get the allocator for the name table. */
151
gs_memory_t *
152
names_memory(const name_table * nt)
153
479k
{
154
479k
    return nt->memory;
155
479k
}
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
592M
{
164
592M
    name *pname;
165
592M
    name_string_t *pnstr;
166
592M
    uint nidx;
167
592M
    uint *phash;
168
169
    /* Compute a hash for the string. */
170
    /* Make a special check for 1-character names. */
171
592M
    switch (size) {
172
9.22k
    case 0:
173
9.22k
        nidx = name_count_to_index(1);
174
9.22k
        pname = names_index_ptr_inline(nt, nidx);
175
9.22k
        goto mkn;
176
84.8M
    case 1:
177
84.8M
        if (*ptr < NT_1CHAR_SIZE) {
178
84.8M
            uint hash = *ptr + NT_1CHAR_FIRST;
179
180
84.8M
            nidx = name_count_to_index(hash);
181
84.8M
            pname = names_index_ptr_inline(nt, nidx);
182
84.8M
            goto mkn;
183
84.8M
        }
184
        /* falls through */
185
507M
    default: {
186
507M
        uint hash;
187
188
507M
        NAME_HASH(hash, hash_permutation, ptr, size);
189
507M
        phash = nt->hash + (hash & (NT_HASH_SIZE - 1));
190
507M
    }
191
592M
    }
192
193
1.22G
    for (nidx = *phash; nidx != 0;
194
717M
         nidx = name_next_index(nidx, pnstr)
195
1.15G
        ) {
196
1.15G
        pnstr = names_index_string_inline(nt, nidx);
197
1.15G
        if (pnstr->string_size == size &&
198
1.15G
            !memcmp_inline(ptr, pnstr->string_bytes, size)
199
1.15G
            ) {
200
435M
            pname = name_index_ptr_inline(nt, nidx);
201
435M
            goto mkn;
202
435M
        }
203
1.15G
    }
204
    /* Name was not in the table.  Make a new entry. */
205
71.3M
    if (enterflag < 0)
206
3.42M
        return_error(gs_error_undefined);
207
67.9M
    if (size > max_name_string)
208
0
        return_error(gs_error_limitcheck);
209
67.9M
    nidx = nt->free;
210
67.9M
    if (nidx == 0) {
211
129k
        int code = name_alloc_sub(nt);
212
213
129k
        if (code < 0)
214
0
            return code;
215
129k
        nidx = nt->free;
216
129k
    }
217
67.9M
    pnstr = names_index_string_inline(nt, nidx);
218
67.9M
    if (enterflag == 1) {
219
60.0M
        byte *cptr = (byte *)gs_alloc_string(nt->memory, size,
220
60.0M
                                             "names_ref(string)");
221
222
60.0M
        if (cptr == 0)
223
0
            return_error(gs_error_VMerror);
224
60.0M
        memcpy(cptr, ptr, size);
225
60.0M
        pnstr->string_bytes = cptr;
226
60.0M
        pnstr->foreign_string = 0;
227
60.0M
    } else {
228
7.86M
        pnstr->string_bytes = ptr;
229
7.86M
        pnstr->foreign_string = (enterflag == 0 ? 1 : 0);
230
7.86M
    }
231
67.9M
    pnstr->string_size = size;
232
67.9M
    pname = name_index_ptr_inline(nt, nidx);
233
67.9M
    pname->pvalue = pv_no_defn;
234
67.9M
    nt->free = name_next_index(nidx, pnstr);
235
67.9M
    set_name_next_index(nidx, pnstr, *phash);
236
67.9M
    *phash = nidx;
237
67.9M
    if_debug_name("new name", nt, nidx, &enterflag);
238
588M
 mkn:
239
588M
    make_name(pref, nidx, pname);
240
588M
    return 0;
241
67.9M
}
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
39.6M
{
248
39.6M
    const name_string_t *pnstr = names_string_inline(nt, pnref);
249
250
39.6M
    make_const_string(psref,
251
39.6M
                      (pnstr->foreign_string ? avm_foreign | a_readonly :
252
39.6M
                       nt->name_string_attrs),
253
39.6M
                      pnstr->string_size,
254
39.6M
                      (const byte *)pnstr->string_bytes);
255
39.6M
}
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
2.47M
{
262
2.47M
    int exec = r_has_attr(psref, a_executable);
263
2.47M
    int code = names_ref(nt, psref->value.bytes, r_size(psref), pnref, 1);
264
265
2.47M
    if (code < 0)
266
0
        return code;
267
2.47M
    if (exec)
268
0
        r_set_attrs(pnref, a_executable);
269
2.47M
    return code;
270
2.47M
}
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
512k
{
276
512k
    return names_ref(nt, (const byte *)str, strlen(str), pref, 0);
277
512k
}
278
279
/* Invalidate the value cache for a name. */
280
void
281
names_invalidate_value_cache(name_table * nt, const ref * pnref)
282
6.94M
{
283
6.94M
    pnref->value.pname->pvalue = pv_other;
284
6.94M
}
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
752M
{
291
752M
    return names_index_inline(nt, pnref);
292
752M
}
293
void
294
names_index_ref(const name_table * nt, name_index_t index, ref * pnref)
295
334M
{
296
334M
    names_index_ref_inline(nt, index, pnref);
297
334M
}
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
133M
{
310
133M
    const name_string_sub_table_t *ssub =
311
133M
        nt->sub[nidx >> nt_log2_sub_size].strings;
312
133M
    const name_string_t *pnstr;
313
314
141M
    do {
315
141M
        ++nidx;
316
141M
        if ((nidx & nt_sub_index_mask) == 0)
317
276k
            for (;; nidx += nt_sub_size) {
318
276k
                if ((nidx >> nt_log2_sub_size) >= nt->sub_count)
319
18.4k
                    return 0;
320
258k
                ssub = nt->sub[nidx >> nt_log2_sub_size].strings;
321
258k
                if (ssub != 0)
322
258k
                    break;
323
258k
            }
324
141M
        pnstr = &ssub->strings[nidx & nt_sub_index_mask];
325
141M
    }
326
141M
    while (pnstr->string_bytes == 0);
327
133M
    return nidx;
328
133M
}
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
18.4k
{
336
18.4k
    uint si;
337
18.4k
    name_string_sub_table_t *ssub;
338
339
295k
    for (si = 0; si < nt->sub_count; ++si)
340
276k
        if ((ssub = nt->sub[si].strings) != 0) {
341
276k
            uint i;
342
343
            /* We can make the test much more efficient if we want.... */
344
142M
            for (i = 0; i < nt_sub_size; ++i)
345
141M
                if (name_index_to_count((si << nt_log2_sub_size) + i) >=
346
141M
                    nt->perm_count)
347
139M
                    ssub->strings[i].mark = 0;
348
276k
        }
349
18.4k
}
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
407M
{
356
407M
    name_string_t *pnstr = names_index_string_inline(nt, nidx);
357
358
407M
    if (pnstr->mark)
359
281M
        return false;
360
126M
    pnstr->mark = 1;
361
126M
    return true;
362
407M
}
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
296M
{
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
296M
    return pnref->value.pname - (r_size(pnref) & nt_sub_index_mask);
374
296M
}
375
void /*obj_header_t */ *
376
names_index_sub_table(name_table * nt, name_index_t index)
377
128M
{
378
128M
    return nt->sub[index >> nt_log2_sub_size].names;
379
128M
}
380
void /*obj_header_t */ *
381
names_index_string_sub_table(name_table * nt, name_index_t index)
382
128M
{
383
128M
    return nt->sub[index >> nt_log2_sub_size].strings;
384
128M
}
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
27.6k
{
394
27.6k
    uint *phash = &nt->hash[0];
395
27.6k
    int i;
396
397
113M
    for (i = 0; i < NT_HASH_SIZE; phash++, i++) {
398
113M
        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
113M
        name_string_t *pnprev = 0;
405
113M
        name_index_t nidx = *phash;
406
407
244M
        while (nidx != 0) {
408
131M
            name_string_t *pnstr = names_index_string_inline(nt, nidx);
409
131M
            name_index_t next = name_next_index(nidx, pnstr);
410
411
131M
            if (pnstr->mark) {
412
126M
                prev = nidx;
413
126M
                pnprev = pnstr;
414
126M
            } else {
415
5.07M
                if_debug_name("GC remove name", nt, nidx, NULL);
416
                /* Zero out the string data for the GC. */
417
5.07M
                pnstr->string_bytes = 0;
418
5.07M
                pnstr->string_size = 0;
419
5.07M
                if (prev == 0)
420
2.38M
                    *phash = next;
421
2.69M
                else
422
2.69M
                    set_name_next_index(prev, pnprev, next);
423
5.07M
            }
424
131M
            nidx = next;
425
131M
        }
426
113M
    }
427
    /* Reconstruct the free list. */
428
27.6k
    nt->free = 0;
429
313k
    for (i = nt->sub_count; --i >= 0;) {
430
286k
        name_sub_table *sub = nt->sub[i].names;
431
432
286k
        if (sub != 0) {
433
286k
            name_scan_sub(nt, i, true, true && (gcst != 0));
434
286k
        }
435
286k
    }
436
27.6k
    nt->sub_next = 0;
437
27.6k
}
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
138k
{
482
138k
    gs_memory_t *mem = nt->memory;
483
138k
    uint sub_index = nt->sub_next;
484
138k
    name_sub_table *sub;
485
138k
    name_string_sub_table_t *ssub;
486
487
147k
    for (;; ++sub_index) {
488
147k
        if (sub_index > nt->max_sub_count)
489
0
            return_error(gs_error_limitcheck);
490
147k
        if (nt->sub[sub_index].names == 0)
491
138k
            break;
492
147k
    }
493
138k
    nt->sub_next = sub_index + 1;
494
138k
    if (nt->sub_next > nt->sub_count)
495
138k
        nt->sub_count = nt->sub_next;
496
138k
    sub = gs_alloc_struct(mem, name_sub_table, &st_name_sub_table,
497
138k
                          "name_alloc_sub(sub-table)");
498
138k
    ssub = gs_alloc_struct(mem, name_string_sub_table_t,
499
138k
                           &st_name_string_sub_table,
500
138k
                          "name_alloc_sub(string sub-table)");
501
138k
    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
138k
    memset(sub, 0, sizeof(name_sub_table));
507
138k
    memset(ssub, 0, sizeof(name_string_sub_table_t));
508
    /* The following code is only used if EXTEND_NAMES is non-zero. */
509
138k
#if name_extension_bits > 0
510
138k
    sub->high_index = (sub_index >> (16 - nt_log2_sub_size)) << 16;
511
138k
#endif
512
138k
    nt->sub[sub_index].names = sub;
513
138k
    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
138k
    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
138k
    return 0;
542
138k
}
543
544
/* Free a sub-table. */
545
static void
546
name_free_sub(name_table * nt, uint sub_index, bool unmark)
547
0
{
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
0
    if (unmark) {
553
0
        name_sub_table *sub = nt->sub[sub_index].names;
554
0
        name_string_sub_table_t *ssub = nt->sub[sub_index].strings;
555
556
0
        o_set_unmarked((obj_header_t *)sub - 1);
557
0
        o_set_unmarked((obj_header_t *)ssub - 1);
558
0
    }
559
560
0
    gs_free_object(nt->memory, nt->sub[sub_index].strings,
561
0
                   "name_free_sub(string sub-table)");
562
0
    gs_free_object(nt->memory, nt->sub[sub_index].names,
563
0
                   "name_free_sub(sub-table)");
564
0
    nt->sub[sub_index].names = 0;
565
0
    nt->sub[sub_index].strings = 0;
566
0
}
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
424k
{
575
424k
    name_string_sub_table_t *ssub = nt->sub[sub_index].strings;
576
424k
    uint free = nt->free;
577
424k
    uint nbase = sub_index << nt_log2_sub_size;
578
424k
    uint ncnt = nbase + (nt_sub_size - 1);
579
424k
    bool keep = !free_empty;
580
581
424k
    if (ssub == 0)
582
0
        return;
583
424k
    if (nbase == 0)
584
36.8k
        nbase = 1, keep = true; /* don't free name 0 */
585
217M
    for (;; --ncnt) {
586
217M
        uint nidx = name_count_to_index(ncnt);
587
217M
        name_string_t *pnstr = &ssub->strings[nidx & nt_sub_index_mask];
588
589
217M
        if (pnstr->mark)
590
129M
            keep = true;
591
87.6M
        else {
592
87.6M
            set_name_next_index(nidx, pnstr, free);
593
87.6M
            free = nidx;
594
87.6M
        }
595
217M
        if (ncnt == nbase)
596
424k
            break;
597
217M
    }
598
424k
    if (keep)
599
424k
        nt->free = free;
600
0
    else {
601
        /* No marked entries, free the sub-table. */
602
0
        name_free_sub(nt, sub_index, unmark);
603
0
        if (sub_index == nt->sub_count - 1) {
604
            /* Back up over a final run of deleted sub-tables. */
605
0
            do {
606
0
                --sub_index;
607
0
            } while (nt->sub[sub_index].names == 0);
608
0
            nt->sub_count = sub_index + 1;
609
0
            if (nt->sub_next > sub_index)
610
0
                nt->sub_next = sub_index;
611
0
        } else if (nt->sub_next == sub_index)
612
0
            nt->sub_next--;
613
0
    }
614
424k
}
615
616
/* Garbage collector enumeration and relocation procedures. */
617
static
618
ENUM_PTRS_BEGIN_PROC(name_table_enum_ptrs)
619
573k
{
620
573k
    EV_CONST name_table *const nt = vptr;
621
573k
    uint i = index >> 1;
622
623
573k
    if (i >= nt->sub_count)
624
18.5k
        return 0;
625
555k
    if (index & 1)
626
277k
        ENUM_RETURN(nt->sub[i].strings);
627
277k
    else
628
277k
        ENUM_RETURN(nt->sub[i].names);
629
555k
}
630
ENUM_PTRS_END_PROC
631
18.4k
static RELOC_PTRS_WITH(name_table_reloc_ptrs, name_table *nt)
632
18.4k
{
633
18.4k
    uint sub_count = nt->sub_count;
634
18.4k
    uint i;
635
636
    /* Now we can relocate the sub-table pointers. */
637
295k
    for (i = 0; i < sub_count; i++) {
638
277k
        RELOC_VAR(nt->sub[i].names);
639
277k
        RELOC_VAR(nt->sub[i].strings);
640
277k
    }
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
18.4k
}
647
18.4k
RELOC_PTRS_END
648
649
static ENUM_PTRS_BEGIN_PROC(name_string_sub_enum_ptrs)
650
277k
{
651
277k
    return 0;
652
277k
}
653
ENUM_PTRS_END_PROC
654
277k
static RELOC_PTRS_BEGIN(name_string_sub_reloc_ptrs)
655
277k
{
656
277k
    name_string_t *pnstr = ((name_string_sub_table_t *)vptr)->strings;
657
277k
    uint i;
658
659
142M
    for (i = 0; i < nt_sub_size; ++pnstr, ++i) {
660
141M
        if (pnstr->string_bytes != 0 && !pnstr->foreign_string) {
661
115M
            gs_const_string nstr;
662
663
115M
            nstr.data = pnstr->string_bytes;
664
115M
            nstr.size = pnstr->string_size;
665
115M
            RELOC_CONST_STRING_VAR(nstr);
666
115M
            pnstr->string_bytes = nstr.data;
667
115M
        }
668
141M
    }
669
277k
}
670
277k
RELOC_PTRS_END