Skip to content

Commit

Permalink
perf(codegen): avoid a heap allocation when printing floats
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Jan 31, 2025
1 parent 3ac5020 commit d44462a
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 16 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/oxc_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ oxc_index = { workspace = true }
oxc_mangler = { workspace = true }
oxc_sourcemap = { workspace = true }
oxc_span = { workspace = true }
oxc_syntax = { workspace = true, features = ["to_js_string"] }
oxc_syntax = { workspace = true }

assert-unchecked = { workspace = true }
bitflags = { workspace = true }
cow-utils = { workspace = true }
nonmax = { workspace = true }
rustc-hash = { workspace = true }
ryu-js = { workspace = true }

[dev-dependencies]
base64 = { workspace = true }
Expand Down
31 changes: 16 additions & 15 deletions crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,12 +575,13 @@ impl<'a> Codegen<'a> {
}

fn print_non_negative_float(&mut self, num: f64) {
use oxc_syntax::number::ToJsString;
// Inline the buffer here to avoid heap allocation on `buffer.format(*self).to_string()`.
let mut buffer = ryu_js::Buffer::new();
if num < 1000.0 && num.fract() == 0.0 {
self.print_str(&num.to_js_string());
self.print_str(buffer.format(num));
self.need_space_before_dot = self.code_len();
} else {
let s = Self::get_minified_number(num);
let s = Self::get_minified_number(num, &mut buffer);
self.print_str(&s);
if !s.bytes().any(|b| matches!(b, b'.' | b'e' | b'x')) {
self.need_space_before_dot = self.code_len();
Expand Down Expand Up @@ -691,40 +692,40 @@ impl<'a> Codegen<'a> {
// `get_minified_number` from terser
// https://github.com/terser/terser/blob/c5315c3fd6321d6b2e076af35a70ef532f498505/lib/output.js#L2418
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_possible_wrap)]
fn get_minified_number(num: f64) -> String {
fn get_minified_number(num: f64, buffer: &mut ryu_js::Buffer) -> Cow<'_, str> {
use cow_utils::CowUtils;
use oxc_syntax::number::ToJsString;

if num < 1000.0 && num.fract() == 0.0 {
return num.to_js_string();
return Cow::Borrowed(buffer.format(num));
}

let mut s = num.to_js_string();
let mut s = buffer.format(num);

if s.starts_with("0.") {
s = s[1..].to_string();
s = &s[1..];
}

s = s.cow_replacen("e+", "e", 1).to_string();
let s = s.cow_replacen("e+", "e", 1);

let mut candidates = vec![s.clone()];

if num.fract() == 0.0 {
candidates.push(format!("0x{:x}", num as u128));
candidates.push(Cow::Owned(format!("0x{:x}", num as u128)));
}

// create `1e-2`
if s.starts_with(".0") {
if let Some((i, _)) = s[1..].bytes().enumerate().find(|(_, c)| *c != b'0') {
let len = i + 1; // `+1` to include the dot.
let digits = &s[len..];
candidates.push(format!("{digits}e-{}", digits.len() + len - 1));
candidates.push(Cow::Owned(format!("{digits}e-{}", digits.len() + len - 1)));
}
}

// create 1e2
if s.ends_with('0') {
if let Some((len, _)) = s.bytes().rev().enumerate().find(|(_, c)| *c != b'0') {
candidates.push(format!("{}e{len}", &s[0..s.len() - len]));
candidates.push(Cow::Owned(format!("{}e{len}", &s[0..s.len() - len])));
}
}

Expand All @@ -733,13 +734,13 @@ impl<'a> Codegen<'a> {
if let Some((integer, point, exponent)) =
s.split_once('.').and_then(|(a, b)| b.split_once('e').map(|e| (a, e.0, e.1)))
{
candidates.push(format!(
candidates.push(Cow::Owned(format!(
"{integer}{point}e{}",
exponent.parse::<isize>().unwrap() - point.len() as isize
));
)));
}

candidates.into_iter().min_by_key(String::len).unwrap()
candidates.into_iter().min_by_key(|c| c.len()).unwrap()
}

fn add_source_mapping(&mut self, span: Span) {
Expand Down

0 comments on commit d44462a

Please sign in to comment.