PageRenderTime 56ms CodeModel.GetById 14ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 1ms

/ngtemplate/libuseful/hashtable.c

https://bitbucket.org/questor/mongoose-single-threaded
C | 258 lines | 145 code | 44 blank | 69 comment | 28 complexity | ae5166193cad60118c51f6f61698be1d MD5 | raw file
  1/**
  2 * Hashtable implementation
  3 */
  4
  5#include <stdlib.h>
  6#include <string.h>
  7
  8#include "hashtable.h"
  9
 10typedef struct hashtable_iter_tag   {
 11    hashtable* ht;
 12
 13    int current_bucket;
 14    list_element* current;
 15} hashtable_iter_impl;
 16
 17// Prototypes for some default functions if none other are given
 18int hashpjw(const void* key);
 19int matchstr(const void* key1, const void* key2);
 20
 21/**
 22 * Initializes the given hashtable, with the given number of buckets to hold values.  Takes
 23 * pointers to functions for:
 24 *   h: The hash function.  If NULL, P.J. Weinberger's string hash function from the Dragon Book
 25 *      will be used
 26 *   match: The match function.  Used to determine if two keys match.  If NULL, !strcmp will be 
 27 *      used
 28 *   destroy: The function to call when the value data of a key needs to be cleaned-up.  Pass in 0
 29 *      if you wish to take care of this yourself, or you could pass in free() if the objects are simple
 30 *
 31 * Returns 0 if the hashtable was initialized successfully, -1 otherwise    
 32 */
 33int ht_init(hashtable* ht, int buckets, int (*h)(const void* key), 
 34    int (*match)(const void* key1, const void* key2), 
 35    void (*destroy)(void *data))    {
 36    
 37    int i;  
 38    
 39    if ((ht->table = (list*)malloc(buckets * sizeof(list))) == 0)   {
 40        return -1;
 41    }
 42    
 43    ht->buckets = buckets;
 44    for (i = 0; i < ht->buckets; i++)   {
 45        list_init(&ht->table[i], destroy);
 46    }
 47
 48    ht->h       = h? h : ht_hashpjw;
 49    ht->match   = match? match : matchstr;
 50    ht->destroy = destroy;
 51    ht->size = 0;
 52    
 53    return 0;
 54}
 55    
 56/** 
 57 * Destroys the given hashtable, calling the user-supplied "destroy" function on each value in the hash
 58 */
 59void ht_destroy(hashtable* ht)  {
 60    int i;
 61    
 62    for (i = 0; i < ht->buckets; i++)   {
 63        list_destroy(&ht->table[i]);
 64    }
 65    
 66    free(ht->table);
 67    memset(ht, 0, sizeof(hashtable));
 68}
 69
 70/**
 71 * Inserts a new value in the given hashtable
 72 *
 73 * Returns 0 if instering the element was successful, 1 if the element was already in the table, 
 74 * -1 if there was a problem
 75 */
 76int ht_insert(hashtable* ht, const void* data)  {
 77    void* temp;
 78    int bucket, retval;
 79    
 80    temp = (void*)data;
 81    if (ht_lookup(ht, &temp) == 0)  {
 82        // Do nothing, return 1 to signify that the element was already in the table
 83        return 1;
 84    }
 85    
 86    // Insert the data into the proper bucket
 87    bucket = ht->h(data) % ht->buckets;
 88    if ((retval = list_insert_next(&ht->table[bucket], NULL, data)) == 0)   {
 89        ht->size++;
 90    }
 91    
 92    return retval;
 93}
 94
 95/**
 96 * Removes an element from the given hashtable.  If successful, data contains a pointer to the data 
 97 * removed.  It is up to the caller to further manage this data.
 98 *
 99 * 0 if removing the element was successful, -1 otherwise
100 */
101int ht_remove(hashtable* ht, void** data)   {
102    list_element *element, *prev;
103    
104    int bucket;
105    
106    // Hash the key, then search for the data in the given bucket
107    bucket = ht->h(*data) % ht->buckets;
108    prev = 0;
109    for (element = list_head(&ht->table[bucket]); element != 0; element = list_next(element))   {
110        if (ht->match(*data, list_data(element)))   {
111            // Found it, remove it
112            if (list_remove_next(&ht->table[bucket], prev, data) == 0)  {
113                ht->size--;
114                return 0;
115            }
116        }
117        
118        prev = element;
119    }
120    
121    // Data not found
122    return -1;
123}
124
125/**
126 * Determines whether an element matches the given data in the hashtable.  If so, data points to 
127 * the matched value in the hashtable
128 *
129 * Returns 0 if a match was found in the hashtable, -1 otherwise
130 */
131int ht_lookup(hashtable* ht, void** data)   {
132    list_element *element;
133    int bucket;
134    
135    // Hash the key, then search for the data in the bucket
136    bucket = ht->h(*data) % ht->buckets;
137    for (element = list_head(&ht->table[bucket]); element != 0; element = list_next(element))   {
138        if (ht->match(*data, list_data(element)))   {
139            // We found it
140            *data = list_data(element);
141            return 0;
142        }
143    }
144    
145    // Data not found
146    return -1;
147}
148
149/**
150 * Begin iterating through the hashtable
151 *
152 * Returns an iterator representing the first item in the table, or NULL if the table is empty
153 */
154hashtable_iter* ht_iter_begin(hashtable* ht)    {
155    hashtable_iter_impl* hi;
156    list_element* elem;
157    int i;
158    
159    hi = 0;
160    elem = 0;
161    for (i = 0; i < ht->buckets; i++)   {
162        elem = list_head(&ht->table[i]);
163        if (elem)   {
164            break;
165        }
166    }
167    
168    if (elem)   {
169        hi = (hashtable_iter_impl*)malloc(sizeof(hashtable_iter_impl));
170        hi->ht = ht;
171        hi->current_bucket = i;
172        hi->current = elem;
173    }
174    
175    return hi;
176}
177
178/**
179 * Given the current iterator, return an iterator representing the next item in the hashtable
180 * NOTE: Do NOT assume that the iterator passed in to "current" is still valid after this call!
181 *       Also, items in the hashtable are NOT guaranteed to be iterated in any particular order
182 *
183 * Returns NULL if we have reached the end of the list
184 */
185hashtable_iter* ht_iter_next(hashtable_iter* current)   {
186    hashtable_iter_impl* cur;
187    list_element* elem;
188    int i;
189    
190    if (!current)   {
191        return 0;
192    }
193    
194    cur = (hashtable_iter_impl*)current;
195    i = cur->current_bucket;
196    elem = cur->current->next;
197    if (!elem)  {
198        for (i = cur->current_bucket+1; i < cur->ht->buckets; i++)  {
199            elem = list_head(&cur->ht->table[i]);
200            if (elem)   {
201                break;
202            }
203        }
204    }
205    
206    if (elem)   {
207        cur->current_bucket = i;
208        cur->current = elem; 
209    } else {
210        free(cur);
211        cur = 0;
212    }
213    
214    return cur;
215}
216
217/**
218 * Returns the value of the given iterator
219 */
220void* ht_value(hashtable_iter* iter)    {
221    return ((hashtable_iter_impl*)iter)->current->data;
222}
223
224/**
225 * The default hashing function - works great for strings.  This was taken from the venerable
226 * Dragon Book and Algorithms With C and was created by P.J. Weinberger
227 *
228 */
229int ht_hashpjw(const void* key) {
230    const char* ptr;
231    unsigned int val;
232    
233    // Hash the key by performing some bit manipulation on it
234    val = 0;
235    ptr = key;
236    
237    while (*ptr != '\0')    {
238        unsigned int tmp;
239        
240        val = (val << 4) + (*ptr);
241        
242        if (tmp = (val & 0xf0000000))   {
243            val = val ^ (tmp >> 24);
244            val = val ^ tmp;
245        }
246        
247        ptr++;
248    }
249    
250    return val;
251}
252
253/**
254 * The default compare function.  Just a strcmp
255 */ 
256int matchstr(const void* key1, const void* key2)    {
257    return !strcmp((const char*)key1, (const char*)key2);
258}