Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. // Copyright (c) 2020 Magomed Kostoev
  2. //
  3. // You may use, distribute and modify this code under the terms of the MIT license.
  4. //
  5. // You should have received a copy of the MIT license with this file. If not, please visit
  6. // https://opensource.org/licenses/MIT or boppan.org/MIT for full license details.
  7.  
  8. // cdict.h - simple dictionaty implementation in C.
  9. //
  10. // Uses hash table with fixed (but configurable) size. Functions named using given type names:
  11. // cdict_<CDICT_KEY_T>_<CDICT_VAL_T>_<name>.
  12. //
  13. // See configuration below (in public "Input macros" section), also:
  14. // CDICT_INST: Instantiate the functions if defined.
  15. //
  16. // Minimal definitions for declaration: CDICT_KEY_T, CDICT_VAL_T
  17. // Minimal definitions for instantiation: CDICT_INST, CDICT_KEY_T, CDICT_VAL_T, CDICT_HASH_FN,
  18. // CDICT_CMP_FN
  19. //
  20. // WARNING: All used definitions will be undefined on header exit.
  21. //
  22. // Dependencies:
  23. //  <stddef.h> or another source of size_t
  24. //  <stdlib.h> or another source of malloc, and calloc
  25. //  <assert.h> or another source of assert
  26.  
  27. //
  28. // Internal macros for external declarations
  29. //
  30.  
  31. #define CDICT_CAT2_IMPL(x, y) x ## _ ## y
  32. #define CDICT_CAT2(x, y) CDICT_CAT2_IMPL(x, y)
  33.  
  34. /// Creates method name according to CDICT_KEY_T and CDICT_VAL_T
  35. #define CDICT_FUN(name) CDICT_CAT2(cdict, CDICT_CAT2(CDICT_KEY_T, CDICT_CAT2(CDICT_VAL_T, name)))
  36.  
  37. #define cdict_init CDICT_FUN(init)
  38. #define cdict_init_ud CDICT_FUN(init_ud)
  39. #define cdict_init_pud CDICT_FUN(init_pud)
  40. #define cdict_add_pp CDICT_FUN(add_pp)
  41. #define cdict_add_vv CDICT_FUN(add_vv)
  42. #define cdict_get_p CDICT_FUN(get_p)
  43. #define cdict_get_v CDICT_FUN(get_v)
  44. #define cdict_error_message CDICT_FUN(error_message)
  45.  
  46. #define cdict_hash CDICT_FUN(hash)
  47. #define cdict_keycmp CDICT_FUN(keycmp)
  48. #define cdict_chain_begin CDICT_FUN(begin)
  49. #define cdict_chain_next CDICT_FUN(next)
  50.  
  51. #define CDictItem CDICT_CAT2(CDictItem, CDICT_CAT2(CDICT_KEY_T, CDICT_VAL_T))
  52. #define CDictItem_s CDICT_CAT2(CDictItem_s, CDICT_CAT2(CDICT_KEY_T, CDICT_VAL_T))
  53. #define CDict CDICT_CAT2(CDict, CDICT_CAT2(CDICT_KEY_T, CDICT_VAL_T))
  54.  
  55. //
  56. // Input macros
  57. //
  58.  
  59. /// Type of the dictionary's keys, named type only - used in functions names, default: CStr
  60. #ifndef CDICT_KEY_T
  61. typedef char *CStr;
  62. #define CDICT_CSTR_IS_THERE
  63. #define CDICT_KEY_T CStr
  64. #endif
  65.  
  66. /// Type of the dictionary's values, named type only - used in functions names, default: CStr
  67. #ifndef CDICT_VAL_T
  68. #ifndef CDICT_CSTR_IS_THERE
  69. typedef char *CStr;
  70. #endif
  71. #define CDICT_VAL_T CStr
  72. #endif
  73.  
  74. /// Type of user data
  75. #ifndef CDICT_USER_DATA_T
  76. #define CDICT_USER_DATA_T void *
  77. #endif
  78.  
  79. //
  80. // Public interface
  81. //
  82.  
  83. #define CDICT_NO_CHECK 0
  84. #define CDICT_REPLACE_EXIST 1
  85. #define CDICT_LEAVE_EXIST 2
  86.  
  87. #define CDICT_ERR_SUCCESS 0
  88. #define CDICT_ERR_OUT_OF_MEMORY 1
  89. #define CDICT_ERR_THIS_IS_NULL 2
  90. #define CDICT_ERR_HASH_TABLE_IS_NULL 3
  91. #define CDICT_ERR_PKEY0_IS_NULL 4
  92. #define CDICT_ERR_PKEY1_IS_NULL 5
  93. #define CDICT_ERR_PKEY_IS_NULL 6
  94. #define CDICT_ERR_PVAL_IS_NULL 7
  95.  
  96. typedef struct CDictItem_s {
  97.     struct CDictItem_s *next_collision;
  98.     CDICT_KEY_T key;
  99.     CDICT_VAL_T val;
  100. } CDictItem;
  101.  
  102. typedef struct {
  103.     CDictItem **hash_table;
  104.     CDICT_USER_DATA_T user_data;
  105.     int error_code;
  106. } CDict;
  107.  
  108. /// Initializes dictionary structure
  109. int cdict_init(CDict *s);
  110.  
  111. /// Initializes dictionary structure with non-standard user data
  112. int cdict_init_ud(CDict *s, CDICT_USER_DATA_T user_data);
  113.  
  114. /// Initializes dictionary structure with non-standard user data
  115. int cdict_init_pud(CDict *s, CDICT_USER_DATA_T *user_data);
  116.  
  117. /// Inserts a value by key (receives pointers to val and key)
  118. CDictItem *cdict_add_pp(CDict *s, CDICT_KEY_T *pkey, CDICT_VAL_T *pval, int if_exists);
  119.  
  120. /// Inserts a value by key (receives values of val and key)
  121. CDictItem *cdict_add_vv(CDict *s, CDICT_KEY_T key, CDICT_VAL_T val, int if_exists);
  122.  
  123. /// Gives a value by key (receives a pointer to key)
  124. CDICT_VAL_T cdict_get_p(CDict *s, CDICT_KEY_T *pkey);
  125.  
  126. /// Gives a vaule by key (receives a value of key)
  127. CDICT_VAL_T cdict_get_v(CDict *s, CDICT_KEY_T key);
  128.  
  129. #ifdef CDICT_INST
  130.  
  131. //
  132. // Input macros (instantiation edition)
  133. //
  134.  
  135. /// The value returned on some error
  136. #ifndef CDICT_VAL_DEFAULT
  137. #define CDICT_VAL_DEFAULT (CDICT_VAL_T){ 0 }
  138. #endif
  139.  
  140. /// Hashing function for the key type
  141. #ifndef CDICT_HASH_FN
  142. #include <string.h>
  143. #define CDICT_HASH_FN(pkey) strlen(*pkey) ^ (*pkey)[0]
  144. #else
  145. #define CDICT_HASH_FN_OVERRIDEN
  146. #endif
  147.  
  148. /// Ammount of pointers to elements in hash table
  149. #ifndef CDICT_HASHTAB_SZ
  150. #define CDICT_HASHTAB_SZ 1024
  151. #endif
  152.  
  153. /// Hash table allocator MUST return zeroed buffer
  154. #ifndef CDICT_HASHTAB_ALLOC_FN
  155. #include <stdlib.h>
  156. #define CDICT_HASHTAB_ALLOC_FN(cdict, size) calloc(1, size)
  157. #else
  158. #define CDICT_HASHTAB_ALLOCATORS_OVERRIDDEN
  159. #endif
  160.  
  161. /// New hash table items allocator (should be just a function call)
  162. #ifndef CDICT_HASHTAB_ITEM_ALLOC_FN
  163. #include <stdlib.h>
  164. #define CDICT_HASHTAB_ITEM_ALLOC_FN(cdict, size) malloc(size)
  165. #define CDICT_HASHTAB_ITEM_FREE_FN(cdict, ptr) free(ptr)
  166. #else
  167. #ifndef CDICT_HASHTAB_ITEM_FREE_FN
  168. #error "CDICT_HASHTAB_ITEM_FREE_FN should be defiend along with CDICT_HASHTAB_ITEM_ALLOC_FN"
  169. #endif
  170. #define CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN
  171. #endif
  172.  
  173. #ifdef CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN
  174. #error "FUCK!"
  175. #endif
  176.  
  177. /// Replacement for assert from <assert.h>
  178. #ifndef CDICT_ASSERT_FN
  179. #include <assert.h>
  180. #define CDICT_ASSERT_FN(x) if (!x) { printf(__FILE__":%d: Disasserted", __LINE__); } assert(x)
  181. #endif
  182.  
  183. /// Function for comparsion of keys, return should be the same as memcmp
  184. #ifndef CDICT_CMP_FN
  185. #include <string.h>
  186. #define CDICT_CMP_FN(pkey0, pkey1) strcmp(*pkey0, *pkey1)
  187. #else
  188. #define CDICT_CMP_FN_OVERRIDDEN
  189. #endif
  190.  
  191. //
  192. // Internal macros (instantiation edition)
  193. //
  194.  
  195. #define CDICT_ASSERT(x) ({ CDICT_ASSERT_FN(x); x; })
  196. // Should write the error code into strucure, but should not rewrite it's already set
  197. #define CDICT_IF_NULL_SET_ERR_RETURN(x, ec, res) if (x == NULL) {   \
  198.         if (s->error_code == CDICT_ERR_SUCCESS) s->error_code = ec; \
  199.         return res;                                                 \
  200.     }
  201. #define CDICT_IF_NULL_RETURN(x, res) if (x == NULL) return res
  202.  
  203. //
  204. // Predeclarations
  205. //
  206.  
  207. #ifdef CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN
  208. void *CDICT_HASHTAB_ITEM_ALLOC_FN(CDict *s, size_t size);
  209. void CDICT_HASHTAB_ITEM_FREE_FN(CDict *s, CDictItem *ptr);
  210. #endif
  211.  
  212. #ifdef CDICT_HASHTAB_ALLOCATORS_OVERRIDDEN
  213. void *CDICT_HASHTAB_ALLOC_FN(CDict *s, size_t size);
  214. #endif
  215.  
  216. #ifdef CDICT_HASH_FN_OVERRIDEN
  217. unsigned long CDICT_HASH_FN(CDICT_KEY_T *pkey);
  218. #endif
  219.  
  220. #ifdef CDICT_CMP_FN_OVERRIDDEN
  221. int CDICT_CMP_FN(CDICT_KEY_T *pkey0, CDICT_KEY_T *pkey1);
  222. #endif
  223.  
  224. //
  225. // The code
  226. //
  227.  
  228. static size_t cdict_hash(CDICT_KEY_T *pkey) {
  229.     return CDICT_HASH_FN(CDICT_ASSERT(pkey)) & (CDICT_HASHTAB_SZ - 1);
  230. }
  231.  
  232. static int cdict_keycmp(CDICT_KEY_T *pkey0, CDICT_KEY_T *pkey1) {
  233.     return CDICT_CMP_FN(CDICT_ASSERT(pkey0), CDICT_ASSERT(pkey1));
  234. }
  235.  
  236. static CDictItem **cdict_chain_begin(CDict *s, CDICT_KEY_T *pkey) {
  237.     CDICT_ASSERT(s);
  238.     size_t hash = cdict_hash(CDICT_ASSERT(pkey));
  239.     CDICT_IF_NULL_SET_ERR_RETURN(s->hash_table, CDICT_ERR_HASH_TABLE_IS_NULL, NULL);
  240.     return &s->hash_table[hash];
  241. }
  242.  
  243. static CDictItem **cdict_chain_next(CDictItem **ppit) {
  244.     CDICT_ASSERT(ppit);
  245.     CDICT_ASSERT(*ppit);
  246.     return &(*ppit)->next_collision;  
  247. }
  248.  
  249. int cdict_init(CDict *s) {
  250.     return cdict_init_pud(s, NULL);
  251. }
  252.  
  253. int cdict_init_ud(CDict *s, CDICT_USER_DATA_T user_data) {
  254.     return cdict_init_pud(s, &user_data);
  255. }
  256.  
  257. int cdict_init_pud(CDict *s, CDICT_USER_DATA_T *user_data) {
  258.     CDICT_IF_NULL_RETURN(s, 0);
  259.     s->user_data = user_data ? *user_data : (CDICT_USER_DATA_T){ 0 };
  260.     s->error_code = CDICT_ERR_SUCCESS;
  261.     s->hash_table = CDICT_HASHTAB_ALLOC_FN(s, sizeof(*s->hash_table) * CDICT_HASHTAB_SZ);
  262.     CDICT_IF_NULL_SET_ERR_RETURN(s->hash_table, CDICT_ERR_OUT_OF_MEMORY, 0);
  263.     return 1;
  264. }
  265.  
  266. CDictItem *cdict_add_pp(CDict *s, CDICT_KEY_T *pkey, CDICT_VAL_T *pval, int if_exists) {
  267.     CDICT_IF_NULL_RETURN(s, NULL);
  268.     CDICT_IF_NULL_SET_ERR_RETURN(pkey, CDICT_ERR_PKEY_IS_NULL, NULL);
  269.     CDICT_IF_NULL_SET_ERR_RETURN(pval, CDICT_ERR_PVAL_IS_NULL, NULL);
  270.     CDictItem *next_collision = NULL;
  271.     CDictItem **ppit = cdict_chain_begin(s, pkey);
  272.     CDICT_IF_NULL_RETURN(ppit, NULL);
  273.     while (*ppit) {
  274.         int exists = if_exists == CDICT_NO_CHECK ? 0 : !cdict_keycmp(pkey, &(*ppit)->key);
  275.         if (exists) {
  276.             if (if_exists == CDICT_LEAVE_EXIST) {
  277.                 return *ppit;
  278.             } else if (if_exists == CDICT_REPLACE_EXIST) {
  279.                 next_collision = (*ppit)->next_collision;
  280.                 CDICT_HASHTAB_ITEM_FREE_FN(s, *ppit);
  281.                 break;
  282.             }
  283.         }
  284.         ppit = cdict_chain_next(ppit);
  285.     }
  286.     *ppit = CDICT_HASHTAB_ITEM_ALLOC_FN(s, sizeof(**ppit));
  287.     CDictItem *pit = *ppit;
  288.     pit->key = *pkey;
  289.     pit->val = *pval;
  290.     pit->next_collision = next_collision;
  291.     return pit;
  292. }
  293.  
  294. CDictItem *cdict_add_vv(CDict *s, CDICT_KEY_T key, CDICT_VAL_T val, int if_exists) {
  295.     return cdict_add_pp(s, &key, &val, if_exists);
  296. }
  297.  
  298. CDICT_VAL_T cdict_get_p(CDict *s, CDICT_KEY_T *pkey) {
  299.     CDICT_IF_NULL_RETURN(s, CDICT_VAL_DEFAULT);
  300.     CDICT_IF_NULL_SET_ERR_RETURN(pkey, CDICT_ERR_PKEY_IS_NULL, CDICT_VAL_DEFAULT);
  301.     CDictItem **ppit = cdict_chain_begin(s, pkey);
  302.     CDICT_IF_NULL_RETURN(ppit, CDICT_VAL_DEFAULT);
  303.     while (*ppit) {
  304.         if (!cdict_keycmp(pkey, &(*ppit)->key)) {
  305.             return (*ppit)->val;
  306.         }
  307.         ppit = cdict_chain_next(ppit);
  308.     }
  309.     return CDICT_VAL_DEFAULT;
  310. }
  311.  
  312. CDICT_VAL_T cdict_get_v(CDict *s, CDICT_KEY_T key) {
  313.     return cdict_get_p(s, &key);
  314. }
  315.  
  316. #endif
  317.  
  318. #undef CDICT_CAT2_IMPL
  319. #undef CDICT_CAT2
  320. #undef CDICT_FUN
  321.  
  322. #undef cdict_init
  323. #undef cdict_init_ud
  324. #undef cdict_init_pud
  325. #undef cdict_add_pp
  326. #undef cdict_add_vv
  327. #undef cdict_get_p
  328. #undef cdict_get_v
  329.  
  330. #undef CDICT_KEY_T
  331. #undef CDICT_VAL_T
  332. #undef CDICT_USER_DATA_T
  333.  
  334. #ifdef CDICT_INST
  335. #undef CDICT_INST
  336. #undef CDICT_HASH_FN
  337. #undef CDICT_HASHTAB_SZ
  338. #undef CDICT_HASHTAB_ALLOC_FN
  339. #undef CDICT_HASHTAB_ITEM_ALLOC_FN
  340. #undef CDICT_HASHTAB_ITEM_FREE_FN
  341. #undef CDICT_ASSERT_FN
  342. #undef CDICT_CMP_FN
  343. #undef CDICT_ASSERT
  344. #undef CDICT_IF_NULL_RETURN
  345. #undef CDICT_IF_NULL_SET_ERR_RETURN
  346. #undef CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN
  347. #undef CDICT_HASHTAB_ALLOCATORS_OVERRIDDEN
  348. #undef CDICT_HASH_FN_OVERRIDEN
  349. #undef CDICT_CMP_FN_OVERRIDDEN
  350. #endif
  351.