diff --git a/src/datastructures.c b/src/datastructures.c
index 5758f5dc0cac41bd0695bd0d165564664521a63d..0cd5d0e4ad84c661fae65110ddd4de29d4d99d06 100644
--- a/src/datastructures.c
+++ b/src/datastructures.c
@@ -97,3 +97,115 @@ void h_slist_free(HSlist *slist) {
   h_arena_free(slist->arena, slist);
 }
 
+HHashTable* h_hashtable_new(HArena *arena) {
+  HHashTable *ht = h_arena_malloc(arena, sizeof(HHashTable*));
+  ht->hashFunc = hashFunc;
+  ht->equalFunc = equalFunc;
+  ht->capacity = 64; // to start; should be tuned later...
+  ht->used = 0;
+  ht->contents = h_arena_malloc(arena, sizeof(HHashTableEntry) * ht->capacity);
+  memset(ht->contents, sizeof(HHashTableEntry) * ht->capacity);
+  return ht;
+}
+
+void* h_hashtable_get(HHashTable* ht, void* key) {
+  HHashValue hashval = ht->hashFunc(key);
+#ifdef CONSISTENCY_CHECK
+  assert((ht->capacity & (ht->capacity - 1)) == 0); // capacity is a power of 2
+#endif
+
+  for (HHashTableEntry *hte = &ht->contents[hashval & (ht->capacity - 1)];
+       hte != NULL;
+       hte = hte->next) {
+    if (hte->hashval != hashval)
+      continue;
+    if (ht->equalFunc(key, hte->key))
+      return hte->value;
+  }
+  return NULL;
+}
+
+void h_hashtable_put(HHashTable* ht, void* key, void* value) {
+  // # Start with a rebalancing
+  h_hashtable_ensure_capacity(ht, ht->used + 1);
+
+  HHashValue hashval = ht->hashFunc(key);
+#ifdef CONSISTENCY_CHECK
+  assert((ht->capacity & (ht->capacity - 1)) == 0); // capacity is a power of 2
+#endif
+
+  hte = &ht->contents[hashval & (ht->capacity - 1)];
+  if (hte->key != NULL) {
+    do {
+      if (hte->hashval == hashval && ht->equalFunc(key, hte->key))
+	goto insert_here;
+    } while (hte->next);
+    // Add a new link...
+    assert (hte->next == NULL);
+    hte->next = h_arena_malloc(ht->arena, sizeof(HHashTableEntry));
+    hte = hte->next;
+    hte->next = NULL;
+  }
+
+ insert_here:
+  hte->key = key;
+  hte->value = value;
+  hte->hashval = hashval;
+}
+
+int   h_hashtable_present(HHashTable* ht, void* key) {
+  HHashValue hashval = ht->hashFunc(key);
+#ifdef CONSISTENCY_CHECK
+  assert((ht->capacity & (ht->capacity - 1)) == 0); // capacity is a power of 2
+#endif
+
+  for (HHashTableEntry *hte = &ht->contents[hashval & (ht->capacity - 1)];
+       hte != NULL;
+       hte = hte->next) {
+    if (hte->hashval != hashval)
+      continue;
+    if (ht->equalFunc(key, hte->key))
+      return true;
+  }
+  return false;
+}
+void  h_hashtable_del(HHashTable* ht, void* key) {
+  HHashValue hashval = ht->hashFunc(key);
+#ifdef CONSISTENCY_CHECK
+  assert((ht->capacity & (ht->capacity - 1)) == 0); // capacity is a power of 2
+#endif
+
+  for (HHashTableEntry *hte = &ht->contents[hashval & (ht->capacity - 1)];
+       hte != NULL;
+       hte = hte->next) {
+    if (hte->hashval != hashval)
+      continue;
+    if (ht->equalFunc(key, hte->key)) {
+      // FIXME: Leaks keys and values.
+      HHashTableEntry* hten = hte->next;
+      if (hten != NULL) {
+	*hte = *hten;
+	h_arena_free(ht->arena, hten);
+      } else {
+	hte->key = hte->value = NULL;
+	hte->hashval = 0;
+      }
+      return;
+    }
+  }
+}
+void  h_hashtable_free(HHashTable* ht) {
+  for (i = 0; i < ht->capacity; i++) {
+    HHashTableEntry *hten, *hte = &ht->contents[i];
+    // FIXME: Free key and value
+    hte = hte->next;
+    while (hte != NULL) {
+      // FIXME: leaks keys and values.
+      hten = hte->next;
+      h_arena_free(ht->arena, hte);
+      hte = hten;
+    }
+  }
+  h_arena_free(ht->arena, ht->contents);
+}
+
diff --git a/src/internal.h b/src/internal.h
index bf2d661cf70bd7a621cf5ffaaa4870e75e641ca7..1636c866db78fd08e696effd8a956017acc23cdb 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -172,6 +172,33 @@ bool h_slist_find(HSlist *slist, const void* item);
 HSlist* h_slist_remove_all(HSlist *slist, const void* item);
 void h_slist_free(HSlist *slist);
 
+typedef unsigned int HHashValue;
+typedef HHashValue (*HHashFunc)(const void* key);
+typedef bool (*HEqualFunc)(const void* key1, const void* key2);
+
+typedef struct HHashTableEntry_ {
+  struct HHashTableEntry_ *next;
+  void* key;
+  void* value;
+  HHashValue hashval;
+} HHashTableEntry;
+
+typedef struct HHashTable_ {
+  HHashTableEntry *contents;
+  HHashFunc hashFunc;
+  HEqualFunc equalFunc;
+  int capacity;
+  int used;
+} HHashTable;
+
+HHashTable* h_hashtable_new(HArena *arena, );
+void* h_hashtable_get(HHashTable* ht, void* key);
+void h_hashtable_put(HHashTable* ht, void* key, void* value);
+int   h_hashtable_present(HHashTable* ht, void* key);
+void  h_hashtable_del(HHashTable* ht, void* key);
+void  h_hashtable_free(HHashTable* ht);
+
+
 #if 0
 #include <malloc.h>
 #define arena_malloc(a, s) malloc(s)