NetSurf
hashtable.c
Go to the documentation of this file.
1/*
2 * Copyright 2006 Rob Kendrick <rjek@rjek.com>
3 * Copyright 2006 Richard Wilson <info@tinct.net>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
6 *
7 * NetSurf is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * NetSurf is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/**
21 * \file
22 * Write-Once hash table for string to string mappings.
23 *
24 * This implementation is unit tested, if you make changes please
25 * ensure the tests continute to pass and if possible, through
26 * valgrind to make sure there are no memory leaks or invalid memory
27 * accesses. If you add new functionality, please include a test for
28 * it that has good coverage along side the other tests.
29 */
30
31#include <stdint.h>
32#include <stdbool.h>
33#include <stdlib.h>
34#include <string.h>
35#include <zlib.h>
36#include <errno.h>
37
38#include "utils/log.h"
39#include "utils/hashtable.h"
40
41
42struct hash_entry {
43 char *pairing; /**< block containing 'key\0value\0' */
44 unsigned int key_length; /**< length of key */
45 struct hash_entry *next; /**< next entry */
46};
47
48struct hash_table {
49 unsigned int nchains;
50 struct hash_entry **chain;
51};
52
53/** maximum length of line for file or inline add */
54#define LINE_BUFFER_SIZE 512
55
56/**
57 * Hash a string, returning a 32bit value. The hash algorithm used is
58 * Fowler Noll Vo - a very fast and simple hash, ideal for short strings.
59 * See http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash for more details.
60 *
61 * \param datum The string to hash.
62 * \param len Pointer to unsigned integer to record datum's length in.
63 * \return The calculated hash value for the datum.
64 */
65static inline unsigned int hash_string_fnv(const char *datum, unsigned int *len)
66{
67 unsigned int z = 0x811c9dc5;
68 const char *start = datum;
69 *len = 0;
70
71 if (datum == NULL)
72 return 0;
73
74 while (*datum) {
75 z *= 0x01000193;
76 z ^= *datum++;
77 }
78 *len = datum - start;
79
80 return z;
81}
82
83
84
85/**
86 * process a line of input.
87 *
88 * \param hash The hash table to add the line to
89 * \param ln The line to process
90 * \param lnlen The length of \ln
91 * \return NSERROR_OK on success else NSERROR_INVALID
92 */
93static nserror
94process_line(struct hash_table *hash, uint8_t *ln, int lnlen)
95{
96 uint8_t *key;
97 uint8_t *value;
98 uint8_t *colon;
99 nserror res;
100
101 key = ln; /* set key to start of line */
102 value = ln + lnlen; /* set value to end of line */
103
104 /* skip leading whitespace */
105 while ((key < value) &&
106 ((*key == ' ') || (*key == '\t'))) {
107 key++;
108 }
109
110 /* empty or comment lines */
111 if ((*key == 0) || (*key == '#')) {
112 return NSERROR_OK;
113 }
114
115 /* find first colon as key/value separator */
116 for (colon = key; colon < value; colon++) {
117 if (*colon == ':') {
118 break;
119 }
120 }
121 if (colon == value) {
122 /* no colon found */
123 return NSERROR_INVALID;
124 }
125
126 *colon = 0; /* terminate key */
127 value = colon + 1;
128
129 res = hash_add(hash, (char *)key, (char *)value);
130 if (res != NSERROR_OK) {
131 NSLOG(netsurf, INFO,
132 "Unable to add %s:%s to hash table", ln, value);
133 }
134 return res;
135}
136
137
138/**
139 * adds key/value pairs to a hash from a memory area
140 */
141static nserror
142hash_add_inline_plain(struct hash_table *ht, const uint8_t *data, size_t size)
143{
144 uint8_t s[LINE_BUFFER_SIZE]; /* line buffer */
145 unsigned int slen = 0;
146 nserror res = NSERROR_OK;
147
148 while (size > 0) {
149 s[slen] = *data;
150
151 if (s[slen] == '\n') {
152 s[slen] = 0; /* replace newline with null termination */
153 res = process_line(ht, s, slen);
154 slen = 0;
155 if (res != NSERROR_OK) {
156 break;
157 }
158 } else {
159 slen++;
160 if (slen > sizeof s) {
161 NSLOG(netsurf, INFO, "Overlength line\n");
162 slen = 0;
163 }
164 }
165
166 size--;
167 data++;
168 }
169 if (slen > 0) {
170 s[slen] = 0;
171 res = process_line(ht, s, slen);
172 }
173
174 return res;
175}
176
177/**
178 * adds key/value pairs to a hash from a compressed memory area
179 */
180static nserror
181hash_add_inline_gzip(struct hash_table *ht, const uint8_t *data, size_t size)
182{
183 nserror res;
184 int ret; /* zlib return value */
185 z_stream strm;
186 uint8_t s[LINE_BUFFER_SIZE]; /* line buffer */
187 size_t used = 0; /* number of bytes in buffer in use */
188 uint8_t *nl;
189
190 strm.zalloc = Z_NULL;
191 strm.zfree = Z_NULL;
192 strm.opaque = Z_NULL;
193
194 strm.next_in = (uint8_t *)data;
195 strm.avail_in = size;
196
197 ret = inflateInit2(&strm, 32 + MAX_WBITS);
198 if (ret != Z_OK) {
199 NSLOG(netsurf, INFO, "inflateInit returned %d", ret);
200 return NSERROR_INVALID;
201 }
202
203 do {
204 strm.next_out = s + used;
205 strm.avail_out = sizeof(s) - used;
206
207 ret = inflate(&strm, Z_NO_FLUSH);
208 if ((ret != Z_OK) && (ret != Z_STREAM_END)) {
209 break;
210 }
211
212 used = sizeof(s) - strm.avail_out;
213 while (used > 0) {
214 /* find nl */
215 for (nl = &s[0]; nl < &s[used]; nl++) {
216 if (*nl == '\n') {
217 break;
218 }
219 }
220 if (nl == &s[used]) {
221 /* no nl found */
222 break;
223 }
224 /* found newline */
225 *nl = 0; /* null terminate line */
226 res = process_line(ht, &s[0], nl - &s[0]);
227 if (res != NSERROR_OK) {
228 inflateEnd(&strm);
229 return res;
230 }
231
232 /* move data down */
233 memmove(&s[0], nl + 1, used - ((nl + 1) - &s[0]) );
234 used -= ((nl +1) - &s[0]);
235 }
236 if (used == sizeof(s)) {
237 /* entire buffer used and no newline */
238 NSLOG(netsurf, INFO, "Overlength line");
239 used = 0;
240 }
241 } while (ret != Z_STREAM_END);
242
243 inflateEnd(&strm);
244
245 if (ret != Z_STREAM_END) {
246 NSLOG(netsurf, INFO, "inflate returned %d", ret);
247 return NSERROR_INVALID;
248 }
249 return NSERROR_OK;
250
251}
252
253
254/* exported interface documented in utils/hashtable.h */
255struct hash_table *hash_create(unsigned int chains)
256{
257 struct hash_table *r = malloc(sizeof(struct hash_table));
258
259 if (r == NULL) {
260 NSLOG(netsurf, INFO, "Not enough memory for hash table.");
261 return NULL;
262 }
263
264 r->nchains = chains;
265 r->chain = calloc(chains, sizeof(struct hash_entry *));
266
267 if (r->chain == NULL) {
268 NSLOG(netsurf, INFO,
269 "Not enough memory for %d hash table chains.", chains);
270 free(r);
271 return NULL;
272 }
273
274 return r;
275}
276
277
278/* exported interface documented in utils/hashtable.h */
279void hash_destroy(struct hash_table *ht)
280{
281 unsigned int i;
282
283 if (ht == NULL)
284 return;
285
286 for (i = 0; i < ht->nchains; i++) {
287 if (ht->chain[i] != NULL) {
288 struct hash_entry *e = ht->chain[i];
289 while (e) {
290 struct hash_entry *n = e->next;
291 free(e->pairing);
292 free(e);
293 e = n;
294 }
295 }
296 }
297
298 free(ht->chain);
299 free(ht);
300}
301
302
303/* exported interface documented in utils/hashtable.h */
304nserror hash_add(struct hash_table *ht, const char *key, const char *value)
305{
306 unsigned int h, c, v;
307 struct hash_entry *e;
308
309 if (ht == NULL || key == NULL || value == NULL)
311
312 e = malloc(sizeof(struct hash_entry));
313 if (e == NULL) {
314 NSLOG(netsurf, INFO, "Not enough memory for hash entry.");
315 return NSERROR_NOMEM;
316 }
317
318 h = hash_string_fnv(key, &(e->key_length));
319 c = h % ht->nchains;
320
321 v = strlen(value) ;
322 e->pairing = malloc(v + e->key_length + 2);
323 if (e->pairing == NULL) {
324 NSLOG(netsurf, INFO,
325 "Not enough memory for string duplication.");
326 free(e);
327 return NSERROR_NOMEM;
328 }
329 memcpy(e->pairing, key, e->key_length + 1);
330 memcpy(e->pairing + e->key_length + 1, value, v + 1);
331
332 e->next = ht->chain[c];
333 ht->chain[c] = e;
334
335 return NSERROR_OK;
336}
337
338
339/* exported interface documented in utils/hashtable.h */
340const char *hash_get(struct hash_table *ht, const char *key)
341{
342 unsigned int h, c, key_length;
343 struct hash_entry *e;
344
345 if (ht == NULL || key == NULL)
346 return NULL;
347
348 h = hash_string_fnv(key, &key_length);
349 c = h % ht->nchains;
350
351 for (e = ht->chain[c]; e; e = e->next) {
352 if ((key_length == e->key_length) &&
353 (memcmp(key, e->pairing, key_length) == 0)) {
354 return e->pairing + key_length + 1;
355 }
356 }
357
358 return NULL;
359}
360
361
362
363/* exported interface documented in utils/hashtable.h */
364nserror hash_add_file(struct hash_table *ht, const char *path)
365{
366 nserror res = NSERROR_OK;
367 char s[LINE_BUFFER_SIZE]; /* line buffer */
368 gzFile fp; /* compressed file handle */
369
370 if (path == NULL) {
372 }
373
374 fp = gzopen(path, "r");
375 if (!fp) {
376 NSLOG(netsurf, INFO,
377 "Unable to open file \"%.100s\": %s", path,
378 strerror(errno));
379
380 return NSERROR_NOT_FOUND;
381 }
382
383 while (gzgets(fp, s, sizeof s)) {
384 int slen = strlen(s);
385 s[--slen] = 0; /* remove \n at end */
386
387 res = process_line(ht, (uint8_t *)s, slen);
388 if (res != NSERROR_OK) {
389 break;
390 }
391 }
392
393 gzclose(fp);
394
395 return res;
396}
397
398
399/* exported interface documented in utils/hashtable.h */
400nserror hash_add_inline(struct hash_table *ht, const uint8_t *data, size_t size)
401{
402 if ((data[0]==0x1f) && (data[1] == 0x8b)) {
403 /* gzip header detected */
404 return hash_add_inline_gzip(ht, data, size);
405 }
406 return hash_add_inline_plain(ht, data, size);
407}
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_NOT_FOUND
Requested item not found.
Definition: errors.h:34
@ NSERROR_BAD_PARAMETER
Bad Parameter.
Definition: errors.h:48
@ NSERROR_INVALID
Invalid data.
Definition: errors.h:49
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
struct hash_table * hash_create(unsigned int chains)
Create a new hash table.
Definition: hashtable.c:255
nserror hash_add(struct hash_table *ht, const char *key, const char *value)
Adds a key/value pair to a hash table.
Definition: hashtable.c:304
static unsigned int hash_string_fnv(const char *datum, unsigned int *len)
Hash a string, returning a 32bit value.
Definition: hashtable.c:65
nserror hash_add_inline(struct hash_table *ht, const uint8_t *data, size_t size)
Add key/value pairs to a hash table with data from a memory buffer.
Definition: hashtable.c:400
nserror hash_add_file(struct hash_table *ht, const char *path)
Add key/value pairs to a hash table with data from a file.
Definition: hashtable.c:364
static nserror hash_add_inline_plain(struct hash_table *ht, const uint8_t *data, size_t size)
adds key/value pairs to a hash from a memory area
Definition: hashtable.c:142
static nserror hash_add_inline_gzip(struct hash_table *ht, const uint8_t *data, size_t size)
adds key/value pairs to a hash from a compressed memory area
Definition: hashtable.c:181
static nserror process_line(struct hash_table *hash, uint8_t *ln, int lnlen)
process a line of input.
Definition: hashtable.c:94
void hash_destroy(struct hash_table *ht)
Destroys a hash table.
Definition: hashtable.c:279
const char * hash_get(struct hash_table *ht, const char *key)
Looks up a the value associated with with a key from a specific hash table.
Definition: hashtable.c:340
#define LINE_BUFFER_SIZE
maximum length of line for file or inline add
Definition: hashtable.c:54
Interface to Write-Once hash table for string to string mapping.
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
Interface to utility string handling.
Definition: hashtable.c:42
struct hash_entry * next
next entry
Definition: hashtable.c:45
char * pairing
block containing 'key\0value\0'
Definition: hashtable.c:43
unsigned int key_length
length of key
Definition: hashtable.c:44
struct hash_entry ** chain
Definition: hashtable.c:50
unsigned int nchains
Definition: hashtable.c:49
static nserror path(const struct redraw_context *ctx, const plot_style_t *pstyle, const float *p, unsigned int n, const float transform[6])
Plots a path.
Definition: plot.c:821