Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpshash.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 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.13k
{
51
1.13k
    if (c >= 'A' && c <= 'Z')
52
288
        return c + 32;
53
848
    return c;
54
1.13k
}
55
56
static unsigned int
57
xps_hash(char *s)
58
16
{
59
16
    unsigned int h = 0;
60
1.15k
    while (*s)
61
1.13k
        h = xps_tolower(*s++) + (h << 6) + (h << 16) - h;
62
16
    return h;
63
16
}
64
65
xps_hash_table_t *
66
xps_hash_new(xps_context_t *ctx)
67
180
{
68
180
    xps_hash_table_t *table;
69
70
180
    table = xps_alloc(ctx, sizeof(xps_hash_table_t));
71
180
    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
180
    table->size = primes[0];
78
180
    table->load = 0;
79
80
180
    table->entries = xps_alloc(ctx, sizeof(xps_hash_entry_t) * (size_t)table->size);
81
180
    if (!table->entries)
82
0
    {
83
0
        xps_free(ctx, table);
84
0
        gs_throw(gs_error_VMerror, "out of memory: hash table entries array");
85
0
        return NULL;
86
0
    }
87
88
180
    memset(table->entries, 0, sizeof(xps_hash_entry_t) * table->size);
89
90
180
    return table;
91
180
}
92
93
static int
94
xps_hash_double(xps_context_t *ctx, xps_hash_table_t *table)
95
0
{
96
0
    xps_hash_entry_t *old_entries;
97
0
    xps_hash_entry_t *new_entries;
98
0
    unsigned int old_size = table->size;
99
0
    unsigned int new_size = table->size * 2;
100
0
    int i;
101
102
0
    for (i = 0; primes[i] != 0; i++)
103
0
    {
104
0
        if (primes[i] > old_size)
105
0
        {
106
0
            new_size = primes[i];
107
0
            break;
108
0
        }
109
0
    }
110
111
0
    old_entries = table->entries;
112
0
    new_entries = xps_alloc(ctx, sizeof(xps_hash_entry_t) * (size_t)new_size);
113
0
    if (!new_entries)
114
0
        return gs_throw(gs_error_VMerror, "out of memory: hash table entries array");
115
116
0
    table->size = new_size;
117
0
    table->entries = new_entries;
118
0
    table->load = 0;
119
120
0
    memset(table->entries, 0, sizeof(xps_hash_entry_t) * table->size);
121
122
0
    for (i = 0; i < old_size; i++)
123
0
        if (old_entries[i].value)
124
0
            xps_hash_insert(ctx, table, old_entries[i].key, old_entries[i].value);
125
126
0
    xps_free(ctx, old_entries);
127
128
0
    return 0;
129
0
}
130
131
void
132
xps_hash_free(xps_context_t *ctx, xps_hash_table_t *table,
133
    void (*free_key)(xps_context_t *ctx, void *),
134
    void (*free_value)(xps_context_t *ctx, void *))
135
180
{
136
180
    int i;
137
138
11.1k
    for (i = 0; i < table->size; i++)
139
10.9k
    {
140
10.9k
        if (table->entries[i].key && free_key)
141
2
            free_key(ctx, table->entries[i].key);
142
10.9k
        if (table->entries[i].value && free_value)
143
2
            free_value(ctx, table->entries[i].value);
144
10.9k
    }
145
146
180
    xps_free(ctx, table->entries);
147
180
    xps_free(ctx, table);
148
180
}
149
150
void *
151
xps_hash_lookup(xps_hash_table_t *table, char *key)
152
14
{
153
14
    xps_hash_entry_t *entries = table->entries;
154
14
    unsigned int size = table->size;
155
14
    unsigned int pos = xps_hash(key) % size;
156
157
14
    while (1)
158
14
    {
159
14
        if (!entries[pos].value)
160
2
            return NULL;
161
162
12
        if (xps_strcasecmp(key, entries[pos].key) == 0)
163
12
            return entries[pos].value;
164
165
0
        pos = (pos + 1) % size;
166
0
    }
167
14
}
168
169
int
170
xps_hash_insert(xps_context_t *ctx, xps_hash_table_t *table, char *key, void *value)
171
2
{
172
2
    xps_hash_entry_t *entries;
173
2
    unsigned int size, pos;
174
175
    /* Grow the table at 80% load */
176
2
    if (table->load > table->size * 8 / 10)
177
0
    {
178
0
        if (xps_hash_double(ctx, table) < 0)
179
0
            return gs_rethrow(-1, "cannot grow hash table");
180
0
    }
181
182
2
    entries = table->entries;
183
2
    size = table->size;
184
2
    pos = xps_hash(key) % size;
185
186
2
    while (1)
187
2
    {
188
2
        if (!entries[pos].value)
189
2
        {
190
2
            entries[pos].key = key;
191
2
            entries[pos].value = value;
192
2
            table->load ++;
193
2
            return 0;
194
2
        }
195
196
0
        if (xps_strcasecmp(key, entries[pos].key) == 0)
197
0
        {
198
0
            return 0;
199
0
        }
200
201
0
        pos = (pos + 1) % size;
202
0
    }
203
2
}
204
205
void
206
xps_hash_debug(xps_hash_table_t *table)
207
0
{
208
0
    int i;
209
210
0
    dlprintf2("hash table load %d / %d\n", table->load, table->size);
211
212
0
    for (i = 0; i < table->size; i++)
213
0
    {
214
0
        if (!table->entries[i].value)
215
0
            dlprintf1("table % 4d: empty\n", i);
216
0
        else
217
0
            dlprintf3("table % 4d: key=%s value="PRI_INTPTR"\n", i,
218
0
                      table->entries[i].key,
219
0
                      (intptr_t)table->entries[i].value);
220
0
    }
221
0
}