use std::collections::HashMap;
use rucc_base::Symbol;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct HideSet(u32);
impl HideSet {
pub const EMPTY: HideSet = HideSet(0);
#[inline]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
#[inline]
pub const fn raw(self) -> u32 {
self.0
}
}
#[derive(Debug)]
pub struct HideSets {
sets: Vec<Box<[Symbol]>>,
map: HashMap<Box<[Symbol]>, HideSet>,
}
impl Default for HideSets {
fn default() -> Self {
Self::new()
}
}
impl HideSets {
pub fn new() -> Self {
let empty: Box<[Symbol]> = Box::new([]);
let mut map = HashMap::new();
map.insert(empty.clone(), HideSet::EMPTY);
Self { sets: vec![empty], map }
}
pub fn members(&self, set: HideSet) -> &[Symbol] {
&self.sets[set.0 as usize]
}
pub fn len(&self) -> usize {
self.sets.len()
}
pub fn is_empty(&self) -> bool {
false
}
#[inline]
pub fn contains(&self, set: HideSet, name: Symbol) -> bool {
!set.is_empty() && self.sets[set.0 as usize].binary_search(&name).is_ok()
}
pub fn add(&mut self, set: HideSet, name: Symbol) -> HideSet {
let current = &self.sets[set.0 as usize];
let Err(at) = current.binary_search(&name) else {
return set;
};
let mut next = Vec::with_capacity(current.len() + 1);
next.extend_from_slice(¤t[..at]);
next.push(name);
next.extend_from_slice(¤t[at..]);
self.insert(next)
}
pub fn union(&mut self, a: HideSet, b: HideSet) -> HideSet {
if a == b || b.is_empty() {
return a;
}
if a.is_empty() {
return b;
}
let merged = merge(&self.sets[a.0 as usize], &self.sets[b.0 as usize], Merge::Union);
self.insert(merged)
}
pub fn intersect(&mut self, a: HideSet, b: HideSet) -> HideSet {
if a == b {
return a;
}
if a.is_empty() || b.is_empty() {
return HideSet::EMPTY;
}
let merged = merge(&self.sets[a.0 as usize], &self.sets[b.0 as usize], Merge::Intersect);
self.insert(merged)
}
fn insert(&mut self, sorted: Vec<Symbol>) -> HideSet {
let sorted: Box<[Symbol]> = sorted.into_boxed_slice();
if let Some(&found) = self.map.get(&sorted) {
return found;
}
let id = HideSet(u32::try_from(self.sets.len()).expect("too many hide sets"));
self.sets.push(sorted.clone());
self.map.insert(sorted, id);
id
}
}
#[derive(Clone, Copy)]
enum Merge {
Union,
Intersect,
}
fn merge(a: &[Symbol], b: &[Symbol], op: Merge) -> Vec<Symbol> {
let mut out = Vec::with_capacity(match op {
Merge::Union => a.len() + b.len(),
Merge::Intersect => a.len().min(b.len()),
});
let (mut i, mut j) = (0, 0);
while i < a.len() && j < b.len() {
match a[i].cmp(&b[j]) {
std::cmp::Ordering::Less => {
if matches!(op, Merge::Union) {
out.push(a[i]);
}
i += 1;
}
std::cmp::Ordering::Greater => {
if matches!(op, Merge::Union) {
out.push(b[j]);
}
j += 1;
}
std::cmp::Ordering::Equal => {
out.push(a[i]);
i += 1;
j += 1;
}
}
}
if matches!(op, Merge::Union) {
out.extend_from_slice(&a[i..]);
out.extend_from_slice(&b[j..]);
}
out
}
#[cfg(test)]
mod tests {
use rucc_base::Interner;
use super::*;
fn syms(n: usize) -> (Interner, Vec<Symbol>) {
let mut interner = Interner::new();
let names = (0..n).map(|i| interner.intern(&format!("M{i}"))).collect();
(interner, names)
}
#[test]
fn the_empty_set_is_index_zero_and_hides_nothing() {
let (_i, s) = syms(1);
let sets = HideSets::new();
assert_eq!(HideSet::EMPTY.raw(), 0);
assert!(!sets.contains(HideSet::EMPTY, s[0]));
}
#[test]
fn adding_a_name_makes_it_hidden() {
let (_i, s) = syms(2);
let mut sets = HideSets::new();
let one = sets.add(HideSet::EMPTY, s[0]);
assert!(sets.contains(one, s[0]));
assert!(!sets.contains(one, s[1]));
}
#[test]
fn adding_the_same_name_twice_changes_nothing() {
let (_i, s) = syms(1);
let mut sets = HideSets::new();
let one = sets.add(HideSet::EMPTY, s[0]);
assert_eq!(sets.add(one, s[0]), one);
}
#[test]
fn sets_built_in_different_orders_are_the_same_set() {
let (_i, s) = syms(3);
let mut sets = HideSets::new();
let forward = {
let a = sets.add(HideSet::EMPTY, s[0]);
let b = sets.add(a, s[1]);
sets.add(b, s[2])
};
let backward = {
let a = sets.add(HideSet::EMPTY, s[2]);
let b = sets.add(a, s[0]);
sets.add(b, s[1])
};
assert_eq!(forward, backward, "interning must not depend on insertion order");
assert_eq!(sets.members(forward).len(), 3);
}
#[test]
fn union_keeps_everything_from_both() {
let (_i, s) = syms(3);
let mut sets = HideSets::new();
let a = sets.add(HideSet::EMPTY, s[0]);
let a = sets.add(a, s[1]);
let b = sets.add(HideSet::EMPTY, s[1]);
let b = sets.add(b, s[2]);
let u = sets.union(a, b);
assert_eq!(sets.members(u), &[s[0], s[1], s[2]]);
}
#[test]
fn intersect_keeps_only_what_is_in_both() {
let (_i, s) = syms(3);
let mut sets = HideSets::new();
let a = sets.add(HideSet::EMPTY, s[0]);
let a = sets.add(a, s[1]);
let b = sets.add(HideSet::EMPTY, s[1]);
let b = sets.add(b, s[2]);
let x = sets.intersect(a, b);
assert_eq!(sets.members(x), &[s[1]]);
}
#[test]
fn intersecting_with_the_empty_set_is_empty() {
let (_i, s) = syms(1);
let mut sets = HideSets::new();
let a = sets.add(HideSet::EMPTY, s[0]);
assert_eq!(sets.intersect(a, HideSet::EMPTY), HideSet::EMPTY);
}
#[test]
fn a_hide_set_is_four_bytes() {
assert_eq!(size_of::<HideSet>(), 4);
}
}