Coverage Report

Created: 2026-04-09 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpshash.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 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
/* Linear probe hash table.
18
 *
19
 * Simple hashtable with open adressing linear probe.
20
 * Does not manage memory of key/value pointers.
21
 * Does not support deleting entries.
22
 */
23
24
#include "ghostxps.h"
25
26
static const unsigned primes[] =
27
{
28
    61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
29
    131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 0
30
};
31
32
typedef struct xps_hash_entry_s xps_hash_entry_t;
33
34
struct xps_hash_entry_s
35
{
36
    char *key;
37
    void *value;
38
};
39
40
struct xps_hash_table_s
41
{
42
    void *ctx;
43
    unsigned int size;
44
    unsigned int load;
45
    xps_hash_entry_t *entries;
46
};
47
48
static inline int
49
xps_tolower(int c)
50
1.77k
{
51
1.77k
    if (c >= 'A' && c <= 'Z')
52
450
        return c + 32;
53
1.32k
    return c;
54
1.77k
}
55
56
static unsigned int
57
xps_hash(char *s)
58
25
{
59
25
    unsigned int h = 0;
60
1.80k
    while (*s)
61
1.77k
        h = xps_tolower(*s++) + (h << 6) + (h << 16) - h;
62
25
    return h;
63
25
}
64
65
xps_hash_table_t *
66
xps_hash_new(xps_context_t *ctx)
67
272
{
68
272
    xps_hash_table_t *table;
69
70
272
    table = xps_alloc(ctx, sizeof(xps_hash_table_t));
71
272
    if (!table)
72
0
    {
73
0
        gs_throw(gs_error_VMerror, "out of memory: hash table struct");
74
0
        return NULL;
75
0
    }
76
77
272
    table->size = primes[0];
78
272
    table->load = 0;
79
80
272
    if (table->size > INT_MAX / sizeof(xps_hash_entry_t)) {
81
0
        gs_throw(gs_error_limitcheck, "out of memory: hash table struct");
82
0
        return NULL;
83
0
    }
84
85
272
    table->entries = xps_alloc(ctx, sizeof(xps_hash_entry_t) * (size_t)table->size);
86
272
    if (!table->entries)
87
0
    {
88
0
        xps_free(ctx, table);
89
0
        gs_throw(gs_error_VMerror, "out of memory: hash table entries array");
90
0
        return NULL;
91
0
    }
92
93
272
    memset(table->entries, 0, sizeof(xps_hash_entry_t) * table->size);
94
95
272
    return table;
96
272
}
97
98
static int
99
xps_hash_double(xps_context_t *ctx, xps_hash_table_t *table)
100
0
{
101
0
    xps_hash_entry_t *old_entries;
102
0
    xps_hash_entry_t *new_entries;
103
0
    unsigned int old_size = table->size;
104
0
    unsigned int new_size = table->size * 2;
105
0
    int i;
106
107
0
    for (i = 0; primes[i] != 0; i++)
108
0
    {
109
0
        if (primes[i] > old_size)
110
0
        {
111
0
            new_size = primes[i];
112
0
            break;
113
0
        }
114
0
    }
115
116
0
    if (new_size > INT_MAX / sizeof(xps_hash_entry_t)) {
117
0
        return gs_throw(gs_error_limitcheck, "out of memory: hash table entries array");
118
0
    }
119
120
0
    old_entries = table->entries;
121
0
    new_entries = xps_alloc(ctx, sizeof(xps_hash_entry_t) * (size_t)new_size);
122
0
    if (!new_entries)
123
0
        return gs_throw(gs_error_VMerror, "out of memory: hash table entries array");
124
125
0
    table->size = new_size;
126
0
    table->entries = new_entries;
127
0
    table->load = 0;
128
129
0
    memset(table->entries, 0, sizeof(xps_hash_entry_t) * table->size);
130
131
0
    for (i = 0; i < old_size; i++)
132
0
        if (old_entries[i].value)
133
0
            xps_hash_insert(ctx, table, old_entries[i].key, old_entries[i].value);
134
135
0
    xps_free(ctx, old_entries);
136
137
0
    return 0;
138
0
}
139
140
void
141
xps_hash_free(xps_context_t *ctx, xps_hash_table_t *table,
142
    void (*free_key)(xps_context_t *ctx, void *),
143
    void (*free_value)(xps_context_t *ctx, void *))
144
272
{
145
272
    int i;
146
147
16.8k
    for (i = 0; i < table->size; i++)
148
16.5k
    {
149
16.5k
        if (table->entries[i].key && free_key)
150
3
            free_key(ctx, table->entries[i].key);
151
16.5k
        if (table->entries[i].value && free_value)
152
3
            free_value(ctx, table->entries[i].value);
153
16.5k
    }
154
155
272
    xps_free(ctx, table->entries);
156
272
    xps_free(ctx, table);
157
272
}
158
159
void *
160
xps_hash_lookup(xps_hash_table_t *table, char *key)
161
22
{
162
22
    xps_hash_entry_t *entries = table->entries;
163
22
    unsigned int size = table->size;
164
22
    unsigned int pos = xps_hash(key) % size;
165
166
22
    while (1)
167
22
    {
168
22
        if (!entries[pos].value)
169
4
            return NULL;
170
171
18
        if (xps_strcasecmp(key, entries[pos].key) == 0)
172
18
            return entries[pos].value;
173
174
0
        pos = (pos + 1) % size;
175
0
    }
176
22
}
177
178
int
179
xps_hash_insert(xps_context_t *ctx, xps_hash_table_t *table, char *key, void *value)
180
3
{
181
3
    xps_hash_entry_t *entries;
182
3
    unsigned int size, pos;
183
184
    /* Grow the table at 80% load */
185
3
    if (table->load > table->size * 8 / 10)
186
0
    {
187
0
        if (xps_hash_double(ctx, table) < 0)
188
0
            return gs_rethrow(-1, "cannot grow hash table");
189
0
    }
190
191
3
    entries = table->entries;
192
3
    size = table->size;
193
3
    pos = xps_hash(key) % size;
194
195
3
    while (1)
196
3
    {
197
3
        if (!entries[pos].value)
198
3
        {
199
3
            entries[pos].key = key;
200
3
            entries[pos].value = value;
201
3
            table->load ++;
202
3
            return 0;
203
3
        }
204
205
0
        if (xps_strcasecmp(key, entries[pos].key) == 0)
206
0
        {
207
0
            return 0;
208
0
        }
209
210
0
        pos = (pos + 1) % size;
211
0
    }
212
3
}
213
214
void
215
xps_hash_debug(xps_hash_table_t *table)
216
0
{
217
0
    int i;
218
219
0
    dlprintf2("hash table load %d / %d\n", table->load, table->size);
220
221
0
    for (i = 0; i < table->size; i++)
222
0
    {
223
0
        if (!table->entries[i].value)
224
0
            dlprintf1("table % 4d: empty\n", i);
225
0
        else
226
0
            dlprintf3("table % 4d: key=%s value="PRI_INTPTR"\n", i,
227
0
                      table->entries[i].key,
228
0
                      (intptr_t)table->entries[i].value);
229
0
    }
230
0
}