v/vlib/builtin/hashmap/hashmap.v

122 lines
2.4 KiB
V
Raw Normal View History

// 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.
2019-12-21 22:38:43 +01:00
module hashmap
/*
This is work in progress.
2019-12-21 22:38:43 +01:00
A very early test version of the Hashmap with a fixed size.
Only works with string keys and int values for now.
2019-12-21 22:38:43 +01:00
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.
*/
2019-12-19 21:52:45 +01:00
2019-12-21 22:38:43 +01:00
struct Hashmap {
2019-12-19 21:52:45 +01:00
cap int
keys []string
2019-12-21 22:38:43 +01:00
table []Hashmapentry
2019-12-19 21:52:45 +01:00
elm_size int
2019-12-21 22:38:43 +01:00
pub mut:
nr_collisions int
}
2019-12-21 22:38:43 +01:00
struct Hashmapentry {
mut:
2019-12-19 21:52:45 +01:00
key string
val int
2019-12-21 22:38:43 +01:00
next &Hashmapentry // linked list for collisions
}
const (
2019-12-19 21:52:45 +01:00
min_cap = 2<<10
max_cap = 2<<20
)
2019-12-21 07:59:12 +01:00
const(
fnv64_prime = 1099511628211
fnv64_offset_basis = 14695981039346656037
2019-12-21 07:59:12 +01:00
)
const(
fnv32_offset_basis = u32(2166136261)
fnv32_prime = u32(16777619)
)
2019-12-21 22:38:43 +01:00
pub fn new_hashmap(planned_nr_items int) Hashmap {
2019-10-11 05:36:46 +02:00
mut cap := planned_nr_items * 5
if cap < min_cap {
cap = min_cap
2019-12-19 21:52:45 +01:00
}
if cap > max_cap {
cap = max_cap
2019-12-19 21:52:45 +01:00
}
2019-12-21 22:38:43 +01:00
return Hashmap{
cap: cap
elm_size: 4
2019-12-21 22:38:43 +01:00
table: make(cap, cap, sizeof(Hashmapentry))
2019-12-19 21:52:45 +01:00
}
}
2019-12-21 22:38:43 +01:00
pub fn (m mut Hashmap) set(key string, val int) {
2019-12-21 07:59:12 +01:00
// mut hash := int(b_fabs(key.hash()))
// idx := hash % m.cap
idx := int(fnv1a32(key) % m.cap)
if m.table[idx].key.len != 0 {
2019-12-19 21:52:45 +01:00
// println('\nset() idx=$idx key="$key" hash="$hash" val=$val')
m.nr_collisions++
2019-12-19 21:52:45 +01:00
// println('collision:' + m.table[idx].key)
mut e := &m.table[idx]
for e.next != 0 {
e = e.next
2019-12-19 21:52:45 +01:00
}
2019-12-21 22:38:43 +01:00
e.next = &Hashmapentry{
2019-12-19 21:52:45 +01:00
key,val,0}
}
else {
2019-12-21 22:38:43 +01:00
m.table[idx] = Hashmapentry{
2019-12-19 21:52:45 +01:00
key,val,0}
}
2019-12-19 21:52:45 +01:00
}
2019-12-21 22:38:43 +01:00
pub fn (m &Hashmap) get(key string) int {
2019-12-21 07:59:12 +01:00
// mut hash := int(b_fabs(key.hash()))
// idx := hash % m.cap
idx := int(fnv1a32(key) % m.cap)
mut e := &m.table[idx]
2019-12-19 21:52:45 +01:00
for e.next != 0 {
// todo unsafe {
if e.key == key {
return e.val
2019-12-19 21:52:45 +01:00
}
e = e.next
2019-12-19 21:52:45 +01:00
}
return e.val
}
2019-12-19 21:52:45 +01:00
[inline]
fn b_fabs(v int) f64 {
return if v < 0 { -v } else { v }
}
2019-12-21 07:59:12 +01:00
// 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
}