perf(codegen): avoid a heap allocation when printing floats (#8807)

This commit is contained in:
Boshen 2025-01-31 08:48:52 +00:00
parent f8548ec9c7
commit d8fac6d76b
3 changed files with 19 additions and 16 deletions

1
Cargo.lock generated
View file

@ -1692,6 +1692,7 @@ dependencies = [
"oxc_syntax",
"pico-args",
"rustc-hash",
"ryu-js",
]
[[package]]

View file

@ -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 }

View file

@ -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();
@ -691,25 +692,25 @@ 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`
@ -717,14 +718,14 @@ impl<'a> Codegen<'a> {
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])));
}
}
@ -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) {