4
\$\begingroup\$

I'm trying to create a general purpose C dictionary (as an opaque data type) capable of dynamically allocating basic types (ints, floats, strings and longs) as well as accepting void pointers to already allocated custom types. It works as far as my testing shows but I question the basic architecture and can't help but to feel like it's missing something or that I may be overlooking something that's very obviously wrong with it. Is there a better way to go about doing this?

Note: mem_free() and mem_alloc() are simple malloc/free wrappers and serialize_type does what it says.

dictionary.c

#include "dictionary.h"

#define DICT_VOID_PTR_TYPE      0
#define DICT_FUNC_PTR_TYPE      1
#define DICT_INT_TYPE           5
#define DICT_LONG_TYPE          6
#define DICT_FLOAT_TYPE         7
#define DICT_STR_TYPE           8

#define DICT_MAX_KEY_LEN        32

#define SERIALIZE_IN            100
#define SERIALIZE_OUT           101


typedef struct dict_node_s{
    int_t              type;
    int_t              alloc_size;
    char_t             *key;
    void               *value;
    struct dict_node_s *next;
}dict_node_t;


struct dictionary{
    dict_node_t  **entries;
    int_t        size;
    int_t        cnt;
};


uint_t dict_hash(dict_t *dict, char_t *s)
{
    uint_t hash_val;
    for (hash_val = 0; *s != '\0'; s++)
        hash_val = *s + 31 * hash_val;
    return hash_val % (dict->size);
}


void dict_clr(dict_t *dict)
{
    int_t cntr = 0;
    for (int_t i = 0; i < dict->size; i++){
        dict_node_t *n = dict->entries[i];
        while (n != NULL) {
            dict_node_t *next = n->next;
            if (n->alloc_size > 0)
                mem_free(n->value);

            mem_free(n->key);
            mem_free(n);

            cntr++;
            n = next;
        }
        dict->entries[i] = NULL;
    }
    dict->cnt -= cntr;
}


bool_t dict_remove(dict_t *dict, char_t *key)
{
    dict_node_t *prev = NULL;
    uint_t       hash = dict_hash(dict, key);

    for (dict_node_t *np = dict->entries[hash]; np != NULL; np = np->next){
        if (strcmp(key, np->key) == 0){
            dict_node_t *next = np->next;

            if (np->alloc_size > 0){
                mem_free(np->value);
            }
            mem_free(np->key);
            mem_free(np);

            if (prev != NULL)
                prev->next = next;
            else if (next != NULL)
                dict->entries[hash] = next;
            else
                dict->entries[hash] = NULL;
            dict->cnt--;
            return TRUE;
        }
        prev = np;
    }
    return FALSE;
}


int_t dict_size(dict_t *dict)
{
    if (dict != NULL){
        return dict->cnt;
    }
    return 0;
}

dict_t *dict_init(int_t size)
{
    dict_t *dict = mem_alloc(sizeof(dict_t));

    ASSERT(dict != NULL);
    dict->size = size;
    dict->cnt  = 0;
    dict->entries = mem_alloc(size * sizeof(dict_node_t*));
    for (int_t i = 0; i < size; i++){
        dict->entries[i] = NULL;
    }
    return dict;
}



