1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-23 15:49:44 -05:00

perf(ext/web): use base64-simd for atob/btoa (#14992)

This commit is contained in:
Nugine 2022-06-29 23:42:39 +08:00 committed by David Sherret
parent 825477b3cd
commit e0a79c0550
4 changed files with 39 additions and 53 deletions

17
Cargo.lock generated
View file

@ -242,6 +242,15 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "base64-simd"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "278c7ba87265587c4823cf1b2fdf57834151540b2e509574adb03627f8c7f22d"
dependencies = [
"simd-abstraction",
]
[[package]]
name = "base64ct"
version = "1.5.0"
@ -1202,7 +1211,7 @@ name = "deno_web"
version = "0.89.0"
dependencies = [
"async-trait",
"base64 0.13.0",
"base64-simd",
"deno_bench_util",
"deno_core",
"deno_url",
@ -3893,6 +3902,12 @@ dependencies = [
"rand_core",
]
[[package]]
name = "simd-abstraction"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2880f3f7b392823ee65bbcc681961cd8e698c6a30e91ab9b4eef1f9c6c226d8"
[[package]]
name = "siphasher"
version = "0.3.10"

View file

@ -110,6 +110,8 @@ opt-level = 3
opt-level = 3
[profile.bench.package.zstd-sys]
opt-level = 3
[profile.bench.package.base64-simd]
opt-level = 3
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
[profile.release.package.rand]
@ -174,3 +176,5 @@ opt-level = 3
opt-level = 3
[profile.release.package.zstd-sys]
opt-level = 3
[profile.release.package.base64-simd]
opt-level = 3

View file

@ -15,7 +15,7 @@ path = "lib.rs"
[dependencies]
async-trait = "0.1.51"
base64 = "0.13.0"
base64-simd = "0.6.2"
deno_core = { version = "0.140.0", path = "../../core" }
encoding_rs = "0.8.31"
flate2 = "1"

View file

@ -124,74 +124,41 @@ pub fn init<P: TimersPermission + 'static>(
#[op]
fn op_base64_decode(input: String) -> Result<ZeroCopyBuf, AnyError> {
let mut input = input.into_bytes();
input.retain(|c| !c.is_ascii_whitespace());
Ok(b64_decode(&input)?.into())
Ok(forgiving_base64_decode(input.into_bytes())?.into())
}
#[op]
fn op_base64_atob(mut s: ByteString) -> Result<ByteString, AnyError> {
s.retain(|c| !c.is_ascii_whitespace());
// If padding is expected, fail if not 4-byte aligned
if s.len() % 4 != 0 && (s.ends_with(b"==") || s.ends_with(b"=")) {
return Err(
DomExceptionInvalidCharacterError::new("Failed to decode base64.").into(),
);
}
Ok(b64_decode(&s)?.into())
fn op_base64_atob(s: ByteString) -> Result<ByteString, AnyError> {
Ok(forgiving_base64_decode(s.into())?.into())
}
fn b64_decode(input: &[u8]) -> Result<Vec<u8>, AnyError> {
// "If the length of input divides by 4 leaving no remainder, then:
// if input ends with one or two U+003D EQUALS SIGN (=) characters,
// remove them from input."
let input = match input.len() % 4 == 0 {
true if input.ends_with(b"==") => &input[..input.len() - 2],
true if input.ends_with(b"=") => &input[..input.len() - 1],
_ => input,
};
/// See <https://infra.spec.whatwg.org/#forgiving-base64>
fn forgiving_base64_decode(mut input: Vec<u8>) -> Result<Vec<u8>, AnyError> {
let error: _ =
|| DomExceptionInvalidCharacterError::new("Failed to decode base64");
// "If the length of input divides by 4 leaving a remainder of 1,
// throw an InvalidCharacterError exception and abort these steps."
if input.len() % 4 == 1 {
return Err(
DomExceptionInvalidCharacterError::new("Failed to decode base64.").into(),
);
}
let decoded = base64_simd::Base64::forgiving_decode_inplace(&mut input)
.map_err(|_| error())?;
let cfg = base64::Config::new(base64::CharacterSet::Standard, true)
.decode_allow_trailing_bits(true);
let out = base64::decode_config(input, cfg).map_err(|err| match err {
base64::DecodeError::InvalidByte(_, _) => {
DomExceptionInvalidCharacterError::new(
"Failed to decode base64: invalid character",
)
}
_ => DomExceptionInvalidCharacterError::new(&format!(
"Failed to decode base64: {:?}",
err
)),
})?;
Ok(out)
let decoded_len = decoded.len();
input.truncate(decoded_len);
Ok(input)
}
#[op]
fn op_base64_encode(s: ZeroCopyBuf) -> String {
b64_encode(&s)
forgiving_base64_encode(s.as_ref())
}
#[op]
fn op_base64_btoa(s: ByteString) -> String {
b64_encode(s)
forgiving_base64_encode(s.as_ref())
}
fn b64_encode(s: impl AsRef<[u8]>) -> String {
let cfg = base64::Config::new(base64::CharacterSet::Standard, true)
.decode_allow_trailing_bits(true);
base64::encode_config(s.as_ref(), cfg)
/// See <https://infra.spec.whatwg.org/#forgiving-base64>
fn forgiving_base64_encode(s: &[u8]) -> String {
let base64 = base64_simd::Base64::STANDARD;
base64.encode_to_boxed_str(s).into_string()
}
#[derive(Deserialize)]