aboutsummaryrefslogtreecommitdiff
path: root/src/hash_map.hpp
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2015-11-23 22:47:25 -0700
committerAndrew Kelley <superjoe30@gmail.com>2015-11-23 22:47:25 -0700
commit3b4a2afb65d4ebb5292c230bada84408758e564c (patch)
treea07801709a0b1531f742e16e83e77d73ce873de7 /src/hash_map.hpp
parenta22bc8d20a1ad9158a28f29f6f866b0008b11e3e (diff)
downloadzig-3b4a2afb65d4ebb5292c230bada84408758e564c.tar.gz
zig-3b4a2afb65d4ebb5292c230bada84408758e564c.zip
semantic analysis checks for multiple definitions of functions
Diffstat (limited to 'src/hash_map.hpp')
-rw-r--r--src/hash_map.hpp220
1 files changed, 220 insertions, 0 deletions
diff --git a/src/hash_map.hpp b/src/hash_map.hpp
new file mode 100644
index 0000000000..1e3f921e96
--- /dev/null
+++ b/src/hash_map.hpp
@@ -0,0 +1,220 @@
+#ifndef ZIG_HASH_MAP_HPP
+#define ZIG_HASH_MAP_HPP
+
+#include "util.hpp"
+
+#include <stdint.h>
+
+template<typename K, typename V, uint32_t (*HashFunction)(K key), bool (*EqualFn)(K a, K b)>
+class HashMap {
+public:
+ void init(int capacity) {
+ init_capacity(capacity);
+ }
+ void deinit(void) {
+ free(_entries);
+ }
+
+ struct Entry {
+ bool used;
+ int distance_from_start_index;
+ K key;
+ V value;
+ };
+
+ void clear() {
+ for (int i = 0; i < _capacity; i += 1) {
+ _entries[i].used = false;
+ }
+ _size = 0;
+ _max_distance_from_start_index = 0;
+ _modification_count += 1;
+ }
+
+ int size() const {
+ return _size;
+ }
+
+ void put(const K &key, const V &value) {
+ _modification_count += 1;
+ internal_put(key, value);
+
+ // if we get too full (80%), double the capacity
+ if (_size * 5 >= _capacity * 4) {
+ Entry *old_entries = _entries;
+ int old_capacity = _capacity;
+ init_capacity(_capacity * 2);
+ // dump all of the old elements into the new table
+ for (int i = 0; i < old_capacity; i += 1) {
+ Entry *old_entry = &old_entries[i];
+ if (old_entry->used)
+ internal_put(old_entry->key, old_entry->value);
+ }
+ free(old_entries);
+ }
+ }
+
+ const V &get(const K &key) const {
+ Entry *entry = internal_get(key);
+ if (!entry)
+ zig_panic("key not found");
+ return entry->value;
+ }
+
+ Entry *maybe_get(const K &key) const {
+ return internal_get(key);
+ }
+
+ void remove(const K &key) {
+ _modification_count += 1;
+ int start_index = key_to_index(key);
+ for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
+ int index = (start_index + roll_over) % _capacity;
+ Entry *entry = &_entries[index];
+
+ if (!entry->used)
+ zig_panic("key not found");
+
+ if (!EqualFn(entry->key, key))
+ continue;
+
+ for (; roll_over < _capacity; roll_over += 1) {
+ int next_index = (start_index + roll_over + 1) % _capacity;
+ Entry *next_entry = &_entries[next_index];
+ if (!next_entry->used || next_entry->distance_from_start_index == 0) {
+ entry->used = false;
+ _size -= 1;
+ return;
+ }
+ *entry = *next_entry;
+ entry->distance_from_start_index -= 1;
+ entry = next_entry;
+ }
+ zig_panic("shifting everything in the table");
+ }
+ zig_panic("key not found");
+ }
+
+ class Iterator {
+ public:
+ Entry *next() {
+ if (_inital_modification_count != _table->_modification_count)
+ zig_panic("concurrent modification");
+ if (_count >= _table->size())
+ return NULL;
+ for (; _index < _table->_capacity; _index += 1) {
+ Entry *entry = &_table->_entries[_index];
+ if (entry->used) {
+ _index += 1;
+ _count += 1;
+ return entry;
+ }
+ }
+ zig_panic("no next item");
+ }
+ private:
+ const HashMap * _table;
+ // how many items have we returned
+ int _count = 0;
+ // iterator through the entry array
+ int _index = 0;
+ // used to detect concurrent modification
+ uint32_t _inital_modification_count;
+ Iterator(const HashMap * table) :
+ _table(table), _inital_modification_count(table->_modification_count) {
+ }
+ friend HashMap;
+ };
+
+ // you must not modify the underlying HashMap while this iterator is still in use
+ Iterator entry_iterator() const {
+ return Iterator(this);
+ }
+
+private:
+
+ Entry *_entries;
+ int _capacity;
+ int _size;
+ int _max_distance_from_start_index;
+ // this is used to detect bugs where a hashtable is edited while an iterator is running.
+ uint32_t _modification_count = 0;
+
+ void init_capacity(int capacity) {
+ _capacity = capacity;
+ _entries = allocate<Entry>(_capacity);
+ _size = 0;
+ _max_distance_from_start_index = 0;
+ for (int i = 0; i < _capacity; i += 1) {
+ _entries[i].used = false;
+ }
+ }
+
+ void internal_put(K key, V value) {
+ int start_index = key_to_index(key);
+ for (int roll_over = 0, distance_from_start_index = 0;
+ roll_over < _capacity; roll_over += 1, distance_from_start_index += 1)
+ {
+ int index = (start_index + roll_over) % _capacity;
+ Entry *entry = &_entries[index];
+
+ if (entry->used && !EqualFn(entry->key, key)) {
+ if (entry->distance_from_start_index < distance_from_start_index) {
+ // robin hood to the rescue
+ Entry tmp = *entry;
+ if (distance_from_start_index > _max_distance_from_start_index)
+ _max_distance_from_start_index = distance_from_start_index;
+ *entry = {
+ true,
+ distance_from_start_index,
+ key,
+ value,
+ };
+ key = tmp.key;
+ value = tmp.value;
+ distance_from_start_index = tmp.distance_from_start_index;
+ }
+ continue;
+ }
+
+ if (!entry->used) {
+ // adding an entry. otherwise overwriting old value with
+ // same key
+ _size += 1;
+ }
+
+ if (distance_from_start_index > _max_distance_from_start_index)
+ _max_distance_from_start_index = distance_from_start_index;
+ *entry = {
+ true,
+ distance_from_start_index,
+ key,
+ value,
+ };
+ return;
+ }
+ zig_panic("put into a full HashMap");
+ }
+
+
+ Entry *internal_get(const K &key) const {
+ int start_index = key_to_index(key);
+ for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
+ int index = (start_index + roll_over) % _capacity;
+ Entry *entry = &_entries[index];
+
+ if (!entry->used)
+ return NULL;
+
+ if (EqualFn(entry->key, key))
+ return entry;
+ }
+ return NULL;
+ }
+
+ int key_to_index(const K &key) const {
+ return (int)(HashFunction(key) % ((uint32_t)_capacity));
+ }
+};
+
+#endif