Skip to content

Commit b97f087

Browse files
committed
perf(ext/web): use base64-simd for atob/btoa
1 parent e1c9096 commit b97f087

File tree

3 files changed

+32
-49
lines changed

3 files changed

+32
-49
lines changed

Cargo.lock

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/web/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ path = "lib.rs"
1515

1616
[dependencies]
1717
async-trait = "0.1.51"
18-
base64 = "0.13.0"
18+
base64-simd = "0.6.0"
1919
deno_core = { version = "0.140.0", path = "../../core" }
2020
encoding_rs = "0.8.31"
2121
flate2 = "1"

ext/web/lib.rs

Lines changed: 15 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -126,72 +126,40 @@ pub fn init<P: TimersPermission + 'static>(
126126
fn op_base64_decode(input: String) -> Result<ZeroCopyBuf, AnyError> {
127127
let mut input = input.into_bytes();
128128
input.retain(|c| !c.is_ascii_whitespace());
129-
Ok(b64_decode(&input)?.into())
129+
Ok(b64_decode(input)?.into())
130130
}
131131

132132
#[op]
133133
fn op_base64_atob(mut s: ByteString) -> Result<ByteString, AnyError> {
134134
s.retain(|c| !c.is_ascii_whitespace());
135-
136-
// If padding is expected, fail if not 4-byte aligned
137-
if s.len() % 4 != 0 && (s.ends_with(b"==") || s.ends_with(b"=")) {
138-
return Err(
139-
DomExceptionInvalidCharacterError::new("Failed to decode base64.").into(),
140-
);
141-
}
142-
143-
Ok(b64_decode(&s)?.into())
135+
Ok(b64_decode(s.into())?.into())
144136
}
145137

146-
fn b64_decode(input: &[u8]) -> Result<Vec<u8>, AnyError> {
147-
// "If the length of input divides by 4 leaving no remainder, then:
148-
// if input ends with one or two U+003D EQUALS SIGN (=) characters,
149-
// remove them from input."
150-
let input = match input.len() % 4 == 0 {
151-
true if input.ends_with(b"==") => &input[..input.len() - 2],
152-
true if input.ends_with(b"=") => &input[..input.len() - 1],
153-
_ => input,
154-
};
155-
156-
// "If the length of input divides by 4 leaving a remainder of 1,
157-
// throw an InvalidCharacterError exception and abort these steps."
158-
if input.len() % 4 == 1 {
159-
return Err(
160-
DomExceptionInvalidCharacterError::new("Failed to decode base64.").into(),
161-
);
162-
}
138+
fn b64_decode(mut buf: Vec<u8>) -> Result<Vec<u8>, AnyError> {
139+
let error: _ =
140+
|| DomExceptionInvalidCharacterError::new("Failed to decode base64");
163141

164-
let cfg = base64::Config::new(base64::CharacterSet::Standard, true)
165-
.decode_allow_trailing_bits(true);
166-
let out = base64::decode_config(input, cfg).map_err(|err| match err {
167-
base64::DecodeError::InvalidByte(_, _) => {
168-
DomExceptionInvalidCharacterError::new(
169-
"Failed to decode base64: invalid character",
170-
)
171-
}
172-
_ => DomExceptionInvalidCharacterError::new(&format!(
173-
"Failed to decode base64: {:?}",
174-
err
175-
)),
176-
})?;
142+
let base64 = base64_simd::Base64::STANDARD;
143+
let decoded = base64.decode_inplace(&mut buf).map_err(|_| error())?;
177144

178-
Ok(out)
145+
let decoded_len = decoded.len();
146+
buf.truncate(decoded_len);
147+
Ok(buf)
179148
}
180149

181150
#[op]
182151
fn op_base64_encode(s: ZeroCopyBuf) -> String {
183-
b64_encode(&s)
152+
b64_encode(s.as_ref())
184153
}
185154

186155
#[op]
187156
fn op_base64_btoa(s: ByteString) -> String {
188-
b64_encode(s)
157+
b64_encode(s.as_ref())
189158
}
190159

191-
fn b64_encode(s: impl AsRef<[u8]>) -> String {
192-
let cfg = base64::Config::new(base64::CharacterSet::Standard, true)
193-
.decode_allow_trailing_bits(true);
194-
base64::encode_config(s.as_ref(), cfg)
160+
fn b64_encode(s: &[u8]) -> String {
161+
let base64 = base64_simd::Base64::STANDARD;
162+
base64.encode_to_boxed_str(s).into_string()
195163
}
196164

197165
#[derive(Deserialize)]

0 commit comments

Comments
 (0)