Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}