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:
parent
825477b3cd
commit
e0a79c0550
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"
|
||||
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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)]
|
||||
|
|
Loading…
Reference in a new issue