Skip to main content
Bumped by Community user
Bumped by Community user
Tweeted twitter.com/StackCodeReview/status/1138098744333934592
added 316 characters in body
Source Link
twodee
  • 230
  • 2
  • 14

Note: The above linked list is a generic circular doubly linked list implementation and not specifically made for the hash table. I understand that a singly linked list should suffice for this, but since I made the Linked List library to be used somewhere else, I went ahead and reused it for the hash table.

Note: The above linked list is a generic circular doubly linked list implementation and not specifically made for the hash table. I understand that a singly linked list should suffice for this, but since I made the Linked List library to be used somewhere else, I went ahead and reused it for the hash table.

Added linked list implementation
Source Link
twodee
  • 230
  • 2
  • 14

Linked list implementation

linked_list.h

#ifndef LLIST_H
#define LLIST_H

/* A generic-ish typed circular doubly linked list implementation
 * A part of the DSLIBC
 * This is loosely based on the Linux kernel's list.h
 */
#include <stddef.h>

typedef struct ll_node 
{
  struct ll_node * next, * prev;
} LL_Node;

void ll_push_back(LL_Node * head, LL_Node * new_node);
void ll_push_front(LL_Node * head, LL_Node * new_node);
void ll_delete(LL_Node * node);
void ll_replace(LL_Node * old, LL_Node * new);
void ll_create_list(LL_Node * new_head_ptr);

/*
 * Get the struct stored in that node location
 * A neat trick that gets the address of the struct housing the \
 * LL_Node
 */
#define ll_get(node, struct_type, struct_member)            \
  ((struct_type *)((char *)(node) - (unsigned long)(offsetof(struct_type, struct_member))))

/*
 * Iterate over the list
 */
#define ll_foreach(ptr, head)           \
  for (ptr = (head)->next; ptr != head;     \
       ptr = ptr->next)

/*
 * Iterate backwards
 */
#define ll_foreach_back(ptr, head)      \
  for (ptr = (head)->prev; ptr != head;     \
       ptr = ptr->prev)
/*
 * Iterate over each item safely 
 * Allows for freeing of objects without any errors
 */
#define ll_foreach_safe(ptr, temp, head)            \
  for (ptr = (head)->next, temp = ptr->next; ptr != head;   \
       ptr = temp, temp = ptr->next)

#endif

linked_list.c:

#include "linked_list.h"

/**
 * Initialize the list
 * Supply the head ptr address from the actual structure that is to be stored
 */
void ll_create_list(LL_Node * new_head_ptr)
{
  new_head_ptr->next = new_head_ptr;
  new_head_ptr->prev = new_head_ptr;
}

void __ll_push_between(LL_Node * prev, LL_Node * next, LL_Node * new)
{
  prev->next = new;
  new->prev = prev;

  new->next = next;
  next->prev = new;
}
/*
 * Insert an element at the end of the list
 * Having a circular list makes sense since we don't have to \
 * traverse all the way to to end to insert an element
 */
void ll_push_back(LL_Node * head, LL_Node * new_node)
{
  __ll_push_between(head->prev, head, new_node);
}

void ll_push_front(LL_Node * head, LL_Node * new_node)
{
  __ll_push_between(head, head->next, new_node);
}

void __ll_re_link(LL_Node * prev, LL_Node * next)
{
  prev->next = next;
  next->prev = prev;
}

void __ll_nullify(LL_Node * node)
{
  node->next = NULL;
  node->prev = NULL;
}
void ll_delete(LL_Node * node)
{
  __ll_re_link(node->prev, node->next);
  __ll_nullify(node);
}

void ll_replace(LL_Node * old, LL_Node * new)
{
  __ll_re_link(old->prev, new);
  __ll_re_link(new, old->next);

  __ll_nullify(old);
}

Linked list implementation

linked_list.h

#ifndef LLIST_H
#define LLIST_H

/* A generic-ish typed circular doubly linked list implementation
 * A part of the DSLIBC
 * This is loosely based on the Linux kernel's list.h
 */
#include <stddef.h>

typedef struct ll_node 
{
  struct ll_node * next, * prev;
} LL_Node;

void ll_push_back(LL_Node * head, LL_Node * new_node);
void ll_push_front(LL_Node * head, LL_Node * new_node);
void ll_delete(LL_Node * node);
void ll_replace(LL_Node * old, LL_Node * new);
void ll_create_list(LL_Node * new_head_ptr);

/*
 * Get the struct stored in that node location
 * A neat trick that gets the address of the struct housing the \
 * LL_Node
 */
