Coverage Report

Created: 2026-10-28 08:05

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