From 80db8bf7bc20d51c53ccd36e81537d7dfcc822bd Mon Sep 17 00:00:00 2001 From: Andrew DiZenzo <59515127+andrewtdiz@users.noreply.github.com> Date: Thu, 28 May 2026 11:11:49 -0700 Subject: [PATCH] fix(node:buffer): estimate base64 byte length --- crates/perry-runtime/src/buffer/query.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/perry-runtime/src/buffer/query.rs b/crates/perry-runtime/src/buffer/query.rs index c4dd643d0..598e151fe 100644 --- a/crates/perry-runtime/src/buffer/query.rs +++ b/crates/perry-runtime/src/buffer/query.rs @@ -171,8 +171,19 @@ pub extern "C" fn js_buffer_byte_length_value(value: f64, encoding: f64) -> i32 return (len / 2) as i32; } if eq_ascii_lower(enc_bytes, b"base64") || eq_ascii_lower(enc_bytes, b"base64url") { - let decoded = base64_decode_into_buffer(bytes); - return (*decoded).length as i32; + // Node's Buffer.byteLength assumes valid base64/base64url input + // instead of decoding. It only discounts up to two trailing `=` + // padding characters, then applies the 3/4 base64 ratio. + let mut code_units = std::str::from_utf8(bytes) + .map(|s| s.encode_utf16().count()) + .unwrap_or(len); + if code_units > 0 && bytes.last() == Some(&b'=') { + code_units -= 1; + if code_units > 1 && bytes.get(bytes.len().saturating_sub(2)) == Some(&b'=') { + code_units -= 1; + } + } + return ((code_units * 3) / 4) as i32; } if eq_ascii_lower(enc_bytes, b"ascii") || eq_ascii_lower(enc_bytes, b"latin1")