v/vlib/builtin/map.v

574 lines
16 KiB
V
Raw Normal View History

2020-02-03 05:00:36 +01:00
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
2019-06-23 04:21:30 +02:00
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
2019-06-22 20:20:28 +02:00
module builtin
// import hash.wyhash as hash
2020-07-18 13:49:00 +02:00
import hash
2020-04-08 00:02:15 +02:00
2020-03-19 06:52:34 +01:00
/*
2020-05-17 13:51:18 +02:00
This is a highly optimized hashmap implementation. It has several traits that
in combination makes it very fast and memory efficient. Here is a short expl-
anation of each trait. After reading this you should have a basic understand-
ing of how it functions:
1. Hash-function: Wyhash. Wyhash is the fastest hash-function for short keys
passing SMHasher, so it was an obvious choice.
2. Open addressing: Robin Hood Hashing. With this method, a hash-collision is
resolved by probing. As opposed to linear probing, Robin Hood hashing has a
simple but clever twist: As new keys are inserted, old keys are shifted arou-
nd in a way such that all keys stay reasonably close to the slot they origin-
ally hash to. A new key may displace a key already inserted if its probe cou-
2020-05-17 13:51:18 +02:00
nt is larger than that of the key at the current position.
3. Memory layout: key-value pairs are stored in a `DenseArray`. This is a dy-
namic array with a very low volume of unused memory, at the cost of more rea-
llocations when inserting elements. It also preserves the order of the key-v-
alues. This array is named `key_values`. Instead of probing a new key-value,
this map probes two 32-bit numbers collectively. The first number has its 8
most significant bits reserved for the probe-count and the remaining 24 bits
are cached bits from the hash which are utilized for faster re-hashing. This
number is often referred to as `meta`. The other 32-bit number is the index
at which the key-value was pushed to in `key_values`. Both of these numbers
are stored in a sparse array `metas`. The `meta`s and `kv_index`s are stored
at even and odd indices, respectively:
metas = [meta, kv_index, 0, 0, meta, kv_index, 0, 0, meta, kv_index, ...]
key_values = [kv, kv, kv, ...]
4. The size of metas is a power of two. This enables the use of bitwise AND
2020-05-26 17:59:52 +02:00
to convert the 64-bit hash to a bucket/index that doesn't overflow metas. If
the size is power of two you can use "hash & (SIZE - 1)" instead of "hash %
SIZE". Modulo is extremely expensive so using '&' is a big performance impro-
vement. The general concern with this approach is that you only make use of
the lower bits of the hash which can cause more collisions. This is solved by
using a well-dispersed hash-function.
5. The hashmap keeps track of the highest probe_count. The trick is to alloc-
ate `extra_metas` > max(probe_count), so you never have to do any bounds-che-
2020-05-17 13:51:18 +02:00
cking since the extra meta memory ensures that a meta will never go beyond
2020-03-21 13:55:07 +01:00
the last index.
2020-03-19 06:52:34 +01:00
6. Cached rehashing. When the `load_factor` of the map exceeds the `max_load_
factor` the size of metas is doubled and all the key-values are "rehashed" to
find the index for their meta's in the new array. Instead of rehashing compl-
etely, it simply uses the cached-hashbits stored in the meta, resulting in
much faster rehashing.
2020-03-19 06:52:34 +01:00
*/
2020-01-24 20:13:59 +01:00
const (
2020-05-26 17:59:52 +02:00
// Number of bits from the hash stored for each entry
2020-05-09 12:42:01 +02:00
hashbits = 24
2020-03-21 13:55:07 +01:00
// Number of bits from the hash stored for rehashing
2020-04-05 22:09:52 +02:00
max_cached_hashbits = 16
2020-02-20 20:04:06 +01:00
// Initial log-number of buckets in the hashtable
2020-05-09 12:42:01 +02:00
init_log_capicity = 5
2020-02-20 20:04:06 +01:00
// Initial number of buckets in the hashtable
2020-05-09 12:42:01 +02:00
init_capicity = 1 << init_log_capicity
2020-06-21 16:51:02 +02:00
// Maximum load-factor (len / capacity)
2020-05-09 12:42:01 +02:00
max_load_factor = 0.8
2020-03-21 13:55:07 +01:00
// Initial highest even index in metas
2020-05-09 12:42:01 +02:00
init_cap = init_capicity - 2
2020-03-19 06:52:34 +01:00
// Used for incrementing `extra_metas` when max
// probe count is too high, to avoid overflow
2020-05-09 12:42:01 +02:00
extra_metas_inc = 4
2020-02-20 20:04:06 +01:00
// Bitmask to select all the hashbits
2020-05-09 12:42:01 +02:00
hash_mask = u32(0x00FFFFFF)
2020-02-20 20:04:06 +01:00
// Used for incrementing the probe-count
2020-05-09 12:42:01 +02:00
probe_inc = u32(0x01000000)
2020-01-24 20:13:59 +01:00
)
2020-05-17 13:51:18 +02:00
// This function is intended to be fast when
// the strings are very likely to be equal
// TODO: add branch prediction hints
[inline]
fn fast_string_eq(a string, b string) bool {
if a.len != b.len {
return false
}
unsafe {
return C.memcmp(a.str, b.str, b.len) == 0
}
}
// Dynamic array with very low growth factor
struct DenseArray {
key_bytes int
value_bytes int
slot_bytes int // sum of 2 fields above
mut:
cap int
len int
deletes u32 // count
data byteptr // array of interspersed key data and value data
2020-03-19 06:52:34 +01:00
}
[inline]
2020-08-09 11:22:11 +02:00
[unsafe]
fn new_dense_array(key_bytes int, value_bytes int) DenseArray {
slot_bytes := key_bytes + value_bytes
cap := 8
return DenseArray{
key_bytes: key_bytes
value_bytes: value_bytes
slot_bytes: slot_bytes
cap: cap
2020-06-21 16:51:02 +02:00
len: 0
deletes: 0
data: malloc(cap * slot_bytes)
2020-03-19 06:52:34 +01:00
}
}
[inline]
fn (d &DenseArray) key(i int) voidptr {
return unsafe {d.data + i * d.slot_bytes}
}
// for cgen
[inline]
fn (d &DenseArray) value(i int) voidptr {
return unsafe {d.data + i * d.slot_bytes + d.key_bytes}
}
[inline]
fn (d &DenseArray) has_index(i int) bool {
// assume string keys for now
pkey := unsafe {&string(d.key(i))}
return pkey.str != 0
}
2020-03-19 06:52:34 +01:00
// Push element to array and return index
// The growth-factor is roughly 1.125 `(x + (x >> 3))`
2020-03-19 06:52:34 +01:00
[inline]
fn (mut d DenseArray) push(key voidptr, value voidptr) int {
2020-06-21 16:51:02 +02:00
if d.cap == d.len {
d.cap += d.cap >> 3
unsafe {
d.data = v_realloc(d.data, d.slot_bytes * d.cap)
}
2020-03-19 06:52:34 +01:00
}
2020-06-21 16:51:02 +02:00
push_index := d.len
unsafe {
ptr := d.key(push_index)
C.memcpy(ptr, key, d.key_bytes)
C.memcpy(byteptr(ptr) + d.key_bytes, value, d.value_bytes)
}
2020-06-21 16:51:02 +02:00
d.len++
2020-03-19 06:52:34 +01:00
return push_index
}
2020-06-24 23:31:19 +02:00
// Move all zeros to the end of the array and resize array
2020-05-17 13:51:18 +02:00
fn (mut d DenseArray) zeros_to_end() {
// TODO alloca?
mut tmp_buf := malloc(d.slot_bytes)
mut count := 0
for i in 0 .. d.len {
if d.has_index(i) {
// swap (TODO: optimize)
unsafe {
C.memcpy(tmp_buf, d.key(count), d.slot_bytes)
C.memcpy(d.key(count), d.key(i), d.slot_bytes)
C.memcpy(d.key(i), tmp_buf, d.slot_bytes)
}
2020-03-19 06:52:34 +01:00
count++
}
}
free(tmp_buf)
2020-03-21 13:55:07 +01:00
d.deletes = 0
2020-06-21 16:51:02 +02:00
d.len = count
d.cap = if count < 8 { 8 } else { count }
unsafe {
d.data = v_realloc(d.data, d.slot_bytes * d.cap)
}
2020-03-19 06:52:34 +01:00
}
pub struct map {
2020-06-24 23:31:19 +02:00
// Number of bytes of a value
2020-05-09 12:42:01 +02:00
value_bytes int
2020-03-19 06:52:34 +01:00
mut:
2020-06-24 23:31:19 +02:00
// Highest even index in the hashtable
2020-05-09 12:42:01 +02:00
cap u32
2020-03-19 06:52:34 +01:00
// Number of cached hashbits left for rehasing
2020-05-09 12:42:01 +02:00
cached_hashbits byte
2020-03-19 06:52:34 +01:00
// Used for right-shifting out used hashbits
2020-05-09 12:42:01 +02:00
shift byte
2020-03-21 13:55:07 +01:00
// Array storing key-values (ordered)
2020-05-09 12:42:01 +02:00
key_values DenseArray
2020-03-21 13:55:07 +01:00
// Pointer to meta-data:
2020-06-24 23:31:19 +02:00
// - Odd indices store kv_index.
// - Even indices store probe_count and hashbits.
2020-05-09 12:42:01 +02:00
metas &u32
2020-03-19 06:52:34 +01:00
// Extra metas that allows for no ranging when incrementing
// index in the hashmap
2020-05-09 12:42:01 +02:00
extra_metas u32
2020-03-19 06:52:34 +01:00
pub mut:
// Number of key-values currently in the hashmap
len int
}
fn new_map_1(value_bytes int) map {
metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc))
2020-02-20 20:04:06 +01:00
return map{
2020-01-24 20:13:59 +01:00
value_bytes: value_bytes
2020-03-19 06:52:34 +01:00
cap: init_cap
2020-04-05 22:09:52 +02:00
cached_hashbits: max_cached_hashbits
2020-03-19 06:52:34 +01:00
shift: init_log_capicity
key_values: new_dense_array(int(sizeof(string)), value_bytes)
metas: &u32(vcalloc(metasize))
2020-03-19 06:52:34 +01:00
extra_metas: extra_metas_inc
2020-06-21 16:51:02 +02:00
len: 0
2019-06-22 20:20:28 +02:00
}
}
fn new_map_init(n int, value_bytes int, keys &string, values voidptr) map {
mut out := new_map_1(value_bytes)
2020-01-24 20:13:59 +01:00
for i in 0 .. n {
unsafe {out.set(keys[i], byteptr(values) + i * value_bytes)}
2019-08-03 09:44:08 +02:00
}
2020-01-24 20:13:59 +01:00
return out
2019-08-29 00:52:32 +02:00
}
2019-08-03 09:44:08 +02:00
2020-03-19 06:52:34 +01:00
[inline]
fn (m &map) key_to_index(key string) (u32, u32) {
2020-07-18 11:14:03 +02:00
hash := hash.wyhash_c(key.str, u64(key.len), 0)
2020-03-19 06:52:34 +01:00
index := hash & m.cap
2020-05-09 12:42:01 +02:00
meta := ((hash >> m.shift) & hash_mask) | probe_inc
return u32(index), u32(meta)
2020-03-19 06:52:34 +01:00
}
[inline]
fn (m &map) meta_less(_index u32, _metas u32) (u32, u32) {
2020-04-05 22:09:52 +02:00
mut index := _index
mut meta := _metas
for meta < unsafe {m.metas[index]} {
2020-03-19 06:52:34 +01:00
index += 2
meta += probe_inc
2020-02-20 20:04:06 +01:00
}
return index, meta
2020-03-19 06:52:34 +01:00
}
[inline]
2020-05-17 13:51:18 +02:00
fn (mut m map) meta_greater(_index u32, _metas u32, kvi u32) {
2020-04-05 22:09:52 +02:00
mut meta := _metas
mut index := _index
2020-03-19 06:52:34 +01:00
mut kv_index := kvi
for unsafe {m.metas[index]} != 0 {
if meta > unsafe {m.metas[index]} {
unsafe {
tmp_meta := m.metas[index]
m.metas[index] = meta
meta = tmp_meta
tmp_index := m.metas[index + 1]
m.metas[index + 1] = kv_index
kv_index = tmp_index
}
2020-01-24 20:13:59 +01:00
}
2020-03-19 06:52:34 +01:00
index += 2
meta += probe_inc
2020-02-20 20:04:06 +01:00
}
unsafe {
m.metas[index] = meta
m.metas[index + 1] = kv_index
}
2020-05-09 12:42:01 +02:00
probe_count := (meta >> hashbits) - 1
2020-05-26 17:59:52 +02:00
m.ensure_extra_metas(probe_count)
}
[inline]
fn (mut m map) ensure_extra_metas(probe_count u32) {
2020-05-09 12:42:01 +02:00
if (probe_count << 1) == m.extra_metas {
2020-03-19 06:52:34 +01:00
m.extra_metas += extra_metas_inc
mem_size := (m.cap + 2 + m.extra_metas)
unsafe {
x := v_realloc(byteptr(m.metas), int(sizeof(u32) * mem_size))
m.metas = &u32(x)
C.memset(m.metas + mem_size - extra_metas_inc, 0, int(sizeof(u32) * extra_metas_inc))
}
2020-03-19 06:52:34 +01:00
// Should almost never happen
if probe_count == 252 {
2020-03-21 13:55:07 +01:00
panic('Probe overflow')
2020-03-19 06:52:34 +01:00
}
2020-03-19 07:05:20 +01:00
}
2020-03-19 06:52:34 +01:00
}
// Insert new element to the map. The element is inserted if its key is
2020-06-24 23:31:19 +02:00
// not equivalent to the key of any other element already in the container.
// If the key already exists, its value is changed to the value of the new element.
2020-05-26 17:59:52 +02:00
fn (mut m map) set(k string, value voidptr) {
key := k.clone()
2020-06-21 16:51:02 +02:00
load_factor := f32(m.len << 1) / f32(m.cap)
2020-03-21 13:55:07 +01:00
if load_factor > max_load_factor {
2020-03-19 06:52:34 +01:00
m.expand()
2020-02-20 20:30:34 +01:00
}
mut index, mut meta := m.key_to_index(key)
index, meta = m.meta_less(index, meta)
2020-03-19 06:52:34 +01:00
// While we might have a match
for meta == unsafe {m.metas[index]} {
kv_index := int(unsafe {m.metas[index + 1]})
pkey := unsafe {&string(m.key_values.key(kv_index))}
if fast_string_eq(key, *pkey) {
unsafe {
pval := pkey + 1 // skip string
C.memcpy(pval, value, m.value_bytes)
}
2020-03-19 06:52:34 +01:00
return
}
2020-03-19 06:52:34 +01:00
index += 2
meta += probe_inc
}
kv_index := m.key_values.push(key, value)
m.meta_greater(index, meta, u32(kv_index))
2020-06-21 16:51:02 +02:00
m.len++
}
2020-03-19 06:52:34 +01:00
// Doubles the size of the hashmap
2020-05-17 13:51:18 +02:00
fn (mut m map) expand() {
2020-03-19 06:52:34 +01:00
old_cap := m.cap
2020-05-09 12:42:01 +02:00
m.cap = ((m.cap + 2) << 1) - 2
2020-03-19 06:52:34 +01:00
// Check if any hashbits are left
2020-04-05 22:09:52 +02:00
if m.cached_hashbits == 0 {
m.shift += max_cached_hashbits
m.cached_hashbits = max_cached_hashbits
2020-03-21 13:55:07 +01:00
m.rehash()
} else {
2020-03-19 06:52:34 +01:00
m.cached_rehash(old_cap)
m.cached_hashbits--
2019-08-29 00:52:32 +02:00
}
}
// A rehash is the reconstruction of the hash table:
// All the elements in the container are rearranged according
// to their hash value into the newly sized key-value container.
2020-06-24 23:31:19 +02:00
// Rehashes are performed when the load_factor is going to surpass
// the max_load_factor in an operation.
2020-05-17 13:51:18 +02:00
fn (mut m map) rehash() {
2020-03-19 06:52:34 +01:00
meta_bytes := sizeof(u32) * (m.cap + 2 + m.extra_metas)
unsafe {
x := v_realloc(byteptr(m.metas), int(meta_bytes))
m.metas = &u32(x)
C.memset(m.metas, 0, meta_bytes)
}
for i := 0; i < m.key_values.len; i++ {
if !m.key_values.has_index(i) {
2020-03-19 06:52:34 +01:00
continue
}
pkey := unsafe {&string(m.key_values.key(i))}
mut index, mut meta := m.key_to_index(*pkey)
index, meta = m.meta_less(index, meta)
m.meta_greater(index, meta, u32(i))
}
2019-08-29 00:52:32 +02:00
}
2020-06-24 23:31:19 +02:00
// This method works like rehash. However, instead of rehashing the
// key completely, it uses the bits cached in `metas`.
2020-05-17 13:51:18 +02:00
fn (mut m map) cached_rehash(old_cap u32) {
2020-04-05 22:09:52 +02:00
old_metas := m.metas
metasize := int(sizeof(u32) * (m.cap + 2 + m.extra_metas))
m.metas = &u32(vcalloc(metasize))
2020-03-19 06:52:34 +01:00
old_extra_metas := m.extra_metas
2020-03-21 13:55:07 +01:00
for i := u32(0); i <= old_cap + old_extra_metas; i += 2 {
if unsafe {old_metas[i]} == 0 {
2020-03-19 06:52:34 +01:00
continue
}
old_meta := unsafe {old_metas[i]}
2020-05-09 12:42:01 +02:00
old_probe_count := ((old_meta >> hashbits) - 1) << 1
old_index := (i - old_probe_count) & (m.cap >> 1)
mut index := (old_index | (old_meta << m.shift)) & m.cap
2020-03-19 06:52:34 +01:00
mut meta := (old_meta & hash_mask) | probe_inc
index, meta = m.meta_less(index, meta)
kv_index := unsafe {old_metas[i + 1]}
2020-04-05 22:09:52 +02:00
m.meta_greater(index, meta, kv_index)
}
unsafe {free(old_metas)}
}
2020-06-24 23:31:19 +02:00
// This method is used for assignment operators. If the argument-key
// does not exist in the map, it's added to the map along with the zero/default value.
// If the key exists, its respective value is returned.
2020-06-24 23:31:19 +02:00
fn (mut m map) get_and_set(key string, zero voidptr) voidptr {
2020-06-24 20:41:26 +02:00
for {
mut index, mut meta := m.key_to_index(key)
2020-06-24 20:41:26 +02:00
for {
if meta == unsafe {m.metas[index]} {
kv_index := int(unsafe {m.metas[index + 1]})
pkey := unsafe {&string(m.key_values.key(kv_index))}
if fast_string_eq(key, *pkey) {
return unsafe {byteptr(pkey) + m.key_values.key_bytes}
2020-06-24 20:41:26 +02:00
}
}
index += 2
meta += probe_inc
if meta > unsafe {m.metas[index]} {
break
}
2020-06-24 20:41:26 +02:00
}
// Key not found, insert key with zero-value
2020-06-24 20:41:26 +02:00
m.set(key, zero)
}
assert false
return voidptr(0)
2020-06-24 20:41:26 +02:00
}
2020-06-24 23:31:19 +02:00
// If `key` matches the key of an element in the container,
2020-06-24 23:31:19 +02:00
// the method returns a reference to its mapped value.
// If not, a zero/default value is returned.
fn (m map) get(key string, zero voidptr) voidptr {
mut index, mut meta := m.key_to_index(key)
2020-06-24 23:31:19 +02:00
for {
if meta == unsafe {m.metas[index]} {
kv_index := int(unsafe {m.metas[index + 1]})
pkey := unsafe {&string(m.key_values.key(kv_index))}
if fast_string_eq(key, *pkey) {
return unsafe {byteptr(pkey) + m.key_values.key_bytes}
2020-06-24 23:31:19 +02:00
}
}
index += 2
meta += probe_inc
if meta > unsafe {m.metas[index]} {
break
}
2020-06-24 23:31:19 +02:00
}
return zero
}
2020-06-24 20:41:26 +02:00
2020-06-24 23:31:19 +02:00
// Checks whether a particular key exists in the map.
2020-02-20 20:04:06 +01:00
fn (m map) exists(key string) bool {
mut index, mut meta := m.key_to_index(key)
2020-05-09 12:42:01 +02:00
for {
if meta == unsafe {m.metas[index]} {
kv_index := int(unsafe {m.metas[index + 1]})
pkey := unsafe {&string(m.key_values.key(kv_index))}
if fast_string_eq(key, *pkey) {
return true
2020-05-09 12:42:01 +02:00
}
}
2020-03-19 06:52:34 +01:00
index += 2
meta += probe_inc
if meta > unsafe {m.metas[index]} {
break
}
}
2020-02-20 20:04:06 +01:00
return false
2019-12-30 06:57:56 +01:00
}
2020-06-24 23:31:19 +02:00
// Removes the mapping of a particular key from the map.
2020-05-17 13:51:18 +02:00
pub fn (mut m map) delete(key string) {
mut index, mut meta := m.key_to_index(key)
index, meta = m.meta_less(index, meta)
2020-03-19 06:52:34 +01:00
// Perform backwards shifting
for meta == unsafe {m.metas[index]} {
kv_index := int(unsafe {m.metas[index + 1]})
pkey := unsafe {&string(m.key_values.key(kv_index))}
if fast_string_eq(key, *pkey) {
for (unsafe {m.metas[index + 2]} >> hashbits) > 1 {
unsafe {
m.metas[index] = m.metas[index + 2] - probe_inc
m.metas[index + 1] = m.metas[index + 3]
}
2020-03-19 06:52:34 +01:00
index += 2
}
2020-06-21 16:51:02 +02:00
m.len--
unsafe {
m.metas[index] = 0
}
2020-03-19 06:52:34 +01:00
m.key_values.deletes++
2020-05-26 17:59:52 +02:00
// Mark key as deleted
unsafe {
(*pkey).free()
C.memset(pkey, 0, sizeof(string))
}
2020-06-21 16:51:02 +02:00
if m.key_values.len <= 32 {
2020-03-21 13:55:07 +01:00
return
}
2020-04-05 22:09:52 +02:00
// Clean up key_values if too many have been deleted
2020-06-21 16:51:02 +02:00
if m.key_values.deletes >= (m.key_values.len >> 1) {
2020-03-19 06:52:34 +01:00
m.key_values.zeros_to_end()
m.rehash()
2020-04-05 22:09:52 +02:00
m.key_values.deletes = 0
2020-03-19 06:52:34 +01:00
}
return
}
index += 2
meta += probe_inc
}
}
2020-06-24 23:31:19 +02:00
// Returns all keys in the map.
2020-01-24 20:13:59 +01:00
pub fn (m &map) keys() []string {
mut keys := []string{len: m.len}
2020-02-20 20:04:06 +01:00
mut j := 0
if m.key_values.deletes == 0 {
for i := 0; i < m.key_values.len; i++ {
pkey := unsafe {&string(m.key_values.key(i))}
keys[j] = pkey.clone()
j++
}
return keys
}
for i := 0; i < m.key_values.len; i++ {
if !m.key_values.has_index(i) {
2020-03-19 06:52:34 +01:00
continue
2020-02-20 20:04:06 +01:00
}
pkey := unsafe {&string(m.key_values.key(i))}
keys[j] = pkey.clone()
2020-03-19 06:52:34 +01:00
j++
2020-02-20 20:04:06 +01:00
}
2020-01-24 20:13:59 +01:00
return keys
}
2020-08-09 11:22:11 +02:00
[unsafe]
2020-05-26 01:52:06 +02:00
pub fn (d DenseArray) clone() DenseArray {
res := DenseArray{
key_bytes: d.key_bytes
2020-05-26 01:52:06 +02:00
value_bytes: d.value_bytes
slot_bytes: d.slot_bytes
cap: d.cap
len: d.len
deletes: d.deletes
data: unsafe {memdup(d.data, d.cap * d.slot_bytes)}
2020-05-26 01:52:06 +02:00
}
// FIXME clone each key
2020-05-26 01:52:06 +02:00
return res
}
2020-08-09 11:22:11 +02:00
[unsafe]
2020-05-26 01:52:06 +02:00
pub fn (m map) clone() map {
metasize := int(sizeof(u32) * (m.cap + 2 + m.extra_metas))
2020-05-26 01:52:06 +02:00
res := map{
value_bytes: m.value_bytes
cap: m.cap
2020-05-26 01:52:06 +02:00
cached_hashbits: m.cached_hashbits
shift: m.shift
key_values: unsafe {m.key_values.clone()}
metas: &u32(malloc(metasize))
extra_metas: m.extra_metas
len: m.len
}
unsafe {C.memcpy(res.metas, m.metas, metasize)}
2020-05-26 01:52:06 +02:00
return res
}
2020-08-09 11:22:11 +02:00
[unsafe]
pub fn (m &map) free() {
unsafe {free(m.metas)}
if m.key_values.deletes == 0 {
for i := 0; i < m.key_values.len; i++ {
unsafe {
pkey := &string(m.key_values.key(i))
(*pkey).free()
}
2020-04-05 23:31:53 +02:00
}
} else {
for i := 0; i < m.key_values.len; i++ {
if !m.key_values.has_index(i) {
continue
}
unsafe {
pkey := &string(m.key_values.key(i))
(*pkey).free()
}
}
}
unsafe {free(m.key_values.data)}
2019-07-23 22:57:06 +02:00
}