lang/c/ ToyHashTable1


Motivation

I started watching https://www.youtube.com/watch?v=2Ti5yvumFTU and decided to have a go at writing the simplest toy hash table I could before watching the rest of the video. As such, this uses a poor choice of hash function (basically just Xor all the characters), and cannot delete items.

Code Version 2

I've added code to print the table, and to delete entries.

hasht.h

typedef struct _hasht hasht;

hasht* hasht_new(void);
void hasht_print(hasht *h);
void hasht_store(hasht *h, const char* key, char* data);
char* hasht_get(hasht *h, const char *key);
char* hasht_get_default(hasht *h, const char *key, char *def);
char* hasht_delete(hasht *h, const char *key);
int hasht_has(hasht *h, const char *key);

hasht.c

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "hasht.h"

typedef struct _llnode {
  char* key;
  char* data;
  struct _llnode *next;
} llnode;
typedef struct _ll {
  struct _llnode *head;
} ll;
#define TABLE_SIZE 16
struct _hasht {
  ll bins[TABLE_SIZE];
};
hasht* hasht_new(void) {
  hasht* h = (hasht*)malloc(sizeof(hasht));
  memset(h,0,sizeof(hasht));
  return h;
}
char hasht_hash(const char* key) {
  char x = 0;
  const char *p = key;
  while(*p != 0) {
    x ^= *p;
    p++;
  }
  return x % TABLE_SIZE;
}
llnode* create_llnode(const char* key, char* data) {
  llnode *n = (llnode*)malloc(sizeof(llnode));
  char *key_cpy = (char*)malloc(strlen(key)+1);
  strcpy(key_cpy,key);
  n->key = key_cpy;
  n->data = data;
  n->next = NULL;
  return n;
}
void hasht_print(hasht *h) {
  for(int i=0; i<TABLE_SIZE; i++) {
    llnode *p = h->bins[i].head;
    if( p != NULL ) {
      printf("Bin %d:\n",i);
      while( p != NULL ) {
        printf("  %s => %s\n",p->key,p->data);
        p = p->next;
      }
    }
  }
}
void hasht_store(hasht *h, const char* key, char* data) {
  char hk = hasht_hash(key);
  llnode *p = h->bins[hk].head;
  if( p == NULL ) {
    p = create_llnode(key,data);
    h->bins[hk].head = p;
    return;
  }
  // if we are here, then the list is nonempty
  while( p->next != NULL ) {
    if( strcmp(p->key,key) == 0 ) {
      p->data = data;
      return;
    }
    p = p->next;
  }
  // if we are here, then p points to the last element in the list
  if( strcmp(p->key,key) == 0 ) {
    p->data = data;
    return;
  }
  // if we are here, then then key is not in the table
  p->next = create_llnode(key,data);
  // so now our new element is last in the bin
}
char* hasht_get(hasht *h, const char *key) {
  char hk = hasht_hash(key);
  llnode *p = h->bins[hk].head;
  while(p != NULL) {
    if( strcmp(p->key,key) == 0) {
      return p->data;
    }
    p = p->next;
  }
  return NULL;
}
char* hasht_get_default(hasht *h, const char *key, char *def) {
  char hk = hasht_hash(key);
  llnode *p = h->bins[hk].head;
  while(p != NULL) {
    if( strcmp(p->key,key) == 0) {
      return p->data;
    }
    p = p->next;
  }
  return def;
}
// returns the data from the deleted node so that the caller can free it if necessary
char* hasht_delete(hasht *h, const char *key) {
  char hk = hasht_hash(key);
  llnode *p = h->bins[hk].head;
  llnode *prev = NULL;
  while(p != NULL) {
    if( strcmp(p->key,key) == 0) {
      llnode *q = p->next;
      if( prev == NULL ) {
        h->bins[hk].head = NULL;
        char* data = p->data;
        free(p->key);
        free(p);
        return data;
      } else {
        char* data = p->data;
        prev->next = p->next;
        free(p->key);
        free(p);
        return data;
      }
    }
    prev = p;
    p = p->next;
  }
  return NULL;
}
int hasht_has(hasht *h, const char *key) {
  char hk = hasht_hash(key);
  llnode *p = h->bins[hk].head;
  while(p != NULL) {
    if( strcmp(p->key,key) == 0) {
      return 1;
    }
    p = p->next;
  }
  return 0;
}

