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