// Copyright (c) 2020 Magomed Kostoev // // You may use, distribute and modify this code under the terms of the MIT license. // // You should have received a copy of the MIT license with this file. If not, please visit // https://opensource.org/licenses/MIT or boppan.org/MIT for full license details. // cdict.h - simple dictionaty implementation in C. // // Uses hash table with fixed (but configurable) size. Functions named using given type names: // cdict___. // // See configuration below (in public "Input macros" section), also: // CDICT_INST: Instantiate the functions if defined. // // Minimal definitions for declaration: CDICT_KEY_T, CDICT_VAL_T // Minimal definitions for instantiation: CDICT_INST, CDICT_KEY_T, CDICT_VAL_T, CDICT_HASH_FN, // CDICT_CMP_FN // // WARNING: All used definitions will be undefined on header exit. // // Dependencies: // or another source of size_t // or another source of malloc, and calloc // or another source of assert // // Internal macros for external declarations // #define CDICT_CAT2_IMPL(x, y) x ## _ ## y #define CDICT_CAT2(x, y) CDICT_CAT2_IMPL(x, y) /// Creates method name according to CDICT_KEY_T and CDICT_VAL_T #define CDICT_FUN(name) CDICT_CAT2(cdict, CDICT_CAT2(CDICT_KEY_T, CDICT_CAT2(CDICT_VAL_T, name))) #define cdict_init CDICT_FUN(init) #define cdict_init_ud CDICT_FUN(init_ud) #define cdict_init_pud CDICT_FUN(init_pud) #define cdict_add_pp CDICT_FUN(add_pp) #define cdict_add_vv CDICT_FUN(add_vv) #define cdict_get_p CDICT_FUN(get_p) #define cdict_get_v CDICT_FUN(get_v) #define cdict_error_message CDICT_FUN(error_message) #define cdict_hash CDICT_FUN(hash) #define cdict_keycmp CDICT_FUN(keycmp) #define cdict_chain_begin CDICT_FUN(begin) #define cdict_chain_next CDICT_FUN(next) #define CDictItem CDICT_CAT2(CDictItem, CDICT_CAT2(CDICT_KEY_T, CDICT_VAL_T)) #define CDictItem_s CDICT_CAT2(CDictItem_s, CDICT_CAT2(CDICT_KEY_T, CDICT_VAL_T)) #define CDict CDICT_CAT2(CDict, CDICT_CAT2(CDICT_KEY_T, CDICT_VAL_T)) // // Input macros // /// Type of the dictionary's keys, named type only - used in functions names, default: CStr #ifndef CDICT_KEY_T typedef char *CStr; #define CDICT_CSTR_IS_THERE #define CDICT_KEY_T CStr #endif /// Type of the dictionary's values, named type only - used in functions names, default: CStr #ifndef CDICT_VAL_T #ifndef CDICT_CSTR_IS_THERE typedef char *CStr; #endif #define CDICT_VAL_T CStr #endif /// Type of user data #ifndef CDICT_USER_DATA_T #define CDICT_USER_DATA_T void * #endif // // Public interface // #define CDICT_NO_CHECK 0 #define CDICT_REPLACE_EXIST 1 #define CDICT_LEAVE_EXIST 2 #define CDICT_ERR_SUCCESS 0 #define CDICT_ERR_OUT_OF_MEMORY 1 #define CDICT_ERR_THIS_IS_NULL 2 #define CDICT_ERR_HASH_TABLE_IS_NULL 3 #define CDICT_ERR_PKEY0_IS_NULL 4 #define CDICT_ERR_PKEY1_IS_NULL 5 #define CDICT_ERR_PKEY_IS_NULL 6 #define CDICT_ERR_PVAL_IS_NULL 7 typedef struct CDictItem_s { struct CDictItem_s *next_collision; CDICT_KEY_T key; CDICT_VAL_T val; } CDictItem; typedef struct { CDictItem **hash_table; CDICT_USER_DATA_T user_data; int error_code; } CDict; /// Initializes dictionary structure int cdict_init(CDict *s); /// Initializes dictionary structure with non-standard user data int cdict_init_ud(CDict *s, CDICT_USER_DATA_T user_data); /// Initializes dictionary structure with non-standard user data int cdict_init_pud(CDict *s, CDICT_USER_DATA_T *user_data); /// Inserts a value by key (receives pointers to val and key) CDictItem *cdict_add_pp(CDict *s, CDICT_KEY_T *pkey, CDICT_VAL_T *pval, int if_exists); /// Inserts a value by key (receives values of val and key) CDictItem *cdict_add_vv(CDict *s, CDICT_KEY_T key, CDICT_VAL_T val, int if_exists); /// Gives a value by key (receives a pointer to key) CDICT_VAL_T cdict_get_p(CDict *s, CDICT_KEY_T *pkey); /// Gives a vaule by key (receives a value of key) CDICT_VAL_T cdict_get_v(CDict *s, CDICT_KEY_T key); #ifdef CDICT_INST // // Input macros (instantiation edition) // /// The value returned on some error #ifndef CDICT_VAL_DEFAULT #define CDICT_VAL_DEFAULT (CDICT_VAL_T){ 0 } #endif /// Hashing function for the key type #ifndef CDICT_HASH_FN #include #define CDICT_HASH_FN(pkey) strlen(*pkey) ^ (*pkey)[0] #else #define CDICT_HASH_FN_OVERRIDEN #endif /// Ammount of pointers to elements in hash table #ifndef CDICT_HASHTAB_SZ #define CDICT_HASHTAB_SZ 1024 #endif /// Hash table allocator MUST return zeroed buffer #ifndef CDICT_HASHTAB_ALLOC_FN #include #define CDICT_HASHTAB_ALLOC_FN(cdict, size) calloc(1, size) #else #define CDICT_HASHTAB_ALLOCATORS_OVERRIDDEN #endif /// New hash table items allocator (should be just a function call) #ifndef CDICT_HASHTAB_ITEM_ALLOC_FN #include #define CDICT_HASHTAB_ITEM_ALLOC_FN(cdict, size) malloc(size) #define CDICT_HASHTAB_ITEM_FREE_FN(cdict, ptr) free(ptr) #else #ifndef CDICT_HASHTAB_ITEM_FREE_FN #error "CDICT_HASHTAB_ITEM_FREE_FN should be defiend along with CDICT_HASHTAB_ITEM_ALLOC_FN" #endif #define CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN #endif #ifdef CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN #error "FUCK!" #endif /// Replacement for assert from #ifndef CDICT_ASSERT_FN #include #define CDICT_ASSERT_FN(x) if (!x) { printf(__FILE__":%d: Disasserted", __LINE__); } assert(x) #endif /// Function for comparsion of keys, return should be the same as memcmp #ifndef CDICT_CMP_FN #include #define CDICT_CMP_FN(pkey0, pkey1) strcmp(*pkey0, *pkey1) #else #define CDICT_CMP_FN_OVERRIDDEN #endif // // Internal macros (instantiation edition) // #define CDICT_ASSERT(x) ({ CDICT_ASSERT_FN(x); x; }) // Should write the error code into strucure, but should not rewrite it's already set #define CDICT_IF_NULL_SET_ERR_RETURN(x, ec, res) if (x == NULL) { \ if (s->error_code == CDICT_ERR_SUCCESS) s->error_code = ec; \ return res; \ } #define CDICT_IF_NULL_RETURN(x, res) if (x == NULL) return res // // Predeclarations // #ifdef CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN void *CDICT_HASHTAB_ITEM_ALLOC_FN(CDict *s, size_t size); void CDICT_HASHTAB_ITEM_FREE_FN(CDict *s, CDictItem *ptr); #endif #ifdef CDICT_HASHTAB_ALLOCATORS_OVERRIDDEN void *CDICT_HASHTAB_ALLOC_FN(CDict *s, size_t size); #endif #ifdef CDICT_HASH_FN_OVERRIDEN unsigned long CDICT_HASH_FN(CDICT_KEY_T *pkey); #endif #ifdef CDICT_CMP_FN_OVERRIDDEN int CDICT_CMP_FN(CDICT_KEY_T *pkey0, CDICT_KEY_T *pkey1); #endif // // The code // static size_t cdict_hash(CDICT_KEY_T *pkey) { return CDICT_HASH_FN(CDICT_ASSERT(pkey)) & (CDICT_HASHTAB_SZ - 1); } static int cdict_keycmp(CDICT_KEY_T *pkey0, CDICT_KEY_T *pkey1) { return CDICT_CMP_FN(CDICT_ASSERT(pkey0), CDICT_ASSERT(pkey1)); } static CDictItem **cdict_chain_begin(CDict *s, CDICT_KEY_T *pkey) { CDICT_ASSERT(s); size_t hash = cdict_hash(CDICT_ASSERT(pkey)); CDICT_IF_NULL_SET_ERR_RETURN(s->hash_table, CDICT_ERR_HASH_TABLE_IS_NULL, NULL); return &s->hash_table[hash]; } static CDictItem **cdict_chain_next(CDictItem **ppit) { CDICT_ASSERT(ppit); CDICT_ASSERT(*ppit); return &(*ppit)->next_collision; } int cdict_init(CDict *s) { return cdict_init_pud(s, NULL); } int cdict_init_ud(CDict *s, CDICT_USER_DATA_T user_data) { return cdict_init_pud(s, &user_data); } int cdict_init_pud(CDict *s, CDICT_USER_DATA_T *user_data) { CDICT_IF_NULL_RETURN(s, 0); s->user_data = user_data ? *user_data : (CDICT_USER_DATA_T){ 0 }; s->error_code = CDICT_ERR_SUCCESS; s->hash_table = CDICT_HASHTAB_ALLOC_FN(s, sizeof(*s->hash_table) * CDICT_HASHTAB_SZ); CDICT_IF_NULL_SET_ERR_RETURN(s->hash_table, CDICT_ERR_OUT_OF_MEMORY, 0); return 1; } CDictItem *cdict_add_pp(CDict *s, CDICT_KEY_T *pkey, CDICT_VAL_T *pval, int if_exists) { CDICT_IF_NULL_RETURN(s, NULL); CDICT_IF_NULL_SET_ERR_RETURN(pkey, CDICT_ERR_PKEY_IS_NULL, NULL); CDICT_IF_NULL_SET_ERR_RETURN(pval, CDICT_ERR_PVAL_IS_NULL, NULL); CDictItem *next_collision = NULL; CDictItem **ppit = cdict_chain_begin(s, pkey); CDICT_IF_NULL_RETURN(ppit, NULL); while (*ppit) { int exists = if_exists == CDICT_NO_CHECK ? 0 : !cdict_keycmp(pkey, &(*ppit)->key); if (exists) { if (if_exists == CDICT_LEAVE_EXIST) { return *ppit; } else if (if_exists == CDICT_REPLACE_EXIST) { next_collision = (*ppit)->next_collision; CDICT_HASHTAB_ITEM_FREE_FN(s, *ppit); break; } } ppit = cdict_chain_next(ppit); } *ppit = CDICT_HASHTAB_ITEM_ALLOC_FN(s, sizeof(**ppit)); CDictItem *pit = *ppit; pit->key = *pkey; pit->val = *pval; pit->next_collision = next_collision; return pit; } CDictItem *cdict_add_vv(CDict *s, CDICT_KEY_T key, CDICT_VAL_T val, int if_exists) { return cdict_add_pp(s, &key, &val, if_exists); } CDICT_VAL_T cdict_get_p(CDict *s, CDICT_KEY_T *pkey) { CDICT_IF_NULL_RETURN(s, CDICT_VAL_DEFAULT); CDICT_IF_NULL_SET_ERR_RETURN(pkey, CDICT_ERR_PKEY_IS_NULL, CDICT_VAL_DEFAULT); CDictItem **ppit = cdict_chain_begin(s, pkey); CDICT_IF_NULL_RETURN(ppit, CDICT_VAL_DEFAULT); while (*ppit) { if (!cdict_keycmp(pkey, &(*ppit)->key)) { return (*ppit)->val; } ppit = cdict_chain_next(ppit); } return CDICT_VAL_DEFAULT; } CDICT_VAL_T cdict_get_v(CDict *s, CDICT_KEY_T key) { return cdict_get_p(s, &key); } #endif #undef CDICT_CAT2_IMPL #undef CDICT_CAT2 #undef CDICT_FUN #undef cdict_init #undef cdict_init_ud #undef cdict_init_pud #undef cdict_add_pp #undef cdict_add_vv #undef cdict_get_p #undef cdict_get_v #undef CDICT_KEY_T #undef CDICT_VAL_T #undef CDICT_USER_DATA_T #ifdef CDICT_INST #undef CDICT_INST #undef CDICT_HASH_FN #undef CDICT_HASHTAB_SZ #undef CDICT_HASHTAB_ALLOC_FN #undef CDICT_HASHTAB_ITEM_ALLOC_FN #undef CDICT_HASHTAB_ITEM_FREE_FN #undef CDICT_ASSERT_FN #undef CDICT_CMP_FN #undef CDICT_ASSERT #undef CDICT_IF_NULL_RETURN #undef CDICT_IF_NULL_SET_ERR_RETURN #undef CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN #undef CDICT_HASHTAB_ALLOCATORS_OVERRIDDEN #undef CDICT_HASH_FN_OVERRIDEN #undef CDICT_CMP_FN_OVERRIDDEN #endif