// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. module hashmap /* This is work in progress. A very early test version of the Hashmap with a fixed size. Only works with string keys and int values for now. I added this to improve performance of the V compiler, which uses lots of O(log n) map get's. Turned out with N < 10 000 the performance gains are basically non-existent. */ struct Hashmap { cap int keys []string table []Hashmapentry elm_size int pub mut: nr_collisions int } struct Hashmapentry { mut: key string val int next &Hashmapentry // linked list for collisions } const ( min_cap = 2<<10 max_cap = 2<<20 ) const( fnv64_prime = 1099511628211 fnv64_offset_basis = 14695981039346656037 ) const( fnv32_offset_basis = u32(2166136261) fnv32_prime = u32(16777619) ) pub fn new_hashmap(planned_nr_items int) Hashmap { mut cap := planned_nr_items * 5 if cap < min_cap { cap = min_cap } if cap > max_cap { cap = max_cap } return Hashmap{ cap: cap elm_size: 4 table: make(cap, cap, sizeof(Hashmapentry)) } } pub fn (m mut Hashmap) set(key string, val int) { // mut hash := int(b_fabs(key.hash())) // idx := hash % m.cap idx := int(fnv1a32(key) % m.cap) if m.table[idx].key.len != 0 { // println('\nset() idx=$idx key="$key" hash="$hash" val=$val') m.nr_collisions++ // println('collision:' + m.table[idx].key) mut e := &m.table[idx] for e.next != 0 { e = e.next } e.next = &Hashmapentry{ key,val,0} } else { m.table[idx] = Hashmapentry{ key,val,0} } } pub fn (m &Hashmap) get(key string) int { // mut hash := int(b_fabs(key.hash())) // idx := hash % m.cap idx := int(fnv1a32(key) % m.cap) mut e := &m.table[idx] for e.next != 0 { // todo unsafe { if e.key == key { return e.val } e = e.next } return e.val } [inline] fn b_fabs(v int) f64 { return if v < 0 { -v } else { v } } // inline functions here for speed // rather than full impl in vlib [inline] fn fnv1a32(data string) u32 { mut hash := fnv32_offset_basis for i := 0; i < data.len; i++ { hash = (hash ^ u32(data[i])) * fnv32_prime } return hash } [inline] fn fnv1a64(data string) u64 { mut hash := fnv64_offset_basis for i := 0; i < data.len; i++ { hash = (hash ^ u64(data[i])) * fnv64_prime } return hash }