]> git.xonotic.org Git - xonotic/gmqcc.git/blob - util.c
e0bf1dfb17470b73118894cb2c22466f2b12222a
[xonotic/gmqcc.git] / util.c
1 /*
2  * Copyright (C) 2012
3  *     Dale Weiler
4  *     Wolfgang Bumiller
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy of
7  * this software and associated documentation files (the "Software"), to deal in
8  * the Software without restriction, including without limitation the rights to
9  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is furnished to do
11  * so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #include <stdarg.h>
25 #include <errno.h>
26 #include "gmqcc.h"
27
28 uint64_t mem_ab = 0;
29 uint64_t mem_db = 0;
30 uint64_t mem_at = 0;
31 uint64_t mem_dt = 0;
32
33 struct memblock_t {
34     const char  *file;
35     unsigned int line;
36     size_t       byte;
37     struct memblock_t *next;
38     struct memblock_t *prev;
39 };
40
41 static struct memblock_t *mem_start = NULL;
42
43 void *util_memory_a(size_t byte, unsigned int line, const char *file) {
44     struct memblock_t *info = malloc(sizeof(struct memblock_t) + byte);
45     void              *data = (void*)(info+1);
46     if (!info) return NULL;
47     info->line = line;
48     info->byte = byte;
49     info->file = file;
50     info->prev = NULL;
51     info->next = mem_start;
52     if (mem_start)
53         mem_start->prev = info;
54     mem_start = info;
55
56     util_debug("MEM", "allocation:   % 8u (bytes) address 0x%08X @ %s:%u\n", byte, data, file, line);
57     mem_at++;
58     mem_ab += info->byte;
59
60     return data;
61 }
62
63 void util_memory_d(void *ptrn, unsigned int line, const char *file) {
64     struct memblock_t *info = NULL;
65
66     if (!ptrn) return;
67     info = ((struct memblock_t*)ptrn - 1);
68
69     util_debug("MEM", "released:     % 8u (bytes) address 0x%08X @ %s:%u\n", info->byte, ptrn, file, line);
70     mem_db += info->byte;
71     mem_dt++;
72
73     if (info->prev)
74         info->prev->next = info->next;
75     if (info->next)
76         info->next->prev = info->prev;
77     if (info == mem_start)
78         mem_start = info->next;
79
80     free(info);
81 }
82
83 void *util_memory_r(void *ptrn, size_t byte, unsigned int line, const char *file) {
84     struct memblock_t *oldinfo = NULL;
85
86     struct memblock_t *newinfo;
87
88     if (!ptrn)
89         return util_memory_a(byte, line, file);
90     if (!byte) {
91         util_memory_d(ptrn, line, file);
92         return NULL;
93     }
94
95     oldinfo = ((struct memblock_t*)ptrn - 1);
96     newinfo = ((struct memblock_t*)malloc(sizeof(struct memblock_t) + byte));
97
98     util_debug("MEM", "reallocation: % 8u -> %u (bytes) address 0x%08X -> 0x%08X @ %s:%u\n", oldinfo->byte, byte, ptrn, (void*)(newinfo+1), file, line);
99
100     /* new data */
101     if (!newinfo) {
102         util_memory_d(oldinfo+1, line, file);
103         return NULL;
104     }
105
106     /* copy old */
107     memcpy(newinfo+1, oldinfo+1, oldinfo->byte);
108
109     /* free old */
110     if (oldinfo->prev)
111         oldinfo->prev->next = oldinfo->next;
112     if (oldinfo->next)
113         oldinfo->next->prev = oldinfo->prev;
114     if (oldinfo == mem_start)
115         mem_start = oldinfo->next;
116
117     /* fill info */
118     newinfo->line = line;
119     newinfo->byte = byte;
120     newinfo->file = file;
121     newinfo->prev = NULL;
122     newinfo->next = mem_start;
123     if (mem_start)
124         mem_start->prev = newinfo;
125     mem_start = newinfo;
126
127     mem_ab -= oldinfo->byte;
128     mem_ab += newinfo->byte;
129
130     free(oldinfo);
131
132     return newinfo+1;
133 }
134
135 void util_meminfo() {
136     struct memblock_t *info;
137
138     if (!opts.memchk)
139         return;
140
141     for (info = mem_start; info; info = info->next) {
142         util_debug("MEM", "lost:       % 8u (bytes) at %s:%u\n",
143             info->byte,
144             info->file,
145             info->line);
146     }
147
148     util_debug("MEM", "Memory information:\n\
149         Total allocations:   %llu\n\
150         Total deallocations: %llu\n\
151         Total allocated:     %llu (bytes)\n\
152         Total deallocated:   %llu (bytes)\n\
153         Leaks found:         lost %llu (bytes) in %d allocations\n",
154             mem_at,   mem_dt,
155             mem_ab,   mem_db,
156            (mem_ab -  mem_db),
157            (mem_at -  mem_dt)
158     );
159 }
160
161 /*
162  * Some string utility functions, because strdup uses malloc, and we want
163  * to track all memory (without replacing malloc).
164  */
165 char *util_strdup(const char *s) {
166     size_t  len = 0;
167     char   *ptr = NULL;
168
169     if (!s)
170         return NULL;
171
172     if ((len = strlen(s)) && (ptr = mem_a(len+1))) {
173         memcpy(ptr, s, len);
174         ptr[len] = '\0';
175     }
176     return ptr;
177 }
178
179 /*
180  * Remove quotes from a string, escapes from \ in string
181  * as well.  This function shouldn't be used to create a
182  * char array that is later freed (it uses pointer arith)
183  */
184 char *util_strrq(const char *s) {
185     char *dst = (char*)s;
186     char *src = (char*)s;
187     char  chr;
188     while ((chr = *src++) != '\0') {
189         if (chr == '\\') {
190             *dst++ = chr;
191             if ((chr = *src++) == '\0')
192                 break;
193             *dst++ = chr;
194         } else if (chr != '"')
195             *dst++ = chr;
196     }
197     *dst = '\0';
198     return dst;
199 }
200
201 /*
202  * Chops a substring from an existing string by creating a
203  * copy of it and null terminating it at the required position.
204  */
205 char *util_strchp(const char *s, const char *e) {
206     const char *c = NULL;
207     if (!s || !e)
208         return NULL;
209
210     c = s;
211     while (c != e)
212         c++;
213
214     return util_strdup(s);
215 }
216
217 /*
218  * Returns true if string is all uppercase, otherwise
219  * it returns false.
220  */
221 bool util_strupper(const char *str) {
222     while (*str) {
223         if(!isupper(*str))
224             return false;
225         str++;
226     }
227     return true;
228 }
229
230 /*
231  * Returns true if string is all digits, otherwise
232  * it returns false.
233  */
234 bool util_strdigit(const char *str) {
235     while (*str) {
236         if(!isdigit(*str))
237             return false;
238         str++;
239     }
240     return true;
241 }
242
243 bool util_strncmpexact(const char *src, const char *ned, size_t len) {
244     return (!strncmp(src, ned, len) && !src[len]);
245 }
246
247 void util_debug(const char *area, const char *ms, ...) {
248     va_list  va;
249     if (!opts.debug)
250         return;
251
252     if (!strcmp(area, "MEM") && !opts.memchk)
253         return;
254
255     va_start(va, ms);
256     con_out ("[%s] ", area);
257     con_vout(ms, va);
258     va_end  (va);
259 }
260
261 /*
262  * Endianess swapping, all data must be stored little-endian.  This
263  * reorders by stride and length, much nicer than other functions for
264  * certian-sized types like short or int.
265  */
266 #if 0
267 void util_endianswap(void *m, int s, int l) {
268     size_t w = 0;
269     size_t i = 0;
270
271     /* ignore if we're already LE */
272     if(*((char *)&s))
273         return;
274
275     for(; w < (size_t)l; w++) {
276         for(;  i < (size_t)(s << 1); i++) {
277             unsigned char *p = (unsigned char *)m+w*s;
278             unsigned char  t = p[i];
279             p[i]             = p[s-i-1];
280             p[s-i-1]         = t;
281         }
282     }
283 }
284 #endif
285
286 /*
287  * only required if big endian .. otherwise no need to swap
288  * data.
289  */   
290 #if PLATFORM_BYTE_ORDER == GMQCC_BYTE_ORDER_BIG
291     static void util_swap16(uint16_t *d, size_t l) {
292         while (l--) {
293             d[l] = (d[l] << 8) | (d[l] >> 8);
294         }
295     }
296
297     static void util_swap32(uint32_t *d, size_t l) {
298         while (l--) {
299             uint32_t v;
300             v = ((d[l] << 8) & 0xFF00FF00) | ((d[l] >> 8) & 0x00FF00FF);
301             d[l] = (v << 16) | (v >> 16);
302         }
303     }
304
305     /* Some strange system doesn't like constants that big, AND doesn't recognize an ULL suffix
306      * so let's go the safe way
307      */
308     static void util_swap64(uint32_t *d, size_t l) {
309         /*
310         while (l--) {
311             uint64_t v;
312             v = ((d[l] << 8) & 0xFF00FF00FF00FF00) | ((d[l] >> 8) & 0x00FF00FF00FF00FF);
313             v = ((v << 16) & 0xFFFF0000FFFF0000) | ((v >> 16) & 0x0000FFFF0000FFFF);
314             d[l] = (v << 32) | (v >> 32);
315         }
316         */
317         size_t i;
318         for (i = 0; i < l; i += 2) {
319             uint32_t v1 = d[i];
320             d[i] = d[i+1];
321             d[i+1] = v1;
322             util_swap32(d+i, 2);
323         }
324     }
325 #endif
326
327 void util_endianswap(void *_data, size_t length, unsigned int typesize) {
328 #   if PLATFORM_BYTE_ORDER == -1 /* runtime check */
329     if (*((char*)&typesize))
330         return;
331 #else
332     /* prevent unused warnings */
333     (void) _data;
334     (void) length;
335     (void) typesize;
336
337 #   if PLATFORM_BYTE_ORDER == GMQCC_BYTE_ORDER_LITTLE
338         return;
339 #   else
340         switch (typesize) {
341             case 1: return;
342             case 2:
343                 util_swap16((uint16_t*)_data, length>>1);
344                 return;
345             case 4:
346                 util_swap32((uint32_t*)_data, length>>2);
347                 return;
348             case 8:
349                 util_swap64((uint32_t*)_data, length>>3);
350                 return;
351
352             default: abort(); /* please blow the fuck up! */
353         }
354 #   endif
355 #endif
356 }
357
358 /*
359  * CRC algorithms vary in the width of the polynomial, the value of said polynomial,
360  * the initial value used for the register, weather the bits of each byte are reflected
361  * before being processed, weather the algorithm itself feeds input bytes through the
362  * register or XORs them with a byte from one end and then straight into the table, as
363  * well as (but not limited to the idea of reflected versions) where the final register
364  * value becomes reversed, and finally weather the value itself is used to XOR the final
365  * register value.  AS such you can already imagine how painfully annoying CRCs are,
366  * of course we stand to target Quake, which expects it's certian set of rules for proper
367  * calculation of a CRC.
368  *
369  * In most traditional CRC algorithms on uses a reflected table driven method where a value
370  * or register is reflected if it's bits are swapped around it's center.  For example:
371  * take the bits 0101 is the 4-bit reflection of 1010, and respectfully 0011 would be the
372  * reflection of 1100. Quake however expects a NON-Reflected CRC on the output, but still
373  * requires a final XOR on the values (0xFFFF and 0x0000) this is a standard CCITT CRC-16
374  * which I respectfully as a programmer don't agree with.
375  *
376  * So now you know what we target, and why we target it, despite how unsettling it may seem
377  * but those are what Quake seems to request.
378  */
379
380 static const uint16_t util_crc16_table[] = {
381     0x0000,     0x1021,     0x2042,     0x3063,     0x4084,     0x50A5,
382     0x60C6,     0x70E7,     0x8108,     0x9129,     0xA14A,     0xB16B,
383     0xC18C,     0xD1AD,     0xE1CE,     0xF1EF,     0x1231,     0x0210,
384     0x3273,     0x2252,     0x52B5,     0x4294,     0x72F7,     0x62D6,
385     0x9339,     0x8318,     0xB37B,     0xA35A,     0xD3BD,     0xC39C,
386     0xF3FF,     0xE3DE,     0x2462,     0x3443,     0x0420,     0x1401,
387     0x64E6,     0x74C7,     0x44A4,     0x5485,     0xA56A,     0xB54B,
388     0x8528,     0x9509,     0xE5EE,     0xF5CF,     0xC5AC,     0xD58D,
389     0x3653,     0x2672,     0x1611,     0x0630,     0x76D7,     0x66F6,
390     0x5695,     0x46B4,     0xB75B,     0xA77A,     0x9719,     0x8738,
391     0xF7DF,     0xE7FE,     0xD79D,     0xC7BC,     0x48C4,     0x58E5,
392     0x6886,     0x78A7,     0x0840,     0x1861,     0x2802,     0x3823,
393     0xC9CC,     0xD9ED,     0xE98E,     0xF9AF,     0x8948,     0x9969,
394     0xA90A,     0xB92B,     0x5AF5,     0x4AD4,     0x7AB7,     0x6A96,
395     0x1A71,     0x0A50,     0x3A33,     0x2A12,     0xDBFD,     0xCBDC,
396     0xFBBF,     0xEB9E,     0x9B79,     0x8B58,     0xBB3B,     0xAB1A,
397     0x6CA6,     0x7C87,     0x4CE4,     0x5CC5,     0x2C22,     0x3C03,
398     0x0C60,     0x1C41,     0xEDAE,     0xFD8F,     0xCDEC,     0xDDCD,
399     0xAD2A,     0xBD0B,     0x8D68,     0x9D49,     0x7E97,     0x6EB6,
400     0x5ED5,     0x4EF4,     0x3E13,     0x2E32,     0x1E51,     0x0E70,
401     0xFF9F,     0xEFBE,     0xDFDD,     0xCFFC,     0xBF1B,     0xAF3A,
402     0x9F59,     0x8F78,     0x9188,     0x81A9,     0xB1CA,     0xA1EB,
403     0xD10C,     0xC12D,     0xF14E,     0xE16F,     0x1080,     0x00A1,
404     0x30C2,     0x20E3,     0x5004,     0x4025,     0x7046,     0x6067,
405     0x83B9,     0x9398,     0xA3FB,     0xB3DA,     0xC33D,     0xD31C,
406     0xE37F,     0xF35E,     0x02B1,     0x1290,     0x22F3,     0x32D2,
407     0x4235,     0x5214,     0x6277,     0x7256,     0xB5EA,     0xA5CB,
408     0x95A8,     0x8589,     0xF56E,     0xE54F,     0xD52C,     0xC50D,
409     0x34E2,     0x24C3,     0x14A0,     0x0481,     0x7466,     0x6447,
410     0x5424,     0x4405,     0xA7DB,     0xB7FA,     0x8799,     0x97B8,
411     0xE75F,     0xF77E,     0xC71D,     0xD73C,     0x26D3,     0x36F2,
412     0x0691,     0x16B0,     0x6657,     0x7676,     0x4615,     0x5634,
413     0xD94C,     0xC96D,     0xF90E,     0xE92F,     0x99C8,     0x89E9,
414     0xB98A,     0xA9AB,     0x5844,     0x4865,     0x7806,     0x6827,
415     0x18C0,     0x08E1,     0x3882,     0x28A3,     0xCB7D,     0xDB5C,
416     0xEB3F,     0xFB1E,     0x8BF9,     0x9BD8,     0xABBB,     0xBB9A,
417     0x4A75,     0x5A54,     0x6A37,     0x7A16,     0x0AF1,     0x1AD0,
418     0x2AB3,     0x3A92,     0xFD2E,     0xED0F,     0xDD6C,     0xCD4D,
419     0xBDAA,     0xAD8B,     0x9DE8,     0x8DC9,     0x7C26,     0x6C07,
420     0x5C64,     0x4C45,     0x3CA2,     0x2C83,     0x1CE0,     0x0CC1,
421     0xEF1F,     0xFF3E,     0xCF5D,     0xDF7C,     0xAF9B,     0xBFBA,
422     0x8FD9,     0x9FF8,     0x6E17,     0x7E36,     0x4E55,     0x5E74,
423     0x2E93,     0x3EB2,     0x0ED1,     0x1EF0
424 };
425
426 /* Non - Reflected */
427 uint16_t util_crc16(uint16_t current, const char *k, size_t len) {
428     register uint16_t h = current;
429     for (; len; --len, ++k) 
430         h = util_crc16_table[(h>>8)^((unsigned char)*k)]^(h<<8);
431     return h;
432 }
433 /* Reflective Varation (for reference) */
434 #if 0
435 uint16_t util_crc16(const char *k, int len, const short clamp) {
436     register uint16_t h= (uint16_t)0xFFFFFFFF;
437     for (; len; --len, ++k) 
438         h = util_crc16_table[(h^((unsigned char)*k))&0xFF]^(h>>8);
439     return (~h)%clamp; 
440 }
441 #endif
442
443 /*
444  * Implements libc getline for systems that don't have it, which is
445  * assmed all.  This works the same as getline().
446  */
447 int util_getline(char **lineptr, size_t *n, FILE *stream) {
448     int   chr;
449     int   ret;
450     char *pos;
451
452     if (!lineptr || !n || !stream)
453         return -1;
454     if (!*lineptr) {
455         if (!(*lineptr = (char*)mem_a((*n=64))))
456             return -1;
457     }
458
459     chr = *n;
460     pos = *lineptr;
461
462     for (;;) {
463         int c = getc(stream);
464
465         if (chr < 2) {
466             *n += (*n > 16) ? *n : 64;
467             chr = *n + *lineptr - pos;
468             if (!(*lineptr = (char*)mem_r(*lineptr,*n)))
469                 return -1;
470             pos = *n - chr + *lineptr;
471         }
472
473         if (ferror(stream))
474             return -1;
475         if (c == EOF) {
476             if (pos == *lineptr)
477                 return -1;
478             else
479                 break;
480         }
481
482         *pos++ = c;
483         chr--;
484         if (c == '\n')
485             break;
486     }
487     *pos = '\0';
488     return (ret = pos - *lineptr);
489 }
490
491 size_t util_strtocmd(const char *in, char *out, size_t outsz) {
492     size_t sz = 1;
493     for (; *in && sz < outsz; ++in, ++out, ++sz)
494         *out = (*in == '-') ? '_' : (isalpha(*in) && !isupper(*in)) ? *in + 'A' - 'a': *in;
495     *out = 0;
496     return sz-1;
497 }
498
499 size_t util_strtononcmd(const char *in, char *out, size_t outsz) {
500     size_t sz = 1;
501     for (; *in && sz < outsz; ++in, ++out, ++sz)
502         *out = (*in == '_') ? '-' : (isalpha(*in) && isupper(*in)) ? *in + 'a' - 'A' : *in;
503     *out = 0;
504     return sz-1;
505 }
506
507
508 FILE *util_fopen(const char *filename, const char *mode)
509 {
510 #ifdef _MSC_VER
511     FILE *out;
512     if (fopen_s(&out, filename, mode) != 0)
513         return NULL;
514     return out;
515 #else
516     return fopen(filename, mode);
517 #endif
518 }
519
520 void _util_vec_grow(void **a, size_t i, size_t s) {
521     size_t m = *a ? 2*_vec_beg(*a)+i : i+1;
522     void  *p = mem_r((*a ? _vec_raw(*a) : NULL), s * m + sizeof(size_t)*2);
523     if (!*a)
524         ((size_t*)p)[1] = 0;
525     *a = (void*)((size_t*)p + 2);
526     _vec_beg(*a) = m;
527 }
528
529 /*
530  * Hash table for generic data, based on dynamic memory allocations
531  * all around.  This is the internal interface, please look for
532  * EXPOSED INTERFACE comment below
533  */
534 typedef struct hash_node_t {
535     char               *key;   /* the key for this node in table */
536     void               *value; /* pointer to the data as void*   */
537     struct hash_node_t *next;  /* next node (linked list)        */
538 } hash_node_t;
539
540 GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) {
541     const uint32_t       mix   = 0x5BD1E995;
542     const uint32_t       rot   = 24;
543     size_t               size  = strlen(key);
544     uint32_t             hash  = 0x1EF0 /* LICRC TAB */  ^ size;
545     uint32_t             alias = 0;
546     const unsigned char *data  = (const unsigned char*)key;
547
548     while (size >= 4) {
549         alias = *(uint32_t*)data;
550
551         alias *= mix;
552         alias ^= alias >> rot;
553         alias *= mix;
554
555         hash  *= mix;
556         hash  ^= alias;
557
558         data += 4;
559         size -= 4;
560     }
561
562     switch (size) {
563         case 3: hash ^= data[2] << 16;
564         case 2: hash ^= data[1] << 8;
565         case 1: hash ^= data[0];
566                 hash *= mix;
567     }
568
569     hash ^= hash >> 13;
570     hash *= mix;
571     hash ^= hash >> 15;
572
573     return (size_t) (hash % ht->size);
574 }
575
576 hash_node_t *_util_htnewpair(const char *key, void *value) {
577     hash_node_t *node;
578     if (!(node = mem_a(sizeof(hash_node_t))))
579         return NULL;
580
581     if (!(node->key = util_strdup(key))) {
582         mem_d(node);
583         return NULL;
584     }
585
586     node->value = value;
587     node->next  = NULL;
588
589     return node;
590 }
591
592 /*
593  * EXPOSED INTERFACE for the hashtable implementation
594  * util_htnew(size)                             -- to make a new hashtable
595  * util_htset(table, key, value, sizeof(value)) -- to set something in the table
596  * util_htget(table, key)                       -- to get something from the table
597  * util_htdel(table)                            -- to delete the table
598  */
599 hash_table_t *util_htnew(size_t size) {
600     hash_table_t *hashtable = NULL;
601     if (size < 1)
602         return NULL;
603
604     if (!(hashtable = mem_a(sizeof(hash_table_t))))
605         return NULL;
606
607     if (!(hashtable->table = mem_a(sizeof(hash_node_t*) * size))) {
608         mem_d(hashtable);
609         return NULL;
610     }
611
612     hashtable->size = size;
613     memset(hashtable->table, 0, sizeof(hash_node_t*) * size);
614
615     return hashtable;
616 }
617
618 void util_htseth(hash_table_t *ht, const char *key, size_t bin, void *value) {
619     hash_node_t *newnode = NULL;
620     hash_node_t *next    = NULL;
621     hash_node_t *last    = NULL;
622
623     next = ht->table[bin];
624
625     while (next && next->key && strcmp(key, next->key) > 0)
626         last = next, next = next->next;
627
628     /* already in table, do a replace */
629     if (next && next->key && strcmp(key, next->key) == 0) {
630         next->value = value;
631     } else {
632         /* not found, grow a pair man :P */
633         newnode = _util_htnewpair(key, value);
634         if (next == ht->table[bin]) {
635             newnode->next  = next;
636             ht->table[bin] = newnode;
637         } else if (!next) {
638             last->next = newnode;
639         } else {
640             newnode->next = next;
641             last->next = newnode;
642         }
643     }
644 }
645
646 void util_htset(hash_table_t *ht, const char *key, void *value) {
647     util_htseth(ht, key, util_hthash(ht, key), value);
648 }
649
650 void *util_htgeth(hash_table_t *ht, const char *key, size_t bin) {
651     hash_node_t *pair = ht->table[bin];
652
653     while (pair && pair->key && strcmp(key, pair->key) > 0)
654         pair = pair->next;
655
656     if (!pair || !pair->key || strcmp(key, pair->key) != 0)
657         return NULL;
658
659     return pair->value;
660 }
661
662 void *util_htget(hash_table_t *ht, const char *key) {
663     return util_htgeth(ht, key, util_hthash(ht, key));
664 }
665
666 /*
667  * Free all allocated data in a hashtable, this is quite the amount
668  * of work.
669  */
670 void util_htdel(hash_table_t *ht) {
671     size_t i = 0;
672     for (; i < ht->size; i++) {
673         hash_node_t *n = ht->table[i];
674         hash_node_t *p;
675
676         /* free in list */
677         while (n) {
678             if (n->key)
679                 mem_d(n->key);
680             p = n;
681             n = n->next;
682             mem_d(p);
683         }
684
685     }
686     /* free table */
687     mem_d(ht->table);
688     mem_d(ht);
689 }