|
| 1 | +// SPDX-FileCopyrightText: Lukas Neubert |
| 2 | +// SPDX-License-Identifier: MIT |
| 3 | +package builtin |
| 4 | + |
| 5 | +import hash.fnv1a |
| 6 | + |
| 7 | +const map_min_cap := 8 |
| 8 | +const map_max_load_num := 7 |
| 9 | +const map_max_load_den := 10 |
| 10 | + |
| 11 | +struct Map { |
| 12 | + mut hashes []u32 |
| 13 | + mut used []bool |
| 14 | + mut keys []string |
| 15 | + mut order []string |
| 16 | + mut vals Array |
| 17 | + pub mut length i32 |
| 18 | + mut cap i32 |
| 19 | + mut val_size i32 |
| 20 | + mut zero &void |
| 21 | +} |
| 22 | + |
| 23 | +fun map_hash(key string) u32 { |
| 24 | + hash := fnv1a.sum32_string(key) |
| 25 | + if hash == 0 { |
| 26 | + return 1 |
| 27 | + } |
| 28 | + return hash |
| 29 | +} |
| 30 | + |
| 31 | +fun map_key_eq(a string, b string) bool { |
| 32 | + if a.length != b.length { |
| 33 | + return false |
| 34 | + } |
| 35 | + for i := 0; i < a.length; i += 1 { |
| 36 | + if a[i] != b[i] { |
| 37 | + return false |
| 38 | + } |
| 39 | + } |
| 40 | + return true |
| 41 | +} |
| 42 | + |
| 43 | +fun map_next_cap(min_cap i32) i32 { |
| 44 | + mut cap := map_min_cap |
| 45 | + for cap < min_cap { |
| 46 | + cap *= 2 |
| 47 | + } |
| 48 | + return cap |
| 49 | +} |
| 50 | + |
| 51 | +pub fun new_map(val_size i32, min_cap i32) Map { |
| 52 | + cap := map_next_cap(min_cap) |
| 53 | + return Map{ |
| 54 | + hashes = []u32{length = cap} |
| 55 | + used = []bool{length = cap} |
| 56 | + keys = []string{length = cap} |
| 57 | + order = []string{cap = cap} |
| 58 | + vals = new_array(cap, cap, val_size) |
| 59 | + cap = cap |
| 60 | + val_size = val_size |
| 61 | + zero = #C.calloc(1, val_size) as &void |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +pub fun new_map_from_c(len i32, val_size i32, keys &string, vals &void) Map { |
| 66 | + mut m := new_map(val_size, len * 2) |
| 67 | + for i := 0; i < len; i += 1 { |
| 68 | + key := *(#C.'keys + i' as &string) |
| 69 | + val := #C.'vals + i * val_size' as &void |
| 70 | + map_set(&m, key, val) |
| 71 | + } |
| 72 | + return m |
| 73 | +} |
| 74 | + |
| 75 | +fun map_rehash(mut m &Map, new_cap i32) { |
| 76 | + old_hashes := m.hashes |
| 77 | + old_used := m.used |
| 78 | + old_keys := m.keys |
| 79 | + old_order := m.order |
| 80 | + old_vals := m.vals |
| 81 | + old_cap := m.cap |
| 82 | + |
| 83 | + m.hashes = []u32{length = new_cap} |
| 84 | + m.used = []bool{length = new_cap} |
| 85 | + m.keys = []string{length = new_cap} |
| 86 | + m.order = []string{cap = old_order.length} |
| 87 | + m.vals = new_array(new_cap, new_cap, m.val_size) |
| 88 | + m.cap = new_cap |
| 89 | + m.length = 0 |
| 90 | + |
| 91 | + old_map := Map{ |
| 92 | + hashes = old_hashes |
| 93 | + used = old_used |
| 94 | + keys = old_keys |
| 95 | + order = old_order |
| 96 | + vals = old_vals |
| 97 | + length = old_order.length |
| 98 | + cap = old_cap |
| 99 | + val_size = m.val_size |
| 100 | + zero = m.zero |
| 101 | + } |
| 102 | + |
| 103 | + for key in old_order { |
| 104 | + map_set(mut m, key, map_get(&old_map, key)) |
| 105 | + } |
| 106 | +} |
| 107 | + |
| 108 | +fun map_ensure_cap(mut m &Map, min_len i32) { |
| 109 | + if m.cap == 0 { |
| 110 | + map_rehash(mut m, map_min_cap) |
| 111 | + return |
| 112 | + } |
| 113 | + |
| 114 | + if min_len * map_max_load_den < m.cap * map_max_load_num { |
| 115 | + return |
| 116 | + } |
| 117 | + |
| 118 | + map_rehash(mut m, m.cap * 2) |
| 119 | +} |
| 120 | + |
| 121 | +pub fun map_contains(m &Map, key string) bool { |
| 122 | + if m.cap == 0 { |
| 123 | + return false |
| 124 | + } |
| 125 | + |
| 126 | + hash := map_hash(key) |
| 127 | + mut i := (hash % (m.cap as u32)) as i32 |
| 128 | + for step := 0; step < m.cap; step += 1 { |
| 129 | + if not m.used[i] { |
| 130 | + return false |
| 131 | + } |
| 132 | + if m.hashes[i] == hash and map_key_eq(m.keys[i], key) { |
| 133 | + return true |
| 134 | + } |
| 135 | + i += 1 |
| 136 | + if i == m.cap { |
| 137 | + i = 0 |
| 138 | + } |
| 139 | + } |
| 140 | + return false |
| 141 | +} |
| 142 | + |
| 143 | +pub fun map_set(mut m &Map, key string, val &void) { |
| 144 | + map_ensure_cap(mut m, m.length + 1) |
| 145 | + |
| 146 | + hash := map_hash(key) |
| 147 | + mut i := (hash % (m.cap as u32)) as i32 |
| 148 | + for step := 0; step < m.cap; step += 1 { |
| 149 | + if not m.used[i] { |
| 150 | + m.used[i] = true |
| 151 | + m.hashes[i] = hash |
| 152 | + m.keys[i] = key |
| 153 | + m.order.push(key) |
| 154 | + m.vals.set(i, val) |
| 155 | + m.length += 1 |
| 156 | + return |
| 157 | + } |
| 158 | + |
| 159 | + if m.hashes[i] == hash and map_key_eq(m.keys[i], key) { |
| 160 | + m.vals.set(i, val) |
| 161 | + return |
| 162 | + } |
| 163 | + |
| 164 | + i += 1 |
| 165 | + if i == m.cap { |
| 166 | + i = 0 |
| 167 | + } |
| 168 | + } |
| 169 | + |
| 170 | + map_rehash(mut m, m.cap * 2) |
| 171 | + map_set(mut m, key, val) |
| 172 | +} |
| 173 | + |
| 174 | +pub fun map_get(m &Map, key string) &void { |
| 175 | + if m.cap == 0 { |
| 176 | + return m.zero |
| 177 | + } |
| 178 | + |
| 179 | + hash := map_hash(key) |
| 180 | + mut i := (hash % (m.cap as u32)) as i32 |
| 181 | + for step := 0; step < m.cap; step += 1 { |
| 182 | + if not m.used[i] { |
| 183 | + return m.zero |
| 184 | + } |
| 185 | + |
| 186 | + if m.hashes[i] == hash and map_key_eq(m.keys[i], key) { |
| 187 | + return m.vals.get(i) |
| 188 | + } |
| 189 | + |
| 190 | + i += 1 |
| 191 | + if i == m.cap { |
| 192 | + i = 0 |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | + return m.zero |
| 197 | +} |
| 198 | + |
| 199 | +pub fun (m Map) keys() []string { |
| 200 | + return m.order.copy() |
| 201 | +} |
0 commit comments