#define ll_get(node, struct_type, struct_member)            \
  ((struct_type *)((char *)(node) - (unsigned long)(offsetof(struct_type, struct_member))))

/*
 * Iterate over the list
 */
#define ll_foreach(ptr, head)           \
  for (ptr = (head)->next; ptr != head;     \
       ptr = ptr->next)

/*
 * Iterate backwards
 */
#define ll_foreach_back(ptr, head)      \
  for (ptr = (head)->prev; ptr != head;     \
       ptr = ptr->prev)
/*
 * Iterate over each item safely 
 * Allows for freeing of objects without any errors
 */
#define ll_foreach_safe(ptr, temp, head)            \
  for (ptr = (head)->next, temp = ptr->next; ptr != head;   \
       ptr = temp, temp = ptr->next)

#endif

linked_list.c:

#include "linked_list.h"

/**
 * Initialize the list
 * Supply the head ptr address from the actual structure that is to be stored
 */
void ll_create_list(LL_Node * new_head_ptr)
{
  new_head_ptr->next = new_head_ptr;
  new_head_ptr->prev = new_head_ptr;
}

void __ll_push_between(LL_Node * prev, LL_Node * next, LL_Node * new)
{
  prev->next = new;
  new->prev = prev;

  new->next = next;
  next->prev = new;
}
/*
 * Insert an element at the end of the list
 * Having a circular list makes sense since we don't have to \
 * traverse all the way to to end to insert an element
 */
void ll_push_back(LL_Node * head, LL_Node * new_node)
{
  __ll_push_between(head->prev, head, new_node);
}

void ll_push_front(LL_Node * head, LL_Node * new_node)
{
  __ll_push_between(head, head->next, new_node);
}

void __ll_re_link(LL_Node * prev, LL_Node * next)
{
  prev->next = next;
  next->prev = prev;
}

void __ll_nullify(LL_Node * node)
{
  node->next = NULL;
  node->prev = NULL;
}
void ll_delete(LL_Node * node)
{
  __ll_re_link(node->prev, node->next);
  __ll_nullify(node);
}

void ll_replace(LL_Node * old, LL_Node * new)
{
  __ll_re_link(old->prev, new);
  __ll_re_link(new, old->next);

  __ll_nullify(old);
}
Source Link
twodee
  • 230
  • 2
  • 14

Hashtable implementation in C for generic values

I am trying to implement a hashtable that supports character keys and can return any type of value contained in a struct as long as it contains as long as it contains Bucket as one of its members.

The user has to supply the size of the hashtable (preferrably a large enough prime number) in the calling function.

It uses sdbm as a hash function. (Maybe adding a function pointer to let the user specify the kind of hash function they want to use could be a flexible option?)

Any code improvements/suggestions are welcome.

It uses a chaining mechanism using linked lists.

Here is the prototype:

#ifndef HASHTABLE_H
#define HASHTABLE_H

#include "../linked_list/linked_list.h"
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

#define HT_INIT_FAILURE -1
#define HT_INIT_SUCCESS 0

#define HT_MAGIC_SIZE 997

typedef struct bucket
{
  char * key;
  LL_Node ll_node;
} Bucket;

typedef struct hashtable
{
  Bucket ** buckets;
  unsigned int size;
} Hashtable;
  

int hashtable_init(Hashtable * hashtable, int size);
void hashtable_put(Hashtable * hashtable, char * key, Bucket * bucket);
int hashtable_key_exists(Hashtable * hashtable, char * key);
void hashtable_close(Hashtable * hashtable);
Bucket * __hashtable_get_bucket(Hashtable * hashtable, char * key);

#define hashtable_get_value(bucket, struct_type, struct_member)     \
  ((struct_type *)((char *)(bucket) - (unsigned long)(offsetof(struct_type, struct_member))))

#define hashtable_get(hashtable, key, struct_type, struct_member)   \
  hashtable_get_value(__hashtable_get_bucket(hashtable, key), struct_type, struct_member);

#endif

hashtable.c

#include "hashtable.h"

int hashtable_init(Hashtable * ht, int size)
{
  if ((ht->buckets = (Bucket **) malloc (sizeof(Bucket *) * size)) == NULL)
    return HT_INIT_FAILURE;
  ht->size = size;
  
  for (int i = 0; i < size; i++)
    ht->buckets[i] = NULL;

  return HT_INIT_SUCCESS;
}

