Skip to content

Commit 946f78f

Browse files
CopilotDunqing
andcommitted
Apply reviewer feedback: optimize number formatting further
- Remove unnecessary .clone() by using move semantics - Use position() instead of enumerate().find() for cleaner code - Replace cow_replacen with split_once to avoid extra allocations - Separate integer/non-integer branches to avoid redundant .0 checks - Apply e+ replacement optimization only when needed Co-authored-by: Dunqing <29533304+Dunqing@users.noreply.github.com>
1 parent 45f4d49 commit 946f78f

File tree

1 file changed

+91
-49
lines changed

1 file changed

+91
-49
lines changed

crates/oxc_codegen/src/lib.rs

Lines changed: 91 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -762,8 +762,6 @@ impl<'a> Codegen<'a> {
762762
// and use self.print_str directly instead of returning intermediate strings
763763
#[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_possible_wrap)]
764764
fn print_minified_number(&mut self, num: f64, buffer: &mut dragonbox_ecma::Buffer) {
765-
use cow_utils::CowUtils;
766-
767765
if num < 1000.0 && num.fract() == 0.0 {
768766
self.print_str(buffer.format(num));
769767
self.need_space_before_dot = self.code_len();
@@ -776,68 +774,112 @@ impl<'a> Codegen<'a> {
776774
s = &s[1..];
777775
}
778776

779-
let s = s.cow_replacen("e+", "e", 1);
780-
781777
// Track the best candidate found so far
782-
let mut best_candidate = s.clone();
783-
let mut best_len = best_candidate.len();
784-
785-
// Check hex format for integers
778+
// Check hex format for integers first
786779
if num.fract() == 0.0 {
780+
// For integers, apply e+ replacement first, then check optimizations
781+
let s = if let Some((prefix, suffix)) = s.split_once("e+") {
782+
format!("{prefix}e{suffix}").into()
783+
} else {
784+
s.into()
785+
};
786+
787+
let mut best_candidate: std::borrow::Cow<'_, str> = s;
788+
let mut best_len = best_candidate.len();
789+
787790
let hex_candidate = format!("0x{:x}", num as u128);
788791
if hex_candidate.len() < best_len {
789792
best_candidate = hex_candidate.into();
790793
best_len = best_candidate.len();
791794
}
792-
}
793-
794-
// Check for scientific notation optimizations for numbers starting with ".0"
795-
if best_candidate.starts_with(".0") {
796-
if let Some((i, _)) = best_candidate[1..].bytes().enumerate().find(|(_, c)| *c != b'0')
795+
796+
// Check for numbers ending with zeros (but not hex numbers)
797+
if best_candidate.ends_with('0') && !best_candidate.starts_with("0x") {
798+
if let Some(len) = best_candidate.bytes().rev().position(|c| c != b'0') {
799+
let base = &best_candidate[0..best_candidate.len() - len];
800+
let scientific_candidate = format!("{base}e{len}");
801+
if scientific_candidate.len() < best_len {
802+
best_candidate = scientific_candidate.into();
803+
best_len = best_candidate.len();
804+
}
805+
}
806+
}
807+
808+
// Check for scientific notation optimization: `1.2e101` -> `12e100`
809+
if let Some((integer, point, exponent)) = best_candidate
810+
.split_once('.')
811+
.and_then(|(a, b)| b.split_once('e').map(|e| (a, e.0, e.1)))
797812
{
798-
let len = i + 1; // `+1` to include the dot.
799-
let digits = &best_candidate[len..];
800-
let scientific_candidate = format!("{digits}e-{}", digits.len() + len - 1);
801-
if scientific_candidate.len() < best_len {
802-
best_candidate = scientific_candidate.into();
803-
best_len = best_candidate.len();
813+
let new_exp = exponent.parse::<isize>().unwrap() - point.len() as isize;
814+
let optimized_candidate = format!("{integer}{point}e{new_exp}");
815+
if optimized_candidate.len() < best_len {
816+
best_candidate = optimized_candidate.into();
804817
}
805818
}
806-
}
807-
808-
// Check for numbers ending with zeros (but not hex numbers)
809-
if best_candidate.ends_with('0') && !best_candidate.starts_with("0x") {
810-
if let Some((len, _)) =
811-
best_candidate.bytes().rev().enumerate().find(|(_, c)| *c != b'0')
819+
820+
// Print the best candidate and update need_space_before_dot
821+
if !best_candidate.bytes().any(|b| matches!(b, b'.' | b'e' | b'x')) {
822+
self.print_str(&best_candidate);
823+
self.need_space_before_dot = self.code_len();
824+
} else {
825+
self.print_str(&best_candidate);
826+
}
827+
} else {
828+
// For non-integers, apply e+ replacement and check for ".0" optimizations
829+
let s = if let Some((prefix, suffix)) = s.split_once("e+") {
830+
format!("{prefix}e{suffix}").into()
831+
} else {
832+
s.into()
833+
};
834+
835+
let mut best_candidate: std::borrow::Cow<'_, str> = s;
836+
let mut best_len = best_candidate.len();
837+
838+
// Check for scientific notation optimizations for numbers starting with ".0"
839+
if best_candidate.starts_with(".0") {
840+
if let Some(i) = best_candidate[1..].bytes().position(|c| c != b'0') {
841+
let len = i + 1; // `+1` to include the dot.
842+
let digits = &best_candidate[len..];
843+
let scientific_candidate = format!("{digits}e-{}", digits.len() + len - 1);
844+
if scientific_candidate.len() < best_len {
845+
best_candidate = scientific_candidate.into();
846+
best_len = best_candidate.len();
847+
}
848+
}
849+
}
850+
851+
// Check for numbers ending with zeros
852+
if best_candidate.ends_with('0') {
853+
if let Some(len) = best_candidate.bytes().rev().position(|c| c != b'0') {
854+
let base = &best_candidate[0..best_candidate.len() - len];
855+
let scientific_candidate = format!("{base}e{len}");
856+
if scientific_candidate.len() < best_len {
857+
best_candidate = scientific_candidate.into();
858+
best_len = best_candidate.len();
859+
}
860+
}
861+
}
862+
863+
// Check for scientific notation optimization: `1.2e101` -> `12e100`
864+
if let Some((integer, point, exponent)) = best_candidate
865+
.split_once('.')
866+
.and_then(|(a, b)| b.split_once('e').map(|e| (a, e.0, e.1)))
812867
{
813-
let base = &best_candidate[0..best_candidate.len() - len];
814-
let scientific_candidate = format!("{base}e{len}");
815-
if scientific_candidate.len() < best_len {
816-
best_candidate = scientific_candidate.into();
817-
best_len = best_candidate.len();
868+
let new_exp = exponent.parse::<isize>().unwrap() - point.len() as isize;
869+
let optimized_candidate = format!("{integer}{point}e{new_exp}");
870+
if optimized_candidate.len() < best_len {
871+
best_candidate = optimized_candidate.into();
818872
}
819873
}
820-
}
821-
822-
// Check for scientific notation optimization: `1.2e101` -> `12e100`
823-
if let Some((integer, point, exponent)) = best_candidate
824-
.split_once('.')
825-
.and_then(|(a, b)| b.split_once('e').map(|e| (a, e.0, e.1)))
826-
{
827-
let new_exp = exponent.parse::<isize>().unwrap() - point.len() as isize;
828-
let optimized_candidate = format!("{integer}{point}e{new_exp}");
829-
if optimized_candidate.len() < best_len {
830-
best_candidate = optimized_candidate.into();
874+
875+
// Print the best candidate and update need_space_before_dot
876+
if !best_candidate.bytes().any(|b| matches!(b, b'.' | b'e' | b'x')) {
877+
self.print_str(&best_candidate);
878+
self.need_space_before_dot = self.code_len();
879+
} else {
880+
self.print_str(&best_candidate);
831881
}
832882
}
833-
834-
// Print the best candidate
835-
self.print_str(&best_candidate);
836-
837-
// Update need_space_before_dot based on the final output
838-
if !best_candidate.bytes().any(|b| matches!(b, b'.' | b'e' | b'x')) {
839-
self.need_space_before_dot = self.code_len();
840-
}
841883
}
842884

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

0 commit comments

Comments
 (0)