ticl

tiny irc channel linker
git clone git://git.ircforever.org/ticl
Log | Files | Refs | Submodules | README | LICENSE

htable.c (4220B)


      1 /*
      2  * This work is dedicated to the public domain.
      3  * See COPYING file for more information.
      4  */
      5 
      6 #include <stdlib.h>
      7 #include <stdio.h>
      8 
      9 #include "htable.h"
     10 
     11 /*
     12  * Destroy the hash table and, if the free_key_val flag is true, free the keys and values.
     13  */
     14 static void
     15 destroy_and_free(struct htable *ht, int free_key_val)
     16 {
     17 	unsigned int i;
     18 	struct htnode *n, *tmp;
     19 
     20 	for (i = 0; i < ht->cap; i++) {
     21 		for (n = ht->nodes[i]; n != NULL;) {
     22 			if (free_key_val) {
     23 				ht->key_free(n->key);
     24 				ht->val_free(n->val);
     25 			}
     26 			tmp = n;
     27 			n = n->next;
     28 			free(tmp);
     29 		}
     30 	}
     31 	free(ht->nodes);
     32 	free(ht);
     33 }
     34 
     35 /*
     36  * Insert a new node or, if the replace flag is true, replace the value of an existing node.
     37  */
     38 static int
     39 insert_or_replace_val(struct htable *ht, void *key, void *val, int replace)
     40 {
     41 	unsigned int i;
     42 	struct htnode *n, *ln;	/* current and last node */
     43 
     44 	i = ht->hash(key, ht->cap);
     45 	ln = NULL;
     46 	for (n = ht->nodes[i]; n != NULL; n = n->next) {
     47 		/* if key already exist */
     48 		if (ht->key_cmp(key, n->key) == 0) {
     49 			if (replace) {
     50 				ht->val_free(n->val);
     51 				n->val = val;
     52 				return 0;
     53 			} else {
     54 				return -1;
     55 			}
     56 		}
     57 		ln = n;
     58 	}
     59 
     60 	if (replace)	/* failed to replace */
     61 		return -1;
     62 
     63 	if ((n = malloc(sizeof(struct htnode))) == NULL) {
     64 		perror("error: malloc");
     65 		return -1;
     66 	}
     67 	n->key = key;
     68 	n->val = val;
     69 	n->next = NULL;
     70 	/* link to the last node */
     71 	if (ln == NULL)
     72 		ht->nodes[i] = n;
     73 	else
     74 		ln->next = n;
     75 	ht->len++;
     76 	return 0;
     77 }
     78 
     79 /*
     80  * Remove a node or, if the replace flag is true, change the key of the node.
     81  */
     82 static int
     83 remove_or_replace_key(struct htable *ht, void *key, int replace, void *newkey)
     84 {
     85 	unsigned int i;
     86 	struct htnode *n, *ln;	/* current and last node */
     87 
     88 	i = ht->hash(key, ht->cap);
     89 	ln = NULL;
     90 	for (n = ht->nodes[i]; n != NULL; n = n->next) {
     91 		if (ht->key_cmp(key, n->key) == 0) {
     92 			ht->key_free(n->key);
     93 			if (!replace) {
     94 				ht->val_free(n->val);
     95 			} else {
     96 				if (htinsert(ht, newkey, n->val) == -1)
     97 					return -1;
     98 			}
     99 			/* link to the last node */
    100 			if (ln == NULL)
    101 				ht->nodes[i] = n->next;
    102 			else
    103 				ln->next = n->next;
    104 			free(n);
    105 			return 0;
    106 		}
    107 		ln = n;
    108 	}
    109 	return -1;
    110 }
    111 
    112 struct htable *
    113 htcreate(hash_fn *hash, key_cmp_fn *key_cmp, key_free_fn *key_free,
    114 			val_free_fn *val_free, unsigned int cap)
    115 {
    116 	unsigned int i;
    117 	struct htable *ht;
    118 
    119 	if ((ht = malloc(sizeof(struct htable))) == NULL) {
    120 		perror("error: malloc");
    121 		return NULL;
    122 	}
    123 	if ((ht->nodes = malloc(cap * sizeof(struct htnode *))) == NULL) {
    124 		perror("error: malloc");
    125 		return NULL;
    126 	}
    127 	for (i = 0; i < cap; i++)
    128 		ht->nodes[i] = NULL;
    129 
    130 	ht->cap		= cap;
    131 	ht->len		= 0;
    132 	ht->hash	= hash;
    133 	ht->key_cmp	= key_cmp;
    134 	ht->key_free	= key_free;
    135 	ht->val_free	= val_free;
    136 	return ht;
    137 }
    138 
    139 void
    140 htdestroy(struct htable *ht)
    141 {
    142 	destroy_and_free(ht, 1);
    143 }
    144 
    145 void *
    146 htsearch(struct htable *ht, void *key)
    147 {
    148 	unsigned int i;
    149 	struct htnode *n;
    150 
    151 	i = ht->hash(key, ht->cap);
    152 	for (n = ht->nodes[i]; n != NULL; n = n->next) {
    153 		if (ht->key_cmp(key, n->key) == 0)
    154 			return n->val;
    155 	}
    156 	return NULL;
    157 }
    158 
    159 int
    160 htinsert(struct htable *ht, void *key, void *val)
    161 {
    162 	return insert_or_replace_val(ht, key, val, 0);
    163 }
    164 
    165 int
    166 htremove(struct htable *ht, void *key)
    167 {
    168 	return remove_or_replace_key(ht, key, 0, NULL);
    169 }
    170 
    171 int
    172 htmodkey(struct htable *ht, void *oldkey, void *newkey)
    173 {
    174 	return remove_or_replace_key(ht, oldkey, 1, newkey);
    175 }
    176 
    177 int
    178 htmodval(struct htable *ht, void *key, void *newval)
    179 {
    180 	return insert_or_replace_val(ht, key, newval, 1);
    181 }
    182 
    183 struct htable *
    184 htresize(struct htable *ht, unsigned int newcap)
    185 {
    186 	struct htable	 *newht;
    187 	struct htiter	  it;
    188 
    189 	newht = htcreate(ht->hash, ht->key_cmp, ht->key_free, ht->val_free, newcap);
    190 	if (newht == NULL)
    191 		return NULL;
    192 
    193 	htiter_init(&it);
    194 	while(htiterate(ht, &it)) {
    195 		if (htinsert(newht, it.node->key, it.node->val) == -1) {
    196 			htdestroy(newht);
    197 			return NULL;
    198 		}
    199 	}
    200 
    201 	destroy_and_free(ht, 0);
    202 	return newht;
    203 }
    204 
    205 void
    206 htiter_init(struct htiter *it)
    207 {
    208 	it->index = 0;
    209 	it->node = NULL;
    210 }
    211 
    212 int
    213 htiterate(struct htable *ht, struct htiter *it)
    214 {
    215 	if (it->node != NULL)
    216 		it->node = it->node->next;
    217 
    218 	while (it->node == NULL && it->index < ht->cap) {
    219 		it->node = ht->nodes[it->index];
    220 		it->index++;
    221 	}
    222 
    223 	if (it->node != NULL)
    224 		return 1;
    225 	else
    226 		return 0;
    227 }