unsigned long __hash_sdbm(char *str)
{
  unsigned long hash = 0;
  int c;
  
  while ((c = *str++))
    hash = c + (hash << 6) + (hash << 16) - hash;
  
  return hash;
}
int __key_matches(char * source, char * target)
{
  return (strcmp(source, target) == 0);
}

void __insert_bucket(Hashtable * hashtable, int index, Bucket * bucket)
{
  if (hashtable->buckets[index] == NULL) {
    
    LL_Node * head = &bucket->ll_node;  
    ll_create_list(head);
    hashtable->buckets[index] = bucket;
  }
  else {

    LL_Node * head = &(hashtable->buckets[index]->ll_node);
    LL_Node * ptr = head;
    int head_key_matches = (__key_matches(bucket->key,
                      hashtable->buckets[index]->key));
    
    Bucket * search_bucket;
    
    ll_foreach(ptr, head) { 
      search_bucket = ll_get(ptr, Bucket, ll_node); 
      if (head_key_matches || __key_matches(bucket->key, search_bucket->key)) { 
        ll_replace(ptr, &bucket->ll_node); 
    return; 
      }     
    }
    ll_push_front(head, &(bucket->ll_node));
  }
}

void hashtable_put(Hashtable * ht, char * key, Bucket * bucket)
{
  int index = __hash_sdbm(key) % ht->size;
  bucket->key = key;
  __insert_bucket(ht, index, bucket);
}

Bucket * __hashtable_get_bucket(Hashtable * hashtable, char * key)
{
  int index = __hash_sdbm(key) % hashtable->size;  

  if (hashtable->buckets[index] == NULL)
    return NULL;

  Bucket * bucket = hashtable->buckets[index];
  
  if (__key_matches(bucket->key, key)) {
    return bucket;
  }

  else {
    LL_Node * ptr;
    LL_Node * head = &(hashtable->buckets[index]->ll_node);
  
    ll_foreach(ptr, head) {
      bucket = ll_get(ptr, Bucket, ll_node);
      if (__key_matches(key, bucket->key)) {
    return bucket;
      }
    }
    return NULL;
  }
}

int hashtable_key_exists(Hashtable * ht, char * key)
{
  return (__hashtable_get_bucket(ht, key) == NULL);
}

void hashtable_close(Hashtable *ht)
{
  free(ht->buckets);
  ht->size = 0;
}

Here is an example of how it can be used:

#include "hashtable.h"
#include <stdlib.h>
#include <stdio.h>

typedef struct entry_t {
  int val;
  Bucket bucket;  
} Entry;

int main()
{
  Hashtable hashtable;
  Hashtable * ht = &hashtable;
  
  if (hashtable_init(ht, 10) == HT_INIT_FAILURE)
    return EXIT_FAILURE;
  
  Entry * entry = (Entry *) malloc(sizeof(Entry));
  entry->val = 10;
  
  hashtable_put(ht, "john", &(entry->bucket));

  Entry * res;

  Entry * entry2 = (Entry *) malloc(sizeof(Entry));
  entry2->val = 12;
  
  hashtable_put(ht, "pan", &(entry2->bucket));

  Entry * entry3 = (Entry *) malloc(sizeof(Entry));
  entry3->val = 15;
  
  hashtable_put(ht, "tran", &(entry3->bucket));

  res = hashtable_get(ht, "john", Entry, bucket);
  printf("%d\n", res->val);
  
  res = hashtable_get(ht, "pan", Entry, bucket);
  printf("%d\n", res->val);

  res = hashtable_get(ht, "tran", Entry, bucket);
  printf("%d\n", res->val);

  Entry * entry4 = (Entry *) malloc(sizeof(Entry));
  entry4->val = 100;
  
  hashtable_put(ht, "pan", &(entry4->bucket));

  res = hashtable_get(ht, "john", Entry, bucket);
  printf("Replaced\n%d\n", res->val);
  
  res = hashtable_get(ht, "pan", Entry, bucket);
  printf("%d\n", res->val);

  res = hashtable_get(ht, "tran", Entry, bucket);
  printf("%d\n", res->val);
  
  if (hashtable_key_exists(ht, "trans"))
    printf("Doesn't exist");
  else
    printf("Exists");

  if (hashtable_key_exists(ht, "tran"))
    printf("Doesn't exist");
  else
    printf("Exists");

  free(entry);
  free(entry3);
  free(entry2);
  free(entry4);

  hashtable_close(ht);

  return EXIT_SUCCESS;
}

Please excuse the lazily written test main function.