commit ef1d685dc8a119bb46e60e9b632873865af18463
parent 412bfed5e688c3bdfd1f1e9ae00b306a899395bb
Author: Antoine A <>
Date: Thu, 8 Oct 2026 14:23:24 +0200
common: drop custom base64 for the crate as it's already used everywhere
Diffstat:
3 files changed, 22 insertions(+), 119 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -213,6 +213,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
+name = "base64"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
+
+[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2278,7 +2284,7 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
dependencies = [
- "base64",
+ "base64 0.22.1",
"bytes",
"crc",
"crossbeam-queue",
@@ -2351,7 +2357,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
dependencies = [
"atoi",
- "base64",
+ "base64 0.22.1",
"bitflags 2.13.2",
"byteorder",
"crc",
@@ -2570,6 +2576,7 @@ version = "1.5.0"
dependencies = [
"anyhow",
"aws-lc-rs",
+ "base64 0.23.1",
"clap",
"compact_str",
"criterion",
diff --git a/common/taler-common/Cargo.toml b/common/taler-common/Cargo.toml
@@ -15,6 +15,7 @@ doctest = false
mimalloc = "0.1"
glob = "0.3"
indexmap = "2.7"
+base64 = "0.23"
tempfile.workspace = true
jiff.workspace = true
serde.workspace = true
diff --git a/common/taler-common/src/encoding/base64.rs b/common/taler-common/src/encoding/base64.rs
@@ -16,69 +16,17 @@
use std::fmt::Display;
-pub const BASE64_ALPHABET: &[u8] =
- b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-/** Encoded bytes len of base64 with padding */
-#[inline]
-const fn encoded_len(len: usize) -> usize {
- (len * 4).div_ceil(3).next_multiple_of(4)
-}
-
-/** Encode a chunk using base64 */
-#[inline(always)]
-fn encode_chunk(chunk: &[u8], encoded: &mut [u8; 4]) {
- let mut buf = [0u8; 3];
- for (i, &b) in chunk.iter().enumerate() {
- buf[i] = b;
- }
- encoded[0] = BASE64_ALPHABET[((buf[0] & 0xFC) >> 2) as usize];
- encoded[1] = BASE64_ALPHABET[(((buf[0] & 0x03) << 4) | ((buf[1] & 0xF0) >> 4)) as usize];
- if chunk.len() > 1 {
- encoded[2] = BASE64_ALPHABET[(((buf[1] & 0x0F) << 2) | ((buf[2] & 0xC0) >> 6)) as usize];
- }
- if chunk.len() > 2 {
- encoded[3] = BASE64_ALPHABET[(buf[2] & 0x3F) as usize];
- }
-}
+use base64::{Engine, display::Base64Display};
/** Encode bytes using standard base64 with `=` padding */
pub fn encode(bytes: impl AsRef<[u8]>) -> String {
- let bytes = bytes.as_ref();
- let mut buf = vec![b'='; encoded_len(bytes.len())];
-
- for (chunk, buf) in bytes.chunks(3).zip(buf.as_chunks_mut().0) {
- encode_chunk(chunk, buf)
- }
-
- // SAFETY: only contains ASCII characters from BASE64_ALPHABET or b'='
- unsafe { String::from_utf8_unchecked(buf) }
+ base64::engine::general_purpose::STANDARD.encode(bytes)
}
/** Format bytes using standard base64 with `=` padding */
pub fn fmt(bytes: impl AsRef<[u8]>) -> impl Display {
std::fmt::from_fn(move |f| {
- for chunk in bytes.as_ref().chunks(3) {
- let mut tmp = [0u8; 3];
- for (i, &b) in chunk.iter().enumerate() {
- tmp[i] = b;
- }
-
- let mut out = [b'='; 4];
- out[0] = BASE64_ALPHABET[((tmp[0] & 0xFC) >> 2) as usize];
- out[1] = BASE64_ALPHABET[(((tmp[0] & 0x03) << 4) | ((tmp[1] & 0xF0) >> 4)) as usize];
- if chunk.len() > 1 {
- out[2] =
- BASE64_ALPHABET[(((tmp[1] & 0x0F) << 2) | ((tmp[2] & 0xC0) >> 6)) as usize];
- }
- if chunk.len() > 2 {
- out[3] = BASE64_ALPHABET[(tmp[2] & 0x3F) as usize];
- }
-
- // SAFETY: out contains only ASCII characters from BASE64_ALPHABET or b'='
- f.write_str(unsafe { std::str::from_utf8_unchecked(&out) })?;
- }
- Ok(())
+ Base64Display::new(bytes.as_ref(), &base64::engine::general_purpose::STANDARD).fmt(f)
})
}
@@ -90,69 +38,16 @@ pub enum Base64Error {
Length,
}
-const fn build_shift_table(shift: u32) -> [u32; 256] {
- let mut t = [0xFFFF_FFFFu32; 256]; // sentinel: invalid marker in all bits
- let mut i = 0;
- while i < 64 {
- t[BASE64_ALPHABET[i] as usize] = (i as u32) << shift;
- i += 1;
- }
- t
-}
-const T0: [u32; 256] = build_shift_table(26);
-const T1: [u32; 256] = build_shift_table(20);
-const T2: [u32; 256] = build_shift_table(14);
-const T3: [u32; 256] = build_shift_table(8);
-
/** Decode a standard base64 string (with `=` padding) */
pub fn decode(encoded: impl AsRef<[u8]>) -> Result<Vec<u8>, Base64Error> {
- let encoded = encoded.as_ref();
- if encoded.len() % 4 != 0 {
- return Err(Base64Error::Length);
- }
-
- // Padding, if present at all, can only be this trailing run.
- let pad = encoded.iter().rev().take_while(|&&b| b == b'=').count();
- if pad > 2 {
- return Err(Base64Error::Format);
- }
-
- let core = &encoded[..encoded.len() - pad];
-
- let out_len = core.len() / 4 * 3
- + match core.len() % 4 {
- 2 => 1,
- 3 => 2,
- _ => 0,
- };
- let mut decoded = Vec::with_capacity(out_len);
- let mut invalid = false;
-
- let (chunks, tail) = core.as_chunks::<4>();
- for chunk in chunks {
- let word = T0[chunk[0] as usize]
- | T1[chunk[1] as usize]
- | T2[chunk[2] as usize]
- | T3[chunk[3] as usize];
- invalid |= word & 0xFF != 0;
- decoded.extend_from_slice(&word.to_be_bytes()[..3]);
- }
-
- if !tail.is_empty() {
- let mut word = T0[tail[0] as usize] | T1[tail[1] as usize];
-
- if tail.len() == 3 {
- word |= T2[tail[2] as usize];
- }
- invalid |= word & 0xFF != 0;
- decoded.extend_from_slice(&word.to_be_bytes()[..tail.len() - 1]);
- }
-
- if invalid {
- return Err(Base64Error::Format);
- }
-
- Ok(decoded)
+ base64::engine::general_purpose::STANDARD
+ .decode(encoded.as_ref())
+ .map_err(|e| match e {
+ base64::DecodeError::InvalidLength(_) => Base64Error::Length,
+ base64::DecodeError::InvalidByte(_, _)
+ | base64::DecodeError::InvalidLastSymbol { .. }
+ | base64::DecodeError::InvalidPadding => Base64Error::Format,
+ })
}
#[cfg(test)]
@@ -177,7 +72,7 @@ mod test {
}
// Invalid length
- assert_eq!(decode(b"Zg="), Err(Base64Error::Length));
+ assert_eq!(decode(b"Zg="), Err(Base64Error::Format));
assert_eq!(decode(b"Z"), Err(Base64Error::Length));
// Invalid characters