dictionary.c 11.6 KB
Newer Older
1 2
/*-------------------------------------------------------------------------*/
/**
3 4 5
   @file    dictionary.c
   @author  N. Devillard
   @brief   Implements a dictionary for string variables.
6 7 8 9 10 11 12 13

   This module implements a simple dictionary object, i.e. a list
   of string/string associations. This object is useful to store e.g.
   informations retrieved from a configuration file (ini files).
*/
/*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
14
                                Includes
15 16 17 18 19 20 21 22 23
 ---------------------------------------------------------------------------*/
#include "dictionary.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/** Maximum value size for integers and doubles. */
24
#define MAXVALSZ    1024
25 26

/** Minimal allocated number of entries in a dictionary */
27
#define DICTMINSZ   128
28 29 30 31 32

/** Invalid key token */
#define DICT_INVALID_KEY    ((char*)-1)

/*---------------------------------------------------------------------------
33
                            Private functions
34 35 36 37 38 39 40 41 42 43 44 45
 ---------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------*/
/**
  @brief    Duplicate a string
  @param    s String to duplicate
  @return   Pointer to a newly allocated string, to be freed with free()

  This is a replacement for strdup(). This implementation is provided
  for systems that do not have it.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
46
static char * xstrdup(const char * s)
47 48
{
    char * t ;
49
    size_t len ;
50 51
    if (!s)
        return NULL ;
52 53

    len = strlen(s) + 1 ;
E
Emmanuel Leblond 已提交
54
    t = (char*) malloc(len) ;
55
    if (t) {
56
        memcpy(t, s, len) ;
57 58 59 60
    }
    return t ;
}

E
Emmanuel Leblond 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
/*-------------------------------------------------------------------------*/
/**
  @brief    Double the size of the dictionary
  @param    d Dictionary to grow
  @return   This function returns non-zero in case of failure
 */
/*--------------------------------------------------------------------------*/
static int dictionary_grow(dictionary * d)
{
    char        ** new_val ;
    char        ** new_key ;
    unsigned     * new_hash ;

    new_val  = (char**) calloc(d->size * 2, sizeof *d->val);
    new_key  = (char**) calloc(d->size * 2, sizeof *d->key);
    new_hash = (unsigned*) calloc(d->size * 2, sizeof *d->hash);
    if (!new_val || !new_key || !new_hash) {
        /* An allocation failed, leave the dictionary unchanged */
        if (new_val)
            free(new_val);
        if (new_key)
            free(new_key);
        if (new_hash)
            free(new_hash);
        return -1 ;
    }
    /* Initialize the newly allocated space */
    memcpy(new_val, d->val, d->size * sizeof(char *));
    memcpy(new_key, d->key, d->size * sizeof(char *));
    memcpy(new_hash, d->hash, d->size * sizeof(unsigned));
    /* Delete previous data */
    free(d->val);
    free(d->key);
    free(d->hash);
    /* Actually update the dictionary */
    d->size *= 2 ;
    d->val = new_val;
    d->key = new_key;
    d->hash = new_hash;
    return 0 ;
}

103
/*---------------------------------------------------------------------------
104
                            Function codes
105 106 107
 ---------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/**
108 109 110
  @brief    Compute the hash key for a string.
  @param    key     Character string to use for key.
  @return   1 unsigned int on at least 32 bits.
111 112 113 114 115 116 117

  This hash function has been taken from an Article in Dr Dobbs Journal.
  This is normally a collision-free function, distributing keys evenly.
  The key is stored anyway in the struct so that collision can be avoided
  by comparing the key itself in last resort.
 */
/*--------------------------------------------------------------------------*/
118
unsigned dictionary_hash(const char * key)
119
{
120
    size_t      len ;
121
    unsigned    hash ;
122
    size_t      i ;
123

E
Emmanuel Leblond 已提交
124 125 126
    if (!key)
        return 0 ;

127 128 129 130 131 132 133 134 135 136
    len = strlen(key);
    for (hash=0, i=0 ; i<len ; i++) {
        hash += (unsigned)key[i] ;
        hash += (hash<<10);
        hash ^= (hash>>6) ;
    }
    hash += (hash <<3);
    hash ^= (hash >>11);
    hash += (hash <<15);
    return hash ;
137 138 139 140
}