test.c

#include <stdio.h>
#include "hasht.h"

int main() {
  char* got;
  hasht* h = hasht_new();

  printf("Insert 3 items\n");
  hasht_store(h, "hello","hello world");
  hasht_store(h, "hex","mr flibble");
  hasht_store(h, "random","turnip");
  hasht_print(h);

  printf("===========\n");
  printf("Get 'hex' default to 'boing'\n");
  got = hasht_get_default(h, "hex", "boing");
  printf("Got %s\n",got);
  printf("Get 'hex' default to NULL\n");
  got = hasht_get(h, "hex");
  printf("Result is null? %s\n",got == NULL ? "yes" : "no");
  printf("===========\n");

  printf("Delete 'hex'\n");
  got = hasht_delete(h,"hex");
  hasht_print(h);
  printf("deleted: %s\n",got);
  printf("===========\n");

  printf("Get 'hex' default to 'boing'\n");
  got = hasht_get_default(h, "hex", "boing");
  printf("Got %s\n",got);
  printf("Get 'hex' default to NULL\n");
  got = hasht_get(h, "hex");
  printf("Result is null? %s\n",got == NULL ? "yes" : "no");
  printf("===========\n");

  printf("Table has 'hex'? %s\n",hasht_has(h,"hex") ? "yes" : "no");
  printf("Table has 'hello'? %s\n",hasht_has(h,"hello") ? "yes" : "no");
}

Compile with

gcc -o test hasht.c test.c

Code Version 1

hasht.h

typedef struct _hasht hasht;
hasht* hasht_new(void);
const char* hasht_get(hasht* h, const char* key);
void hasht_store(hasht* h, const char* key, const char* value);

hasht.c

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "hasht.h"

typedef struct _llnode {
  const char* key;
  const char* data;
  struct _llnode *next;
} llnode;
typedef struct _ll {
  struct _llnode *head;
} ll;
struct _hasht {
  ll bins[256];
};
hasht* hasht_new(void) {
  hasht* h = (hasht*)malloc(sizeof(hasht));
  memset(h,0,sizeof(hasht));
  return h;
}
char hasht_hash(const char* key) {
  char x = 0;
  const char *p = key;
  while(*p != 0) {
    x ^= *p;
    p++;
  }
  return x;
}
llnode* create_llnode(const char* key, const char* data) {
  llnode *n = (llnode*)malloc(sizeof(llnode));
  char *key_cpy = (char*)malloc(strlen(key)+1);
  strcpy(key_cpy,key);
  n->key = key_cpy;
  n->data = data;
  n->next = NULL;
  return n;
}
void hasht_store(hasht *h, const char* key, const char* data) {
  char hk = hasht_hash(key);
  llnode *p = h->bins[hk].head;
  if( p == NULL ) {
    p = create_llnode(key,data);
    h->bins[hk].head = p;
    return;
  }
  // if we are here, then the list is nonempty
  while( p->next != NULL ) {
    if( strcmp(p->key,key) == 0 ) {
      p->data = data;
      return;
    }
    p = p->next;
  }
  // if we are here, then p points to the last element in the list
  if( strcmp(p->key,key) == 0 ) {
    p->data = data;
    return;
  }
  // if we are here, then then key is not in the table
  p->next = create_llnode(key,data);
  // so now our new element is last in the bin
}
const char* hasht_get(hasht *h, const char *key) {
  char hk = hasht_hash(key);
  llnode *p = h->bins[hk].head;
  while(p != NULL) {
    if( strcmp(p->key,key) == 0) {
      return p->data;
    }
    p = p->next;
  }
  return "";
}

Test program

test.c

#include <stdio.h>
#include "hasht.h"

int main() {
  hasht* h = hasht_new();
  char* item1 = "hello world";
  char* item2 = "mr flibble";
  hasht_store(h, "hello",item1);
  hasht_store(h, "hex",item2);
  printf("1:\n");
  printf("%s %s\n",hasht_get(h, "hello"),hasht_get(h, "hex"));
  hasht_store(h, "hello", "boing");
  printf("2:\n");
  printf("%s %s\n",hasht_get(h, "hello"),hasht_get(h, "hex"));
}