compiler/
lib/
examples/
std/
arch/
char/
collections/
dict.rad
2.0 KiB
graph/
lang/
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
graph.rad
4.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
299 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CELL_PERMISSIONS
6.8 KiB
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
808 B
lib/std/collections/dict.rad
raw
| 1 | //! Open-addressed hash map from string keys to `i32` values. |
| 2 | //! |
| 3 | //! Uses DJB2 hashing with linear probing. The table size must be a power |
| 4 | //! of two and is provided by the caller via arena-allocated storage. |
| 5 | |
| 6 | use std::mem; |
| 7 | |
| 8 | /// Hash map entry. |
| 9 | export record Entry: Copy { |
| 10 | /// Key. |
| 11 | key: *[u8], |
| 12 | /// Associated value. |
| 13 | value: i32, |
| 14 | } |
| 15 | |
| 16 | /// Open-addressed hash map with caller-provided storage. |
| 17 | export record Dict { |
| 18 | /// Hash table entries. |
| 19 | entries: *mut [Entry], |
| 20 | /// Number of occupied entries. |
| 21 | count: u32, |
| 22 | } |
| 23 | |
| 24 | /// Create a dict backed by the given entry storage. |
| 25 | /// The storage length must be a power of two. |
| 26 | export fn init(entries: *mut [Entry]) -> Dict { |
| 27 | for i in 0..entries.len { |
| 28 | set entries[i] = Entry { key: &[], value: 0 }; |
| 29 | } |
| 30 | return Dict { entries, count: 0 }; |
| 31 | } |
| 32 | |
| 33 | /// Insert or update a key-value pair. Panics if the table exceeds 50% load. |
| 34 | export fn insert(m: &mut Dict, key: *[u8], value: i32) { |
| 35 | let mask = m.entries.len - 1; |
| 36 | let mut idx = hash(key) & mask; |
| 37 | |
| 38 | loop { |
| 39 | let entry = m.entries[idx]; |
| 40 | if entry.key.len == 0 { |
| 41 | assert m.count < m.entries.len / 2, "dict::insert: table full"; |
| 42 | set m.entries[idx] = Entry { key, value }; |
| 43 | set m.count += 1; |
| 44 | return; |
| 45 | } |
| 46 | if mem::eq(entry.key, key) { |
| 47 | set m.entries[idx].value = value; |
| 48 | return; |
| 49 | } |
| 50 | set idx = (idx + 1) & mask; |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | /// Look up a value by key. Returns `nil` if not found. |
| 55 | export fn get(m: &Dict, key: *[u8]) -> ?i32 { |
| 56 | let mask = m.entries.len - 1; |
| 57 | let mut idx = hash(key) & mask; |
| 58 | |
| 59 | loop { |
| 60 | let entry = m.entries[idx]; |
| 61 | if entry.key.len == 0 { |
| 62 | return nil; |
| 63 | } |
| 64 | if mem::eq(entry.key, key) { |
| 65 | return entry.value; |
| 66 | } |
| 67 | set idx = (idx + 1) & mask; |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /// DJB2 hash function. |
| 72 | export fn hash(str: *[u8]) -> u32 { |
| 73 | let mut h: u32 = 5381; |
| 74 | for b in str { |
| 75 | set h = ((h << 5) + h) + b as u32; |
| 76 | } |
| 77 | return h; |
| 78 | } |