Coverage Report

Created: 2026-07-24 07:44

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
4.59G
  ((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
197M
{
80
197M
    pl_dict_entry_t **ppde = &pdict->entries;
81
197M
    pl_dict_entry_t *pde;
82
83
4.60G
    for (; (pde = *ppde) != 0; ppde = &pde->next) {
84
4.47G
        if (pde->key.size == ksize &&
85
4.47G
            !memcmp(entry_key_data(pde), kdata, ksize)
86
4.47G
            )
87
72.3M
            return ppde;
88
4.47G
    }
89
124M
    return 0;
90
197M
}
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
2.85M
{
96
2.85M
    pl_dict_entry_t *pde = *ppde;
97
2.85M
    gs_memory_t *mem = pdict->memory;
98
99
2.85M
    *ppde = pde->next;
100
2.85M
    if (!pde->link)             /* values are not freed for links */
101
2.85M
        (*pdict->free_proc) (mem, pde->value, cname);
102
2.85M
    if (pde->key.size > pl_dict_max_short_key)
103
309k
        gs_free_string(mem, (byte *) pde->key.data, pde->key.size, cname);
104
2.85M
    gs_free_object(mem, pde, cname);
105
2.85M
    pdict->entry_count--;
106
2.85M
}
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
816k
{
115
816k
    pdict->memory = mem;
116
816k
    pdict->free_proc = (free_proc ? free_proc : pl_dict_value_free);
117
816k
    pdict->entries = 0;
118
816k
    pdict->entry_count = 0;
119
816k
    pdict->parent = 0;
120
816k
}
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
193M
{
133
193M
    pl_dict_t *pdcur = pdict;
134
193M
    pl_dict_entry_t **ppde;
135
136
193M
    while ((ppde = pl_dict_lookup_entry(pdcur, kdata, ksize)) == 0) {
137
121M
        if (!with_stack || (pdcur = pdcur->parent) == 0)
138
121M
            return false;
139
121M
    }
140
72.3M
    *pvalue = (*ppde)->value;
141
72.3M
    if (ppdict)
142
0
        *ppdict = pdcur;
143
72.3M
    return true;
144
193M
}
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
2.85M
{                               /* Make a new entry. */
153
2.85M
    byte *kstr;
154
2.85M
    gs_memory_t *mem = pdict->memory;
155
2.85M
    pl_dict_entry_t *pde;
156
157
2.85M
    pde = gs_alloc_struct(mem, pl_dict_entry_t, &st_pl_dict_entry,
158
2.85M
                          "pl_dict_put(entry)");
159
2.85M
    kstr = (ksize <= pl_dict_max_short_key ? pde->short_key :
160
2.85M
            gs_alloc_string(mem, ksize, "pl_dict_put(key)"));
161
2.85M
    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
2.85M
    memcpy(kstr, kdata, ksize);
168
2.85M
    pde->key.data = (ksize <= pl_dict_max_short_key ? 0 : kstr);
169
2.85M
    pde->key.size = ksize;
170
2.85M
    pde->link = link;
171
2.85M
    pde->value = value;
172
2.85M
    pde->next = pdict->entries;
173
2.85M
    pdict->entries = pde;
174
2.85M
    pdict->entry_count++;
175
2.85M
    return 0;
176
2.85M
}
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
2.89M
{
187
2.89M
    pl_dict_entry_t **ppde = pl_dict_lookup_entry(pdict, kdata, ksize);
188
189
2.89M
    if (!ppde) {
190
2.85M
        void *link = 0;
191
2.85M
        int code = pl_dict_build_new_entry(pdict, kdata, ksize, value, link);
192
193
2.85M
        if (code < 0)
194
0
            (*pdict->free_proc) (pdict->memory, value, "pl_dict_put(new value)");
195
196
2.85M
        return code;
197
2.85M
    } else {                    /* Replace the value in an existing entry. */
198
39.7k
        pl_dict_entry_t *pde;
199
200
39.7k
        pde = *ppde;
201
39.7k
        (*pdict->free_proc) (pdict->memory, pde->value,
202
39.7k
                             "pl_dict_put(old value)");
203
39.7k
        pde->value = value;
204
39.7k
        return 1;
205
39.7k
    }
206
2.89M
}
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
29
{
243
29
    pl_dict_entry_t **ppde = &pdict->entries;
244
29
    pl_dict_entry_t **pptarget = pl_dict_lookup_entry(pdict, kdata, ksize);
245
29
    pl_dict_entry_t *pde;
246
29
    pl_dict_entry_t *ptarget;
247
248
29
    if (!pptarget)
249
0
        return;
250
29
    ptarget = *pptarget;
251
    /* get the real entry if this is a link. */
252
29
    if (ptarget->link)
253
0
        ptarget = ptarget->link;
254
29
#define dict_get_key_data(entry)  ((entry)->key.size > pl_dict_max_short_key ?\
255
29
  (entry)->key.data : (entry)->short_key)
256
29
    pl_dict_undef(pdict, dict_get_key_data(ptarget), ptarget->key.size);
257
    /* delete links to the target */
258
29
    pde = *ppde;
259
29
    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
29
#undef dict_get_key_data
267
29
}
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
870k
{
275
870k
    pl_dict_entry_t **ppde = pl_dict_lookup_entry(pdict, kdata, ksize);
276
277
870k
    if (!ppde)
278
868k
        return false;
279
2.61k
    pl_dict_free(pdict, ppde, "pl_dict_undef");
280
2.61k
    return true;
281
870k
}
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
386k
{
289
386k
    uint count = pdict->entry_count;
290
291
386k
    if (with_stack) {
292
386k
        const pl_dict_t *pdcur;
293
294
386k
        for (pdcur = pdict->parent; pdcur != 0; pdcur = pdcur->parent)
295
0
            count += pdcur->entry_count;
296
386k
    }
297
386k
    return count;
298
386k
}
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
1.63M
{
308
1.63M
    penum->pdict = pdict;
309
1.63M
    penum->next = 0;
310
1.63M
    penum->first = true;
311
1.63M
    penum->next_dict = (with_stack ? pdict->parent : 0);
312
1.63M
}
313
314
bool
315
pl_dict_enum_next(pl_dict_enum_t * penum, gs_const_string * pkey,
316
                  void **pvalue)
317
116M
{
318
116M
    pl_dict_entry_t *pde;
319
320
117M
    while ((pde = (penum->first ? penum->pdict->entries : penum->next)) == 0) {
321
2.09M
        if (penum->next_dict == 0)
322
967k
            return false;
323
1.13M
        penum->next_dict = (penum->pdict = penum->next_dict)->parent;
324
1.13M
        penum->first = true;
325
1.13M
    }
326
115M
    pkey->data = entry_key_data(pde);
327
115M
    pkey->size = pde->key.size;
328
115M
    *pvalue = pde->value;
329
115M
    penum->next = pde->next;
330
115M
    penum->first = false;
331
115M
    return true;
332
116M
}
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
704k
{
340
3.55M
    while (pdict->entries)
341
2.84M
        pl_dict_free(pdict, &pdict->entries, "pl_dict_release");
342
704k
}