| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module builtin |
| 5 | |
| 6 | /* |
| 7 | This is a highly optimized hashmap implementation. It has several traits that |
| 8 | in combination makes it very fast and memory efficient. Here is a short expl- |
| 9 | anation of each trait. After reading this you should have a basic understand- |
| 10 | ing of how it functions: |
| 11 | |
| 12 | 1. Hash-function: Wyhash. Wyhash is the fastest hash-function for short keys |
| 13 | passing SMHasher, so it was an obvious choice. |
| 14 | |
| 15 | 2. Open addressing: Robin Hood Hashing. With this method, a hash-collision is |
| 16 | resolved by probing. As opposed to linear probing, Robin Hood hashing has a |
| 17 | simple but clever twist: As new keys are inserted, old keys are shifted arou- |
| 18 | nd in a way such that all keys stay reasonably close to the slot they origin- |
| 19 | ally hash to. A new key may displace a key already inserted if its probe cou- |
| 20 | nt is larger than that of the key at the current position. |
| 21 | |
| 22 | 3. Memory layout: key-value pairs are stored in a `DenseArray`. This is a dy- |
| 23 | namic array with a very low volume of unused memory, at the cost of more rea- |
| 24 | llocations when inserting elements. It also preserves the order of the key-v- |
| 25 | alues. This array is named `key_values`. Instead of probing a new key-value, |
| 26 | this map probes two 32-bit numbers collectively. The first number has its 8 |
| 27 | most significant bits reserved for the probe-count and the remaining 24 bits |
| 28 | are cached bits from the hash which are utilized for faster re-hashing. This |
| 29 | number is often referred to as `meta`. The other 32-bit number is the index |
| 30 | at which the key-value was pushed to in `key_values`. Both of these numbers |
| 31 | are stored in a sparse array `metas`. The `meta`s and `kv_index`s are stored |
| 32 | at even and odd indices, respectively: |
| 33 | |
| 34 | metas = [meta, kv_index, 0, 0, meta, kv_index, 0, 0, meta, kv_index, ...] |
| 35 | key_values = [kv, kv, kv, ...] |
| 36 | |
| 37 | 4. The size of metas is a power of two. This enables the use of bitwise AND |
| 38 | to convert the 64-bit hash to a bucket/index that doesn't overflow metas. If |
| 39 | the size is power of two you can use "hash & (SIZE - 1)" instead of "hash % |
| 40 | SIZE". Modulo is extremely expensive so using '&' is a big performance impro- |
| 41 | vement. The general concern with this approach is that you only make use of |
| 42 | the lower bits of the hash which can cause more collisions. This is solved by |
| 43 | using a well-dispersed hash-function. |
| 44 | |
| 45 | 5. The hashmap keeps track of the highest probe_count. The trick is to alloc- |
| 46 | ate `extra_metas` > max(probe_count), so you never have to do any bounds-che- |
| 47 | cking since the extra meta memory ensures that a meta will never go beyond |
| 48 | the last index. |
| 49 | |
| 50 | 6. Cached rehashing. When the `load_factor` of the map exceeds the `max_load_ |
| 51 | factor` the size of metas is doubled and all the key-values are "rehashed" to |
| 52 | find the index for their meta's in the new array. Instead of rehashing compl- |
| 53 | etely, it simply uses the cached-hashbits stored in the meta, resulting in |
| 54 | much faster rehashing. |
| 55 | */ |
| 56 | // Number of bits from the hash stored for each entry |
| 57 | const hashbits = 24 |
| 58 | // Number of bits from the hash stored for rehashing |
| 59 | const max_cached_hashbits = 16 |
| 60 | // Initial log-number of buckets in the hashtable |
| 61 | const init_log_capicity = 5 |
| 62 | // Initial number of buckets in the hashtable |
| 63 | const init_capicity = 1 << init_log_capicity |
| 64 | // Maximum load-factor (len / capacity) |
| 65 | const max_load_factor = 0.8 |
| 66 | // Initial highest even index in metas |
| 67 | const init_even_index = init_capicity - 2 |
| 68 | // Used for incrementing `extra_metas` when max |
| 69 | // probe count is too high, to avoid overflow |
| 70 | const extra_metas_inc = 4 |
| 71 | // Bitmask to select all the hashbits |
| 72 | const hash_mask = u32(0x00FFFFFF) |
| 73 | // Used for incrementing the probe-count |
| 74 | const probe_inc = u32(0x01000000) |
| 75 | |
| 76 | // DenseArray represents a dynamic array with very low growth factor |
| 77 | struct DenseArray { |
| 78 | key_bytes int |
| 79 | value_bytes int |
| 80 | mut: |
| 81 | cap int |
| 82 | len int |
| 83 | deletes u32 // count |
| 84 | // array allocated (with `cap` bytes) on first deletion |
| 85 | // has non-zero element when key deleted |
| 86 | all_deleted &u8 = unsafe { nil } |
| 87 | keys &u8 = unsafe { nil } |
| 88 | values &u8 = unsafe { nil } |
| 89 | } |
| 90 | |
| 91 | @[inline] |
| 92 | fn new_dense_array(key_bytes int, value_bytes int) DenseArray { |
| 93 | cap := 8 |
| 94 | return DenseArray{ |
| 95 | key_bytes: key_bytes |
| 96 | value_bytes: value_bytes |
| 97 | cap: cap |
| 98 | len: 0 |
| 99 | deletes: 0 |
| 100 | all_deleted: unsafe { nil } |
| 101 | keys: unsafe { malloc(__at_least_one(u64(cap) * u64(key_bytes))) } |
| 102 | values: unsafe { malloc(__at_least_one(u64(cap) * u64(value_bytes))) } |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | @[inline] |
| 107 | fn (d &DenseArray) key(i int) voidptr { |
| 108 | return unsafe { voidptr(d.keys + i * d.key_bytes) } |
| 109 | } |
| 110 | |
| 111 | // for cgen |
| 112 | @[inline] |
| 113 | fn (d &DenseArray) value(i int) voidptr { |
| 114 | return unsafe { voidptr(d.values + i * d.value_bytes) } |
| 115 | } |
| 116 | |
| 117 | @[inline] |
| 118 | fn (d &DenseArray) has_index(i int) bool { |
| 119 | return d.deletes == 0 || unsafe { d.all_deleted[i] } == 0 |
| 120 | } |
| 121 | |
| 122 | @[inline] |
| 123 | fn (mut d DenseArray) trim_deleted_tail() { |
| 124 | if d.deletes == 0 { |
| 125 | return |
| 126 | } |
| 127 | for d.len > 0 && unsafe { d.all_deleted[d.len - 1] } != 0 { |
| 128 | unsafe { |
| 129 | d.all_deleted[d.len - 1] = 0 |
| 130 | } |
| 131 | d.deletes-- |
| 132 | d.len-- |
| 133 | } |
| 134 | if d.deletes == 0 { |
| 135 | unsafe { |
| 136 | free(d.all_deleted) |
| 137 | d.all_deleted = nil |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | // Make space to append an element and return index |
| 143 | // The growth-factor is roughly 1.125 `(x + (x >> 3))` |
| 144 | @[inline] |
| 145 | fn (mut d DenseArray) expand() int { |
| 146 | old_cap := d.cap |
| 147 | old_key_size := d.key_bytes * old_cap |
| 148 | old_value_size := d.value_bytes * old_cap |
| 149 | if d.cap == d.len { |
| 150 | d.cap += d.cap >> 3 |
| 151 | unsafe { |
| 152 | d.keys = realloc_data(d.keys, old_key_size, d.key_bytes * d.cap) |
| 153 | d.values = realloc_data(d.values, old_value_size, d.value_bytes * d.cap) |
| 154 | if d.deletes != 0 { |
| 155 | d.all_deleted = realloc_data(d.all_deleted, old_cap, d.cap) |
| 156 | vmemset(voidptr(d.all_deleted + d.len), 0, d.cap - d.len) |
| 157 | } |
| 158 | } |
| 159 | } |
| 160 | push_index := d.len |
| 161 | unsafe { |
| 162 | if d.deletes != 0 { |
| 163 | d.all_deleted[push_index] = 0 |
| 164 | } |
| 165 | } |
| 166 | d.len++ |
| 167 | return push_index |
| 168 | } |
| 169 | |
| 170 | type MapHashFn = fn (voidptr) u64 |
| 171 | |
| 172 | type MapEqFn = fn (voidptr, voidptr) bool |
| 173 | |
| 174 | type MapCloneFn = fn (voidptr, voidptr) |
| 175 | |
| 176 | type MapFreeFn = fn (voidptr) |
| 177 | |
| 178 | // map is the internal representation of a V `map` type. |
| 179 | pub struct map { |
| 180 | // Number of bytes of a key |
| 181 | key_bytes int |
| 182 | // Number of bytes of a value |
| 183 | value_bytes int |
| 184 | mut: |
| 185 | // Highest even index in the hashtable |
| 186 | even_index u32 |
| 187 | // Number of cached hashbits left for rehashing |
| 188 | cached_hashbits u8 |
| 189 | // Used for right-shifting out used hashbits |
| 190 | shift u8 |
| 191 | // Array storing key-values (ordered) |
| 192 | key_values DenseArray |
| 193 | // Pointer to meta-data: |
| 194 | // - Odd indices store kv_index. |
| 195 | // - Even indices store probe_count and hashbits. |
| 196 | metas &u32 |
| 197 | // Extra metas that allows for no ranging when incrementing |
| 198 | // index in the hashmap |
| 199 | extra_metas u32 |
| 200 | has_string_keys bool |
| 201 | hash_fn MapHashFn |
| 202 | key_eq_fn MapEqFn |
| 203 | clone_fn MapCloneFn |
| 204 | free_fn MapFreeFn |
| 205 | pub mut: |
| 206 | // Number of key-values currently in the hashmap |
| 207 | len int |
| 208 | } |
| 209 | |
| 210 | @[inline] |
| 211 | fn map_eq_string(a voidptr, b voidptr) bool { |
| 212 | return fast_string_eq(*unsafe { &string(a) }, *unsafe { &string(b) }) |
| 213 | } |
| 214 | |
| 215 | @[inline] |
| 216 | fn map_eq_int_1(a voidptr, b voidptr) bool { |
| 217 | return unsafe { *&u8(a) == *&u8(b) } |
| 218 | } |
| 219 | |
| 220 | @[inline] |
| 221 | fn map_eq_int_2(a voidptr, b voidptr) bool { |
| 222 | return unsafe { *&u16(a) == *&u16(b) } |
| 223 | } |
| 224 | |
| 225 | @[inline] |
| 226 | fn map_eq_int_4(a voidptr, b voidptr) bool { |
| 227 | return unsafe { *&u32(a) == *&u32(b) } |
| 228 | } |
| 229 | |
| 230 | @[inline] |
| 231 | fn map_eq_int_8(a voidptr, b voidptr) bool { |
| 232 | return unsafe { *&u64(a) == *&u64(b) } |
| 233 | } |
| 234 | |
| 235 | // map_map_eq compares two maps for equality. |
| 236 | // Returns true if both maps have the same keys and associated values. |
| 237 | fn map_map_eq(a map, b map) bool { |
| 238 | if a.len != b.len { |
| 239 | return false |
| 240 | } |
| 241 | for i := 0; i < a.key_values.len; i++ { |
| 242 | if !a.key_values.has_index(i) { |
| 243 | continue |
| 244 | } |
| 245 | k := a.key_values.key(i) |
| 246 | if !b.exists(k) { |
| 247 | return false |
| 248 | } |
| 249 | va := a.key_values.value(i) |
| 250 | vb := b.get(k, va) |
| 251 | if unsafe { vmemcmp(va, vb, a.value_bytes) } != 0 { |
| 252 | return false |
| 253 | } |
| 254 | } |
| 255 | return true |
| 256 | } |
| 257 | |
| 258 | @[inline] |
| 259 | fn map_clone_string(dest voidptr, pkey voidptr) { |
| 260 | unsafe { |
| 261 | s := *&string(pkey) |
| 262 | cloned := s.clone() |
| 263 | // Use memcpy for native backend compatibility |
| 264 | // (*&string(dest)) = cloned doesn't reliably store full struct |
| 265 | vmemcpy(dest, voidptr(&cloned), sizeof(string)) |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | @[inline] |
| 270 | fn map_clone_int_1(dest voidptr, pkey voidptr) { |
| 271 | unsafe { |
| 272 | *&u8(dest) = *&u8(pkey) |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | @[inline] |
| 277 | fn map_clone_int_2(dest voidptr, pkey voidptr) { |
| 278 | unsafe { |
| 279 | *&u16(dest) = *&u16(pkey) |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | @[inline] |
| 284 | fn map_clone_int_4(dest voidptr, pkey voidptr) { |
| 285 | unsafe { |
| 286 | *&u32(dest) = *&u32(pkey) |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | @[inline] |
| 291 | fn map_clone_int_8(dest voidptr, pkey voidptr) { |
| 292 | unsafe { |
| 293 | *&u64(dest) = *&u64(pkey) |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | @[inline] |
| 298 | fn map_free_string(pkey voidptr) { |
| 299 | unsafe { |
| 300 | (*&string(pkey)).free() |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | @[inline] |
| 305 | fn map_free_nop(_ voidptr) { |
| 306 | } |
| 307 | |
| 308 | fn new_map(key_bytes int, value_bytes int, hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn) map { |
| 309 | metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc)) |
| 310 | // for now assume anything bigger than a pointer is a string |
| 311 | has_string_keys := key_bytes > int(sizeof(voidptr)) |
| 312 | return map{ |
| 313 | key_bytes: key_bytes |
| 314 | value_bytes: value_bytes |
| 315 | even_index: init_even_index |
| 316 | cached_hashbits: max_cached_hashbits |
| 317 | shift: init_log_capicity |
| 318 | key_values: new_dense_array(key_bytes, value_bytes) |
| 319 | metas: unsafe { &u32(vcalloc_noscan(metasize)) } |
| 320 | extra_metas: extra_metas_inc |
| 321 | len: 0 |
| 322 | has_string_keys: has_string_keys |
| 323 | hash_fn: hash_fn |
| 324 | key_eq_fn: key_eq_fn |
| 325 | clone_fn: clone_fn |
| 326 | free_fn: free_fn |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | fn new_map_init(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn, n int, key_bytes int, |
| 331 | value_bytes int, keys voidptr, values voidptr) map { |
| 332 | mut out := new_map(key_bytes, value_bytes, hash_fn, key_eq_fn, clone_fn, free_fn) |
| 333 | // TODO: pre-allocate n slots |
| 334 | mut pkey := &u8(keys) |
| 335 | mut pval := &u8(values) |
| 336 | for _ in 0 .. n { |
| 337 | unsafe { |
| 338 | out.set(pkey, pval) |
| 339 | pkey = pkey + key_bytes |
| 340 | pval = pval + value_bytes |
| 341 | } |
| 342 | } |
| 343 | return out |
| 344 | } |
| 345 | |
| 346 | fn new_map_update_init(update &map, n int, key_bytes int, value_bytes int, keys voidptr, values voidptr) map { |
| 347 | mut out := unsafe { update.clone() } |
| 348 | mut pkey := &u8(keys) |
| 349 | mut pval := &u8(values) |
| 350 | for _ in 0 .. n { |
| 351 | unsafe { |
| 352 | out.set(pkey, pval) |
| 353 | pkey = pkey + key_bytes |
| 354 | pval = pval + value_bytes |
| 355 | } |
| 356 | } |
| 357 | return out |
| 358 | } |
| 359 | |
| 360 | // move moves the map to a new location in memory. |
| 361 | // It does this by copying to a new location, then setting the |
| 362 | // old location to all `0` with `vmemset` |
| 363 | pub fn (mut m map) move() map { |
| 364 | r := *m |
| 365 | unsafe { |
| 366 | vmemset(m, 0, int(sizeof(map))) |
| 367 | } |
| 368 | return r |
| 369 | } |
| 370 | |
| 371 | // clear clears the map without deallocating the allocated data. |
| 372 | // It does it by setting the map length to `0` |
| 373 | // Example: mut m := {'abc': 'xyz', 'def': 'aaa'}; m.clear(); assert m.len == 0 |
| 374 | pub fn (mut m map) clear() { |
| 375 | unsafe { |
| 376 | if m.key_values.all_deleted != 0 { |
| 377 | free(m.key_values.all_deleted) |
| 378 | m.key_values.all_deleted = nil |
| 379 | } |
| 380 | vmemset(m.key_values.keys, 0, m.key_values.key_bytes * m.key_values.cap) |
| 381 | vmemset(m.metas, 0, sizeof(u32) * (m.even_index + 2 + m.extra_metas)) |
| 382 | } |
| 383 | m.key_values.len = 0 |
| 384 | m.key_values.deletes = 0 |
| 385 | m.even_index = init_even_index |
| 386 | m.cached_hashbits = max_cached_hashbits |
| 387 | m.shift = init_log_capicity |
| 388 | m.len = 0 |
| 389 | } |
| 390 | |
| 391 | @[inline] |
| 392 | fn (m &map) key_to_index(pkey voidptr) (u32, u32) { |
| 393 | if voidptr(m.hash_fn) == unsafe { nil } { |
| 394 | unsafe { |
| 395 | p := &u64(m) |
| 396 | prev2 := (&u64(usize(m) - usize(16)))[0] |
| 397 | prev1 := (&u64(usize(m) - usize(8)))[0] |
| 398 | panic('map.hash_fn is nil map_ptr=${usize(m)} key_bytes=${m.key_bytes} value_bytes=${m.value_bytes} even_index=${m.even_index} shift=${m.shift} metas=${usize(m.metas)} prev2=${prev2} prev1=${prev1} w0=${p[0]} w1=${p[1]} w2=${p[2]} w3=${p[3]} w4=${p[4]} w5=${p[5]} w6=${p[6]} w7=${p[7]} hash_fn=${usize(voidptr(m.hash_fn))}') |
| 399 | } |
| 400 | } |
| 401 | hash := m.hash_fn(pkey) |
| 402 | index := hash & m.even_index |
| 403 | meta := ((hash >> m.shift) & hash_mask) | probe_inc |
| 404 | return u32(index), u32(meta) |
| 405 | } |
| 406 | |
| 407 | @[inline] |
| 408 | fn (m &map) meta_less(_index u32, _metas u32) (u32, u32) { |
| 409 | mut index := _index |
| 410 | mut meta := _metas |
| 411 | for meta < unsafe { m.metas[index] } { |
| 412 | index += 2 |
| 413 | meta += probe_inc |
| 414 | } |
| 415 | return index, meta |
| 416 | } |
| 417 | |
| 418 | @[inline] |
| 419 | fn (mut m map) meta_greater(_index u32, _metas u32, kvi u32) { |
| 420 | mut meta := _metas |
| 421 | mut index := _index |
| 422 | mut kv_index := kvi |
| 423 | for unsafe { m.metas[index] } != 0 { |
| 424 | if meta > unsafe { m.metas[index] } { |
| 425 | unsafe { |
| 426 | tmp_meta := m.metas[index] |
| 427 | m.metas[index] = meta |
| 428 | meta = tmp_meta |
| 429 | tmp_index := m.metas[index + 1] |
| 430 | m.metas[index + 1] = kv_index |
| 431 | kv_index = tmp_index |
| 432 | } |
| 433 | } |
| 434 | index += 2 |
| 435 | meta += probe_inc |
| 436 | // Grow metas if probing is approaching the buffer boundary |
| 437 | if index + 2 >= m.even_index + 2 + m.extra_metas { |
| 438 | m.ensure_extra_metas_grow() |
| 439 | } |
| 440 | } |
| 441 | unsafe { |
| 442 | m.metas[index] = meta |
| 443 | m.metas[index + 1] = kv_index |
| 444 | } |
| 445 | probe_count := (meta >> hashbits) - 1 |
| 446 | m.ensure_extra_metas(probe_count) |
| 447 | } |
| 448 | |
| 449 | fn (mut m map) ensure_extra_metas_grow() { |
| 450 | size_of_u32 := sizeof(u32) |
| 451 | old_mem_size := (m.even_index + 2 + m.extra_metas) |
| 452 | m.extra_metas += extra_metas_inc |
| 453 | mem_size := (m.even_index + 2 + m.extra_metas) |
| 454 | unsafe { |
| 455 | x := realloc_data(byteptr(m.metas), int(size_of_u32 * old_mem_size), |
| 456 | int(size_of_u32 * mem_size)) |
| 457 | m.metas = &u32(x) |
| 458 | vmemset(byteptr(m.metas) + (mem_size - extra_metas_inc) * size_of_u32, 0, |
| 459 | int(sizeof(u32) * extra_metas_inc)) |
| 460 | } |
| 461 | } |
| 462 | |
| 463 | @[inline] |
| 464 | fn (mut m map) ensure_extra_metas(probe_count u32) { |
| 465 | if (probe_count << 1) == m.extra_metas { |
| 466 | size_of_u32 := sizeof(u32) |
| 467 | old_mem_size := (m.even_index + 2 + m.extra_metas) |
| 468 | m.extra_metas += extra_metas_inc |
| 469 | mem_size := (m.even_index + 2 + m.extra_metas) |
| 470 | unsafe { |
| 471 | x := realloc_data(byteptr(m.metas), int(size_of_u32 * old_mem_size), |
| 472 | int(size_of_u32 * mem_size)) |
| 473 | m.metas = &u32(x) |
| 474 | vmemset(byteptr(m.metas) + (mem_size - extra_metas_inc) * size_of_u32, 0, |
| 475 | int(sizeof(u32) * extra_metas_inc)) |
| 476 | } |
| 477 | // Should almost never happen |
| 478 | if probe_count == 252 { |
| 479 | panic('Probe overflow') |
| 480 | } |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | // Insert new element to the map. The element is inserted if its key is |
| 485 | // not equivalent to the key of any other element already in the container. |
| 486 | // If the key already exists, its value is changed to the value of the new element. |
| 487 | fn (mut m map) set(key voidptr, value voidptr) { |
| 488 | // Integer-based load factor check: equivalent to (2*len)/even_index > 0.8 |
| 489 | // which simplifies to 5*len > 2*even_index (avoids float ops broken in ARM64 backend) |
| 490 | if u32(5) * u32(m.len) > u32(2) * m.even_index { |
| 491 | m.expand() |
| 492 | } |
| 493 | mut index, mut meta := m.key_to_index(key) |
| 494 | index, meta = m.meta_less(index, meta) |
| 495 | // While we might have a match |
| 496 | for meta == unsafe { m.metas[index] } { |
| 497 | kv_index := int(unsafe { m.metas[index + 1] }) |
| 498 | pkey := unsafe { m.key_values.key(kv_index) } |
| 499 | if m.key_eq_fn(key, pkey) { |
| 500 | unsafe { |
| 501 | pval := m.key_values.value(kv_index) |
| 502 | vmemcpy(pval, value, m.value_bytes) |
| 503 | } |
| 504 | return |
| 505 | } |
| 506 | index += 2 |
| 507 | meta += probe_inc |
| 508 | } |
| 509 | kv_index := m.key_values.expand() |
| 510 | unsafe { |
| 511 | pkey := m.key_values.key(kv_index) |
| 512 | pvalue := m.key_values.value(kv_index) |
| 513 | m.clone_fn(pkey, key) |
| 514 | vmemcpy(pvalue, value, m.value_bytes) |
| 515 | } |
| 516 | m.meta_greater(index, meta, u32(kv_index)) |
| 517 | m.len++ |
| 518 | } |
| 519 | |
| 520 | // Doubles the size of the hashmap |
| 521 | fn (mut m map) expand() { |
| 522 | old_cap := m.even_index |
| 523 | m.even_index = ((m.even_index + 2) << 1) - 2 |
| 524 | // Check if any hashbits are left |
| 525 | if m.cached_hashbits == 0 { |
| 526 | m.shift += max_cached_hashbits |
| 527 | m.cached_hashbits = max_cached_hashbits |
| 528 | m.rehash() |
| 529 | } else { |
| 530 | m.cached_rehash(old_cap) |
| 531 | m.cached_hashbits-- |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | // rehash reconstructs the hash table. |
| 536 | // All the elements in the container are rearranged according |
| 537 | // to their hash value into the newly sized key-value container. |
| 538 | // Rehashes are performed when the load_factor is going to surpass |
| 539 | // the max_load_factor in an operation. |
| 540 | fn (mut m map) rehash() { |
| 541 | meta_bytes := sizeof(u32) * (m.even_index + 2 + m.extra_metas) |
| 542 | m.reserve_metas(meta_bytes) |
| 543 | } |
| 544 | |
| 545 | fn (mut m map) reserve_metas(meta_bytes u32) { |
| 546 | unsafe { |
| 547 | // TODO: use realloc_data here too |
| 548 | x := v_realloc(byteptr(m.metas), int(meta_bytes)) |
| 549 | m.metas = &u32(x) |
| 550 | vmemset(m.metas, 0, int(meta_bytes)) |
| 551 | } |
| 552 | for i := 0; i < m.key_values.len; i++ { |
| 553 | if !m.key_values.has_index(i) { |
| 554 | continue |
| 555 | } |
| 556 | pkey := unsafe { m.key_values.key(i) } |
| 557 | mut index, mut meta := m.key_to_index(pkey) |
| 558 | index, meta = m.meta_less(index, meta) |
| 559 | m.meta_greater(index, meta, u32(i)) |
| 560 | } |
| 561 | } |
| 562 | |
| 563 | // reserve ensures that the map can store at least `n` entries without rehashing. |
| 564 | pub fn (mut m map) reserve(n u32) { |
| 565 | for u64(n) * 5 > u64(m.even_index) * 2 { |
| 566 | m.expand() |
| 567 | } |
| 568 | } |
| 569 | |
| 570 | // cached_rehashd works like rehash. However, instead of rehashing the |
| 571 | // key completely, it uses the bits cached in `metas`. |
| 572 | fn (mut m map) cached_rehash(old_cap u32) { |
| 573 | old_metas := m.metas |
| 574 | metasize := int(sizeof(u32) * (m.even_index + 2 + m.extra_metas)) |
| 575 | m.metas = unsafe { &u32(vcalloc(metasize)) } |
| 576 | old_extra_metas := m.extra_metas |
| 577 | for i := u32(0); i <= old_cap + old_extra_metas; i += 2 { |
| 578 | if unsafe { old_metas[i] } == 0 { |
| 579 | continue |
| 580 | } |
| 581 | old_meta := unsafe { old_metas[i] } |
| 582 | old_probe_count := ((old_meta >> hashbits) - 1) << 1 |
| 583 | old_index := (i - old_probe_count) & (m.even_index >> 1) |
| 584 | mut index := (old_index | (old_meta << m.shift)) & m.even_index |
| 585 | mut meta := (old_meta & hash_mask) | probe_inc |
| 586 | kv_index := unsafe { old_metas[i + 1] } |
| 587 | index, meta = m.meta_less(index, meta) |
| 588 | m.meta_greater(index, meta, kv_index) |
| 589 | } |
| 590 | unsafe { free(old_metas) } |
| 591 | } |
| 592 | |
| 593 | // get_and_set is used for assignment operators. If the argument-key |
| 594 | // does not exist in the map, it's added to the map along with the zero/default value. |
| 595 | // If the key exists, its respective value is returned. |
| 596 | fn (mut m map) get_and_set(key voidptr, zero voidptr) voidptr { |
| 597 | for { |
| 598 | mut index, mut meta := m.key_to_index(key) |
| 599 | for { |
| 600 | if meta == unsafe { m.metas[index] } { |
| 601 | kv_index := int(unsafe { m.metas[index + 1] }) |
| 602 | pkey := unsafe { m.key_values.key(kv_index) } |
| 603 | if m.key_eq_fn(key, pkey) { |
| 604 | pval := unsafe { m.key_values.value(kv_index) } |
| 605 | return unsafe { &u8(pval) } |
| 606 | } |
| 607 | } |
| 608 | index += 2 |
| 609 | meta += probe_inc |
| 610 | if meta > unsafe { m.metas[index] } { |
| 611 | break |
| 612 | } |
| 613 | } |
| 614 | // Key not found, insert key with zero-value |
| 615 | m.set(key, zero) |
| 616 | } |
| 617 | return unsafe { nil } |
| 618 | } |
| 619 | |
| 620 | // If `key` matches the key of an element in the container, |
| 621 | // the method returns a reference to its mapped value. |
| 622 | // If not, a zero/default value is returned. |
| 623 | fn (m &map) get(key voidptr, zero voidptr) voidptr { |
| 624 | if m.len == 0 { |
| 625 | return zero |
| 626 | } |
| 627 | mut index, mut meta := m.key_to_index(key) |
| 628 | for { |
| 629 | if meta == unsafe { m.metas[index] } { |
| 630 | kv_index := int(unsafe { m.metas[index + 1] }) |
| 631 | pkey := unsafe { m.key_values.key(kv_index) } |
| 632 | if m.key_eq_fn(key, pkey) { |
| 633 | pval := unsafe { m.key_values.value(kv_index) } |
| 634 | return unsafe { &u8(pval) } |
| 635 | } |
| 636 | } |
| 637 | index += 2 |
| 638 | meta += probe_inc |
| 639 | if meta > unsafe { m.metas[index] } { |
| 640 | break |
| 641 | } |
| 642 | } |
| 643 | return zero |
| 644 | } |
| 645 | |
| 646 | // If `key` matches the key of an element in the container, |
| 647 | // the method returns a reference to its mapped value. |
| 648 | // If not, a zero pointer is returned. |
| 649 | // This is used in `x := m['key'] or { ... }` |
| 650 | fn (m &map) get_check(key voidptr) voidptr { |
| 651 | if m.len == 0 { |
| 652 | return 0 |
| 653 | } |
| 654 | mut index, mut meta := m.key_to_index(key) |
| 655 | for { |
| 656 | if meta == unsafe { m.metas[index] } { |
| 657 | kv_index := int(unsafe { m.metas[index + 1] }) |
| 658 | pkey := unsafe { m.key_values.key(kv_index) } |
| 659 | if m.key_eq_fn(key, pkey) { |
| 660 | pval := unsafe { m.key_values.value(kv_index) } |
| 661 | return unsafe { &u8(pval) } |
| 662 | } |
| 663 | } |
| 664 | index += 2 |
| 665 | meta += probe_inc |
| 666 | if meta > unsafe { m.metas[index] } { |
| 667 | break |
| 668 | } |
| 669 | } |
| 670 | return 0 |
| 671 | } |
| 672 | |
| 673 | // Checks whether a particular key exists in the map. |
| 674 | fn (m &map) exists(key voidptr) bool { |
| 675 | if m.len == 0 { |
| 676 | return false |
| 677 | } |
| 678 | mut index, mut meta := m.key_to_index(key) |
| 679 | for { |
| 680 | if meta == unsafe { m.metas[index] } { |
| 681 | kv_index := int(unsafe { m.metas[index + 1] }) |
| 682 | pkey := unsafe { m.key_values.key(kv_index) } |
| 683 | if m.key_eq_fn(key, pkey) { |
| 684 | return true |
| 685 | } |
| 686 | } |
| 687 | index += 2 |
| 688 | meta += probe_inc |
| 689 | if meta > unsafe { m.metas[index] } { |
| 690 | break |
| 691 | } |
| 692 | } |
| 693 | return false |
| 694 | } |
| 695 | |
| 696 | @[inline] |
| 697 | fn (mut d DenseArray) delete(i int) { |
| 698 | if i == d.len - 1 { |
| 699 | d.len-- |
| 700 | d.trim_deleted_tail() |
| 701 | return |
| 702 | } |
| 703 | if d.deletes == 0 { |
| 704 | d.all_deleted = vcalloc(d.cap) // sets to 0 |
| 705 | } |
| 706 | d.deletes++ |
| 707 | unsafe { |
| 708 | d.all_deleted[i] = 1 |
| 709 | } |
| 710 | } |
| 711 | |
| 712 | // delete removes the mapping of a particular key from the map. |
| 713 | @[unsafe] |
| 714 | pub fn (mut m map) delete(key voidptr) { |
| 715 | mut index, mut meta := m.key_to_index(key) |
| 716 | index, meta = m.meta_less(index, meta) |
| 717 | // Perform backwards shifting |
| 718 | for meta == unsafe { m.metas[index] } { |
| 719 | kv_index := int(unsafe { m.metas[index + 1] }) |
| 720 | pkey := unsafe { m.key_values.key(kv_index) } |
| 721 | if m.key_eq_fn(key, pkey) { |
| 722 | for (unsafe { m.metas[index + 2] } >> hashbits) > 1 { |
| 723 | unsafe { |
| 724 | m.metas[index] = m.metas[index + 2] - probe_inc |
| 725 | m.metas[index + 1] = m.metas[index + 3] |
| 726 | } |
| 727 | index += 2 |
| 728 | } |
| 729 | m.len-- |
| 730 | m.key_values.delete(kv_index) |
| 731 | unsafe { |
| 732 | m.metas[index] = 0 |
| 733 | m.free_fn(pkey) |
| 734 | // Mark key as deleted |
| 735 | vmemset(pkey, 0, m.key_bytes) |
| 736 | } |
| 737 | if m.key_values.len <= 32 { |
| 738 | return |
| 739 | } |
| 740 | // Clean up key_values if too many have been deleted |
| 741 | if m.key_values.deletes >= (m.key_values.len >> 1) { |
| 742 | m.key_values.zeros_to_end() |
| 743 | m.rehash() |
| 744 | } |
| 745 | return |
| 746 | } |
| 747 | index += 2 |
| 748 | meta += probe_inc |
| 749 | } |
| 750 | } |
| 751 | |
| 752 | // keys returns all keys in the map. |
| 753 | pub fn (m &map) keys() array { |
| 754 | mut keys := __new_array(m.len, 0, m.key_bytes) |
| 755 | mut item := unsafe { &u8(keys.data) } |
| 756 | if m.key_values.deletes == 0 { |
| 757 | for i := 0; i < m.key_values.len; i++ { |
| 758 | unsafe { |
| 759 | pkey := m.key_values.key(i) |
| 760 | m.clone_fn(item, pkey) |
| 761 | item = item + m.key_bytes |
| 762 | } |
| 763 | } |
| 764 | return keys |
| 765 | } |
| 766 | for i := 0; i < m.key_values.len; i++ { |
| 767 | if !m.key_values.has_index(i) { |
| 768 | continue |
| 769 | } |
| 770 | unsafe { |
| 771 | pkey := m.key_values.key(i) |
| 772 | m.clone_fn(item, pkey) |
| 773 | item = item + m.key_bytes |
| 774 | } |
| 775 | } |
| 776 | return keys |
| 777 | } |
| 778 | |
| 779 | // values returns all values in the map. |
| 780 | pub fn (m &map) values() array { |
| 781 | mut values := __new_array(m.len, 0, m.value_bytes) |
| 782 | mut item := unsafe { &u8(values.data) } |
| 783 | |
| 784 | if m.key_values.deletes == 0 { |
| 785 | unsafe { |
| 786 | vmemcpy(item, m.key_values.values, m.value_bytes * m.key_values.len) |
| 787 | } |
| 788 | return values |
| 789 | } |
| 790 | |
| 791 | for i := 0; i < m.key_values.len; i++ { |
| 792 | if !m.key_values.has_index(i) { |
| 793 | continue |
| 794 | } |
| 795 | unsafe { |
| 796 | pvalue := m.key_values.value(i) |
| 797 | vmemcpy(item, pvalue, m.value_bytes) |
| 798 | item = item + m.value_bytes |
| 799 | } |
| 800 | } |
| 801 | return values |
| 802 | } |
| 803 | |
| 804 | // warning: only copies keys, does not clone |
| 805 | @[unsafe] |
| 806 | fn (d &DenseArray) clone() DenseArray { |
| 807 | res := DenseArray{ |
| 808 | key_bytes: d.key_bytes |
| 809 | value_bytes: d.value_bytes |
| 810 | cap: d.cap |
| 811 | len: d.len |
| 812 | deletes: d.deletes |
| 813 | all_deleted: unsafe { nil } |
| 814 | values: unsafe { nil } |
| 815 | keys: unsafe { nil } |
| 816 | } |
| 817 | unsafe { |
| 818 | if d.deletes != 0 { |
| 819 | res.all_deleted = memdup(d.all_deleted, d.cap) |
| 820 | } |
| 821 | res.keys = memdup(d.keys, d.cap * d.key_bytes) |
| 822 | res.values = memdup(d.values, d.cap * d.value_bytes) |
| 823 | } |
| 824 | return res |
| 825 | } |
| 826 | |
| 827 | // clone returns a clone of the `map`. |
| 828 | @[unsafe] |
| 829 | pub fn (m &map) clone() map { |
| 830 | metasize := int(sizeof(u32) * (m.even_index + 2 + m.extra_metas)) |
| 831 | res := map{ |
| 832 | key_bytes: m.key_bytes |
| 833 | value_bytes: m.value_bytes |
| 834 | even_index: m.even_index |
| 835 | cached_hashbits: m.cached_hashbits |
| 836 | shift: m.shift |
| 837 | key_values: unsafe { m.key_values.clone() } |
| 838 | metas: unsafe { &u32(malloc_noscan(metasize)) } |
| 839 | extra_metas: m.extra_metas |
| 840 | len: m.len |
| 841 | has_string_keys: m.has_string_keys |
| 842 | hash_fn: m.hash_fn |
| 843 | key_eq_fn: m.key_eq_fn |
| 844 | clone_fn: m.clone_fn |
| 845 | free_fn: m.free_fn |
| 846 | } |
| 847 | unsafe { vmemcpy(res.metas, m.metas, metasize) } |
| 848 | if !m.has_string_keys { |
| 849 | return res |
| 850 | } |
| 851 | // clone keys |
| 852 | for i in 0 .. m.key_values.len { |
| 853 | if !m.key_values.has_index(i) { |
| 854 | continue |
| 855 | } |
| 856 | m.clone_fn(res.key_values.key(i), m.key_values.key(i)) |
| 857 | } |
| 858 | return res |
| 859 | } |
| 860 | |
| 861 | // free releases all memory resources occupied by the `map`. |
| 862 | @[unsafe] |
| 863 | pub fn (m &map) free() { |
| 864 | unsafe { free(m.metas) } |
| 865 | unsafe { |
| 866 | m.metas = nil |
| 867 | } |
| 868 | if m.key_values.deletes == 0 { |
| 869 | for i := 0; i < m.key_values.len; i++ { |
| 870 | unsafe { |
| 871 | pkey := m.key_values.key(i) |
| 872 | m.free_fn(pkey) |
| 873 | vmemset(pkey, 0, m.key_bytes) |
| 874 | } |
| 875 | } |
| 876 | } else { |
| 877 | for i := 0; i < m.key_values.len; i++ { |
| 878 | if !m.key_values.has_index(i) { |
| 879 | continue |
| 880 | } |
| 881 | unsafe { |
| 882 | pkey := m.key_values.key(i) |
| 883 | m.free_fn(pkey) |
| 884 | vmemset(pkey, 0, m.key_bytes) |
| 885 | } |
| 886 | } |
| 887 | } |
| 888 | unsafe { |
| 889 | if m.key_values.all_deleted != nil { |
| 890 | free(m.key_values.all_deleted) |
| 891 | m.key_values.all_deleted = nil |
| 892 | } |
| 893 | if m.key_values.keys != nil { |
| 894 | free(m.key_values.keys) |
| 895 | m.key_values.keys = nil |
| 896 | } |
| 897 | if m.key_values.values != nil { |
| 898 | free(m.key_values.values) |
| 899 | m.key_values.values = nil |
| 900 | } |
| 901 | // TODO: the next lines assume that callback functions are static and independent from each particular |
| 902 | // map instance. Closures may invalidate that assumption, so revisit when RC for closures works. |
| 903 | m.hash_fn = nil |
| 904 | m.key_eq_fn = nil |
| 905 | m.clone_fn = nil |
| 906 | m.free_fn = nil |
| 907 | m.key_values.cap = 0 |
| 908 | m.key_values.len = 0 |
| 909 | m.key_values.deletes = 0 |
| 910 | m.even_index = 0 |
| 911 | m.cached_hashbits = 0 |
| 912 | m.shift = 0 |
| 913 | m.extra_metas = 0 |
| 914 | m.has_string_keys = false |
| 915 | m.len = 0 |
| 916 | } |
| 917 | } |
| 918 | |