module Std::Map { extern func bux_hash_bytes(ptr: *void, size: uint) -> uint; extern func bux_hash_string(s: String) -> uint; // --------------------------------------------------------------------------- // Generic Map — works with value-type keys (int, float, etc.) // For String keys, use StringMap below. // --------------------------------------------------------------------------- struct MapEntry { key: K, value: V, occupied: bool, } struct Map { entries: *MapEntry, cap: uint, len: uint, } func Map_New(cap: uint) -> Map { let total: uint = cap * sizeof(MapEntry); let data: *MapEntry = bux_alloc(total) as *MapEntry; var i: uint = 0; while i < cap { data[i].occupied = false; i = i + 1; } return Map { entries: data, cap: cap, len: 0 }; } func Map_Set(m: *Map, key: K, value: V) { var keyPtr: *K = &key; let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K)); var idx: uint = hash % m.cap; while m.entries[idx].occupied { if m.entries[idx].key == key { m.entries[idx].value = value; return; } idx = (idx + 1) % m.cap; } m.entries[idx].key = key; m.entries[idx].value = value; m.entries[idx].occupied = true; m.len = m.len + 1; } func Map_Get(m: *Map, key: K) -> V { var keyPtr: *K = &key; let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K)); var idx: uint = hash % m.cap; while m.entries[idx].occupied { if m.entries[idx].key == key { return m.entries[idx].value; } idx = (idx + 1) % m.cap; } // Return zero value for missing key var zero: V = 0 as V; return zero; } /// Like `Map_Get`, but returns `defaultVal` when the key is absent. func Map_GetOr(m: *Map, key: K, defaultVal: V) -> V { if Map_Has(m, key) { return Map_Get(m, key); } return defaultVal; } func Map_Has(m: *Map, key: K) -> bool { var keyPtr: *K = &key; let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K)); var idx: uint = hash % m.cap; while m.entries[idx].occupied { if m.entries[idx].key == key { return true; } idx = (idx + 1) % m.cap; } return false; } func Map_Len(m: *Map) -> uint { return m.len; } func Map_IsEmpty(m: *Map) -> bool { return m.len == 0; } /* Remove key if present. Rebuilds the table to keep open-addressing correct. */ func Map_Remove(m: *Map, key: K) -> bool { if !Map_Has(m, key) { return false; } var fresh: Map = Map_New(m.cap); var i: uint = 0; while i < m.cap { if m.entries[i].occupied { if m.entries[i].key != key { Map_Set(&fresh, m.entries[i].key, m.entries[i].value); } } i = i + 1; } bux_free(m.entries as *void); m.entries = fresh.entries; m.cap = fresh.cap; m.len = fresh.len; // Ownership transferred to `m` — clear `fresh` so auto-Drop does not free twice fresh.entries = null as *MapEntry; fresh.cap = 0; fresh.len = 0; return true; } func Map_Clear(m: *Map) { var i: uint = 0; while i < m.cap { m.entries[i].occupied = false; i = i + 1; } m.len = 0; } func Map_Free(m: *Map) { bux_free(m.entries as *void); m.entries = null as *MapEntry; m.cap = 0; m.len = 0; } func Map_Drop(m: *Map) { Map_Free(m); } // --------------------------------------------------------------------------- // StringMap — specialized Map for String keys, using strcmp // --------------------------------------------------------------------------- struct StringMapEntry { key: String, value: V, occupied: bool, } struct StringMap { entries: *StringMapEntry, cap: uint, len: uint, } func StringMap_New(cap: uint) -> StringMap { let total: uint = cap * sizeof(StringMapEntry); let data: *StringMapEntry = bux_alloc(total) as *StringMapEntry; var i: uint = 0; while i < cap { data[i].occupied = false; i = i + 1; } return StringMap { entries: data, cap: cap, len: 0 }; } func StringMap_Set(m: *StringMap, key: String, value: V) { let hash: uint = bux_hash_string(key); var idx: uint = hash % m.cap; while m.entries[idx].occupied { if String_Eq(m.entries[idx].key, key) { m.entries[idx].value = value; return; } idx = (idx + 1) % m.cap; } m.entries[idx].key = key; m.entries[idx].value = value; m.entries[idx].occupied = true; m.len = m.len + 1; } func StringMap_Get(m: *StringMap, key: String) -> V { let hash: uint = bux_hash_string(key); var idx: uint = hash % m.cap; while m.entries[idx].occupied { if String_Eq(m.entries[idx].key, key) { return m.entries[idx].value; } idx = (idx + 1) % m.cap; } var zero: V = 0 as V; return zero; } /// Like `StringMap_Get`, but returns `defaultVal` when the key is absent. func StringMap_GetOr(m: *StringMap, key: String, defaultVal: V) -> V { if StringMap_Has(m, key) { return StringMap_Get(m, key); } return defaultVal; } func StringMap_Has(m: *StringMap, key: String) -> bool { let hash: uint = bux_hash_string(key); var idx: uint = hash % m.cap; while m.entries[idx].occupied { if String_Eq(m.entries[idx].key, key) { return true; } idx = (idx + 1) % m.cap; } return false; } func StringMap_Len(m: *StringMap) -> uint { return m.len; } func StringMap_IsEmpty(m: *StringMap) -> bool { return m.len == 0; } func StringMap_Remove(m: *StringMap, key: String) -> bool { if !StringMap_Has(m, key) { return false; } var fresh: StringMap = StringMap_New(m.cap); var i: uint = 0; while i < m.cap { if m.entries[i].occupied { if !String_Eq(m.entries[i].key, key) { StringMap_Set(&fresh, m.entries[i].key, m.entries[i].value); } } i = i + 1; } bux_free(m.entries as *void); m.entries = fresh.entries; m.cap = fresh.cap; m.len = fresh.len; // Ownership transferred to `m` — clear `fresh` so auto-Drop does not free twice fresh.entries = null as *StringMapEntry; fresh.cap = 0; fresh.len = 0; return true; } func StringMap_Clear(m: *StringMap) { var i: uint = 0; while i < m.cap { m.entries[i].occupied = false; i = i + 1; } m.len = 0; } func StringMap_Free(m: *StringMap) { bux_free(m.entries as *void); m.entries = null as *StringMapEntry; m.cap = 0; m.len = 0; } }