aboutsummaryrefslogblamecommitdiff
path: root/src/hash.c
blob: edb313922e2584525c002a63bdadc031a4933b64 (plain) (tree)


















































































































































































































































                                                                                                  
/*
 * =====================================================================================
 *
 *       Filename:  hash.c
 *
 *    Description:  Implementation of a basic hashmap 
 *
 *        Version:  1.0
 *        Created:  12/09/2022 07:24:38 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), *   Organization:  
 *
 * =====================================================================================
 */
#include <stdlib.h>
#include <math.h>
#include "hash.h"
#include "clog.h"


/*-----------------------------------------------------------------------------
 * We're defining the structs inside the .c file because really no one should be
 * messing around with the internals except through the exported functions
 *-----------------------------------------------------------------------------*/

typedef struct _map_element {
    int key;
    int is_used;
    any_type value;
} map_element;

typedef struct _map {
    int max_size;
    int cur_size;
    map_element* data;
} map;


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  map_init
 *  Description:  Initializes a new hashmap
 * =====================================================================================
 */
hash_map map_init () {
    map* m = (map*) malloc(sizeof(map));

    m->data = (map_element*) calloc(512, sizeof(map_element));

    m->max_size = 512;
    m->cur_size = 0;
    return m;
}		/* -----  end of function map_init  ----- */


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  hash_int
 *  Description:  
 * =====================================================================================
 */
unsigned int hash_int (map* ma, unsigned int key) {

    /*-----------------------------------------------------------------------------
     * This is a pretty basic hashing function, basically we're still using
     * modulo but in such a way that collisions are less likely than if we just
     * used it raw.
     *
     * The advantage of this is that it doesn't matter the max size of the
     * hashmap, it doesn't have to be sufficiently removed from a power of 2
     *
     * See: Hashing by Multiplication here: https://en.wikipedia.org/wiki/Hash_table#Hash_function
     *-----------------------------------------------------------------------------*/
    return fmodf(floor(ma->max_size * (key * fmodf(0.3, 1.0))), ma->max_size); 
}		/* -----  end of function hash_int  ----- */


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  hash_get_idx
 *  Description:  
 * =====================================================================================
 */
int hash_get_idx (hash_map* m, int key) {
    int c;
    map* ma = (map*) m;

    if (ma->cur_size == ma->max_size) {
        // Hash map is full, we need to exit now
        return -2;
    }

    c = hash_int(ma, key);                      /* This is the theoretically ideal hash index */

    /*-----------------------------------------------------------------------------
     * Pretty basic collision resolution. Needs to be able to resolve them
     *-----------------------------------------------------------------------------*/

    for(int i = 0; i < ma->max_size; i++) {
        if (ma->data[c].is_used == 0) {
            /*-----------------------------------------------------------------------------
             * This index isn't being used, we can return it 
             *-----------------------------------------------------------------------------*/
            return c;
        }

        if (ma->data[c].key == key && ma->data[c].is_used == 1) {

            /*-----------------------------------------------------------------------------
             * This index is being used, but by this key, so we can return it 
             *-----------------------------------------------------------------------------*/
            return c;
        }


        /*-----------------------------------------------------------------------------
         * This index is being used but not by this key. Increment the index
         * modulo the map size and try again
         *-----------------------------------------------------------------------------*/
        c = (c + 1) % ma->max_size;
    }
    return -2;
}		/* -----  end of function hash_get_idx  ----- */


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  map_resize
 *  Description:  
 * =====================================================================================
 */
int map_resize(hash_map in) {
    int i;
    int old_size;
    map_element* cur;

    /*  New elements! */
    map *m = (map*) in;
    map_element* tmp = (map_element*) calloc(2 * m->max_size, sizeof(map_element));
    if (!tmp) return 4;                         /* We're out of memory! */

    cur = m->data;
    m->data = tmp;

    old_size = m->max_size;
    m->max_size = 2 * old_size;
    m->cur_size = 0;

    /*  Rehash */
    for (i = 0; i < old_size; i++) {
        int status = map_add(m, cur[i].key, cur[i].value);
        if (status != 0) {
            return status;
        }
    }

    free(cur);
    return 0;
}		/* -----  end of function map_resize  ----- */

/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  map_add
 *  Description:  
 * =====================================================================================
 */
int map_add(hash_map m, int key, any_type val) {
    map* ma = (map*) m; 

    int h = hash_get_idx(m, key);

    while (h == -2) {
        if (map_resize(m) == 4) {
            return 4;
        }
        h = hash_get_idx(m, key);
    }

    ma->data[h].value = val;
    ma->data[h].key = key;
    ma->data[h].is_used = 1;
    return 0;
}		/* -----  end of function map_add  ----- */


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  map_get
 *  Description:  
 * =====================================================================================
 */
int map_get (hash_map m, int key, any_type* val) {
    map* ma = (map*) m;

    int h = hash_get_idx(m, key);

    if (ma->data[h].is_used == 1 && ma->data[h].key == key) {
        *val = ma->data[h].value;
        return 0;
    }
    return -3;
}		/* -----  end of function map_get  ----- */


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  map_del
 *  Description:  
 * =====================================================================================
 */
int map_del (hash_map m, int key) {
  map* ma = (map*) m;

  int h = hash_get_idx(m, key);

  if (ma->data[h].is_used == 1 && ma->data[h].key == key) {
    ma->data[h].is_used = 0;
    ma->data[h].key = 0;
  }

  return 0;
}		/* -----  end of function map_del  ----- */

/*
 *map_free cleans up the memory used by a hashmap
 */
void map_free(hash_map m) {
  map* ma = (map*) m;

  free(ma->data);
  free(ma);
}

/*
 *map_len returns the current length of the hashmap
 */
int map_len(hash_map m) {
  map* ma = (map*) m;

  return ma->cur_size;
}