diff --git a/Cargo.lock b/Cargo.lock index 05c82f58b4697..a25f9b1c329fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2065,7 +2065,6 @@ dependencies = [ name = "oxc_mangler" version = "0.85.0" dependencies = [ - "fixedbitset", "itertools", "oxc_allocator", "oxc_ast", diff --git a/Cargo.toml b/Cargo.toml index cdf8ab6ea340b..903703cc7c6f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -184,7 +184,6 @@ encoding_rs = "0.8.35" encoding_rs_io = "0.1.7" env_logger = { version = "0.11.8", default-features = false } fast-glob = "1.0.0" -fixedbitset = "0.5.7" flate2 = "1.1.2" futures = "0.3.31" handlebars = "6.3.2" diff --git a/crates/oxc_mangler/Cargo.toml b/crates/oxc_mangler/Cargo.toml index 39dbf009ea675..d8f4833b6ecc1 100644 --- a/crates/oxc_mangler/Cargo.toml +++ b/crates/oxc_mangler/Cargo.toml @@ -28,7 +28,6 @@ oxc_index = { workspace = true } oxc_semantic = { workspace = true } oxc_span = { workspace = true } -fixedbitset = { workspace = true } itertools = { workspace = true } rustc-hash = { workspace = true } diff --git a/crates/oxc_mangler/src/bitset.rs b/crates/oxc_mangler/src/bitset.rs new file mode 100644 index 0000000000000..3e1b8fa7fe7bc --- /dev/null +++ b/crates/oxc_mangler/src/bitset.rs @@ -0,0 +1,101 @@ +use std::fmt::{Debug, Display}; + +use oxc_allocator::{Allocator, CloneIn, Vec}; + +#[derive(PartialEq, Eq, Hash)] +pub struct BitSet<'alloc> { + entries: Vec<'alloc, u8>, +} + +impl<'alloc> BitSet<'alloc> { + pub fn new_in(max_bit_count: usize, allocator: &'alloc Allocator) -> Self { + Self { + entries: Vec::from_iter_in( + std::iter::repeat_n(0, max_bit_count.div_ceil(8)), + allocator, + ), + } + } + + pub fn has_bit(&self, bit: usize) -> bool { + (self.entries[bit / 8] & (1 << (bit & 7))) != 0 + } + + pub fn set_bit(&mut self, bit: usize) { + self.entries[bit / 8] |= 1 << (bit & 7); + } +} + +impl Display for BitSet<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // using little endian representation + // e.g. 256 + // 00000001_00000000 + // ^ ^ + // msb lsb + let mut iter = self.entries.iter().rev(); + if let Some(first) = iter.next() { + f.write_str(&format!("{first:08b}"))?; + } + for e in iter { + f.write_str(&format!("_{e:08b}"))?; + } + Ok(()) + } +} + +impl Debug for BitSet<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("BitSet").field(&self.to_string()).finish() + } +} + +impl<'allocator> CloneIn<'allocator> for BitSet<'allocator> { + type Cloned = Self; + + fn clone_in(&self, allocator: &'allocator Allocator) -> Self { + Self { entries: self.entries.clone_in(allocator) } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + let allocator = Allocator::default(); + let mut bs = BitSet::new_in(1, &allocator); + assert_eq!(bs.to_string(), "00000000"); + bs.set_bit(0); + bs.set_bit(1); + bs.set_bit(7); + assert_eq!(bs.to_string(), "10000011"); + + let mut bs = BitSet::new_in(9, &allocator); + assert_eq!(bs.to_string(), "00000000_00000000"); + bs.set_bit(0); + bs.set_bit(1); + bs.set_bit(7); + assert_eq!(bs.to_string(), "00000000_10000011"); + bs.set_bit(8); + assert_eq!(bs.to_string(), "00000001_10000011"); + bs.set_bit(15); + assert_eq!(bs.to_string(), "10000001_10000011"); + } + + #[test] + fn union() { + let allocator = Allocator::default(); + let mut bs = BitSet::new_in(9, &allocator); + assert_eq!(bs.to_string(), "00000000_00000000"); + let mut bs2 = bs.clone_in(&allocator); + bs.set_bit(0); + bs.set_bit(1); + bs.set_bit(7); + assert_eq!(bs.to_string(), "00000000_10000011"); + bs2.set_bit(8); + bs2.set_bit(15); + assert_eq!(bs2.to_string(), "10000001_00000000"); + } +} diff --git a/crates/oxc_mangler/src/lib.rs b/crates/oxc_mangler/src/lib.rs index 731633e69dda4..d6a88383674ed 100644 --- a/crates/oxc_mangler/src/lib.rs +++ b/crates/oxc_mangler/src/lib.rs @@ -1,6 +1,5 @@ use std::iter::{self, repeat_with}; -use fixedbitset::FixedBitSet; use itertools::Itertools; use keep_names::collect_name_symbols; use rustc_hash::FxHashSet; @@ -14,10 +13,13 @@ use oxc_semantic::{AstNodes, Scoping, Semantic, SemanticBuilder, SymbolId}; use oxc_span::Atom; pub(crate) mod base54; +mod bitset; mod keep_names; pub use keep_names::MangleOptionsKeepNames; +use crate::bitset::BitSet; + #[derive(Default, Debug, Clone, Copy)] pub struct MangleOptions { /// Pass true to mangle names declared in the top level scope. @@ -305,7 +307,7 @@ impl<'t> Mangler<'t> { let mut slots = Vec::from_iter_in(iter::repeat_n(0, scoping.symbols_len()), temp_allocator); // Stores the lived scope ids for each slot. Keyed by slot number. - let mut slot_liveness: std::vec::Vec = vec![]; + let mut slot_liveness: Vec = Vec::new_in(temp_allocator); let mut tmp_bindings = Vec::with_capacity_in(100, temp_allocator); let mut reusable_slots = Vec::new_in(temp_allocator); @@ -335,7 +337,7 @@ impl<'t> Mangler<'t> { slot_liveness .iter() .enumerate() - .filter(|(_, slot_liveness)| !slot_liveness.contains(scope_id.index())) + .filter(|(_, slot_liveness)| !slot_liveness.has_bit(scope_id.index())) .map(|(slot, _)| slot) .take(tmp_bindings.len()), ); @@ -346,8 +348,10 @@ impl<'t> Mangler<'t> { slot += remaining_count; if slot_liveness.len() < slot { - slot_liveness - .resize_with(slot, || FixedBitSet::with_capacity(scoping.scopes_len())); + slot_liveness.extend( + iter::repeat_with(|| BitSet::new_in(scoping.scopes_len(), temp_allocator)) + .take(remaining_count), + ); } for (&symbol_id, assigned_slot) in @@ -379,7 +383,9 @@ impl<'t> Mangler<'t> { }); // Since the slot is now assigned to this symbol, it is alive in all the scopes that this symbol is alive in. - slot_liveness[assigned_slot].extend(lived_scope_ids.map(oxc_index::Idx::index)); + for scope_id in lived_scope_ids { + slot_liveness[assigned_slot].set_bit(scope_id.index()); + } } }