feat(serialize): 添加Base36编解码模块

新增Base36编码和解码功能,支持字节数据与Base36字符串之间的相互转换,
并提供对i64整数的Base36编解码支持。同时更新了相关依赖版本以提升安全性
和性能。

- 新增 `base36` 模块,实现完整的Base36编解码逻辑
- 支持去除填充字符 `=` 后的容错处理
- 提供 `encode`, `decode`, `encode_int64`, `decode_to_int64` 等公共接口
- 更新 Cargo.toml 中多项依赖至最新稳定版本
This commit is contained in:
徐涛
2025-10-09 13:38:20 +08:00
parent ea194a1fd1
commit 7b1834479f
3 changed files with 158 additions and 12 deletions

View File

@@ -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"] }

144
src/serialize/base36.rs Normal file
View File

@@ -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<u8>模拟大整数运算
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<Vec<u8>, 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<u8>模拟大整数进行解码
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<Vec<u8>, 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<i64, Base36Error> {
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))
}

View File

@@ -1,6 +1,8 @@
use base64::Engine;
use thiserror::Error;
pub mod base36;
#[derive(Debug, Error)]
pub enum HexSerializeError {
#[error("Invalid hex char: {0}")]