2006-08-20 16:25:41 +04:00
|
|
|
/*
|
|
|
|
* Copyright 2006 Rob Kendrick <rjek@rjek.com>
|
2006-10-13 19:09:30 +04:00
|
|
|
* Copyright 2006 Richard Wilson <info@tinct.net>
|
2007-08-08 20:16:03 +04:00
|
|
|
*
|
|
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
|
|
*
|
|
|
|
* NetSurf is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; version 2 of the License.
|
|
|
|
*
|
|
|
|
* NetSurf is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2006-08-20 16:25:41 +04:00
|
|
|
*/
|
|
|
|
|
2015-07-12 19:28:03 +03:00
|
|
|
/**
|
|
|
|
* \file
|
|
|
|
* Write-Once hash table for string to string mappings.
|
|
|
|
*
|
|
|
|
* This implementation is unit tested, if you make changes please
|
|
|
|
* ensure the tests continute to pass and if possible, through
|
|
|
|
* valgrind to make sure there are no memory leaks or invalid memory
|
|
|
|
* accesses. If you add new functionality, please include a test for
|
|
|
|
* it that has good coverage along side the other tests.
|
|
|
|
*/
|
2006-08-20 16:25:41 +04:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2006-08-20 17:46:30 +04:00
|
|
|
#include <stdbool.h>
|
2007-05-31 02:39:54 +04:00
|
|
|
#include "utils/hashtable.h"
|
|
|
|
#include "utils/log.h"
|
2006-08-20 16:25:41 +04:00
|
|
|
|
2006-10-13 19:09:30 +04:00
|
|
|
|
|
|
|
struct hash_entry {
|
2012-10-08 17:51:48 +04:00
|
|
|
char *pairing; /**< block containing 'key\0value\0' */
|
|
|
|
unsigned int key_length; /**< length of key */
|
|
|
|
struct hash_entry *next; /**< next entry */
|
2006-10-13 19:09:30 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct hash_table {
|
|
|
|
unsigned int nchains;
|
|
|
|
struct hash_entry **chain;
|
|
|
|
};
|
|
|
|
|
2015-07-12 19:28:03 +03:00
|
|
|
|
2008-07-27 02:29:15 +04:00
|
|
|
/**
|
|
|
|
* Hash a string, returning a 32bit value. The hash algorithm used is
|
|
|
|
* Fowler Noll Vo - a very fast and simple hash, ideal for short strings.
|
|
|
|
* See http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash for more details.
|
|
|
|
*
|
|
|
|
* \param datum The string to hash.
|
|
|
|
* \param len Pointer to unsigned integer to record datum's length in.
|
|
|
|
* \return The calculated hash value for the datum.
|
|
|
|
*/
|
|
|
|
static inline unsigned int hash_string_fnv(const char *datum, unsigned int *len)
|
|
|
|
{
|
2008-09-08 19:23:34 +04:00
|
|
|
unsigned int z = 0x811c9dc5;
|
2008-07-27 02:29:15 +04:00
|
|
|
const char *start = datum;
|
|
|
|
*len = 0;
|
|
|
|
|
|
|
|
if (datum == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (*datum) {
|
|
|
|
z *= 0x01000193;
|
|
|
|
z ^= *datum++;
|
|
|
|
}
|
|
|
|
*len = datum - start;
|
|
|
|
|
|
|
|
return z;
|
|
|
|
}
|
|
|
|
|
2006-10-13 19:09:30 +04:00
|
|
|
|
2015-07-12 19:28:03 +03:00
|
|
|
/* exported interface documented in utils/hashtable.h */
|
2006-08-20 16:25:41 +04:00
|
|
|
struct hash_table *hash_create(unsigned int chains)
|
|
|
|
{
|
2006-08-20 20:02:22 +04:00
|
|
|
struct hash_table *r = malloc(sizeof(struct hash_table));
|
2006-08-20 16:25:41 +04:00
|
|
|
|
|
|
|
if (r == NULL) {
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO, "Not enough memory for hash table.");
|
2006-08-20 16:25:41 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
r->nchains = chains;
|
2013-02-23 02:33:36 +04:00
|
|
|
r->chain = calloc(chains, sizeof(struct hash_entry *));
|
2006-08-20 16:25:41 +04:00
|
|
|
|
|
|
|
if (r->chain == NULL) {
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO,
|
|
|
|
"Not enough memory for %d hash table chains.", chains);
|
2006-08-20 16:25:41 +04:00
|
|
|
free(r);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2006-08-21 20:51:39 +04:00
|
|
|
|
2015-07-12 19:28:03 +03:00
|
|
|
/* exported interface documented in utils/hashtable.h */
|
2006-08-20 16:25:41 +04:00
|
|
|
void hash_destroy(struct hash_table *ht)
|
|
|
|
{
|
2006-08-20 20:02:22 +04:00
|
|
|
unsigned int i;
|
2006-08-20 16:25:41 +04:00
|
|
|
|
2006-10-12 18:00:40 +04:00
|
|
|
if (ht == NULL)
|
|
|
|
return;
|
|
|
|
|
2006-08-20 16:25:41 +04:00
|
|
|
for (i = 0; i < ht->nchains; i++) {
|
|
|
|
if (ht->chain[i] != NULL) {
|
|
|
|
struct hash_entry *e = ht->chain[i];
|
|
|
|
while (e) {
|
2006-10-12 18:00:40 +04:00
|
|
|
struct hash_entry *n = e->next;
|
2006-10-20 16:13:28 +04:00
|
|
|
free(e->pairing);
|
2006-08-20 16:25:41 +04:00
|
|
|
free(e);
|
|
|
|
e = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(ht->chain);
|
|
|
|
free(ht);
|
|
|
|
}
|
|
|
|
|
2006-08-21 20:51:39 +04:00
|
|
|
|
2015-07-12 19:28:03 +03:00
|
|
|
/* exported interface documented in utils/hashtable.h */
|
2006-08-20 17:46:30 +04:00
|
|
|
bool hash_add(struct hash_table *ht, const char *key, const char *value)
|
2006-08-20 16:25:41 +04:00
|
|
|
{
|
2006-10-20 16:13:28 +04:00
|
|
|
unsigned int h, c, v;
|
|
|
|
struct hash_entry *e;
|
2006-10-12 18:00:40 +04:00
|
|
|
|
|
|
|
if (ht == NULL || key == NULL || value == NULL)
|
|
|
|
return false;
|
|
|
|
|
2006-10-20 16:13:28 +04:00
|
|
|
e = malloc(sizeof(struct hash_entry));
|
2006-08-20 20:02:22 +04:00
|
|
|
if (e == NULL) {
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO, "Not enough memory for hash entry.");
|
2006-08-20 20:02:22 +04:00
|
|
|
return false;
|
|
|
|
}
|
2006-08-20 16:25:41 +04:00
|
|
|
|
2006-10-13 19:50:11 +04:00
|
|
|
h = hash_string_fnv(key, &(e->key_length));
|
2006-10-12 18:00:40 +04:00
|
|
|
c = h % ht->nchains;
|
2006-11-27 18:35:18 +03:00
|
|
|
|
2006-10-20 16:13:28 +04:00
|
|
|
v = strlen(value) ;
|
|
|
|
e->pairing = malloc(v + e->key_length + 2);
|
|
|
|
if (e->pairing == NULL) {
|
Use coccinelle to change logging macro calls in c files
for F in $(git ls-files '*.c');do spatch --sp-file foo.cocci --in-place ${F};done
@@ expression E; @@
-LOG(E);
+NSLOG(netsurf, INFO, E);
@@ expression E, E1; @@
-LOG(E, E1);
+NSLOG(netsurf, INFO, E, E1);
@@ expression E, E1, E2; @@
-LOG(E, E1, E2);
+NSLOG(netsurf, INFO, E, E1, E2);
@@ expression E, E1, E2, E3; @@
-LOG(E, E1, E2, E3);
+NSLOG(netsurf, INFO, E, E1, E2, E3);
@@ expression E, E1, E2, E3, E4; @@
-LOG(E, E1, E2, E3, E4);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4);
@@ expression E, E1, E2, E3, E4, E5; @@
-LOG(E, E1, E2, E3, E4, E5);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5);
@@ expression E, E1, E2, E3, E4, E5, E6; @@
-LOG(E, E1, E2, E3, E4, E5, E6);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6);
@@ expression E, E1, E2, E3, E4, E5, E6, E7; @@
-LOG(E, E1, E2, E3, E4, E5, E6, E7);
+NSLOG(netsurf, INFO, E, E1, E2, E3, E4, E5, E6, E7);
2017-09-06 20:28:12 +03:00
|
|
|
NSLOG(netsurf, INFO,
|
|
|
|
"Not enough memory for string duplication.");
|
2006-08-20 17:46:30 +04:00
|
|
|
free(e);
|
|
|
|
return false;
|
|
|
|
}
|
2006-10-20 16:13:28 +04:00
|
|
|
memcpy(e->pairing, key, e->key_length + 1);
|
|
|
|
memcpy(e->pairing + e->key_length + 1, value, v + 1);
|
2006-08-20 17:46:30 +04:00
|
|
|
|
2006-08-20 16:25:41 +04:00
|
|
|
e->next = ht->chain[c];
|
|
|
|
ht->chain[c] = e;
|
2006-08-20 17:46:30 +04:00
|
|
|
|
|
|
|
return true;
|
2006-08-20 16:25:41 +04:00
|
|
|
}
|
|
|
|
|
2006-08-21 20:51:39 +04:00
|
|
|
|
2015-07-12 19:28:03 +03:00
|
|
|
/* exported interface documented in utils/hashtable.h */
|
2006-08-20 16:25:41 +04:00
|
|
|
const char *hash_get(struct hash_table *ht, const char *key)
|
|
|
|
{
|
2006-10-20 16:13:28 +04:00
|
|
|
unsigned int h, c, key_length;
|
2006-10-12 18:00:40 +04:00
|
|
|
struct hash_entry *e;
|
|
|
|
|
|
|
|
if (ht == NULL || key == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2006-10-13 19:50:11 +04:00
|
|
|
h = hash_string_fnv(key, &key_length);
|
2006-10-12 18:00:40 +04:00
|
|
|
c = h % ht->nchains;
|
|
|
|
|
2006-10-13 19:09:30 +04:00
|
|
|
for (e = ht->chain[c]; e; e = e->next)
|
2006-10-20 16:13:28 +04:00
|
|
|
if ((key_length == e->key_length) &&
|
|
|
|
(memcmp(key, e->pairing, key_length) == 0))
|
|
|
|
return e->pairing + key_length + 1;
|
2006-08-20 16:25:41 +04:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|