Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /*-------------------------------------------------------------------------*/ |
michael@0 | 2 | /** |
michael@0 | 3 | @file dictionary.c |
michael@0 | 4 | @author N. Devillard |
michael@0 | 5 | @date Sep 2007 |
michael@0 | 6 | @version $Revision: 1.27 $ |
michael@0 | 7 | @brief Implements a dictionary for string variables. |
michael@0 | 8 | |
michael@0 | 9 | This module implements a simple dictionary object, i.e. a list |
michael@0 | 10 | of string/string associations. This object is useful to store e.g. |
michael@0 | 11 | informations retrieved from a configuration file (ini files). |
michael@0 | 12 | */ |
michael@0 | 13 | /*--------------------------------------------------------------------------*/ |
michael@0 | 14 | |
michael@0 | 15 | /* |
michael@0 | 16 | $Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $ |
michael@0 | 17 | $Revision: 1.27 $ |
michael@0 | 18 | */ |
michael@0 | 19 | /*--------------------------------------------------------------------------- |
michael@0 | 20 | Includes |
michael@0 | 21 | ---------------------------------------------------------------------------*/ |
michael@0 | 22 | #include "dictionary.h" |
michael@0 | 23 | |
michael@0 | 24 | #include <stdio.h> |
michael@0 | 25 | #include <stdlib.h> |
michael@0 | 26 | #include <string.h> |
michael@0 | 27 | #ifndef _WIN32 |
michael@0 | 28 | #include <unistd.h> |
michael@0 | 29 | #endif |
michael@0 | 30 | |
michael@0 | 31 | /** Maximum value size for integers and doubles. */ |
michael@0 | 32 | #define MAXVALSZ 1024 |
michael@0 | 33 | |
michael@0 | 34 | /** Minimal allocated number of entries in a dictionary */ |
michael@0 | 35 | #define DICTMINSZ 128 |
michael@0 | 36 | |
michael@0 | 37 | /** Invalid key token */ |
michael@0 | 38 | #define DICT_INVALID_KEY ((char*)-1) |
michael@0 | 39 | |
michael@0 | 40 | /*--------------------------------------------------------------------------- |
michael@0 | 41 | Private functions |
michael@0 | 42 | ---------------------------------------------------------------------------*/ |
michael@0 | 43 | |
michael@0 | 44 | /* Doubles the allocated size associated to a pointer */ |
michael@0 | 45 | /* 'size' is the current allocated size. */ |
michael@0 | 46 | static void * mem_double(void * ptr, int size) |
michael@0 | 47 | { |
michael@0 | 48 | void * newptr ; |
michael@0 | 49 | |
michael@0 | 50 | newptr = calloc(2*size, 1); |
michael@0 | 51 | if (newptr==NULL) { |
michael@0 | 52 | return NULL ; |
michael@0 | 53 | } |
michael@0 | 54 | memcpy(newptr, ptr, size); |
michael@0 | 55 | free(ptr); |
michael@0 | 56 | return newptr ; |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | /*-------------------------------------------------------------------------*/ |
michael@0 | 60 | /** |
michael@0 | 61 | @brief Duplicate a string |
michael@0 | 62 | @param s String to duplicate |
michael@0 | 63 | @return Pointer to a newly allocated string, to be freed with free() |
michael@0 | 64 | |
michael@0 | 65 | This is a replacement for strdup(). This implementation is provided |
michael@0 | 66 | for systems that do not have it. |
michael@0 | 67 | */ |
michael@0 | 68 | /*--------------------------------------------------------------------------*/ |
michael@0 | 69 | static char * xstrdup(char * s) |
michael@0 | 70 | { |
michael@0 | 71 | char * t ; |
michael@0 | 72 | if (!s) |
michael@0 | 73 | return NULL ; |
michael@0 | 74 | t = malloc(strlen(s)+1) ; |
michael@0 | 75 | if (t) { |
michael@0 | 76 | strcpy(t,s); |
michael@0 | 77 | } |
michael@0 | 78 | return t ; |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | /*--------------------------------------------------------------------------- |
michael@0 | 82 | Function codes |
michael@0 | 83 | ---------------------------------------------------------------------------*/ |
michael@0 | 84 | /*-------------------------------------------------------------------------*/ |
michael@0 | 85 | /** |
michael@0 | 86 | @brief Compute the hash key for a string. |
michael@0 | 87 | @param key Character string to use for key. |
michael@0 | 88 | @return 1 unsigned int on at least 32 bits. |
michael@0 | 89 | |
michael@0 | 90 | This hash function has been taken from an Article in Dr Dobbs Journal. |
michael@0 | 91 | This is normally a collision-free function, distributing keys evenly. |
michael@0 | 92 | The key is stored anyway in the struct so that collision can be avoided |
michael@0 | 93 | by comparing the key itself in last resort. |
michael@0 | 94 | */ |
michael@0 | 95 | /*--------------------------------------------------------------------------*/ |
michael@0 | 96 | unsigned dictionary_hash(char * key) |
michael@0 | 97 | { |
michael@0 | 98 | int len ; |
michael@0 | 99 | unsigned hash ; |
michael@0 | 100 | int i ; |
michael@0 | 101 | |
michael@0 | 102 | len = strlen(key); |
michael@0 | 103 | for (hash=0, i=0 ; i<len ; i++) { |
michael@0 | 104 | hash += (unsigned)key[i] ; |
michael@0 | 105 | hash += (hash<<10); |
michael@0 | 106 | hash ^= (hash>>6) ; |
michael@0 | 107 | } |
michael@0 | 108 | hash += (hash <<3); |
michael@0 | 109 | hash ^= (hash >>11); |
michael@0 | 110 | hash += (hash <<15); |
michael@0 | 111 | return hash ; |
michael@0 | 112 | } |
michael@0 | 113 | |
michael@0 | 114 | /*-------------------------------------------------------------------------*/ |
michael@0 | 115 | /** |
michael@0 | 116 | @brief Create a new dictionary object. |
michael@0 | 117 | @param size Optional initial size of the dictionary. |
michael@0 | 118 | @return 1 newly allocated dictionary objet. |
michael@0 | 119 | |
michael@0 | 120 | This function allocates a new dictionary object of given size and returns |
michael@0 | 121 | it. If you do not know in advance (roughly) the number of entries in the |
michael@0 | 122 | dictionary, give size=0. |
michael@0 | 123 | */ |
michael@0 | 124 | /*--------------------------------------------------------------------------*/ |
michael@0 | 125 | dictionary * dictionary_new(int size) |
michael@0 | 126 | { |
michael@0 | 127 | dictionary * d ; |
michael@0 | 128 | |
michael@0 | 129 | /* If no size was specified, allocate space for DICTMINSZ */ |
michael@0 | 130 | if (size<DICTMINSZ) size=DICTMINSZ ; |
michael@0 | 131 | |
michael@0 | 132 | if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) { |
michael@0 | 133 | return NULL; |
michael@0 | 134 | } |
michael@0 | 135 | d->size = size ; |
michael@0 | 136 | d->val = (char **)calloc(size, sizeof(char*)); |
michael@0 | 137 | d->key = (char **)calloc(size, sizeof(char*)); |
michael@0 | 138 | d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); |
michael@0 | 139 | return d ; |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | /*-------------------------------------------------------------------------*/ |
michael@0 | 143 | /** |
michael@0 | 144 | @brief Delete a dictionary object |
michael@0 | 145 | @param d dictionary object to deallocate. |
michael@0 | 146 | @return void |
michael@0 | 147 | |
michael@0 | 148 | Deallocate a dictionary object and all memory associated to it. |
michael@0 | 149 | */ |
michael@0 | 150 | /*--------------------------------------------------------------------------*/ |
michael@0 | 151 | void dictionary_del(dictionary * d) |
michael@0 | 152 | { |
michael@0 | 153 | int i ; |
michael@0 | 154 | |
michael@0 | 155 | if (d==NULL) return ; |
michael@0 | 156 | for (i=0 ; i<d->size ; i++) { |
michael@0 | 157 | if (d->key[i]!=NULL) |
michael@0 | 158 | free(d->key[i]); |
michael@0 | 159 | if (d->val[i]!=NULL) |
michael@0 | 160 | free(d->val[i]); |
michael@0 | 161 | } |
michael@0 | 162 | free(d->val); |
michael@0 | 163 | free(d->key); |
michael@0 | 164 | free(d->hash); |
michael@0 | 165 | free(d); |
michael@0 | 166 | return ; |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | /*-------------------------------------------------------------------------*/ |
michael@0 | 170 | /** |
michael@0 | 171 | @brief Get a value from a dictionary. |
michael@0 | 172 | @param d dictionary object to search. |
michael@0 | 173 | @param key Key to look for in the dictionary. |
michael@0 | 174 | @param def Default value to return if key not found. |
michael@0 | 175 | @return 1 pointer to internally allocated character string. |
michael@0 | 176 | |
michael@0 | 177 | This function locates a key in a dictionary and returns a pointer to its |
michael@0 | 178 | value, or the passed 'def' pointer if no such key can be found in |
michael@0 | 179 | dictionary. The returned character pointer points to data internal to the |
michael@0 | 180 | dictionary object, you should not try to free it or modify it. |
michael@0 | 181 | */ |
michael@0 | 182 | /*--------------------------------------------------------------------------*/ |
michael@0 | 183 | char * dictionary_get(dictionary * d, char * key, char * def) |
michael@0 | 184 | { |
michael@0 | 185 | unsigned hash ; |
michael@0 | 186 | int i ; |
michael@0 | 187 | |
michael@0 | 188 | hash = dictionary_hash(key); |
michael@0 | 189 | for (i=0 ; i<d->size ; i++) { |
michael@0 | 190 | if (d->key[i]==NULL) |
michael@0 | 191 | continue ; |
michael@0 | 192 | /* Compare hash */ |
michael@0 | 193 | if (hash==d->hash[i]) { |
michael@0 | 194 | /* Compare string, to avoid hash collisions */ |
michael@0 | 195 | if (!strcmp(key, d->key[i])) { |
michael@0 | 196 | return d->val[i] ; |
michael@0 | 197 | } |
michael@0 | 198 | } |
michael@0 | 199 | } |
michael@0 | 200 | return def ; |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | /*-------------------------------------------------------------------------*/ |
michael@0 | 204 | /** |
michael@0 | 205 | @brief Set a value in a dictionary. |
michael@0 | 206 | @param d dictionary object to modify. |
michael@0 | 207 | @param key Key to modify or add. |
michael@0 | 208 | @param val Value to add. |
michael@0 | 209 | @return int 0 if Ok, anything else otherwise |
michael@0 | 210 | |
michael@0 | 211 | If the given key is found in the dictionary, the associated value is |
michael@0 | 212 | replaced by the provided one. If the key cannot be found in the |
michael@0 | 213 | dictionary, it is added to it. |
michael@0 | 214 | |
michael@0 | 215 | It is Ok to provide a NULL value for val, but NULL values for the dictionary |
michael@0 | 216 | or the key are considered as errors: the function will return immediately |
michael@0 | 217 | in such a case. |
michael@0 | 218 | |
michael@0 | 219 | Notice that if you dictionary_set a variable to NULL, a call to |
michael@0 | 220 | dictionary_get will return a NULL value: the variable will be found, and |
michael@0 | 221 | its value (NULL) is returned. In other words, setting the variable |
michael@0 | 222 | content to NULL is equivalent to deleting the variable from the |
michael@0 | 223 | dictionary. It is not possible (in this implementation) to have a key in |
michael@0 | 224 | the dictionary without value. |
michael@0 | 225 | |
michael@0 | 226 | This function returns non-zero in case of failure. |
michael@0 | 227 | */ |
michael@0 | 228 | /*--------------------------------------------------------------------------*/ |
michael@0 | 229 | int dictionary_set(dictionary * d, char * key, char * val) |
michael@0 | 230 | { |
michael@0 | 231 | int i ; |
michael@0 | 232 | unsigned hash ; |
michael@0 | 233 | |
michael@0 | 234 | if (d==NULL || key==NULL) return -1 ; |
michael@0 | 235 | |
michael@0 | 236 | /* Compute hash for this key */ |
michael@0 | 237 | hash = dictionary_hash(key) ; |
michael@0 | 238 | /* Find if value is already in dictionary */ |
michael@0 | 239 | if (d->n>0) { |
michael@0 | 240 | for (i=0 ; i<d->size ; i++) { |
michael@0 | 241 | if (d->key[i]==NULL) |
michael@0 | 242 | continue ; |
michael@0 | 243 | if (hash==d->hash[i]) { /* Same hash value */ |
michael@0 | 244 | if (!strcmp(key, d->key[i])) { /* Same key */ |
michael@0 | 245 | /* Found a value: modify and return */ |
michael@0 | 246 | if (d->val[i]!=NULL) |
michael@0 | 247 | free(d->val[i]); |
michael@0 | 248 | d->val[i] = val ? xstrdup(val) : NULL ; |
michael@0 | 249 | /* Value has been modified: return */ |
michael@0 | 250 | return 0 ; |
michael@0 | 251 | } |
michael@0 | 252 | } |
michael@0 | 253 | } |
michael@0 | 254 | } |
michael@0 | 255 | /* Add a new value */ |
michael@0 | 256 | /* See if dictionary needs to grow */ |
michael@0 | 257 | if (d->n==d->size) { |
michael@0 | 258 | |
michael@0 | 259 | /* Reached maximum size: reallocate dictionary */ |
michael@0 | 260 | d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ; |
michael@0 | 261 | d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ; |
michael@0 | 262 | d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; |
michael@0 | 263 | if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) { |
michael@0 | 264 | /* Cannot grow dictionary */ |
michael@0 | 265 | return -1 ; |
michael@0 | 266 | } |
michael@0 | 267 | /* Double size */ |
michael@0 | 268 | d->size *= 2 ; |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | /* Insert key in the first empty slot */ |
michael@0 | 272 | for (i=0 ; i<d->size ; i++) { |
michael@0 | 273 | if (d->key[i]==NULL) { |
michael@0 | 274 | /* Add key here */ |
michael@0 | 275 | break ; |
michael@0 | 276 | } |
michael@0 | 277 | } |
michael@0 | 278 | /* Copy key */ |
michael@0 | 279 | d->key[i] = xstrdup(key); |
michael@0 | 280 | d->val[i] = val ? xstrdup(val) : NULL ; |
michael@0 | 281 | d->hash[i] = hash; |
michael@0 | 282 | d->n ++ ; |
michael@0 | 283 | return 0 ; |
michael@0 | 284 | } |
michael@0 | 285 | |
michael@0 | 286 | /*-------------------------------------------------------------------------*/ |
michael@0 | 287 | /** |
michael@0 | 288 | @brief Delete a key in a dictionary |
michael@0 | 289 | @param d dictionary object to modify. |
michael@0 | 290 | @param key Key to remove. |
michael@0 | 291 | @return void |
michael@0 | 292 | |
michael@0 | 293 | This function deletes a key in a dictionary. Nothing is done if the |
michael@0 | 294 | key cannot be found. |
michael@0 | 295 | */ |
michael@0 | 296 | /*--------------------------------------------------------------------------*/ |
michael@0 | 297 | void dictionary_unset(dictionary * d, char * key) |
michael@0 | 298 | { |
michael@0 | 299 | unsigned hash ; |
michael@0 | 300 | int i ; |
michael@0 | 301 | |
michael@0 | 302 | if (key == NULL) { |
michael@0 | 303 | return; |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | hash = dictionary_hash(key); |
michael@0 | 307 | for (i=0 ; i<d->size ; i++) { |
michael@0 | 308 | if (d->key[i]==NULL) |
michael@0 | 309 | continue ; |
michael@0 | 310 | /* Compare hash */ |
michael@0 | 311 | if (hash==d->hash[i]) { |
michael@0 | 312 | /* Compare string, to avoid hash collisions */ |
michael@0 | 313 | if (!strcmp(key, d->key[i])) { |
michael@0 | 314 | /* Found key */ |
michael@0 | 315 | break ; |
michael@0 | 316 | } |
michael@0 | 317 | } |
michael@0 | 318 | } |
michael@0 | 319 | if (i>=d->size) |
michael@0 | 320 | /* Key not found */ |
michael@0 | 321 | return ; |
michael@0 | 322 | |
michael@0 | 323 | free(d->key[i]); |
michael@0 | 324 | d->key[i] = NULL ; |
michael@0 | 325 | if (d->val[i]!=NULL) { |
michael@0 | 326 | free(d->val[i]); |
michael@0 | 327 | d->val[i] = NULL ; |
michael@0 | 328 | } |
michael@0 | 329 | d->hash[i] = 0 ; |
michael@0 | 330 | d->n -- ; |
michael@0 | 331 | return ; |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | /*-------------------------------------------------------------------------*/ |
michael@0 | 335 | /** |
michael@0 | 336 | @brief Dump a dictionary to an opened file pointer. |
michael@0 | 337 | @param d Dictionary to dump |
michael@0 | 338 | @param f Opened file pointer. |
michael@0 | 339 | @return void |
michael@0 | 340 | |
michael@0 | 341 | Dumps a dictionary onto an opened file pointer. Key pairs are printed out |
michael@0 | 342 | as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as |
michael@0 | 343 | output file pointers. |
michael@0 | 344 | */ |
michael@0 | 345 | /*--------------------------------------------------------------------------*/ |
michael@0 | 346 | void dictionary_dump(dictionary * d, FILE * out) |
michael@0 | 347 | { |
michael@0 | 348 | int i ; |
michael@0 | 349 | |
michael@0 | 350 | if (d==NULL || out==NULL) return ; |
michael@0 | 351 | if (d->n<1) { |
michael@0 | 352 | fprintf(out, "empty dictionary\n"); |
michael@0 | 353 | return ; |
michael@0 | 354 | } |
michael@0 | 355 | for (i=0 ; i<d->size ; i++) { |
michael@0 | 356 | if (d->key[i]) { |
michael@0 | 357 | fprintf(out, "%20s\t[%s]\n", |
michael@0 | 358 | d->key[i], |
michael@0 | 359 | d->val[i] ? d->val[i] : "UNDEF"); |
michael@0 | 360 | } |
michael@0 | 361 | } |
michael@0 | 362 | return ; |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | |
michael@0 | 366 | /* Test code */ |
michael@0 | 367 | #ifdef TESTDIC |
michael@0 | 368 | #define NVALS 20000 |
michael@0 | 369 | int main(int argc, char *argv[]) |
michael@0 | 370 | { |
michael@0 | 371 | dictionary * d ; |
michael@0 | 372 | char * val ; |
michael@0 | 373 | int i ; |
michael@0 | 374 | char cval[90] ; |
michael@0 | 375 | |
michael@0 | 376 | /* Allocate dictionary */ |
michael@0 | 377 | printf("allocating...\n"); |
michael@0 | 378 | d = dictionary_new(0); |
michael@0 | 379 | |
michael@0 | 380 | /* Set values in dictionary */ |
michael@0 | 381 | printf("setting %d values...\n", NVALS); |
michael@0 | 382 | for (i=0 ; i<NVALS ; i++) { |
michael@0 | 383 | sprintf(cval, "%04d", i); |
michael@0 | 384 | dictionary_set(d, cval, "salut"); |
michael@0 | 385 | } |
michael@0 | 386 | printf("getting %d values...\n", NVALS); |
michael@0 | 387 | for (i=0 ; i<NVALS ; i++) { |
michael@0 | 388 | sprintf(cval, "%04d", i); |
michael@0 | 389 | val = dictionary_get(d, cval, DICT_INVALID_KEY); |
michael@0 | 390 | if (val==DICT_INVALID_KEY) { |
michael@0 | 391 | printf("cannot get value for key [%s]\n", cval); |
michael@0 | 392 | } |
michael@0 | 393 | } |
michael@0 | 394 | printf("unsetting %d values...\n", NVALS); |
michael@0 | 395 | for (i=0 ; i<NVALS ; i++) { |
michael@0 | 396 | sprintf(cval, "%04d", i); |
michael@0 | 397 | dictionary_unset(d, cval); |
michael@0 | 398 | } |
michael@0 | 399 | if (d->n != 0) { |
michael@0 | 400 | printf("error deleting values\n"); |
michael@0 | 401 | } |
michael@0 | 402 | printf("deallocating...\n"); |
michael@0 | 403 | dictionary_del(d); |
michael@0 | 404 | return 0 ; |
michael@0 | 405 | } |
michael@0 | 406 | #endif |
michael@0 | 407 | /* vim: set ts=4 et sw=4 tw=75 */ |