module Std::Set { extern func bux_hash_bytes(ptr: *void, size: uint) -> uint; extern func bux_mem_eq(a: *void, b: *void, size: uint) -> int; extern func bux_alloc(size: uint) -> *void; extern func bux_free(ptr: *void); struct SetEntry { value: T, occupied: bool, } struct Set { entries: *SetEntry, cap: uint, len: uint, } /// Create a set with at least `cap` slots. `cap == 0` defaults to 8. func Set_New(cap: uint) -> Set { var c: uint = cap; if c == 0 { c = 8; } let total: uint = c * sizeof(SetEntry); let data: *SetEntry = bux_alloc(total) as *SetEntry; var i: uint = 0; while i < c { data[i].occupied = false; i = i + 1; } return Set { entries: data, cap: c, len: 0 }; } func Set_Rehash(s: *Set, newCap: uint) { var nc: uint = newCap; if nc == 0 { nc = 8; } var fresh: Set = Set_New(nc); var i: uint = 0; while i < s.cap { if s.entries[i].occupied { Set_AddInsertOnly(&fresh, s.entries[i].value); } i = i + 1; } bux_free(s.entries as *void); s.entries = fresh.entries; s.cap = fresh.cap; s.len = fresh.len; fresh.entries = null as *SetEntry; fresh.cap = 0; fresh.len = 0; } func Set_AddInsertOnly(s: *Set, value: T) { var valuePtr: *T = &value; let hash: uint = bux_hash_bytes(valuePtr as *void, sizeof(T)); var idx: uint = hash % s.cap; while s.entries[idx].occupied { var entryPtr: *T = &s.entries[idx].value; if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) != 0 { return; } idx = (idx + 1) % s.cap; } s.entries[idx].value = value; s.entries[idx].occupied = true; s.len = s.len + 1; } func Set_Add(s: *Set, value: T) { if s.cap == 0 || s.len * 2 >= s.cap { var nc: uint = s.cap * 2; if nc < 8 { nc = 8; } Set_Rehash(s, nc); } Set_AddInsertOnly(s, value); } func Set_Has(s: *Set, value: T) -> bool { if s.cap == 0 { return false; } var valuePtr: *T = &value; let hash: uint = bux_hash_bytes(valuePtr as *void, sizeof(T)); var idx: uint = hash % s.cap; while s.entries[idx].occupied { var entryPtr: *T = &s.entries[idx].value; if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) != 0 { return true; } idx = (idx + 1) % s.cap; } return false; } func Set_Len(s: *Set) -> uint { return s.len; } func Set_IsEmpty(s: *Set) -> bool { return s.len == 0; } /* Remove value if present. Rebuilds the table to keep open-addressing correct. */ func Set_Remove(s: *Set, value: T) -> bool { if !Set_Has(s, value) { return false; } var keepCap: uint = s.cap; if keepCap == 0 { keepCap = 8; } var fresh: Set = Set_New(keepCap); var i: uint = 0; while i < s.cap { if s.entries[i].occupied { var entryPtr: *T = &s.entries[i].value; var valuePtr: *T = &value; if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) == 0 { Set_AddInsertOnly(&fresh, s.entries[i].value); } } i = i + 1; } bux_free(s.entries as *void); s.entries = fresh.entries; s.cap = fresh.cap; s.len = fresh.len; // Ownership transferred to `s` — clear `fresh` so auto-Drop does not free twice fresh.entries = null as *SetEntry; fresh.cap = 0; fresh.len = 0; return true; } func Set_Clear(s: *Set) { var i: uint = 0; while i < s.cap { s.entries[i].occupied = false; i = i + 1; } s.len = 0; } func Set_Free(s: *Set) { bux_free(s.entries as *void); s.entries = null as *SetEntry; s.cap = 0; s.len = 0; } func Set_Drop(s: *Set) { Set_Free(s); } }