From 7fc6e97cd8a8eab91e00588112df759320674b75 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Sat, 28 Feb 2026 11:22:31 +0800 Subject: [PATCH] Re-implement forgiving base64 decode without intermediate allocation Was looking at, what I thought was a related issue, and started to extract this code to re-use it (in DataURIs). Realized it could be written without the intermediate allocation. Then I realized the dataURI issue is something else, but wanted to keep this improvement. --- src/browser/webapi/Window.zig | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index ed5423ef..897dfaad 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -396,28 +396,19 @@ pub fn btoa(_: *const Window, input: []const u8, page: *Page) ![]const u8 { pub fn atob(_: *const Window, input: []const u8, page: *Page) ![]const u8 { const trimmed = std.mem.trim(u8, input, &std.ascii.whitespace); - // Per HTML spec "forgiving-base64 decode" algorithm: + // Forgiving base64 decode per WHATWG spec: // https://infra.spec.whatwg.org/#forgiving-base64-decode - const padded: []const u8 = switch (trimmed.len % 4) { - 1 => return error.InvalidCharacterError, - 2 => blk: { - const buf = try page.call_arena.alloc(u8, trimmed.len + 2); - @memcpy(buf[0..trimmed.len], trimmed); - buf[trimmed.len] = '='; - buf[trimmed.len + 1] = '='; - break :blk buf; - }, - 3 => blk: { - const buf = try page.call_arena.alloc(u8, trimmed.len + 1); - @memcpy(buf[0..trimmed.len], trimmed); - buf[trimmed.len] = '='; - break :blk buf; - }, - else => trimmed, - }; - const decoded_len = std.base64.standard.Decoder.calcSizeForSlice(padded) catch return error.InvalidCharacterError; + // Remove trailing padding to use standard_no_pad decoder + const unpadded = std.mem.trimRight(u8, trimmed, "="); + + // Length % 4 == 1 is invalid (can't represent valid base64) + if (unpadded.len % 4 == 1) { + return error.InvalidCharacterError; + } + + const decoded_len = std.base64.standard_no_pad.Decoder.calcSizeForSlice(unpadded) catch return error.InvalidCharacterError; const decoded = try page.call_arena.alloc(u8, decoded_len); - std.base64.standard.Decoder.decode(decoded, padded) catch return error.InvalidCharacterError; + std.base64.standard_no_pad.Decoder.decode(decoded, unpadded) catch return error.InvalidCharacterError; return decoded; }