-
-
Notifications
You must be signed in to change notification settings - Fork 509
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(minifier): fold String::charAt / String::charCodeAt more precise…
…ly (#9082)
- Loading branch information
1 parent
237ffba
commit 125d610
Showing
7 changed files
with
163 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,56 @@ | ||
use crate::ToInt32; | ||
use num_traits::ToPrimitive; | ||
|
||
use crate::to_integer_or_infinity::ToIntegerOrInfinityResult; | ||
|
||
pub trait StringCharAt { | ||
/// `String.prototype.charAt ( pos )` | ||
/// <https://tc39.es/ecma262/#sec-string.prototype.charat> | ||
fn char_at(&self, index: Option<f64>) -> Option<char>; | ||
fn char_at(&self, pos: Option<f64>) -> StringCharAtResult; | ||
} | ||
|
||
impl StringCharAt for &str { | ||
#[expect(clippy::cast_sign_loss)] | ||
fn char_at(&self, index: Option<f64>) -> Option<char> { | ||
let index = index.unwrap_or(0.0); | ||
if index.fract() != 0.0 || index.is_nan() || index.is_infinite() { | ||
return None; | ||
} | ||
let index = index.to_int_32() as isize; | ||
if index < 0 { | ||
None | ||
} else { | ||
self.encode_utf16().nth(index as usize).and_then(|n| char::from_u32(u32::from(n))) | ||
} | ||
fn char_at(&self, pos: Option<f64>) -> StringCharAtResult { | ||
use crate::to_integer_or_infinity::ToIntegerOrInfinity; | ||
|
||
let position = pos.unwrap_or(0.0).to_integer_or_infinity_as_i64(); | ||
let position = match position { | ||
ToIntegerOrInfinityResult::Value(v) if v >= 0 => v.to_usize().unwrap(), | ||
_ => return StringCharAtResult::OutOfRange, | ||
}; | ||
|
||
self.encode_utf16().nth(position).map_or(StringCharAtResult::OutOfRange, |n| { | ||
char::from_u32(u32::from(n)) | ||
.map_or(StringCharAtResult::InvalidChar(n), StringCharAtResult::Value) | ||
}) | ||
} | ||
} | ||
|
||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] | ||
pub enum StringCharAtResult { | ||
Value(char), | ||
InvalidChar(u16), | ||
OutOfRange, | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use crate::string_char_at::StringCharAtResult; | ||
|
||
use super::StringCharAt; | ||
|
||
#[test] | ||
fn test_evaluate_string_char_at() { | ||
let s = "test"; | ||
assert_eq!(s.char_at(Some(0.0)), Some('t')); | ||
assert_eq!(s.char_at(Some(1.0)), Some('e')); | ||
assert_eq!(s.char_at(Some(2.0)), Some('s')); | ||
assert_eq!(s.char_at(Some(0.5)), None); | ||
assert_eq!(s.char_at(Some(-1.0)), None); | ||
assert_eq!(s.char_at(Some(-1.1)), None); | ||
assert_eq!(s.char_at(Some(-1_073_741_825.0)), None); | ||
assert_eq!(s.char_at(Some(0.0)), StringCharAtResult::Value('t')); | ||
assert_eq!(s.char_at(Some(1.0)), StringCharAtResult::Value('e')); | ||
assert_eq!(s.char_at(Some(2.0)), StringCharAtResult::Value('s')); | ||
assert_eq!(s.char_at(Some(4.0)), StringCharAtResult::OutOfRange); | ||
assert_eq!(s.char_at(Some(0.5)), StringCharAtResult::Value('t')); | ||
assert_eq!(s.char_at(None), StringCharAtResult::Value('t')); | ||
assert_eq!(s.char_at(Some(f64::INFINITY)), StringCharAtResult::OutOfRange); | ||
assert_eq!(s.char_at(Some(f64::NEG_INFINITY)), StringCharAtResult::OutOfRange); | ||
assert_eq!(s.char_at(Some(-1.0)), StringCharAtResult::OutOfRange); | ||
assert_eq!(s.char_at(Some(-1.1)), StringCharAtResult::OutOfRange); | ||
assert_eq!(s.char_at(Some(-1_073_741_825.0)), StringCharAtResult::OutOfRange); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
use num_traits::ToPrimitive; | ||
|
||
pub trait ToIntegerOrInfinity { | ||
/// `ToIntegerOrInfinity` | ||
/// <https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tointegerorinfinity> | ||
fn to_integer_or_infinity(&self) -> f64; | ||
|
||
/// Convert the value to i64. If the value is bigger or smaller than i64::MIN or i64::MAX, | ||
/// it will be converted to Infinity or NegativeInfinity. | ||
fn to_integer_or_infinity_as_i64(&self) -> ToIntegerOrInfinityResult { | ||
let res = self.to_integer_or_infinity(); | ||
match res { | ||
f64::INFINITY => ToIntegerOrInfinityResult::Infinity, | ||
f64::NEG_INFINITY => ToIntegerOrInfinityResult::NegativeInfinity, | ||
_ => res.to_i64().map_or_else( | ||
|| { | ||
if res >= 0.0 { | ||
ToIntegerOrInfinityResult::Infinity | ||
} else { | ||
ToIntegerOrInfinityResult::NegativeInfinity | ||
} | ||
}, | ||
ToIntegerOrInfinityResult::Value, | ||
), | ||
} | ||
} | ||
} | ||
|
||
impl ToIntegerOrInfinity for f64 { | ||
fn to_integer_or_infinity(&self) -> f64 { | ||
if self.is_nan() || *self == 0.0 { | ||
return 0.0; | ||
} | ||
if self.is_infinite() { | ||
return *self; | ||
} | ||
self.trunc() | ||
} | ||
} | ||
|
||
pub enum ToIntegerOrInfinityResult { | ||
Infinity, | ||
NegativeInfinity, | ||
Value(i64), | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[expect(clippy::float_cmp)] | ||
#[test] | ||
fn test_to_integer_or_infinity() { | ||
assert_eq!(f64::NAN.to_integer_or_infinity(), 0.0); | ||
assert_eq!(0.0.to_integer_or_infinity(), 0.0); | ||
assert_eq!(f64::INFINITY.to_integer_or_infinity(), f64::INFINITY); | ||
assert_eq!(f64::NEG_INFINITY.to_integer_or_infinity(), f64::NEG_INFINITY); | ||
assert_eq!(1.0.to_integer_or_infinity(), 1.0); | ||
assert_eq!(-1.0.to_integer_or_infinity(), -1.0); | ||
assert_eq!(1.1.to_integer_or_infinity(), 1.0); | ||
assert_eq!(-1.1.to_integer_or_infinity(), -1.0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters