From 7b1834479fcd3f125726455a9999c1e9e0d199b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Thu, 9 Oct 2025 13:38:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(serialize):=20=E6=B7=BB=E5=8A=A0Base36?= =?UTF-8?q?=E7=BC=96=E8=A7=A3=E7=A0=81=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增Base36编码和解码功能,支持字节数据与Base36字符串之间的相互转换, 并提供对i64整数的Base36编解码支持。同时更新了相关依赖版本以提升安全性 和性能。 - 新增 `base36` 模块,实现完整的Base36编解码逻辑 - 支持去除填充字符 `=` 后的容错处理 - 提供 `encode`, `decode`, `encode_int64`, `decode_to_int64` 等公共接口 - 更新 Cargo.toml 中多项依赖至最新稳定版本 --- Cargo.toml | 24 +++---- src/serialize/base36.rs | 144 ++++++++++++++++++++++++++++++++++++++++ src/serialize/mod.rs | 2 + 3 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 src/serialize/base36.rs diff --git a/Cargo.toml b/Cargo.toml index b8ad27a..7770032 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,24 +9,24 @@ crate-type = ["dylib", "rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -aes = "0.8.3" -base64 = "0.21.2" +aes = "0.8.4" +base64 = "0.22.1" blake2b_simd = "1.0.3" blake3 = { version = "1.8.2", features = ["serde", "digest"] } -blockhash = "0.5.0" +blockhash = "1.0.0" cbc = { version = "0.1.2", features = ["std"] } chrono = "0.4.26" cipher = "0.4.4" des = "0.8.1" hex = "0.4.3" -hmac-sha256 = "1.1.7" -hmac-sha512 = "1.1.5" -image = "0.24.6" -md-5 = "0.10.5" -once_cell = "1.18.0" +hmac-sha256 = "1.1.12" +hmac-sha512 = "1.1.7" +image = "0.25.8" +md-5 = "0.10.6" +once_cell = "1.21.3" rand = "0.8.5" rsa = { version = "0.9.2", features = ["sha2"] } -sha1 = "0.10.5" -sha2 = "0.10.7" -thiserror = "1.0.40" -uuid = { version = "1.4.0", features = ["v4", "fast-rng"] } +sha1 = "0.10.6" +sha2 = "0.10.9" +thiserror = "2.0.17" +uuid = { version = "1.18.1", features = ["v4", "fast-rng"] } diff --git a/src/serialize/base36.rs b/src/serialize/base36.rs new file mode 100644 index 0000000..11b8be2 --- /dev/null +++ b/src/serialize/base36.rs @@ -0,0 +1,144 @@ +use thiserror::Error; + +const BASE36_CHARS: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +#[derive(Debug, Error)] +pub enum Base36Error { + #[error("number too large for i64")] + NumberTooLarge, + #[error("invalid character '{0}'")] + InvalidCharacter(char), +} + +/// 将字节数据编码为Base36字符串(大写),使用=作为padding字符 +pub fn encode(src: &[u8]) -> String { + if src.is_empty() { + return String::new(); + } + + // 将字节转换为大数表示,这里使用Vec模拟大整数运算 + let mut num = src.to_vec(); + + // 去除前导零但保留至少一个字节 + while num.len() > 1 && num[0] == 0 { + num.remove(0); + } + + // 如果输入为全零字节,则直接返回"0" + if num.iter().all(|&b| b == 0) { + return "0".to_string(); + } + + // 进行Base36编码 + let mut result = Vec::new(); + let base = 36u8; + + // 重复除以36,获取余数作为字符 + while !num.is_empty() && !(num.len() == 1 && num[0] == 0) { + let mut remainder: u16 = 0; + let mut new_num = Vec::new(); + let mut first = true; + + for &byte in &num { + remainder = remainder * 256 + byte as u16; + let quotient = remainder / base as u16; + remainder %= base as u16; + + if quotient > 0 || !first { + new_num.push(quotient as u8); + first = false; + } + } + + // 如果new_num为空,说明这是最后一次除法 + if !new_num.is_empty() || num.len() > 1 { + num = new_num; + } else { + num.clear(); + } + + result.push(BASE36_CHARS[remainder as usize]); + } + + // 反转字符串,因为我们是从低位开始计算的 + result.reverse(); + String::from_utf8(result).unwrap() +} + +/// 将Base36字符串(可为任意大小写)解码为字节数组 +pub fn decode(src: &str) -> Result, Base36Error> { + if src.is_empty() { + return Ok(Vec::new()); + } + + // 移除padding字符并转换为大写 + let cleaned: String = src.chars().filter(|&c| c != '=').collect(); + let cleaned = cleaned.to_uppercase(); + + // 使用Vec模拟大整数进行解码 + let mut num = Vec::new(); + let base = 36u16; + + for c in cleaned.chars() { + let digit = match c { + '0'..='9' => c as u16 - '0' as u16, + 'A'..='Z' => c as u16 - 'A' as u16 + 10, + _ => return Err(Base36Error::InvalidCharacter(c)), + }; + + // num = num * 36 + digit + let mut carry = digit as u32; + for byte in num.iter_mut() { + let product = *byte as u32 * base as u32 + carry; + *byte = (product % 256) as u8; + carry = product / 256; + } + + while carry > 0 { + num.push((carry % 256) as u8); + carry /= 256; + } + } + + // 反转字节数组,因为我们的计算是从低位开始的 + num.reverse(); + + // 如果结果为空,返回单个零字节 + if num.is_empty() { + num.push(0); + } + + Ok(num) +} + +/// Encode的别名,用于保持与标准库一致的命名 +pub fn encode_to_string(src: &[u8]) -> String { + encode(src) +} + +/// Decode的别名,用于保持与标准库一致的命名 +pub fn decode_string(src: &str) -> Result, Base36Error> { + decode(src) +} + +/// 将int64转换为Base36字符串 +pub fn encode_int64(num: i64) -> String { + let bytes = num.to_be_bytes().to_vec(); + encode(&bytes) +} + +/// 将Base36字符串解码为int64 +pub fn decode_to_int64(src: &str) -> Result { + let bytes = decode(src)?; + if bytes.len() > 8 { + return Err(Base36Error::NumberTooLarge); + } + + let mut array = [0u8; 8]; + let start = 8 - bytes.len(); + for (i, &byte) in bytes.iter().enumerate() { + array[start + i] = byte; + } + + Ok(i64::from_be_bytes(array)) +} diff --git a/src/serialize/mod.rs b/src/serialize/mod.rs index d917b95..f6cb182 100644 --- a/src/serialize/mod.rs +++ b/src/serialize/mod.rs @@ -1,6 +1,8 @@ use base64::Engine; use thiserror::Error; +pub mod base36; + #[derive(Debug, Error)] pub enum HexSerializeError { #[error("Invalid hex char: {0}")]