244 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			V
		
	
	
| // 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
 | |
| 
 | |
| import hash.wyhash
 | |
| 
 | |
| const (
 | |
| 	log_size = 5
 | |
| 	n_hashbits = 24
 | |
| 	window_size = 16
 | |
| 	initial_size = 1 << log_size
 | |
| 	initial_cap = initial_size - 1
 | |
| 	default_load_factor = 0.8
 | |
| 	hashbit_mask = u32(0xFFFFFF)
 | |
| 	probe_offset = u32(0x1000000)
 | |
| 	max_probe = u32(0xFF000000)
 | |
| )
 | |
| 
 | |
| pub struct Hashmap {
 | |
| mut:
 | |
| 	cap         u32
 | |
| 	shift       byte
 | |
| 	window      byte
 | |
| 	info        &u32
 | |
| 	key_values  &KeyValue
 | |
| pub mut:
 | |
| 	load_factor f32
 | |
| 	size        int
 | |
| }
 | |
| 
 | |
| struct KeyValue {
 | |
| 	key   string
 | |
| mut:
 | |
| 	value int
 | |
| }
 | |
| 
 | |
| pub fn new_hashmap() Hashmap {
 | |
| 	return Hashmap{
 | |
| 		cap: initial_cap
 | |
| 		shift: log_size
 | |
| 		window: window_size
 | |
| 		info: &u32(calloc(sizeof(u32) * initial_size))
 | |
| 		key_values: &KeyValue(calloc(sizeof(KeyValue) * initial_size))
 | |
| 		load_factor: default_load_factor
 | |
| 		size: 0
 | |
| 	}
 | |
| }
 | |
| 
 | |