/*-------------------------------------------------------------------------*/
/**
141 142 143
  @brief    Create a new dictionary object.
  @param    size    Optional initial size of the dictionary.
  @return   1 newly allocated dictionary objet.
144 145 146 147 148

  This function allocates a new dictionary object of given size and returns
  it. If you do not know in advance (roughly) the number of entries in the
  dictionary, give size=0.
 */
149
/*-------------------------------------------------------------------------*/
150
dictionary * dictionary_new(size_t size)
151
{
152 153 154 155 156
    dictionary  *   d ;

    /* If no size was specified, allocate space for DICTMINSZ */
    if (size<DICTMINSZ) size=DICTMINSZ ;

E
Emmanuel Leblond 已提交
157
    d = (dictionary*) calloc(1, sizeof *d) ;
158 159 160

    if (d) {
        d->size = size ;
E
Emmanuel Leblond 已提交
161 162 163
        d->val  = (char**) calloc(size, sizeof *d->val);
        d->key  = (char**) calloc(size, sizeof *d->key);
        d->hash = (unsigned*) calloc(size, sizeof *d->hash);
164 165
    }
    return d ;
166 167 168 169
}

/*-------------------------------------------------------------------------*/
/**
170 171 172
  @brief    Delete a dictionary object
  @param    d   dictionary object to deallocate.
  @return   void
173 174 175 176 177 178

  Deallocate a dictionary object and all memory associated to it.
 */
/*--------------------------------------------------------------------------*/
void dictionary_del(dictionary * d)
{
E
Emmanuel Leblond 已提交
179
    ssize_t  i ;
180 181

    if (d==NULL) return ;
E
Emmanuel Leblond 已提交
182
    for (i=0 ; i<d->size ; i++) {
183 184 185 186 187 188 189 190 191 192
        if (d->key[i]!=NULL)
            free(d->key[i]);
        if (d->val[i]!=NULL)
            free(d->val[i]);
    }
    free(d->val);
    free(d->key);
    free(d->hash);
    free(d);
    return ;
193 194 195 196
}

/*-------------------------------------------------------------------------*/
/**
197 198 199
  @brief    Get a value from a dictionary.
  @param    d       dictionary object to search.
  @param    key     Key to look for in the dictionary.
200
  @param    def     Default value to return if key not found.
201
  @return   1 pointer to internally allocated character string.
202 203 204 205 206 207 208

  This function locates a key in a dictionary and returns a pointer to its
  value, or the passed 'def' pointer if no such key can be found in
  dictionary. The returned character pointer points to data internal to the
  dictionary object, you should not try to free it or modify it.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
209
const char * dictionary_get(const dictionary * d, const char * key, const char * def)
210
{
211
    unsigned    hash ;
E
Emmanuel Leblond 已提交
212
    ssize_t      i ;
213

214
    hash = dictionary_hash(key);
E
Emmanuel Leblond 已提交
215
    for (i=0 ; i<d->size ; i++) {
216 217 218
        if (d->key[i]==NULL)
            continue ;
        /* Compare hash */
219
        if (hash==d->hash[i]) {
220 221
            /* Compare string, to avoid hash collisions */
            if (!strcmp(key, d->key[i])) {
222 223 224 225 226
                return d->val[i] ;
            }
        }
    }
    return def ;
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Set a value in a dictionary.
  @param    d       dictionary object to modify.
  @param    key     Key to modify or add.
  @param    val     Value to add.
  @return   int     0 if Ok, anything else otherwise

  If the given key is found in the dictionary, the associated value is
  replaced by the provided one. If the key cannot be found in the
  dictionary, it is added to it.

  It is Ok to provide a NULL value for val, but NULL values for the dictionary
  or the key are considered as errors: the function will return immediately
  in such a case.

  Notice that if you dictionary_set a variable to NULL, a call to
  dictionary_get will return a NULL value: the variable will be found, and
  its value (NULL) is returned. In other words, setting the variable
  content to NULL is equivalent to deleting the variable from the
  dictionary. It is not possible (in this implementation) to have a key in
  the dictionary without value.

  This function returns non-zero in case of failure.
 */
