diff --git a/.gitmodules b/.gitmodules index 904d869076..cc5f77d136 100644 --- a/.gitmodules +++ b/.gitmodules @@ -69,13 +69,6 @@ ignore = dirty depth = 1 shallow = true fetchRecurseSubmodules = false -[submodule "src/deps/base64"] -path = src/deps/base64 -url = https://github.com/aklomp/base64.git -ignore = dirty -depth = 1 -shallow = true -fetchRecurseSubmodules = false [submodule "src/deps/ls-hpack"] path = src/deps/ls-hpack url = https://github.com/litespeedtech/ls-hpack.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a6b18d897..d79dfb0d3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -301,7 +301,6 @@ option(USE_CUSTOM_LIBARCHIVE "Use Bun's recommended version of libarchive" ON) option(USE_CUSTOM_MIMALLOC "Use Bun's recommended version of Mimalloc" ON) option(USE_CUSTOM_ZSTD "Use Bun's recommended version of zstd" ON) option(USE_CUSTOM_CARES "Use Bun's recommended version of c-ares" ON) -option(USE_CUSTOM_BASE64 "Use Bun's recommended version of libbase64" ON) option(USE_CUSTOM_LOLHTML "Use Bun's recommended version of lolhtml" ON) option(USE_CUSTOM_TINYCC "Use Bun's recommended version of tinycc" ON) option(USE_CUSTOM_LIBUV "Use Bun's recommended version of libuv (Windows only)" ON) @@ -1333,19 +1332,6 @@ else() target_link_libraries(${bun} PRIVATE c-ares::cares) endif() -if(USE_CUSTOM_BASE64) - include_directories(${BUN_DEPS_DIR}/base64/include) - - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/base64.lib") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libbase64.a") - endif() -else() - find_package(base64 REQUIRED) - target_link_libraries(${bun} PRIVATE base64::base64) -endif() - if(USE_CUSTOM_TINYCC) if(WIN32) target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/tcc.lib") diff --git a/Dockerfile b/Dockerfile index 32f17f8d89..0f7063db25 100644 --- a/Dockerfile +++ b/Dockerfile @@ -297,19 +297,6 @@ RUN --mount=type=cache,target=${CCACHE_DIR} \ && make boringssl \ && rm -rf src/deps/boringssl Makefile -FROM bun-base as base64 - -ARG BUN_DIR -ARG CPU_TARGET -ENV CPU_TARGET=${CPU_TARGET} - -COPY Makefile ${BUN_DIR}/Makefile -COPY src/deps/base64 ${BUN_DIR}/src/deps/base64 - -WORKDIR $BUN_DIR - -RUN cd $BUN_DIR && \ - make base64 && rm -rf src/deps/base64 Makefile FROM bun-base as zstd @@ -508,7 +495,6 @@ COPY src/symbols.dyn src/linker.lds ${BUN_DIR}/src/ COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt COPY --from=zlib ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=base64 ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ COPY --from=libarchive ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ COPY --from=boringssl ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ COPY --from=lolhtml ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ @@ -572,7 +558,6 @@ COPY src/symbols.dyn src/linker.lds ${BUN_DIR}/src/ COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt COPY --from=zlib ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ -COPY --from=base64 ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ COPY --from=libarchive ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ COPY --from=boringssl ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ COPY --from=lolhtml ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ diff --git a/LICENSE b/LICENSE index 21b2b8466a..0206d2e994 100644 --- a/LICENSE +++ b/LICENSE @@ -92,11 +92,6 @@ Bun statically links these libraries: --- -- [`libbase64`](https://github.com/aklomp/base64/blob/master/LICENSE) -- BSD 2-Clause - ---- - - A fork of [`uWebsockets`](https://github.com/jarred-sumner/uwebsockets) - Apache 2.0 licensed diff --git a/Makefile b/Makefile index 2a108abaaf..5ffca73ee4 100644 --- a/Makefile +++ b/Makefile @@ -449,8 +449,7 @@ MINIMUM_ARCHIVE_FILES = -L$(BUN_DEPS_OUT_DIR) \ -ldecrepit \ -lssl \ -lcrypto \ - -llolhtml \ - -lbase64 + -llolhtml ARCHIVE_FILES_WITHOUT_LIBCRYPTO = $(MINIMUM_ARCHIVE_FILES) \ -larchive \ @@ -1971,11 +1970,6 @@ copy-to-bun-release-dir-bin: PACKAGE_MAP = --pkg-begin async_io $(BUN_DIR)/src/io/io_darwin.zig --pkg-begin bun $(BUN_DIR)/src/bun_redirect.zig --pkg-end --pkg-end --pkg-begin javascript_core $(BUN_DIR)/src/jsc.zig --pkg-begin bun $(BUN_DIR)/src/bun_redirect.zig --pkg-end --pkg-end --pkg-begin bun $(BUN_DIR)/src/bun_redirect.zig --pkg-end -.PHONY: base64 -base64: - cd $(BUN_DEPS_DIR)/base64 && make clean && rm -rf CMakeCache.txt CMakeFiles && cmake $(CMAKE_FLAGS) . && make - cp $(BUN_DEPS_DIR)/base64/libbase64.a $(BUN_DEPS_OUT_DIR)/libbase64.a - .PHONY: cold-jsc-start cold-jsc-start: $(CXX_WITH_CCACHE) $(CLANG_FLAGS) \ @@ -1993,7 +1987,7 @@ cold-jsc-start: misctools/cold-jsc-start.cpp -o cold-jsc-start .PHONY: vendor-without-npm -vendor-without-npm: node-fallbacks runtime_js fallback_decoder bun_error mimalloc picohttp zlib boringssl libarchive lolhtml sqlite usockets uws lshpack tinycc c-ares zstd base64 +vendor-without-npm: node-fallbacks runtime_js fallback_decoder bun_error mimalloc picohttp zlib boringssl libarchive lolhtml sqlite usockets uws lshpack tinycc c-ares zstd .PHONY: vendor-without-check diff --git a/bench/snippets/buffer-base64.mjs b/bench/snippets/buffer-base64.mjs new file mode 100644 index 0000000000..96bab6f039 --- /dev/null +++ b/bench/snippets/buffer-base64.mjs @@ -0,0 +1,21 @@ +import { bench, run } from "./runner.mjs"; + +function makeBenchmark(size, isToString) { + const base64Input = Buffer.alloc(size, "latin1").toString("base64"); + const base64From = Buffer.from(base64Input, "base64"); + + if (!isToString) + bench(`Buffer.from(${size} bytes, 'base64')`, () => { + Buffer.from(base64Input, "base64"); + }); + + if (isToString) + bench(`Buffer(${size}).toString('base64')`, () => { + base64From.toString("base64"); + }); +} + +[32, 512, 64 * 1024, 512 * 1024, 1024 * 1024 * 8].forEach(s => makeBenchmark(s, true)); +[32, 512, 64 * 1024, 512 * 1024, 1024 * 1024 * 8].forEach(s => makeBenchmark(s, false)); + +await run(); diff --git a/scripts/all-dependencies.ps1 b/scripts/all-dependencies.ps1 index f4426e4709..cc11006047 100644 --- a/scripts/all-dependencies.ps1 +++ b/scripts/all-dependencies.ps1 @@ -47,9 +47,6 @@ function Build-Dependency { $Script:DidAnything = $true } -Build-Dependency ` - -Script "base64" ` - -Outputs @("base64.lib") Build-Dependency ` -Script "boringssl" ` -Outputs @("crypto.lib", "ssl.lib", "decrepit.lib") diff --git a/scripts/all-dependencies.sh b/scripts/all-dependencies.sh index 06bf35fc6c..70c576a67f 100755 --- a/scripts/all-dependencies.sh +++ b/scripts/all-dependencies.sh @@ -4,18 +4,18 @@ source "$(dirname -- "${BASH_SOURCE[0]}")/env.sh" FORCE= while getopts "f" opt; do - case ${opt} in - f ) - FORCE=1 - ;; - \? ) - echo "Usage: all-dependencies.sh [-h] [-f]" - echo "Options:" - echo " h Print this help message" - echo " f Set force to 1" - exit 1 - ;; - esac + case ${opt} in + f) + FORCE=1 + ;; + \?) + echo "Usage: all-dependencies.sh [-h] [-f]" + echo "Options:" + echo " h Print this help message" + echo " f Set force to 1" + exit 1 + ;; + esac done BUILT_ANY=0 @@ -52,7 +52,6 @@ dep() { BUILT_ANY=1 } -dep base64 libbase64.a dep boringssl libcrypto.a libssl.a libdecrepit.a dep cares libcares.a dep libarchive libarchive.a diff --git a/scripts/build-base64.ps1 b/scripts/build-base64.ps1 deleted file mode 100644 index 91f5ed08cf..0000000000 --- a/scripts/build-base64.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -$ErrorActionPreference = 'Stop' # Setting strict mode, similar to 'set -euo pipefail' in bash -. (Join-Path $PSScriptRoot "env.ps1") - -Push-Location (Join-Path $BUN_DEPS_DIR 'base64') -try { - Set-Location (mkdir -Force build) - - Run cmake @CMAKE_FLAGS -DBASE64_WERROR=0 .. - Run cmake --build . --clean-first --config Release - - Copy-Item base64.lib $BUN_DEPS_OUT_DIR - Write-Host "-> base64.lib" -} -finally { - Pop-Location -} diff --git a/scripts/build-base64.sh b/scripts/build-base64.sh deleted file mode 100755 index 160875f96e..0000000000 --- a/scripts/build-base64.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -euxo pipefail -source $(dirname -- "${BASH_SOURCE[0]}")/env.sh - -cd $BUN_DEPS_DIR/base64 - -rm -rf build -mkdir -p build -cd build - -cmake "${CMAKE_FLAGS[@]}" .. -GNinja -B . -ninja - -cp libbase64.a $BUN_DEPS_OUT_DIR/libbase64.a diff --git a/src/base64/base64.zig b/src/base64/base64.zig index 8a495c8775..76a5b635d7 100644 --- a/src/base64/base64.zig +++ b/src/base64/base64.zig @@ -1,27 +1,6 @@ const std = @import("std"); const bun = @import("root").bun; -pub const DecodeResult = struct { - written: usize, - fail: bool = false, -}; - -pub const LibBase64 = struct { - pub const State = extern struct { - eof: c_int, - bytes: c_int, - flags: c_int, - carry: u8, - }; - pub extern fn base64_encode(src: [*]const u8, srclen: usize, out: [*]u8, outlen: *usize, flags: c_int) void; - pub extern fn base64_stream_encode_init(state: *State, flags: c_int) void; - pub extern fn base64_stream_encode(state: *State, src: [*]const u8, srclen: usize, out: [*]u8, outlen: *usize) void; - pub extern fn base64_stream_encode_final(state: *State, out: [*]u8, outlen: *usize) void; - pub extern fn base64_decode(src: [*]const u8, srclen: usize, out: [*]u8, outlen: *usize, flags: c_int) c_int; - pub extern fn base64_stream_decode_init(state: *State, flags: c_int) void; - pub extern fn base64_stream_decode(state: *State, src: [*]const u8, srclen: usize, out: [*]u8, outlen: *usize) c_int; -}; - const mixed_decoder = brk: { var decoder = zig_base64.standard.decoderWithIgnore("\xff \t\r\n" ++ [_]u8{ std.ascii.control_code.vt, @@ -35,21 +14,28 @@ const mixed_decoder = brk: { break :brk decoder; }; -pub fn decode(destination: []u8, source: []const u8) DecodeResult { - var wrote: usize = 0; - mixed_decoder.decode(destination, source, &wrote) catch { - return .{ - .written = wrote, - .fail = true, +pub fn decode(destination: []u8, source: []const u8) bun.simdutf.SIMDUTFResult { + const result = bun.simdutf.base64.decode(source, destination, false); + + if (!result.isSuccessful()) { + // The input does not follow the WHATWG forgiving-base64 specification + // https://infra.spec.whatwg.org/#forgiving-base64-decode + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/string_bytes.cc#L359 + var wrote: usize = 0; + mixed_decoder.decode(destination, source, &wrote) catch { + return .{ + .count = wrote, + .status = .invalid_base64_character, + }; }; - }; - return .{ .written = wrote, .fail = false }; + return .{ .count = wrote, .status = .success }; + } + + return result; } pub fn encode(destination: []u8, source: []const u8) usize { - var outlen: usize = destination.len; - LibBase64.base64_encode(source.ptr, source.len, destination.ptr, &outlen, 0); - return outlen; + return bun.simdutf.base64.encode(source, destination, false); } pub fn decodeLenUpperBound(len: usize) usize { diff --git a/src/bun.js/bindings/bun-simdutf.cpp b/src/bun.js/bindings/bun-simdutf.cpp index d02ff98543..4909be19aa 100644 --- a/src/bun.js/bindings/bun-simdutf.cpp +++ b/src/bun.js/bindings/bun-simdutf.cpp @@ -324,4 +324,33 @@ size_t simdutf__utf32_length_from_utf8(const char* input, size_t length) { return simdutf::utf32_length_from_utf8(input, length); } + +size_t simdutf__base64_encode(const char* input, size_t length, char* output, int is_urlsafe) +{ + return simdutf::binary_to_base64(input, length, output, is_urlsafe ? simdutf::base64_url : simdutf::base64_default); +} + +SIMDUTFResult simdutf__base64_decode_from_binary(const char* input, size_t length, char* output, size_t outlen_, int is_urlsafe) +{ + size_t outlen = outlen_; + auto res = simdutf::base64_to_binary_safe(input, length, output, outlen, is_urlsafe ? simdutf::base64_url : simdutf::base64_default); + + if (res.error == simdutf::error_code::SUCCESS) { + return { .error = 0, .count = outlen }; + } + + return { .error = res.error, .count = res.count }; +} + +SIMDUTFResult simdutf__base64_decode_from_binary16(const char16_t* input, size_t length, char* output, size_t outlen_, int is_urlsafe) +{ + size_t outlen = outlen_; + auto res = simdutf::base64_to_binary_safe(input, length, output, outlen, is_urlsafe ? simdutf::base64_url : simdutf::base64_default); + + if (res.error == simdutf::error_code::SUCCESS) { + return { .error = 0, .count = outlen }; + } + + return { .error = res.error, .count = res.count }; +} } \ No newline at end of file diff --git a/src/bun.js/bindings/bun-simdutf.zig b/src/bun.js/bindings/bun-simdutf.zig index f7ab8b21db..98f426cd76 100644 --- a/src/bun.js/bindings/bun-simdutf.zig +++ b/src/bun.js/bindings/bun-simdutf.zig @@ -5,6 +5,10 @@ pub const SIMDUTFResult = extern struct { status: Status, count: usize = 0, + pub fn isSuccessful(this: *const SIMDUTFResult) bool { + return this.status == Status.success; + } + pub const Status = enum(i32) { success = 0, /// Any byte must have fewer than 5 header bits. @@ -364,3 +368,21 @@ pub const trim = struct { return buf[0..utf8_len(buf)]; } }; + +pub const base64 = struct { + extern fn simdutf__base64_encode(input: [*]const u8, length: usize, output: [*]u8, is_urlsafe: c_int) usize; + extern fn simdutf__base64_decode_from_binary(input: [*]const u8, length: usize, output: [*]u8, outlen: usize, is_urlsafe: c_int) SIMDUTFResult; + extern fn simdutf__base64_decode_from_binary16(input: [*]const u16, length: usize, output: [*]u8, outlen: usize, is_urlsafe: c_int) SIMDUTFResult; + + pub fn encode(input: []const u8, output: []u8, is_urlsafe: bool) usize { + return simdutf__base64_encode(input.ptr, input.len, output.ptr, @intFromBool(is_urlsafe)); + } + + pub fn decode(input: []const u8, output: []u8, is_urlsafe: bool) SIMDUTFResult { + return simdutf__base64_decode_from_binary(input.ptr, input.len, output.ptr, output.len, @intFromBool(is_urlsafe)); + } + + pub fn decode16(input: []const u16, output: []u8, is_urlsafe: bool) SIMDUTFResult { + return simdutf__base64_decode_from_binary16(input.ptr, input.len, output.ptr, output.len, @intFromBool(is_urlsafe)); + } +}; diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index f24dfbf8f0..8156ca0f7e 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -1089,7 +1089,7 @@ pub const Encoder = struct { }, .base64, .base64url => { - return bun.base64.decode(to_ptr[0..to_len], input[0..len]).written; + return bun.base64.decode(to_ptr[0..to_len], input[0..len]).count; }, } } @@ -1261,7 +1261,7 @@ pub const Encoder = struct { const outlen = bun.base64.decodeLen(slice); const to = allocator.alloc(u8, outlen) catch return &[_]u8{}; - const wrote = bun.base64.decode(to[0..outlen], slice).written; + const wrote = bun.base64.decode(to[0..outlen], slice).count; return to[0..wrote]; }, } diff --git a/src/deps/base64 b/src/deps/base64 deleted file mode 160000 index 3a5add8652..0000000000 --- a/src/deps/base64 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3a5add8652076612a8407627a42c768736a4263f diff --git a/src/resolver/data_url.zig b/src/resolver/data_url.zig index 002044ce69..177337fc96 100644 --- a/src/resolver/data_url.zig +++ b/src/resolver/data_url.zig @@ -122,7 +122,7 @@ pub const DataURL = struct { const len = bun.base64.decodeLen(percent_decoded); const buf = try allocator.alloc(u8, len); const result = bun.base64.decode(buf, percent_decoded); - if (result.fail or result.written != len) { + if (!result.isSuccessful() or result.count != len) { return error.Base64DecodeError; } return buf; diff --git a/src/sourcemap/sourcemap.zig b/src/sourcemap/sourcemap.zig index 19f7d20917..6f4855fac9 100644 --- a/src/sourcemap/sourcemap.zig +++ b/src/sourcemap/sourcemap.zig @@ -86,10 +86,10 @@ pub fn parseUrl( const len = bun.base64.decodeLen(base64_data); const bytes = arena.alloc(u8, len) catch bun.outOfMemory(); const decoded = bun.base64.decode(bytes, base64_data); - if (decoded.fail) { + if (!decoded.isSuccessful()) { return error.InvalidBase64; } - break :json_bytes bytes[0..decoded.written]; + break :json_bytes bytes[0..decoded.count]; }, ',' => break :json_bytes source[data_prefix.len + 1 ..], else => break :try_data_url,