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