/*--------------------------------------------------------------------------*/
255
int dictionary_set(dictionary * d, const char * key, const char * val)
256
{
E
Emmanuel Leblond 已提交
257 258
    ssize_t         i ;
    unsigned       hash ;
259 260

    if (d==NULL || key==NULL) return -1 ;
A
Andrey Mazo 已提交
261

262 263 264 265
    /* Compute hash for this key */
    hash = dictionary_hash(key) ;
    /* Find if value is already in dictionary */
    if (d->n>0) {
E
Emmanuel Leblond 已提交
266
        for (i=0 ; i<d->size ; i++) {
267 268
            if (d->key[i]==NULL)
                continue ;
269 270 271 272 273
            if (hash==d->hash[i]) { /* Same hash value */
                if (!strcmp(key, d->key[i])) {   /* Same key */
                    /* Found a value: modify and return */
                    if (d->val[i]!=NULL)
                        free(d->val[i]);
E
Emmanuel Leblond 已提交
274
                    d->val[i] = (val ? xstrdup(val) : NULL);
275
                    /* Value has been modified: return */
276 277 278 279 280 281 282 283 284
                    return 0 ;
                }
            }
        }
    }
    /* Add a new value */
    /* See if dictionary needs to grow */
    if (d->n==d->size) {
        /* Reached maximum size: reallocate dictionary */
E
Emmanuel Leblond 已提交
285 286
        if (dictionary_grow(d) != 0)
            return -1;
287
    }
288

289 290 291 292 293
    /* Insert key in the first empty slot. Start at d->n and wrap at
       d->size. Because d->n < d->size this will necessarily
       terminate. */
    for (i=d->n ; d->key[i] ; ) {
        if(++i == d->size) i = 0;
294
    }
295 296
    /* Copy key */
    d->key[i]  = xstrdup(key);
E
Emmanuel Leblond 已提交
297
    d->val[i]  = (val ? xstrdup(val) : NULL) ;
298 299 300
    d->hash[i] = hash;
    d->n ++ ;
    return 0 ;
301 302 303 304
}

/*-------------------------------------------------------------------------*/
/**
305 306 307
  @brief    Delete a key in a dictionary
  @param    d       dictionary object to modify.
  @param    key     Key to remove.
308 309 310 311 312 313
  @return   void

  This function deletes a key in a dictionary. Nothing is done if the
  key cannot be found.
 */
/*--------------------------------------------------------------------------*/
314
void dictionary_unset(dictionary * d, const char * key)
315
{
316
    unsigned    hash ;
E
Emmanuel Leblond 已提交
317
    ssize_t      i ;
318

E
Emmanuel Leblond 已提交
319
    if (key == NULL || d == NULL) {
320 321
        return;
    }
322

323
    hash = dictionary_hash(key);
E
Emmanuel Leblond 已提交
324
    for (i=0 ; i<d->size ; i++) {
325 326 327
        if (d->key[i]==NULL)
            continue ;
        /* Compare hash */
328
        if (hash==d->hash[i]) {
329 330 331 332
            /* Compare string, to avoid hash collisions */
            if (!strcmp(key, d->key[i])) {
                /* Found key */
                break ;
333 334 335
            }
        }
    }
E
Emmanuel Leblond 已提交
336
    if (i>=d->size)
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
        /* Key not found */
        return ;

    free(d->key[i]);
    d->key[i] = NULL ;
    if (d->val[i]!=NULL) {
        free(d->val[i]);
        d->val[i] = NULL ;
    }
    d->hash[i] = 0 ;
    d->n -- ;
    return ;
}

/*-------------------------------------------------------------------------*/
/**
353 354 355 356
  @brief    Dump a dictionary to an opened file pointer.
  @param    d   Dictionary to dump
  @param    f   Opened file pointer.
  @return   void
357 358 359 360 361 362

  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
  output file pointers.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
363
void dictionary_dump(const dictionary * d, FILE * out)
364
{
E
Emmanuel Leblond 已提交
365
    ssize_t  i ;
366 367 368 369 370 371

    if (d==NULL || out==NULL) return ;
    if (d->n<1) {
        fprintf(out, "empty dictionary\n");
        return ;
    }
E
Emmanuel Leblond 已提交
372
    for (i=0 ; i<d->size ; i++) {
373 374 375 376 377
        if (d->key[i]) {
            fprintf(out, "%20s\t[%s]\n",
                    d->key[i],
                    d->val[i] ? d->val[i] : "UNDEF");
        }
378 379
    }
    return ;
380
}