dictionary.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /* Copyright (c) 2000-2007 by Nicolas Devillard.
  2. * Copyright (x) 2009 by Tim Post <tinkertim@gmail.com>
  3. * MIT License
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a
  6. * copy of this software and associated documentation files (the "Software"),
  7. * to deal in the Software without restriction, including without limitation
  8. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. * and/or sell copies of the Software, and to permit persons to whom the
  10. * Software is furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21. * DEALINGS IN THE SOFTWARE.
  22. */
  23. /** @addtogroup ciniparser
  24. * @{
  25. */
  26. /**
  27. * @file dictionary.c
  28. * @author N. Devillard
  29. * @date Sep 2007
  30. * @version $Revision: 1.27 $
  31. * @brief Implements a dictionary for string variables.
  32. *
  33. * This module implements a simple dictionary object, i.e. a list
  34. * of string/string associations. This object is useful to store e.g.
  35. * informations retrieved from a configuration file (ini files).
  36. */
  37. #include "dictionary.h"
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <unistd.h>
  42. /** Maximum value size for integers and doubles. */
  43. #define MAXVALSZ 1024
  44. /** Minimal allocated number of entries in a dictionary */
  45. #define DICTMINSZ 128
  46. /** Invalid key token */
  47. #define DICT_INVALID_KEY ((char*)-1)
  48. /**
  49. * @brief Double the allocated size associated to a pointer
  50. * @param size the current allocated size
  51. * @return re-allocated pointer on success, NULL on failure
  52. */
  53. static void *mem_double(void *ptr, int size)
  54. {
  55. void *newptr;
  56. newptr = calloc(2 * size, 1);
  57. if (newptr == NULL) {
  58. return NULL;
  59. }
  60. memcpy(newptr, ptr, size);
  61. free(ptr);
  62. return newptr;
  63. }
  64. /* The remaining exposed functions are documented in dictionary.h */
  65. unsigned dictionary_hash(char *key)
  66. {
  67. int len;
  68. unsigned hash;
  69. int i;
  70. len = strlen(key);
  71. for (hash = 0, i = 0; i < len; i++) {
  72. hash += (unsigned) key[i];
  73. hash += (hash << 10);
  74. hash ^= (hash >> 6);
  75. }
  76. hash += (hash << 3);
  77. hash ^= (hash >> 11);
  78. hash += (hash << 15);
  79. return hash;
  80. }
  81. dictionary *dictionary_new(int size)
  82. {
  83. dictionary *d;
  84. /* If no size was specified, allocate space for DICTMINSZ */
  85. if (size<DICTMINSZ) size=DICTMINSZ;
  86. if (!(d = (dictionary *) calloc(1, sizeof(dictionary)))) {
  87. return NULL;
  88. }
  89. d->size = size;
  90. d->val = (char **) calloc(size, sizeof(char *));
  91. d->key = (char **) calloc(size, sizeof(char *));
  92. d->hash = (unsigned int *) calloc(size, sizeof(unsigned));
  93. return d;
  94. }
  95. void dictionary_del(dictionary *d)
  96. {
  97. int i;
  98. if (d == NULL)
  99. return;
  100. for (i = 0; i < d->size; i++) {
  101. if (d->key[i] != NULL)
  102. free(d->key[i]);
  103. if (d->val[i] != NULL)
  104. free(d->val[i]);
  105. }
  106. free(d->val);
  107. free(d->key);
  108. free(d->hash);
  109. free(d);
  110. return;
  111. }
  112. char *dictionary_get(dictionary *d, char *key, char *def)
  113. {
  114. unsigned hash;
  115. int i;
  116. hash = dictionary_hash(key);
  117. for (i=0; i < d->size; i++) {
  118. if (d->key[i] == NULL)
  119. continue;
  120. /* Compare hash */
  121. if (hash == d->hash[i]) {
  122. /* Compare string, to avoid hash collisions */
  123. if (!strcmp(key, d->key[i])) {
  124. return d->val[i];
  125. }
  126. }
  127. }
  128. return def;
  129. }
  130. int dictionary_set(dictionary *d, char *key, char *val)
  131. {
  132. int i;
  133. unsigned hash;
  134. if (d==NULL || key==NULL)
  135. return -1;
  136. /* Compute hash for this key */
  137. hash = dictionary_hash(key);
  138. /* Find if value is already in dictionary */
  139. if (d->n > 0) {
  140. for (i = 0; i < d->size; i++) {
  141. if (d->key[i] == NULL)
  142. continue;
  143. /* Same hash value */
  144. if (hash == d->hash[i]) {
  145. /* Same key */
  146. if (!strcmp(key, d->key[i])) {
  147. /* Found a value: modify and return */
  148. if (d->val[i] != NULL)
  149. free(d->val[i]);
  150. d->val[i] = val ? strdup(val) : NULL;
  151. /* Value has been modified: return */
  152. return 0;
  153. }
  154. }
  155. }
  156. }
  157. /* Add a new value
  158. * See if dictionary needs to grow */
  159. if (d->n == d->size) {
  160. /* Reached maximum size: reallocate dictionary */
  161. d->val = (char **) mem_double(d->val, d->size * sizeof(char *));
  162. d->key = (char **) mem_double(d->key, d->size * sizeof(char *));
  163. d->hash = (unsigned int *)
  164. mem_double(d->hash, d->size * sizeof(unsigned));
  165. if ((d->val == NULL) || (d->key == NULL) || (d->hash == NULL))
  166. /* Cannot grow dictionary */
  167. return -1;
  168. /* Double size */
  169. d->size *= 2;
  170. }
  171. /* Insert key in the first empty slot */
  172. for (i = 0; i < d->size; i++) {
  173. if (d->key[i] == NULL) {
  174. /* Add key here */
  175. break;
  176. }
  177. }
  178. /* Copy key */
  179. d->key[i] = strdup(key);
  180. d->val[i] = val ? strdup(val) : NULL;
  181. d->hash[i] = hash;
  182. d->n ++;
  183. return 0;
  184. }
  185. void dictionary_unset(dictionary *d, char *key)
  186. {
  187. unsigned hash;
  188. int i;
  189. if (key == NULL)
  190. return;
  191. hash = dictionary_hash(key);
  192. for (i = 0; i < d->size; i++) {
  193. if (d->key[i] == NULL)
  194. continue;
  195. /* Compare hash */
  196. if (hash == d->hash[i]) {
  197. /* Compare string, to avoid hash collisions */
  198. if (!strcmp(key, d->key[i])) {
  199. /* Found key */
  200. break;
  201. }
  202. }
  203. }
  204. if (i >= d->size)
  205. /* Key not found */
  206. return;
  207. free(d->key[i]);
  208. d->key[i] = NULL;
  209. if (d->val[i]!=NULL) {
  210. free(d->val[i]);
  211. d->val[i] = NULL;
  212. }
  213. d->hash[i] = 0;
  214. d->n --;
  215. return;
  216. }
  217. void dictionary_dump(dictionary *d, FILE *out)
  218. {
  219. int i;
  220. if (d == NULL || out == NULL)
  221. return;
  222. if (d->n < 1) {
  223. fprintf(out, "empty dictionary\n");
  224. return;
  225. }
  226. for (i = 0; i < d->size; i++) {
  227. if (d->key[i]) {
  228. fprintf(out, "%20s\t[%s]\n",
  229. d->key[i],
  230. d->val[i] ? d->val[i] : "UNDEF");
  231. }
  232. }
  233. return;
  234. }
  235. /** @}
  236. */