| pub fn (h mut Hashmap) set(key string, value int) {
 | |
| 	// load_factor can be adjusted.
 | |
| 	if (f32(h.size) / f32(h.cap)) > h.load_factor {
 | |
| 		h.rehash()
 | |
| 	}
 | |
| 	hash := wyhash.wyhash_c(key.str, u64(key.len), 0)
 | |
| 	mut info := u32(((hash >> h.shift) & hashbit_mask) | probe_offset)
 | |
| 	mut index := hash & h.cap
 | |
| 	// While probe count is less
 | |
| 	for info < h.info[index] {
 | |
| 		index = (index + 1) & h.cap
 | |
| 		info += probe_offset
 | |
| 	}
 | |
| 	// While we might have a match
 | |
| 	for info == h.info[index] {
 | |
| 		if key == h.key_values[index].key {
 | |
| 			h.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_kv := KeyValue{key, value}
 | |
| 	for h.info[index] != 0 {
 | |
| 		if info > h.info[index] {
 | |
| 			// Swap info word
 | |
| 			tmp_info := h.info[index]
 | |
| 			h.info[index] = info
 | |
| 			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
 | |
| 	}
 | |
| 	// Should almost never happen
 | |
| 	if (info & max_probe) == max_probe {
 | |
| 		h.rehash()
 | |
| 		h.set(current_kv.key, current_kv.value)
 | |
| 		return
 | |
| 	}
 | |
| 	h.info[index] = info
 | |
| 	h.key_values[index] = current_kv
 | |
| 	h.size++
 | |
| }
 | |
| 
 | |
| fn (h mut Hashmap) rehash() {
 | |
| 	old_cap := h.cap
 | |
| 	h.window--
 | |
| 	// check if any hashbits are left
 | |
| 	if h.window == 0 {
 | |
| 		h.shift += window_size
 | |
| 	}
 | |
| 	// double the size of the hashmap
 | |
| 	h.cap = ((h.cap + 1) << 1) - 1
 | |
| 	mut new_key_values := &KeyValue(calloc(sizeof(KeyValue) * (h.cap + 1)))
 | |
| 	mut new_info := &u32(calloc(sizeof(u32) * (h.cap + 1)))
 | |
| 	for i in 0 .. (old_cap + 1) {
 | |
| 		if h.info[i] != 0 {
 | |
| 			mut kv := h.key_values[i]
 | |
| 			mut hash := u64(0)
 | |
| 			mut info := u32(0)
 | |
| 			if h.window == 0 {
 | |
| 				hash = wyhash.wyhash_c(kv.key.str, u64(kv.key.len), 0)
 | |
| 				info = u32(((hash >> h.shift) & hashbit_mask) | probe_offset)
 | |
| 			}
 | |
| 			else {
 | |
| 				original := u64(i - ((h.info[i] >> n_hashbits) - 1)) & (h.cap >> 1)
 | |
| 				hash = original | (h.info[i] << h.shift)
 | |
| 				info = (h.info[i] & hashbit_mask) | 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
 | |
| 			}
 | |
| 			// Probe until an empty index is found.
 | |
| 			// Swap when probe count is higher/richer (Robin Hood).
 | |
| 			for new_info[index] != 0 {
 | |
| 				if info > new_info[index] {
 | |
| 					// Swap info word
 | |
| 					tmp_info := new_info[index]
 | |
| 					new_info[index] = info
 | |
| 					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
 | |
| 			}
 | |
| 			// Should almost never happen
 | |
| 			if (info & max_probe) == max_probe {
 | |
| 				h.rehash()
 | |
| 				h.set(kv.key, kv.value)
 | |
| 				return
 | |
| 			}
 | |
| 			new_info[index] = info
 | |
| 			new_key_values[index] = kv
 | |
| 		}
 | |
| 	}
 | |
| 	if h.window == 0 {
 | |
| 		h.window = window_size
 | |
| 	}
 | |
| 	free(h.key_values)
 | |
| 	free(h.info)
 | |
| 	h.key_values = new_key_values
 | |
| 	h.info = new_info
 | |
| }
 | |
| 
 | |
| pub fn (h mut Hashmap) delete(key string) {
 | |
| 	hash := wyhash.wyhash_c(key.str, u64(key.len), 0)
 | |
| 	mut index := hash & h.cap
 | |
| 	mut info := u32(((hash >> h.shift) & hashbit_mask) | probe_offset)
 | |
| 	for info < h.info[index] {
 | |
| 		index = (index + 1) & h.cap
 | |
| 		info += probe_offset
 | |
| 	}
 | |
| 	// Perform backwards shifting
 | |
| 	for info == h.info[index] {
 | |
| 		if key == h.key_values[index].key {
 | |
| 			mut old_index := index
 | |
| 			index = (index + 1) & h.cap
 | |
| 			mut current_info := h.info[index]
 | |
| 			for (current_info >> n_hashbits) > 1 {
 | |
| 				h.info[old_index] = current_info - probe_offset
 | |
| 				h.key_values[old_index] = h.key_values[index]
 | |
| 				old_index = index
 | |
| 				index = (index + 1) & h.cap
 | |
| 				current_info = h.info[index]
 | |
| 			}
 | |
| 			h.info[old_index] = 0
 | |
| 			h.size--
 | |
| 			return
 | |
| 		}
 | |
| 		index = (index + 1) & h.cap
 | |
| 		info += probe_offset
 | |
| 	}
 | |
| }
 | |
| 
 | |
| pub fn (h Hashmap) get(key string) int {
 | |
| 	hash := wyhash.wyhash_c(key.str, u64(key.len), 0)
 | |
| 	mut index := hash & h.cap
 | |
| 	mut info := u32(((hash >> h.shift) & hashbit_mask) | probe_offset)
 | |
| 	for info < h.info[index] {
 | |
| 		index = (index + 1) & h.cap
 | |
| 		info += probe_offset
 | |
| 	}
 | |
| 	for info == h.info[index] {
 | |
| 		if key == h.key_values[index].key {
 | |
| 			return h.key_values[index].value
 | |
| 		}
 | |
| 		index = (index + 1) & h.cap
 | |
| 		info += probe_offset
 | |
| 	}
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| pub fn (h Hashmap) exists(key string) bool {
 | |
| 	hash := wyhash.wyhash_c(key.str, u64(key.len), 0)
 | |
| 	mut index := hash & h.cap
 | |
| 	mut info := u32(((hash >> h.shift) & hashbit_mask) | probe_offset)
 | |
| 	for info < h.info[index] {
 | |
| 		index = (index + 1) & h.cap
 | |
| 		info += probe_offset
 | |
| 	}
 | |
| 	for info == h.info[index] {
 | |
| 		if key == h.key_values[index].key {
 | |
| 			return true
 | |
| 		}
 | |
| 		index = (index + 1) & h.cap
 | |
| 		info += probe_offset
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| pub fn (h Hashmap) keys() []string {
 | |
| 	mut keys := [''].repeat(h.size)
 | |
| 	mut j := 0
 | |
| 	for i in 0 .. (h.cap + 1) {
 | |
| 		if h.info[i] != 0 {
 | |
| 			keys[j] = h.key_values[i].key
 | |
| 			j++
 | |
| 		}
 | |
| 	}
 | |
| 	return keys
 | |
| }
 |