Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add x namespace for experimental libraries and inthashmap implementation #2209

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions fns.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
new_array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fns.txt should be removed from the PR. It is a generated file by v -debug

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, please remove fns.txt

_make
array_repeat_old
_panic_debug
acos
copy
compare_ints
backtrace
calloc
compare_strings
compare_lower_strings
compare_strings_by_len
is_space
exit
eprintln
error
free
memcpy
isnil
malloc
main__test_int2int
math__abs
math__acos
math__asin
math__atan
math__atan2
math__cbrt
math__ceil
math__cos
math__cosh
math__degrees
math__exp
math__digits
math__erf
math__erfc
math__exp2
math__floor
math__fmod
math__gamma
math__gcd
math__hypot
math__lcm
math__log
math__log2
math__log10
math__log_gamma
math__log_n
math__max
math__min
math__pow
math__radians
math__round
math__sin
math__sinh
math__sqrt
math__tan
math__tanh
math__trunc
memmove
memdup
new_array_from_c_array
new_array_from_c_array_no_alloc
strlen
realloc
on_panic
new_map
new_hashmap
new_map_init
new_node
print_backtrace_skipping_top_frames
print_backtrace
panic
opt_ok
opt_none
print
preorder_keys
println
ptr_str
string_from_wide
string_from_wide2
strings__new_builder
strings__levenshtein_distance
strings__dice_coefficient
strings__levenshtein_distance_percentage
strings__repeat
vstrlen
todo
tos
tos_clone
tos2
v_ptr_free
utf8_char_len
utf32_to_str
utf32_to_str_no_malloc
utf8_len
utf8_getchar
x_dot_hashmap__hm_next_power_of_two
x_dot_hashmap__hm_math_max
x_dot_hashmap__hm_get_pot_array_size
x_dot_hashmap__hm_phi_mix
x_dot_hashmap__new_int_hashmap
x_dot_hashmap__new_int_hashmap_options
288 changes: 288 additions & 0 deletions vlib/x/hashmap/int_hashmap.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
module hashmap

import math

[inline]
fn hm_next_power_of_two(_x i64) i64 {
if _x == 0 {
return 1
}

mut x := _x
x--
x |= x >> 1
x |= x >> 2
x |= x >> 4
x |= x >> 8
x |= x >> 16
x |= x >> 32

return x + 1
}

[inline]
fn hm_math_max(num_a, num_b int) int {
if num_a > num_b {
return num_a
}

return num_b
}

[inline]
fn hm_get_pot_array_size(expected int, factor f32) int {
return hm_math_max(2, int(hm_next_power_of_two(i64(math.ceil(f32(expected) / factor)))))
}


[inline]
fn hm_phi_mix(x int) int {
h := x * 0x9e3779b9
return h ^ (h >> 16)
}

// imagine having to implement a hash map yourself just to store some tiles in an efficient way

struct IntHashMap {
mut:
fill_factor f32
threshold int
_size int
mask u32

_keys []int
_values []voidptr
_used []bool
}

const (
HMDefSize = 16
HMDefFillFactor = 0.75
)

[inline]
pub fn new_int_hashmap() IntHashMap {
return new_int_hashmap_options(HMDefSize, HMDefFillFactor)
}

pub fn new_int_hashmap_options(size int, fill_factor f32) IntHashMap {
mut hashmap := IntHashMap{}
capacity := hm_get_pot_array_size(size, fill_factor)
hashmap.mask = u32(capacity - 1)
hashmap.fill_factor = fill_factor

hashmap._keys = [0].repeat(capacity)
hashmap._values = [voidptr(0)].repeat(capacity)
hashmap._used = [false].repeat(capacity)
hashmap.threshold = int(f32(capacity) * f32(fill_factor))

return hashmap
}

pub fn (hashmap &IntHashMap) get(key int) voidptr {
idx := hashmap._read_index(key)

if idx != -1 { // unsafe!
return C.array__get(hashmap._values, idx)
} else {
return 0
}
}

[inline]
pub fn (hashmap &IntHashMap) has(key int) bool {
return hashmap._read_index(key) != -1
}