void serialize_dict(dict_t *dict, FILE *fp, int_t kind)
{
    ASSERT(dict != NULL);

    if (kind == SERIALIZE_OUT){
        serialize_int(&dict->size, fp, kind);
        serialize_int(&dict->cnt, fp, kind);

        for (int_t i = 0; i < dict->size; i++){
            for (dict_node_t *node = dict->entries[i]; node != NULL; node = node->next){
                serialize_int(&node->type, fp, kind);
                serialize_int(&node->alloc_size, fp, kind);
                for (int_t i = 0; i < DICT_MAX_KEY_LEN; i++){
                    serialize_char(&node->key[i], fp, kind);
                }
                switch (node->type) {
                    case DICT_INT_TYPE:
                        serialize_int((int_t*)node->value, fp, kind);
                        break;
                    case DICT_FLOAT_TYPE:
                        serialize_float((float_t*)node->value, fp, kind);
                        break;
                    case DICT_LONG_TYPE:
                        serialize_long((long_t*)node->value, fp, kind);
                        break;
                    case DICT_STR_TYPE:
                        for (int_t i = 0; i < node->alloc_size; i++){
                            serialize_char((char_t*)&node->value[i], fp, kind);
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    }
    else if (kind == SERIALIZE_IN) {
        char_t  key[DICT_MAX_KEY_LEN];
        int_t   size, cnt;
        int_t   alloc_size = 0, type = 0, key_len = -1;
        int_t   ival;
        float_t fval;
        long_t  lval;
        char_t  *s;

        dict_clr(dict);

        serialize_int(&size, fp, kind);
        serialize_int(&cnt, fp, kind);

        dict->size = size;

        for (int_t i = 0; i < cnt; i++){
            serialize_int(&type, fp, kind);
            serialize_int(&alloc_size, fp, kind);
            for (int_t j = 0; j < DICT_MAX_KEY_LEN; j++){
                serialize_char(&key[j], fp, kind);
            }

            switch (type) {
                case DICT_INT_TYPE:
                    serialize_int(&ival, fp, kind);
                    dict_add_int(dict, key, ival);
                    break;
                case DICT_FLOAT_TYPE:
                    serialize_float(&fval, fp, kind);
                    dict_add_float(dict, key, fval);
                    break;
                case DICT_LONG_TYPE:
                    serialize_long(&lval, fp, kind);
                    dict_add_long(dict, key, lval);
                    break;
                case DICT_STR_TYPE:
                    s = mem_alloc(alloc_size);
                    for (int_t j = 0; j < alloc_size; j++){
                        serialize_char(&s[j], fp, kind);
                    }
                    dict_add_str(dict, key, s, alloc_size);
                    mem_free(s);
                    break;
                default:
                    break;
            }
        }
    }
}


int_t dict_atmoic_size(dict_t *dict, char_t *key)
{
    for (dict_node_t *np = dict->entries[dict_hash(dict, key)]; np != NULL; np = np->next){
        if (strcmp(key, np->key) == 0){
            return np->alloc_size;
        }
    }
    return -1;
}

void *dict_atmoic_ptr(dict_t *dict,
                      char_t *key,
                      int_t  type,
                      void   *func_param)
{
    for (dict_node_t *np = dict->entries[dict_hash(dict, key)]; np != NULL; np = np->next){
        if (strcmp(key, np->key) == 0 && np->type == type){

            if (type == DICT_VOID_PTR_TYPE){
                if (np->alloc_size > 0)
                    return &np->value;
                else
                    return np->value;
            }
            else if (type == DICT_FUNC_PTR_TYPE){
                dict_func_ptr func_ptr = np->value;
                func_ptr(func_param);
            }
            else{
                return np->value;
            }
        }
    }
    return NULL;
}

void *dict_atomic_add(dict_t *dict,
                      char_t *name,
                      void   *ptr,
                      int_t  type,
                      int_t  alloc_size)
{
    dict_node_t  *node;
    uint_t       hashval;

    for (node = dict->entries[dict_hash(dict, name)]; node != NULL; node = node->next){
        if (strcmp(name, node->key) == 0){
            return NULL;
        }
    }

    ASSERT((ptr != NULL || alloc_size > 0) && strlen(name) < DICT_MAX_KEY_LEN);

    node = (dict_node_t*) mem_alloc(sizeof(dict_node_t));

    if (node != NULL){
        node->alloc_size    = alloc_size;
        node->key = mem_alloc(DICT_MAX_KEY_LEN);
        strcpy(node->key, name);

        if (node->alloc_size > 0)
            node->value = mem_alloc(node->alloc_size);
        else if (ptr != NULL)
            node->value = ptr;

        node->type             = type;
        hashval                = dict_hash(dict, name);
        node->next             = dict->entries[hashval];
        dict->entries[hashval] = node;
        dict->cnt++;

        if (type == DICT_FUNC_PTR_TYPE)
            return NULL;
        else
            return node->value;
    }
    return NULL;
}


int_t dict_int(dict_t *dict, char_t *key)
{
    return *(int_t*)dict_atmoic_ptr(dict, key, DICT_INT_TYPE, NULL);
}


void dict_set_int(dict_t *dict, char_t *key, int_t value)
{
    *(int_t*)dict_atmoic_ptr(dict, key, DICT_INT_TYPE, NULL) = value;
}


bool_t dict_add_int(dict_t *dict, char_t *name, int_t initial_val)
{
    bool_t success = dict_atomic_add(dict, name, NULL, DICT_INT_TYPE, sizeof(int_t));
    if (success)
        dict_set_int(dict, name, initial_val);
    return success;
}


float_t dict_float(dict_t *dict, char_t *key)
{
    return *(float_t*)dict_atmoic_ptr(dict, key, DICT_FLOAT_TYPE, NULL);
}


void dict_set_float(dict_t *dict, char_t *key, float_t value)
{
    *(float_t*)dict_atmoic_ptr(dict, key, DICT_FLOAT_TYPE, NULL) = value;
}


bool_t dict_add_float(dict_t *dict, char_t *name, float_t initial_val)
{
    bool_t success = dict_atomic_add(dict, name, NULL, DICT_FLOAT_TYPE, sizeof(float_t));
    if (success)
        dict_set_float(dict, name, initial_val);
    return success;
}


long_t dict_long(dict_t *dict, char_t *key)
{
    return *(long_t*)dict_atmoic_ptr(dict, key, DICT_LONG_TYPE, NULL);
}


void dict_set_long(dict_t *dict, char_t *key, long_t value)
{
    *(long_t*)dict_atmoic_ptr(dict, key, DICT_LONG_TYPE, NULL) = value;
}


bool_t dict_add_long(dict_t *dict, char_t *name, long_t initial_val)
{
    bool_t success = dict_atomic_add(dict, name, NULL, DICT_LONG_TYPE, sizeof(long_t));
    if (success)
        dict_set_long(dict, name, initial_val);
    return success;
}


char_t *dict_str(dict_t *dict, char_t *key)
{
    return (char_t*)dict_atmoic_ptr(dict, key, DICT_STR_TYPE, NULL);
}

void dict_set_str(dict_t *dict, char_t *key, char_t *value)
{
    if (strlen(value) < dict_atmoic_size(dict, key)-1){
        strcpy((char_t*)dict_atmoic_ptr(dict, key, DICT_STR_TYPE, NULL), value);
    }
}


bool_t dict_add_str(dict_t *dict, char_t *key, char_t *initial_str, int_t max_len)
{
    int_t str_size = max_len;

    if (str_size <= 0 && initial_str != NULL)
        str_size = strlen(initial_str)+1;

    bool_t success = dict_atomic_add(dict, key, NULL, DICT_STR_TYPE, str_size);
    if (success && initial_str != NULL)
        dict_set_str(dict, key, initial_str);
    return success;
}


void dict_func(dict_t *dict, char_t *key, void *param)
{
    dict_atmoic_ptr(dict, key, DICT_FUNC_PTR_TYPE, param);
}


bool_t dict_add_func(dict_t *dict, char_t *name, dict_func_ptr func)
{
    return dict_atomic_add(dict, name, func, DICT_FUNC_PTR_TYPE, 0);
}



void *dict_ptr(dict_t *dict, char_t *name)
{
    return dict_atmoic_ptr(dict, name, DICT_VOID_PTR_TYPE, NULL);
}


bool_t dict_add_ptr(dict_t *dict, char_t* name, void *data)
{
    return (dict_atomic_add(dict, name, data, DICT_VOID_PTR_TYPE, 0) != NULL);
}
\$\endgroup\$
  • \$\begingroup\$ Spelling: should dict_atmoic_ptr be dict_atomic_ptr? \$\endgroup\$ – Toby Speight Mar 26 '18 at 10:23
  • 2
    \$\begingroup\$ Do you have a matching dictionary.h and runnable main() that exercises this code, so we can compile it and try it out ourselves? \$\endgroup\$ – Toby Speight Mar 26 '18 at 10:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.