diff --git a/vlib/builtin/hashmap/hashmap.v b/vlib/builtin/hashmap/hashmap.v index 518e725e83..9874b8d025 100644 --- a/vlib/builtin/hashmap/hashmap.v +++ b/vlib/builtin/hashmap/hashmap.v @@ -4,31 +4,20 @@ module hashmap const ( - initial_size = 2<<4 + initial_size = 2 << 4 initial_cap = initial_size - 1 - load_factor = 0.5 probe_offset = u16(256) + load_factor = 0.8 +) + +// hash-function should not be in this file +const ( fnv64_prime = 1099511628211 fnv64_offset_basis = 14695981039346656037 fnv32_offset_basis = u32(2166136261) fnv32_prime = u32(16777619) ) -pub struct Hashmap { -mut: - info &u16 - key_values &KeyValue - cap int -pub mut: - size int -} - -struct KeyValue { - key string -mut: - value int -} - [inline] fn fnv1a64(data string) u64 { mut hash := fnv64_offset_basis @@ -38,11 +27,28 @@ fn fnv1a64(data string) u64 { return hash } +pub struct Hashmap { +mut: + info &u16 + key_values &KeyValue + cap int +pub mut: + load_factor f32 + size int +} + +struct KeyValue { + key string +mut: + value int +} + pub fn new_hashmap() Hashmap { return Hashmap{ info: &u16(calloc(sizeof(u16) * initial_size)) key_values: &KeyValue(calloc(sizeof(KeyValue) * initial_size)) cap: initial_cap + load_factor: 0.8 size: 0 } } @@ -51,12 +57,12 @@ pub fn (h mut Hashmap) set(key string, value int) { // The load factor is 0.5. // It will be adjustable in the future and with // a higher default settings to lower memory usage. - if (h.size<<1) == (h.cap - 1) { + if (f32(h.size) / f32(h.cap)) > h.load_factor { h.rehash() } // Hash-function will be swapped for wyhash hash := fnv1a64(key) - mut info := u16((hash>>56) | probe_offset) + mut info := u16((hash >> 56) | probe_offset) mut index := hash & h.cap // While probe count is less for info < h.info[index] { @@ -75,18 +81,17 @@ pub fn (h mut Hashmap) set(key string, value int) { // Match is not possible anymore. // Probe until an empty index is found. // Swap when probe count is higher/richer (Robin Hood). - mut current_key := key - mut current_value := value + mut current_kv := KeyValue{key, value} for h.info[index] != 0 { if info > h.info[index] { - tmp_kv := h.key_values[index] + // Swap info word tmp_info := h.info[index] - h.key_values[index] = KeyValue{ - current_key,current_value} h.info[index] = info - current_key = tmp_kv.key - current_value = tmp_kv.value info = tmp_info + // Swap KeyValue + tmp_kv := h.key_values[index] + h.key_values[index] = current_kv + current_kv = tmp_kv } index = (index + 1) & h.cap info += probe_offset @@ -94,56 +99,42 @@ pub fn (h mut Hashmap) set(key string, value int) { // Should almost never happen if (info & 0xFF00) == 0xFF00 { h.rehash() - h.set(current_key, current_value) + h.set(current_kv.key, current_kv.value) return } h.info[index] = info - h.key_values[index] = KeyValue{ - current_key,current_value} + h.key_values[index] = current_kv h.size++ } fn (h mut Hashmap) rehash() { old_cap := h.cap - h.cap = ((h.cap + 1)<<1) - 1 + h.cap = ((h.cap + 1) << 1) - 1 mut new_key_values := &KeyValue(calloc(sizeof(KeyValue) * (h.cap + 1))) mut new_info := &u16(calloc(sizeof(u16) * (h.cap + 1))) for i in 0 .. (old_cap + 1) { if h.info[i] != 0 { - key := h.key_values[i].key - value := h.key_values[i].value - hash := fnv1a64(key) - mut info := u16((hash>>56) | probe_offset) + mut kv := h.key_values[i] + hash := fnv1a64(kv.key) + mut info := u16((hash >> 56) | probe_offset) mut index := hash & h.cap // While probe count is less for info < new_info[index] { index = (index + 1) & h.cap info += probe_offset } - // While we might have a match - for info == new_info[index] { - if key == new_key_values[index].key { - new_key_values[index].value = value - return - } - index = (index + 1) & h.cap - info += probe_offset - } - // Match is not possible anymore. // Probe until an empty index is found. // Swap when probe count is higher/richer (Robin Hood). - mut current_key := key - mut current_value := value for new_info[index] != 0 { if info > new_info[index] { - tmp_kv := new_key_values[index] + // Swap info word tmp_info := new_info[index] - new_key_values[index] = KeyValue{ - current_key,current_value} new_info[index] = info - current_key = tmp_kv.key - current_value = tmp_kv.value info = tmp_info + // Swap KeyValue + tmp_kv := new_key_values[index] + new_key_values[index] = kv + kv = tmp_kv } index = (index + 1) & h.cap info += probe_offset @@ -151,12 +142,11 @@ fn (h mut Hashmap) rehash() { // Should almost never happen if (info & 0xFF00) == 0xFF00 { h.rehash() - h.set(current_key, current_value) + h.set(kv.key, kv.value) return } new_info[index] = info - new_key_values[index] = KeyValue{ - current_key,current_value} + new_key_values[index] = kv } } h.key_values = new_key_values @@ -166,7 +156,7 @@ fn (h mut Hashmap) rehash() { pub fn (h mut Hashmap) delete(key string) { hash := fnv1a64(key) mut index := hash & h.cap - mut info := u16((hash>>56) | probe_offset) + mut info := u16((hash >> 56) | probe_offset) for info < h.info[index] { index = (index + 1) & h.cap info += probe_offset @@ -177,7 +167,7 @@ pub fn (h mut Hashmap) delete(key string) { mut old_index := index index = (index + 1) & h.cap mut current_info := h.info[index] - for (current_info>>8) > 1 { + for (current_info >> 8) > 1 { h.info[old_index] = current_info - probe_offset h.key_values[old_index] = h.key_values[index] old_index = index @@ -196,7 +186,7 @@ pub fn (h mut Hashmap) delete(key string) { pub fn (h Hashmap) get(key string) int { hash := fnv1a64(key) mut index := hash & h.cap - mut info := u16((hash>>56) | probe_offset) + mut info := u16((hash >> 56) | probe_offset) for info < h.info[index] { index = (index + 1) & h.cap info += probe_offset @@ -214,7 +204,7 @@ pub fn (h Hashmap) get(key string) int { pub fn (h Hashmap) exists(key string) bool { hash := fnv1a64(key) mut index := hash & h.cap - mut info := u16((hash>>56) | probe_offset) + mut info := u16((hash >> 56) | probe_offset) for info < h.info[index] { index = (index + 1) & h.cap info += probe_offset @@ -230,8 +220,7 @@ pub fn (h Hashmap) exists(key string) bool { } pub fn (h Hashmap) keys() []string { - size := h.size - mut keys := [''].repeat(size) + mut keys := [''].repeat(h.size) mut j := 0 for i in 0 .. (h.cap + 1) { if h.info[i] != 0 {