pub fn (hashmap mut IntHashMap) put(key int, value voidptr) {
mut idx := hashmap._put_index(key)
if idx < 0 {
//println('no space, rehashing')
hashmap._rehash(hashmap._keys.len * 2)
idx = hashmap._put_index(key)
}

if !hashmap._used[idx] {
hashmap._keys[idx] = key
hashmap._values[idx] = value
hashmap._used[idx] = true
hashmap._size++

if hashmap._size >= hashmap.threshold {
//println('hit hashmap threshold, rehashing')
hashmap._rehash(hashmap._keys.len * 2)
}
} else {
hashmap._values[idx] = value
}
}

[inline]
pub fn (hashmap &IntHashMap) size() int {
return hashmap._size
}

pub fn (hashmap &IntHashMap) keys() []int {
mut keys := [0].repeat(hashmap._size)
mut kidx := 0

for idx := 0; idx < hashmap._keys.len; idx++ {
if hashmap._used[idx] {
keys[kidx] = hashmap._keys[idx]
kidx++
}
}

return keys
}

// unsafe
pub fn (hashmap mut IntHashMap) remove(key int) voidptr {
idx := hashmap._read_index(key)

if idx == -1 {
return 0
}

res := C.array__get(hashmap._values, idx)
hashmap._size--
hashmap._shift_keys(idx)
return res
}

pub fn (hashmap mut IntHashMap) clear() {
for idx := 0; idx < hashmap._keys.len; idx++ {
if hashmap._used[idx] {
hashmap._size--
hashmap._shift_keys(idx)
}
}
}

[inline]
fn (hashmap &IntHashMap) _start_index(key int) int {
return int(u32(hm_phi_mix(key)) & hashmap.mask)
}

[inline]
fn (hashmap &IntHashMap) _next_index(key int) int {
return int(u32(key + 1) & hashmap.mask)
}

[inline]
fn (hashmap &IntHashMap) _read_index(key int) int {
mut idx := hashmap._start_index(key)

if !hashmap._used[idx] {
return -1
}

if hashmap._keys[idx] == key && hashmap._used[idx] {
return idx
}

start_idx := idx
for {
idx = hashmap._next_index(idx)

if idx == start_idx || !hashmap._used[idx] {
return -1
}

if hashmap._keys[idx] == key && hashmap._used[idx] {
return idx
}
}

return -1
}

[inline]
fn (hashmap &IntHashMap) _put_index(key int) int {
read_idx := hashmap._read_index(key)
if read_idx >= 0 {
return read_idx
}

start_idx := hashmap._start_index(key)
mut idx := start_idx

for {
if !hashmap._used[idx] {
break
}

idx = hashmap._next_index(key)
if idx == start_idx {
return -1
}
}

return idx
}

fn (hashmap mut IntHashMap) _rehash(new_capacity int) {
hashmap.threshold = int(f32(new_capacity) * f32(hashmap.fill_factor))
hashmap.mask = u32(new_capacity - 1)

old_capacity := hashmap._keys.len
old_keys := hashmap._keys
old_values := hashmap._values
old_used := hashmap._used
//println('rehash $old_capacity -> $new_capacity')

hashmap._keys = [0].repeat(new_capacity)
hashmap._values = [voidptr(0)].repeat(new_capacity)
hashmap._used = [false].repeat(new_capacity)
hashmap._size = 0

for i := old_capacity; i > 0; i-- {
if old_used[i] {
hashmap.put(old_keys[i], old_values[i])
}
}
}

fn (hashmap mut IntHashMap) _shift_keys(_pos int) int {
mut last := 0
mut k := 0
mut pos := _pos

for {
last = pos
pos = hashmap._next_index(pos)

for {
k = hashmap._keys[pos]
if !hashmap._used[pos] {
hashmap._keys[last] = 0
hashmap._values[last] = voidptr(0)
hashmap._used[last] = false

return last
}

slot := hashmap._start_index(k)

if last <= pos {
if last >= slot || slot > pos {
break
}
} else {
if last >= slot && slot > pos {
break
}
}

pos = hashmap._next_index(pos)
}

hashmap._keys[last] = k
hashmap._values[last] = hashmap._values[pos]
hashmap._used[last] = hashmap._used[pos]
}

return -1

//panic('should not happen!')
}
Loading