mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 13:00:36 -05:00
perf(ext/web): use base64-simd for atob/btoa (#14992)
This commit is contained in:
parent
3ad8bd8557
commit
1328a56230
4 changed files with 39 additions and 53 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -242,6 +242,15 @@ version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
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]]
|
[[package]]
|
||||||
name = "base64ct"
|
name = "base64ct"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -1202,7 +1211,7 @@ name = "deno_web"
|
||||||
version = "0.89.0"
|
version = "0.89.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.13.0",
|
"base64-simd",
|
||||||
"deno_bench_util",
|
"deno_bench_util",
|
||||||
"deno_core",
|
"deno_core",
|
||||||
"deno_url",
|
"deno_url",
|
||||||
|
@ -3893,6 +3902,12 @@ dependencies = [
|
||||||
"rand_core",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-abstraction"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2880f3f7b392823ee65bbcc681961cd8e698c6a30e91ab9b4eef1f9c6c226d8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.10"
|
version = "0.3.10"
|
||||||
|
|
|
@ -110,6 +110,8 @@ opt-level = 3
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
[profile.bench.package.zstd-sys]
|
[profile.bench.package.zstd-sys]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
[profile.bench.package.base64-simd]
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
||||||
[profile.release.package.rand]
|
[profile.release.package.rand]
|
||||||
|
@ -174,3 +176,5 @@ opt-level = 3
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
[profile.release.package.zstd-sys]
|
[profile.release.package.zstd-sys]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
[profile.release.package.base64-simd]
|
||||||
|
opt-level = 3
|
||||||
|
|
|
@ -15,7 +15,7 @@ path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1.51"
|
async-trait = "0.1.51"
|
||||||
base64 = "0.13.0"
|
base64-simd = "0.6.2"
|
||||||
deno_core = { version = "0.140.0", path = "../../core" }
|
deno_core = { version = "0.140.0", path = "../../core" }
|
||||||
encoding_rs = "0.8.31"
|
encoding_rs = "0.8.31"
|
||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
|
|
|
@ -124,74 +124,41 @@ pub fn init<P: TimersPermission + 'static>(
|
||||||
|
|
||||||
#[op]
|
#[op]
|
||||||
fn op_base64_decode(input: String) -> Result<ZeroCopyBuf, AnyError> {
|
fn op_base64_decode(input: String) -> Result<ZeroCopyBuf, AnyError> {
|
||||||
let mut input = input.into_bytes();
|
Ok(forgiving_base64_decode(input.into_bytes())?.into())
|
||||||
input.retain(|c| !c.is_ascii_whitespace());
|
|
||||||
Ok(b64_decode(&input)?.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op]
|
#[op]
|
||||||
fn op_base64_atob(mut s: ByteString) -> Result<ByteString, AnyError> {
|
fn op_base64_atob(s: ByteString) -> Result<ByteString, AnyError> {
|
||||||
s.retain(|c| !c.is_ascii_whitespace());
|
Ok(forgiving_base64_decode(s.into())?.into())
|
||||||
|
|
||||||
// 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 b64_decode(input: &[u8]) -> Result<Vec<u8>, AnyError> {
|
/// See <https://infra.spec.whatwg.org/#forgiving-base64>
|
||||||
// "If the length of input divides by 4 leaving no remainder, then:
|
fn forgiving_base64_decode(mut input: Vec<u8>) -> Result<Vec<u8>, AnyError> {
|
||||||
// if input ends with one or two U+003D EQUALS SIGN (=) characters,
|
let error: _ =
|
||||||
// remove them from input."
|
|| DomExceptionInvalidCharacterError::new("Failed to decode base64");
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// "If the length of input divides by 4 leaving a remainder of 1,
|
let decoded = base64_simd::Base64::forgiving_decode_inplace(&mut input)
|
||||||
// throw an InvalidCharacterError exception and abort these steps."
|
.map_err(|_| error())?;
|
||||||
if input.len() % 4 == 1 {
|
|
||||||
return Err(
|
|
||||||
DomExceptionInvalidCharacterError::new("Failed to decode base64.").into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let cfg = base64::Config::new(base64::CharacterSet::Standard, true)
|
let decoded_len = decoded.len();
|
||||||
.decode_allow_trailing_bits(true);
|
input.truncate(decoded_len);
|
||||||
let out = base64::decode_config(input, cfg).map_err(|err| match err {
|
Ok(input)
|
||||||
base64::DecodeError::InvalidByte(_, _) => {
|
|
||||||
DomExceptionInvalidCharacterError::new(
|
|
||||||
"Failed to decode base64: invalid character",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => DomExceptionInvalidCharacterError::new(&format!(
|
|
||||||
"Failed to decode base64: {:?}",
|
|
||||||
err
|
|
||||||
)),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op]
|
#[op]
|
||||||
fn op_base64_encode(s: ZeroCopyBuf) -> String {
|
fn op_base64_encode(s: ZeroCopyBuf) -> String {
|
||||||
b64_encode(&s)
|
forgiving_base64_encode(s.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op]
|
#[op]
|
||||||
fn op_base64_btoa(s: ByteString) -> String {
|
fn op_base64_btoa(s: ByteString) -> String {
|
||||||
b64_encode(s)
|
forgiving_base64_encode(s.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn b64_encode(s: impl AsRef<[u8]>) -> String {
|
/// See <https://infra.spec.whatwg.org/#forgiving-base64>
|
||||||
let cfg = base64::Config::new(base64::CharacterSet::Standard, true)
|
fn forgiving_base64_encode(s: &[u8]) -> String {
|
||||||
.decode_allow_trailing_bits(true);
|
let base64 = base64_simd::Base64::STANDARD;
|
||||||
base64::encode_config(s.as_ref(), cfg)
|
base64.encode_to_boxed_str(s).into_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue