module Std::Map { extern func bux_hash_bytes(ptr: *void, size: uint) -> uint; extern func bux_hash_string(s: String) -> uint; extern func bux_alloc(size: uint) -> *void; extern func bux_free(ptr: *void); // --------------------------------------------------------------------------- // 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, } /// Create a map with at least `cap` slots (open addressing). /// `cap == 0` defaults to 8 so inserts never hit `% 0` or a full table with no growth room. func Map_New(cap: uint) -> Map { var c: uint = cap; if c == 0 { c = 8; } let total: uint = c * sizeof(MapEntry); let data: *MapEntry = bux_alloc(total) as *MapEntry; var i: uint = 0; while i < c { data[i].occupied = false; i = i + 1; } return Map { entries: data, cap: c, len: 0 }; } /// Grow / rehash to `newCap` (must be > 0). Transfers ownership; does not Drop `fresh`. func Map_Rehash(m: *Map, newCap: uint) { var nc: uint = newCap; if nc == 0 { nc = 8; } var fresh: Map = Map_New(nc); var i: uint = 0; while i < m.cap { if m.entries[i].occupied { Map_SetInsertOnly(&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; fresh.entries = null as *MapEntry; fresh.cap = 0; fresh.len = 0; } /// Insert assuming free slots exist (no grow). Used by rehash. func Map_SetInsertOnly(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_Set(m: *Map, key: K, value: V) { // Keep load factor under ~50% for open addressing (also handles cap==0). if m.cap == 0 || m.len * 2 >= m.cap { var nc: uint = m.cap * 2; if nc < 8 { nc = 8; } Map_Rehash(m, nc); } Map_SetInsertOnly(m, key, value); } func Map_Get(m: *Map, key: K) -> V { if m.cap == 0 { var zero: V = 0 as V; return zero; } 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 { if m.cap == 0 { return false; } 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 keepCap: uint = m.cap; if keepCap == 0 { keepCap = 8; } var fresh: Map = Map_New(keepCap); var i: uint = 0; while i < m.cap { if m.entries[i].occupied { if m.entries[i].key != key { Map_SetInsertOnly(&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 { var c: uint = cap; if c == 0 { c = 8; } let total: uint = c * sizeof(StringMapEntry); let data: *StringMapEntry = bux_alloc(total) as *StringMapEntry; var i: uint = 0; while i < c { data[i].occupied = false; i = i + 1; } return StringMap { entries: data, cap: c, len: 0 }; } func StringMap_Rehash(m: *StringMap, newCap: uint) { var nc: uint = newCap; if nc == 0 { nc = 8; } var fresh: StringMap = StringMap_New(nc); var i: uint = 0; while i < m.cap { if m.entries[i].occupied { StringMap_SetInsertOnly(&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; fresh.entries = null as *StringMapEntry; fresh.cap = 0; fresh.len = 0; } func StringMap_SetInsertOnly(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_Set(m: *StringMap, key: String, value: V) { if m.cap == 0 || m.len * 2 >= m.cap { var nc: uint = m.cap * 2; if nc < 8 { nc = 8; } StringMap_Rehash(m, nc); } StringMap_SetInsertOnly(m, key, value); } func StringMap_Get(m: *StringMap, key: String) -> V { if m.cap == 0 { var zero: V = 0 as V; return zero; } 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 { if m.cap == 0 { return false; } 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 keepCap: uint = m.cap; if keepCap == 0 { keepCap = 8; } var fresh: StringMap = StringMap_New(keepCap); var i: uint = 0; while i < m.cap { if m.entries[i].occupied { if !String_Eq(m.entries[i].key, key) { StringMap_SetInsertOnly(&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; } }