Skip to main content

dxcode/
lib.rs

1//! dxcode - 带有 `dx` 前缀的自定义编码算法
2//!
3//! Rust 实现 - 带 CRC16 校验和和智能压缩
4//!
5//! # 示例
6//!
7//! ```
8//! use dxcode::{encode, decode, encode_str, decode_str};
9//!
10//! // 编码字符串
11//! let encoded = encode_str("你好,Dogxi!");
12//! println!("{}", encoded); // dxXXXX...
13//!
14//! // 解码(自动验证校验和,自动解压缩)
15//! let decoded = decode_str(&encoded).unwrap();
16//! println!("{}", decoded); // 你好,Dogxi!
17//!
18//! // 验证完整性
19//! use dxcode::verify;
20//! assert!(verify(&encoded).unwrap());
21//! ```
22//!
23//! # 作者
24//!
25//! Dogxi
26//!
27//! # 版本
28//!
29//! 2.3.0
30//!
31//! # 许可证
32//!
33//! MIT
34
35use flate2::read::DeflateDecoder;
36use flate2::write::DeflateEncoder;
37use flate2::Compression;
38use std::collections::HashMap;
39use std::error::Error;
40use std::fmt;
41use std::io::{Read, Write};
42use std::sync::LazyLock;
43
44/// DX 字符集 - 以 DXdx 开头作为签名,共64个字符
45pub const CHARSET: &str = "DXdx0123456789ABCEFGHIJKLMNOPQRSTUVWYZabcefghijklmnopqrstuvwyz-_";
46
47/// 魔数 - 用于 XOR 变换,'D' 的 ASCII 值
48pub const MAGIC: u8 = 0x44;
49
50/// 前缀
51pub const PREFIX: &str = "dx";
52
53/// 填充字符
54pub const PADDING: char = '=';
55
56/// 基础头部大小(1字节 flags + 2字节 CRC16)
57const HEADER_SIZE: usize = 3;
58
59/// TTL 头部大小(4字节 created_at + 4字节 ttl_seconds)
60const TTL_HEADER_SIZE: usize = 8;
61
62/// 压缩阈值(字节数),小于此值不压缩
63const COMPRESSION_THRESHOLD: usize = 32;
64
65/// Flags 位定义
66const FLAG_COMPRESSED: u8 = 0x01;
67const FLAG_ALGO_DEFLATE: u8 = 0x02;
68const FLAG_HAS_TTL: u8 = 0x04;
69
70/// 有效的 flags 掩码(用于验证)
71const VALID_FLAGS_MASK: u8 = FLAG_COMPRESSED | FLAG_ALGO_DEFLATE | FLAG_HAS_TTL;
72
73/// 字符集字节数组
74static CHARSET_BYTES: LazyLock<Vec<u8>> = LazyLock::new(|| CHARSET.as_bytes().to_vec());
75
76/// 反向查找表
77static DECODE_MAP: LazyLock<HashMap<u8, u8>> = LazyLock::new(|| {
78    let mut map = HashMap::new();
79    for (i, &byte) in CHARSET_BYTES.iter().enumerate() {
80        map.insert(byte, i as u8);
81    }
82    map
83});
84
85/// CRC16 查找表 (CRC-16-CCITT)
86static CRC16_TABLE: LazyLock<[u16; 256]> = LazyLock::new(|| {
87    let mut table = [0u16; 256];
88    for i in 0..256 {
89        let mut crc = (i as u16) << 8;
90        for _ in 0..8 {
91            if crc & 0x8000 != 0 {
92                crc = (crc << 1) ^ 0x1021;
93            } else {
94                crc <<= 1;
95            }
96        }
97        table[i] = crc;
98    }
99    table
100});
101
102/// DX 编码错误类型
103#[derive(Debug, Clone, PartialEq, Eq)]
104pub enum DxError {
105    /// 缺少 dx 前缀
106    InvalidPrefix,
107    /// 长度不正确
108    InvalidLength,
109    /// 包含非法字符
110    InvalidCharacter(char),
111    /// UTF-8 解码错误
112    Utf8Error(String),
113    /// 校验和不匹配
114    ChecksumMismatch { expected: u16, actual: u16 },
115    /// 头部无效
116    InvalidHeader,
117    /// 压缩/解压缩错误
118    CompressionError(String),
119    /// 无效的 flags
120    InvalidFlags(u8),
121    /// TTL 已过期
122    TtlExpired {
123        created_at: u64,
124        ttl_seconds: u32,
125        expired_at: u64,
126    },
127}
128
129impl fmt::Display for DxError {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        match self {
132            DxError::InvalidPrefix => write!(f, "无效的 DX 编码:缺少 dx 前缀"),
133            DxError::InvalidLength => write!(f, "无效的 DX 编码:长度不正确"),
134            DxError::InvalidCharacter(c) => write!(f, "无效的 DX 编码:包含非法字符 '{}'", c),
135            DxError::Utf8Error(s) => write!(f, "UTF-8 解码错误:{}", s),
136            DxError::ChecksumMismatch { expected, actual } => {
137                write!(
138                    f,
139                    "校验和不匹配:期望 0x{:04X},实际 0x{:04X}",
140                    expected, actual
141                )
142            }
143            DxError::InvalidHeader => write!(f, "无效的格式头部"),
144            DxError::CompressionError(s) => write!(f, "压缩/解压缩错误:{}", s),
145            DxError::InvalidFlags(flags) => write!(f, "无效的 flags 字节:0x{:02X}", flags),
146            DxError::TtlExpired {
147                created_at,
148                ttl_seconds,
149                expired_at,
150            } => {
151                write!(
152                    f,
153                    "TTL 已过期:创建于 {},有效期 {} 秒,已于 {} 过期",
154                    created_at, ttl_seconds, expired_at
155                )
156            }
157        }
158    }
159}
160
161
162impl Error for DxError {}
163
164/// DX 编码结果类型
165pub type Result<T> = std::result::Result<T, DxError>;
166
167/// 计算 CRC16-CCITT 校验和
168pub fn crc16(data: &[u8]) -> u16 {
169    let mut crc: u16 = 0xFFFF;
170    for &byte in data {
171        let index = ((crc >> 8) ^ (byte as u16)) as usize;
172        crc = (crc << 8) ^ CRC16_TABLE[index];
173    }
174    crc
175}
176
177/// 使用 DEFLATE 压缩数据
178fn compress_deflate(data: &[u8]) -> Result<Vec<u8>> {
179    let mut encoder = DeflateEncoder::new(Vec::new(), Compression::default());
180    encoder
181        .write_all(data)
182        .map_err(|e| DxError::CompressionError(e.to_string()))?;
183    encoder
184        .finish()
185        .map_err(|e| DxError::CompressionError(e.to_string()))
186}
187
188/// 使用 DEFLATE 解压缩数据
189fn decompress_deflate(data: &[u8]) -> Result<Vec<u8>> {
190    let mut decoder = DeflateDecoder::new(data);
191    let mut decompressed = Vec::new();
192    decoder
193        .read_to_end(&mut decompressed)
194        .map_err(|e| DxError::CompressionError(e.to_string()))?;
195    Ok(decompressed)
196}
197
198/// 内部编码函数(不带前缀)
199fn encode_raw(data: &[u8]) -> String {
200    if data.is_empty() {
201        return String::new();
202    }
203
204    let mut result = String::with_capacity((data.len() + 2) / 3 * 4);
205    let charset = &*CHARSET_BYTES;
206
207    // 每 3 字节处理一组
208    for chunk in data.chunks(3) {
209        let b0 = chunk[0];
210        let b1 = chunk.get(1).copied().unwrap_or(0);
211        let b2 = chunk.get(2).copied().unwrap_or(0);
212
213        // 将 3 字节(24位)分成 4 个 6 位组
214        let v0 = (b0 >> 2) & 0x3F;
215        let v1 = ((b0 & 0x03) << 4 | (b1 >> 4)) & 0x3F;
216        let v2 = ((b1 & 0x0F) << 2 | (b2 >> 6)) & 0x3F;
217        let v3 = b2 & 0x3F;
218
219        // XOR 变换并映射到字符
220        result.push(charset[((v0 ^ MAGIC) & 0x3F) as usize] as char);
221        result.push(charset[((v1 ^ MAGIC) & 0x3F) as usize] as char);
222
223        if chunk.len() > 1 {
224            result.push(charset[((v2 ^ MAGIC) & 0x3F) as usize] as char);
225        } else {
226            result.push(PADDING);
227        }
228
229        if chunk.len() > 2 {
230            result.push(charset[((v3 ^ MAGIC) & 0x3F) as usize] as char);
231        } else {
232            result.push(PADDING);
233        }
234    }
235
236    result
237}
238
239/// 内部解码函数(不带前缀验证)
240fn decode_raw(data: &str) -> Result<Vec<u8>> {
241    if data.is_empty() {
242        return Ok(Vec::new());
243    }
244
245    // 验证长度
246    if data.len() % 4 != 0 {
247        return Err(DxError::InvalidLength);
248    }
249
250    // 计算填充数量
251    let padding_count = if data.ends_with("==") {
252        2
253    } else if data.ends_with('=') {
254        1
255    } else {
256        0
257    };
258
259    // 计算输出长度
260    let output_len = (data.len() / 4) * 3 - padding_count;
261    let mut result = Vec::with_capacity(output_len);
262
263    let decode_map = &*DECODE_MAP;
264    let data_bytes = data.as_bytes();
265
266    // 每 4 字符处理一组
267    for chunk in data_bytes.chunks(4) {
268        let c0 = chunk[0];
269        let c1 = chunk[1];
270        let c2 = chunk[2];
271        let c3 = chunk[3];
272
273        // 字符转索引
274        let i0 = *decode_map
275            .get(&c0)
276            .ok_or_else(|| DxError::InvalidCharacter(c0 as char))?;
277        let i1 = *decode_map
278            .get(&c1)
279            .ok_or_else(|| DxError::InvalidCharacter(c1 as char))?;
280
281        let i2 = if c2 == PADDING as u8 {
282            0
283        } else {
284            *decode_map
285                .get(&c2)
286                .ok_or_else(|| DxError::InvalidCharacter(c2 as char))?
287        };
288
289        let i3 = if c3 == PADDING as u8 {
290            0
291        } else {
292            *decode_map
293                .get(&c3)
294                .ok_or_else(|| DxError::InvalidCharacter(c3 as char))?
295        };
296
297        // XOR 逆变换
298        let v0 = (i0 ^ MAGIC) & 0x3F;
299        let v1 = (i1 ^ MAGIC) & 0x3F;
300        let v2 = (i2 ^ MAGIC) & 0x3F;
301        let v3 = (i3 ^ MAGIC) & 0x3F;
302
303        // 重建字节
304        let b0 = (v0 << 2) | (v1 >> 4);
305        let b1 = ((v1 & 0x0F) << 4) | (v2 >> 2);
306        let b2 = ((v2 & 0x03) << 6) | v3;
307
308        if result.len() < output_len {
309            result.push(b0);
310        }
311        if result.len() < output_len {
312            result.push(b1);
313        }
314        if result.len() < output_len {
315            result.push(b2);
316        }
317    }
318
319    Ok(result)
320}
321
322/// 将字节切片编码为 DX 格式(带 CRC16 校验和和智能压缩)
323///
324/// # 参数
325///
326/// * `data` - 要编码的字节数据
327///
328/// # 返回值
329///
330/// 以 'dx' 为前缀、包含 CRC16 校验和的编码字符串(可能压缩)
331///
332/// # 示例
333///
334/// ```
335/// use dxcode::encode;
336///
337/// let encoded = encode(b"Hello, Dogxi!");
338/// assert!(encoded.starts_with("dx"));
339/// ```
340pub fn encode(data: &[u8]) -> String {
341    encode_with_options(data, true)
342}
343
344/// 将字节切片编码为 DX 格式,可选择是否启用压缩
345///
346/// # 参数
347///
348/// * `data` - 要编码的字节数据
349/// * `allow_compression` - 是否允许压缩
350///
351/// # 返回值
352///
353/// 以 'dx' 为前缀的编码字符串
354pub fn encode_with_options(data: &[u8], allow_compression: bool) -> String {
355    // 计算原始数据的 CRC16
356    let checksum = crc16(data);
357
358    // 决定是否压缩
359    let (flags, payload) = if allow_compression && data.len() >= COMPRESSION_THRESHOLD {
360        // 尝试压缩
361        match compress_deflate(data) {
362            Ok(compressed) => {
363                // 压缩后需要额外存储 2 字节原始大小
364                // 只有当压缩后的大小 + 2 < 原始大小时才使用压缩
365                if compressed.len() + 2 < data.len() && data.len() <= 65535 {
366                    // 使用压缩
367                    let mut payload = Vec::with_capacity(2 + compressed.len());
368                    // 存储原始大小(大端序)
369                    payload.push((data.len() >> 8) as u8);
370                    payload.push((data.len() & 0xFF) as u8);
371                    payload.extend_from_slice(&compressed);
372                    (FLAG_COMPRESSED | FLAG_ALGO_DEFLATE, payload)
373                } else {
374                    // 压缩无收益,使用原始数据
375                    (0u8, data.to_vec())
376                }
377            }
378            Err(_) => {
379                // 压缩失败,使用原始数据
380                (0u8, data.to_vec())
381            }
382        }
383    } else {
384        // 不压缩
385        (0u8, data.to_vec())
386    };
387
388    // 构建头部(1字节 flags + 2字节 CRC16,大端序)
389    let header = [flags, (checksum >> 8) as u8, (checksum & 0xFF) as u8];
390
391    // 合并头部和数据
392    let mut combined = Vec::with_capacity(HEADER_SIZE + payload.len());
393    combined.extend_from_slice(&header);
394    combined.extend_from_slice(&payload);
395
396    // 编码
397    let mut result = String::with_capacity(PREFIX.len() + (combined.len() + 2) / 3 * 4);
398    result.push_str(PREFIX);
399    result.push_str(&encode_raw(&combined));
400    result
401}
402
403/// 将字符串编码为 DX 格式(带 CRC16 校验和和智能压缩)
404///
405/// # 参数
406///
407/// * `s` - 要编码的字符串
408///
409/// # 返回值
410///
411/// 以 'dx' 为前缀、包含 CRC16 校验和的编码字符串
412///
413/// # 示例
414///
415/// ```
416/// use dxcode::encode_str;
417///
418/// let encoded = encode_str("你好,Dogxi!");
419/// assert!(encoded.starts_with("dx"));
420/// ```
421pub fn encode_str(s: &str) -> String {
422    encode(s.as_bytes())
423}
424
425/// 将字符串编码为 DX 格式,可选择是否启用压缩
426pub fn encode_str_with_options(s: &str, allow_compression: bool) -> String {
427    encode_with_options(s.as_bytes(), allow_compression)
428}
429
430/// 将 DX 编码的字符串解码为字节向量(带校验和验证,自动解压缩)
431///
432/// # 参数
433///
434/// * `encoded` - DX 编码的字符串(必须以 'dx' 开头)
435///
436/// # 返回值
437///
438/// 解码后的字节向量,如果输入无效或校验和不匹配则返回错误
439///
440/// # 示例
441///
442/// ```
443/// use dxcode::{encode, decode};
444///
445/// let encoded = encode(b"Hello");
446/// let decoded = decode(&encoded).unwrap();
447/// assert_eq!(decoded, b"Hello");
448/// ```
449pub fn decode(encoded: &str) -> Result<Vec<u8>> {
450    decode_with_options(encoded, true)
451}
452
453/// 将 DX 编码的字符串解码为字节数组,可选择是否检查 TTL
454///
455/// # 参数
456///
457/// * `encoded` - DX 编码的字符串
458/// * `check_ttl` - 是否检查 TTL 过期(如果为 true 且 TTL 已过期则返回错误)
459///
460/// # 返回值
461///
462/// 解码后的字节数组,如果输入无效或校验和不匹配则返回错误
463pub fn decode_with_options(encoded: &str, check_ttl: bool) -> Result<Vec<u8>> {
464    // 验证前缀
465    if !encoded.starts_with(PREFIX) {
466        return Err(DxError::InvalidPrefix);
467    }
468
469    // 移除前缀
470    let data = &encoded[PREFIX.len()..];
471
472    // 解码
473    let combined = decode_raw(data)?;
474
475    // 验证长度
476    if combined.len() < HEADER_SIZE {
477        return Err(DxError::InvalidHeader);
478    }
479
480    // 提取头部
481    let flags = combined[0];
482    let expected_checksum = ((combined[1] as u16) << 8) | (combined[2] as u16);
483
484    // 验证 flags 的保留位
485    if flags & !VALID_FLAGS_MASK != 0 {
486        return Err(DxError::InvalidFlags(flags));
487    }
488
489    // 确定 payload 的起始位置
490    let payload_start = if flags & FLAG_HAS_TTL != 0 {
491        // 验证有 TTL 头部的最小长度
492        if combined.len() < HEADER_SIZE + TTL_HEADER_SIZE {
493            return Err(DxError::InvalidHeader);
494        }
495
496        // 如果需要检查 TTL
497        if check_ttl {
498            let created_at = u32::from_be_bytes([
499                combined[HEADER_SIZE],
500                combined[HEADER_SIZE + 1],
501                combined[HEADER_SIZE + 2],
502                combined[HEADER_SIZE + 3],
503            ]) as u64;
504
505            let ttl_seconds = u32::from_be_bytes([
506                combined[HEADER_SIZE + 4],
507                combined[HEADER_SIZE + 5],
508                combined[HEADER_SIZE + 6],
509                combined[HEADER_SIZE + 7],
510            ]);
511
512            // 检查是否过期
513            if ttl_seconds > 0 {
514                let expires_at = created_at + ttl_seconds as u64;
515                let now = std::time::SystemTime::now()
516                    .duration_since(std::time::UNIX_EPOCH)
517                    .map(|d| d.as_secs())
518                    .unwrap_or(0);
519
520                if now > expires_at {
521                    return Err(DxError::TtlExpired {
522                        created_at,
523                        ttl_seconds,
524                        expired_at: expires_at,
525                    });
526                }
527            }
528        }
529
530        HEADER_SIZE + TTL_HEADER_SIZE
531    } else {
532        HEADER_SIZE
533    };
534
535    // 提取数据部分
536    let payload = &combined[payload_start..];
537
538    // 根据 flags 决定是否需要解压缩
539    let original_data = if flags & FLAG_COMPRESSED != 0 {
540        // 数据已压缩,需要解压
541        if payload.len() < 2 {
542            return Err(DxError::InvalidHeader);
543        }
544
545        // 提取原始大小(用于验证)
546        let _original_size = ((payload[0] as usize) << 8) | (payload[1] as usize);
547
548        // 解压缩
549        let compressed_data = &payload[2..];
550        decompress_deflate(compressed_data)?
551    } else {
552        // 数据未压缩
553        payload.to_vec()
554    };
555
556    // 验证校验和(针对原始数据)
557    let actual_checksum = crc16(&original_data);
558    if expected_checksum != actual_checksum {
559        return Err(DxError::ChecksumMismatch {
560            expected: expected_checksum,
561            actual: actual_checksum,
562        });
563    }
564
565    Ok(original_data)
566}
567
568/// 将 DX 编码的字符串解码为字符串(带校验和验证,自动解压缩)
569///
570/// # 参数
571///
572/// * `encoded` - DX 编码的字符串
573///
574/// # 返回值
575///
576/// 解码后的字符串,如果输入无效、校验和不匹配或不是有效的 UTF-8 则返回错误
577///
578/// # 示例
579///
580/// ```
581/// use dxcode::{encode_str, decode_str};
582///
583/// let encoded = encode_str("你好,Dogxi!");
584/// let decoded = decode_str(&encoded).unwrap();
585/// assert_eq!(decoded, "你好,Dogxi!");
586/// ```
587pub fn decode_str(encoded: &str) -> Result<String> {
588    decode_str_with_options(encoded, true)
589}
590
591/// 将 DX 编码的字符串解码为字符串,可选择是否检查 TTL
592///
593/// # 参数
594///
595/// * `encoded` - DX 编码的字符串
596/// * `check_ttl` - 是否检查 TTL 过期
597///
598/// # 返回值
599///
600/// 解码后的字符串
601pub fn decode_str_with_options(encoded: &str, check_ttl: bool) -> Result<String> {
602    let bytes = decode_with_options(encoded, check_ttl)?;
603    String::from_utf8(bytes).map_err(|e| DxError::Utf8Error(e.to_string()))
604}
605
606/// 检查字符串是否为有效的 DX 编码
607///
608/// # 参数
609///
610/// * `s` - 要检查的字符串
611///
612/// # 返回值
613///
614/// 如果是有效的 DX 编码返回 `true`,否则返回 `false`
615///
616/// # 示例
617///
618/// ```
619/// use dxcode::{encode_str, is_encoded};
620///
621/// let encoded = encode_str("Hello");
622/// assert!(is_encoded(&encoded));
623/// assert!(!is_encoded("hello"));
624/// ```
625pub fn is_encoded(s: &str) -> bool {
626    if !s.starts_with(PREFIX) {
627        return false;
628    }
629
630    let data = &s[PREFIX.len()..];
631
632    // 检查长度(至少需要头部)
633    if data.is_empty() || data.len() % 4 != 0 {
634        return false;
635    }
636
637    let decode_map = &*DECODE_MAP;
638
639    // 检查字符
640    for (i, c) in data.bytes().enumerate() {
641        if c == PADDING as u8 {
642            // 填充只能在末尾
643            if i < data.len() - 2 {
644                return false;
645            }
646        } else if !decode_map.contains_key(&c) {
647            return false;
648        }
649    }
650
651    true
652}
653
654/// 验证 DX 编码的校验和(不返回解码数据)
655///
656/// # 参数
657///
658/// * `encoded` - DX 编码的字符串
659///
660/// # 返回值
661///
662/// 如果校验和匹配返回 `Ok(true)`,不匹配返回 `Ok(false)`,格式无效返回错误
663///
664/// # 示例
665///
666/// ```
667/// use dxcode::{encode_str, verify};
668///
669/// let encoded = encode_str("Hello");
670/// assert!(verify(&encoded).unwrap());
671/// ```
672pub fn verify(encoded: &str) -> Result<bool> {
673    match decode(encoded) {
674        Ok(_) => Ok(true),
675        Err(DxError::ChecksumMismatch { .. }) => Ok(false),
676        Err(e) => Err(e),
677    }
678}
679
680/// 获取 DX 编码的校验和信息
681///
682/// # 参数
683///
684/// * `encoded` - DX 编码的字符串
685///
686/// # 返回值
687///
688/// 返回 `(存储的校验和, 实际计算的校验和)`
689///
690/// # 示例
691///
692/// ```
693/// use dxcode::{encode_str, get_checksum};
694///
695/// let encoded = encode_str("Hello");
696/// let (stored, computed) = get_checksum(&encoded).unwrap();
697/// assert_eq!(stored, computed);
698/// ```
699pub fn get_checksum(encoded: &str) -> Result<(u16, u16)> {
700    // 验证前缀
701    if !encoded.starts_with(PREFIX) {
702        return Err(DxError::InvalidPrefix);
703    }
704
705    // 移除前缀
706    let data = &encoded[PREFIX.len()..];
707
708    // 解码
709    let combined = decode_raw(data)?;
710
711    // 验证长度
712    if combined.len() < HEADER_SIZE {
713        return Err(DxError::InvalidHeader);
714    }
715
716    // 提取 flags 和校验和
717    let flags = combined[0];
718    let stored = ((combined[1] as u16) << 8) | (combined[2] as u16);
719
720    // 提取数据部分
721    let payload = &combined[HEADER_SIZE..];
722
723    // 根据 flags 决定是否需要解压缩
724    let original_data = if flags & FLAG_COMPRESSED != 0 {
725        if payload.len() < 2 {
726            return Err(DxError::InvalidHeader);
727        }
728        let compressed_data = &payload[2..];
729        decompress_deflate(compressed_data)?
730    } else {
731        payload.to_vec()
732    };
733
734    let computed = crc16(&original_data);
735
736    Ok((stored, computed))
737}
738
739/// 检查编码是否使用了压缩
740///
741/// # 参数
742///
743/// * `encoded` - DX 编码的字符串
744///
745/// # 返回值
746///
747/// 返回 `true` 如果数据已压缩,否则返回 `false`
748///
749/// # 示例
750///
751/// ```
752/// use dxcode::{encode_str, is_compressed};
753///
754/// let short_data = encode_str("Hi");
755/// assert!(!is_compressed(&short_data).unwrap());
756///
757/// let long_data = encode_str(&"x".repeat(100));
758/// // 可能压缩也可能不压缩,取决于压缩效果
759/// ```
760pub fn is_compressed(encoded: &str) -> Result<bool> {
761    // 验证前缀
762    if !encoded.starts_with(PREFIX) {
763        return Err(DxError::InvalidPrefix);
764    }
765
766    // 移除前缀
767    let data = &encoded[PREFIX.len()..];
768
769    // 解码
770    let combined = decode_raw(data)?;
771
772    // 验证长度
773    if combined.len() < HEADER_SIZE {
774        return Err(DxError::InvalidHeader);
775    }
776
777    // 检查 flags
778    let flags = combined[0];
779    Ok(flags & FLAG_COMPRESSED != 0)
780}
781
782/// TTL 信息
783#[derive(Debug, Clone)]
784pub struct TtlInfo {
785    /// 创建时间(Unix 时间戳,秒)
786    pub created_at: u64,
787    /// 有效期(秒),0 表示永不过期
788    pub ttl_seconds: u32,
789    /// 过期时间(Unix 时间戳,秒),None 表示永不过期
790    pub expires_at: Option<u64>,
791    /// 是否已过期
792    pub is_expired: bool,
793}
794
795/// 检查编码是否包含 TTL 信息
796///
797/// # 参数
798///
799/// * `encoded` - DX 编码的字符串
800///
801/// # 返回值
802///
803/// 返回 `true` 如果包含 TTL,否则返回 `false`
804pub fn has_ttl(encoded: &str) -> Result<bool> {
805    // 验证前缀
806    if !encoded.starts_with(PREFIX) {
807        return Err(DxError::InvalidPrefix);
808    }
809
810    // 移除前缀
811    let data = &encoded[PREFIX.len()..];
812
813    // 解码
814    let combined = decode_raw(data)?;
815
816    // 验证长度
817    if combined.len() < HEADER_SIZE {
818        return Err(DxError::InvalidHeader);
819    }
820
821    // 检查 flags
822    let flags = combined[0];
823    Ok(flags & FLAG_HAS_TTL != 0)
824}
825
826/// 获取编码的 TTL 信息
827///
828/// # 参数
829///
830/// * `encoded` - DX 编码的字符串
831///
832/// # 返回值
833///
834/// 如果编码包含 TTL 信息,返回 `Some(TtlInfo)`,否则返回 `None`
835///
836/// # 示例
837///
838/// ```
839/// use dxcode::{encode_with_ttl, get_ttl_info};
840///
841/// let encoded = encode_with_ttl(b"Hello", 3600); // 1小时有效期
842/// if let Some(info) = get_ttl_info(&encoded).unwrap() {
843///     println!("创建于: {}", info.created_at);
844///     println!("过期: {}", info.is_expired);
845/// }
846/// ```
847pub fn get_ttl_info(encoded: &str) -> Result<Option<TtlInfo>> {
848    // 验证前缀
849    if !encoded.starts_with(PREFIX) {
850        return Err(DxError::InvalidPrefix);
851    }
852
853    // 移除前缀
854    let data = &encoded[PREFIX.len()..];
855
856    // 解码
857    let combined = decode_raw(data)?;
858
859    // 验证长度
860    if combined.len() < HEADER_SIZE {
861        return Err(DxError::InvalidHeader);
862    }
863
864    let flags = combined[0];
865
866    // 检查是否有 TTL
867    if flags & FLAG_HAS_TTL == 0 {
868        return Ok(None);
869    }
870
871    // 验证有 TTL 头部的最小长度
872    if combined.len() < HEADER_SIZE + TTL_HEADER_SIZE {
873        return Err(DxError::InvalidHeader);
874    }
875
876    // 提取 TTL 信息
877    let created_at = u32::from_be_bytes([
878        combined[HEADER_SIZE],
879        combined[HEADER_SIZE + 1],
880        combined[HEADER_SIZE + 2],
881        combined[HEADER_SIZE + 3],
882    ]) as u64;
883
884    let ttl_seconds = u32::from_be_bytes([
885        combined[HEADER_SIZE + 4],
886        combined[HEADER_SIZE + 5],
887        combined[HEADER_SIZE + 6],
888        combined[HEADER_SIZE + 7],
889    ]);
890
891    // 计算过期时间和状态
892    let (expires_at, is_expired) = if ttl_seconds == 0 {
893        (None, false)
894    } else {
895        let expires = created_at + ttl_seconds as u64;
896        let now = std::time::SystemTime::now()
897            .duration_since(std::time::UNIX_EPOCH)
898            .map(|d| d.as_secs())
899            .unwrap_or(0);
900        (Some(expires), now > expires)
901    };
902
903    Ok(Some(TtlInfo {
904        created_at,
905        ttl_seconds,
906        expires_at,
907        is_expired,
908    }))
909}
910
911/// 检查编码是否已过期
912///
913/// # 参数
914///
915/// * `encoded` - DX 编码的字符串
916///
917/// # 返回值
918///
919/// 返回 `true` 如果已过期或没有 TTL,否则返回 `false`
920///
921/// # 示例
922///
923/// ```
924/// use dxcode::{encode_with_ttl, is_expired};
925///
926/// let encoded = encode_with_ttl(b"Hello", 3600);
927/// assert!(!is_expired(&encoded).unwrap()); // 还没过期
928/// ```
929pub fn is_expired(encoded: &str) -> Result<bool> {
930    match get_ttl_info(encoded)? {
931        Some(info) => Ok(info.is_expired),
932        None => Ok(false), // 没有 TTL 的数据永不过期
933    }
934}
935
936/// 使用 TTL 编码字节数据
937///
938/// # 参数
939///
940/// * `data` - 要编码的字节数据
941/// * `ttl_seconds` - 有效期(秒),0 表示永不过期
942///
943/// # 返回值
944///
945/// 带有 TTL 的 DX 编码字符串
946///
947/// # 示例
948///
949/// ```
950/// use dxcode::encode_with_ttl;
951///
952/// // 编码数据,1小时后过期
953/// let encoded = encode_with_ttl(b"Secret Data", 3600);
954/// assert!(encoded.starts_with("dx"));
955/// ```
956pub fn encode_with_ttl(data: &[u8], ttl_seconds: u32) -> String {
957    encode_with_ttl_and_options(data, ttl_seconds, true)
958}
959
960/// 使用 TTL 编码字节数据,可选择是否启用压缩
961///
962/// # 参数
963///
964/// * `data` - 要编码的字节数据
965/// * `ttl_seconds` - 有效期(秒),0 表示永不过期
966/// * `allow_compression` - 是否允许压缩
967///
968/// # 返回值
969///
970/// 带有 TTL 的 DX 编码字符串
971pub fn encode_with_ttl_and_options(data: &[u8], ttl_seconds: u32, allow_compression: bool) -> String {
972    // 获取当前时间戳
973    let created_at = std::time::SystemTime::now()
974        .duration_since(std::time::UNIX_EPOCH)
975        .map(|d| d.as_secs() as u32)
976        .unwrap_or(0);
977
978    // 计算原始数据的 CRC16
979    let checksum = crc16(data);
980
981    // 决定是否压缩
982    let (mut flags, payload) = if allow_compression && data.len() >= COMPRESSION_THRESHOLD {
983        match compress_deflate(data) {
984            Ok(compressed) => {
985                if compressed.len() + 2 < data.len() && data.len() <= 65535 {
986                    let mut payload = Vec::with_capacity(2 + compressed.len());
987                    payload.push((data.len() >> 8) as u8);
988                    payload.push((data.len() & 0xFF) as u8);
989                    payload.extend_from_slice(&compressed);
990                    (FLAG_COMPRESSED | FLAG_ALGO_DEFLATE, payload)
991                } else {
992                    (0u8, data.to_vec())
993                }
994            }
995            Err(_) => (0u8, data.to_vec()),
996        }
997    } else {
998        (0u8, data.to_vec())
999    };
1000
1001    // 设置 TTL flag
1002    flags |= FLAG_HAS_TTL;
1003
1004    // 构建完整数据:[flags(1)] [CRC16(2)] [created_at(4)] [ttl(4)] [payload]
1005    let mut combined = Vec::with_capacity(HEADER_SIZE + TTL_HEADER_SIZE + payload.len());
1006    combined.push(flags);
1007    combined.push((checksum >> 8) as u8);
1008    combined.push((checksum & 0xFF) as u8);
1009    combined.extend_from_slice(&created_at.to_be_bytes());
1010    combined.extend_from_slice(&ttl_seconds.to_be_bytes());
1011    combined.extend_from_slice(&payload);
1012
1013    // 编码
1014    let mut result = String::with_capacity(PREFIX.len() + (combined.len() + 2) / 3 * 4);
1015    result.push_str(PREFIX);
1016    result.push_str(&encode_raw(&combined));
1017    result
1018}
1019
1020/// 使用 TTL 编码字符串
1021///
1022/// # 参数
1023///
1024/// * `s` - 要编码的字符串
1025/// * `ttl_seconds` - 有效期(秒),0 表示永不过期
1026///
1027/// # 返回值
1028///
1029/// 带有 TTL 的 DX 编码字符串
1030///
1031/// # 示例
1032///
1033/// ```
1034/// use dxcode::encode_str_with_ttl;
1035///
1036/// // 编码字符串,30分钟后过期
1037/// let encoded = encode_str_with_ttl("临时令牌", 1800);
1038/// ```
1039pub fn encode_str_with_ttl(s: &str, ttl_seconds: u32) -> String {
1040    encode_with_ttl(s.as_bytes(), ttl_seconds)
1041}
1042
1043/// DX 编码信息
1044#[derive(Debug, Clone)]
1045pub struct Info {
1046    pub name: &'static str,
1047    pub version: &'static str,
1048    pub author: &'static str,
1049    pub charset: &'static str,
1050    pub prefix: &'static str,
1051    pub magic: u8,
1052    pub padding: char,
1053    pub checksum: &'static str,
1054    pub compression: &'static str,
1055    pub compression_threshold: usize,
1056}
1057
1058/// 获取 DX 编码的信息
1059///
1060/// # 返回值
1061///
1062/// 包含版本、作者、字符集等信息的 `Info` 结构体
1063///
1064/// # 示例
1065///
1066/// ```
1067/// use dxcode::get_info;
1068///
1069/// let info = get_info();
1070/// println!("名称: {}", info.name);
1071/// println!("作者: {}", info.author);
1072/// ```
1073pub fn get_info() -> Info {
1074    Info {
1075        name: "DX Encoding",
1076        version: "2.3.0",
1077        author: "Dogxi",
1078        charset: CHARSET,
1079        prefix: PREFIX,
1080        magic: MAGIC,
1081        padding: PADDING,
1082        checksum: "CRC16-CCITT",
1083        compression: "DEFLATE",
1084        compression_threshold: COMPRESSION_THRESHOLD,
1085    }
1086}
1087
1088#[cfg(test)]
1089mod tests {
1090    use super::*;
1091
1092    #[test]
1093    fn test_simple_string() {
1094        let original = "Hello";
1095        let encoded = encode_str(original);
1096        let decoded = decode_str(&encoded).unwrap();
1097        assert_eq!(decoded, original);
1098        assert!(encoded.starts_with("dx"));
1099    }
1100
1101    #[test]
1102    fn test_chinese_string() {
1103        let original = "你好,世界!";
1104        let encoded = encode_str(original);
1105        let decoded = decode_str(&encoded).unwrap();
1106        assert_eq!(decoded, original);
1107    }
1108
1109    #[test]
1110    fn test_emoji() {
1111        let original = "🎉🚀✨";
1112        let encoded = encode_str(original);
1113        let decoded = decode_str(&encoded).unwrap();
1114        assert_eq!(decoded, original);
1115    }
1116
1117    #[test]
1118    fn test_empty_string() {
1119        let original = "";
1120        let encoded = encode_str(original);
1121        let decoded = decode_str(&encoded).unwrap();
1122        assert_eq!(decoded, original);
1123        assert!(encoded.starts_with("dx"));
1124    }
1125
1126    #[test]
1127    fn test_binary_data() {
1128        let original: Vec<u8> = vec![0x00, 0x01, 0x02, 0xFE, 0xFF];
1129        let encoded = encode(&original);
1130        let decoded = decode(&encoded).unwrap();
1131        assert_eq!(decoded, original);
1132    }
1133
1134    #[test]
1135    fn test_all_byte_values() {
1136        let original: Vec<u8> = (0..=255).collect();
1137        let encoded = encode(&original);
1138        let decoded = decode(&encoded).unwrap();
1139        assert_eq!(decoded, original);
1140    }
1141
1142    #[test]
1143    fn test_is_encoded() {
1144        let encoded = encode_str("Hello");
1145        assert!(is_encoded(&encoded));
1146        assert!(!is_encoded("hello"));
1147    }
1148
1149    #[test]
1150    fn test_decode_invalid_prefix() {
1151        let result = decode("invalid");
1152        assert!(matches!(result, Err(DxError::InvalidPrefix)));
1153    }
1154
1155    #[test]
1156    fn test_decode_invalid_length() {
1157        let result = decode("dxABC");
1158        assert!(matches!(result, Err(DxError::InvalidLength)));
1159    }
1160
1161    #[test]
1162    fn test_checksum_verification() {
1163        let encoded = encode_str("Hello");
1164        assert!(verify(&encoded).unwrap());
1165
1166        let (stored, computed) = get_checksum(&encoded).unwrap();
1167        assert_eq!(stored, computed);
1168    }
1169
1170    #[test]
1171    fn test_checksum_mismatch() {
1172        let encoded = encode_str("Hello World Test Data");
1173
1174        // 篡改数据(修改编码字符串中的一个字符)
1175        let mut chars: Vec<char> = encoded.chars().collect();
1176
1177        // 找到一个可以修改的位置(跳过 "dx" 前缀,在数据部分修改)
1178        if chars.len() > 10 {
1179            let pos = 10;
1180            let original_char = chars[pos];
1181            // 用字符集中的另一个有效字符替换
1182            chars[pos] = if original_char == 'A' { 'B' } else { 'A' };
1183        }
1184
1185        let modified: String = chars.into_iter().collect();
1186
1187        // 验证应该失败(校验和不匹配或无效字符)
1188        let result = decode(&modified);
1189        assert!(
1190            matches!(result, Err(DxError::ChecksumMismatch { .. }))
1191                || matches!(result, Err(DxError::InvalidCharacter(_)))
1192        );
1193    }
1194
1195    #[test]
1196    fn test_get_info() {
1197        let info = get_info();
1198        assert_eq!(info.name, "DX Encoding");
1199        assert_eq!(info.author, "Dogxi");
1200        assert_eq!(info.prefix, "dx");
1201        assert_eq!(info.magic, 0x44);
1202        assert_eq!(info.charset.len(), 64);
1203        assert_eq!(info.version, "2.3.0");
1204        assert_eq!(info.checksum, "CRC16-CCITT");
1205        assert_eq!(info.compression, "DEFLATE");
1206    }
1207
1208    #[test]
1209    fn test_various_lengths() {
1210        for length in 0..100 {
1211            let original: Vec<u8> = (0..length).map(|i| (i % 256) as u8).collect();
1212            let encoded = encode(&original);
1213            let decoded = decode(&encoded).unwrap();
1214            assert_eq!(decoded, original, "长度 {} 失败", length);
1215        }
1216    }
1217
1218    #[test]
1219    fn test_crc16() {
1220        // 测试空数据
1221        assert_eq!(crc16(&[]), 0xFFFF);
1222
1223        // 测试已知值 - CRC-16-CCITT for "123456789" should be 0x29B1
1224        let data = b"123456789";
1225        let crc = crc16(data);
1226        assert_eq!(crc, 0x29B1);
1227    }
1228
1229    #[test]
1230    fn test_crc16_deterministic() {
1231        let data = b"Hello, World!";
1232        let crc1 = crc16(data);
1233        let crc2 = crc16(data);
1234        assert_eq!(crc1, crc2);
1235    }
1236
1237    #[test]
1238    fn test_verify_function() {
1239        let encoded = encode_str("Test data for verification");
1240        assert!(verify(&encoded).unwrap());
1241    }
1242
1243    // ========== 压缩测试 ==========
1244
1245    #[test]
1246    fn test_short_data_not_compressed() {
1247        let original = "Short";
1248        let encoded = encode_str(original);
1249        assert!(!is_compressed(&encoded).unwrap());
1250
1251        let decoded = decode_str(&encoded).unwrap();
1252        assert_eq!(decoded, original);
1253    }
1254
1255    #[test]
1256    fn test_long_repetitive_data_compressed() {
1257        // 重复数据压缩效果好
1258        let original = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
1259        let encoded = encode_str(original);
1260
1261        // 验证解码正确
1262        let decoded = decode_str(&encoded).unwrap();
1263        assert_eq!(decoded, original);
1264
1265        // 重复数据应该被压缩(压缩效果好)
1266        assert!(is_compressed(&encoded).unwrap());
1267    }
1268
1269    #[test]
1270    fn test_compression_saves_space() {
1271        // 创建大量重复数据
1272        let original = "Hello World! ".repeat(100);
1273        let encoded_compressed = encode_str(&original);
1274        let encoded_uncompressed = encode_str_with_options(&original, false);
1275
1276        // 压缩版本应该更短
1277        assert!(
1278            encoded_compressed.len() < encoded_uncompressed.len(),
1279            "压缩版本 ({}) 应该比未压缩版本 ({}) 短",
1280            encoded_compressed.len(),
1281            encoded_uncompressed.len()
1282        );
1283
1284        // 两种方式都能正确解码
1285        assert_eq!(decode_str(&encoded_compressed).unwrap(), original);
1286        assert_eq!(decode_str(&encoded_uncompressed).unwrap(), original);
1287    }
1288
1289    #[test]
1290    fn test_incompressible_data() {
1291        // 随机数据压缩效果差
1292        let original: Vec<u8> = (0..100).map(|i| (i * 7 + 13) as u8).collect();
1293        let encoded = encode(&original);
1294
1295        // 验证解码正确
1296        let decoded = decode(&encoded).unwrap();
1297        assert_eq!(decoded, original);
1298    }
1299
1300    #[test]
1301    fn test_encode_without_compression() {
1302        let original = "A".repeat(100);
1303        let encoded = encode_str_with_options(&original, false);
1304
1305        // 强制不压缩
1306        assert!(!is_compressed(&encoded).unwrap());
1307
1308        // 仍然能正确解码
1309        let decoded = decode_str(&encoded).unwrap();
1310        assert_eq!(decoded, original);
1311    }
1312
1313    #[test]
1314    fn test_compression_threshold() {
1315        // 刚好在阈值以下
1316        let short_data = "x".repeat(COMPRESSION_THRESHOLD - 1);
1317        let encoded_short = encode_str(&short_data);
1318        assert!(!is_compressed(&encoded_short).unwrap());
1319
1320        // 刚好在阈值以上
1321        let long_data = "x".repeat(COMPRESSION_THRESHOLD + 10);
1322        let encoded_long = encode_str(&long_data);
1323        // 重复数据应该被压缩
1324        assert!(is_compressed(&encoded_long).unwrap());
1325    }
1326
1327    #[test]
1328    fn test_large_data_compression() {
1329        // 测试较大数据
1330        let original = "The quick brown fox jumps over the lazy dog. ".repeat(500);
1331        let encoded = encode_str(&original);
1332
1333        // 验证解码正确
1334        let decoded = decode_str(&encoded).unwrap();
1335        assert_eq!(decoded, original);
1336
1337        // 验证校验和
1338        assert!(verify(&encoded).unwrap());
1339    }
1340
1341    // ========== TTL 测试 ==========
1342
1343    #[test]
1344    fn test_encode_with_ttl() {
1345        let original = b"Secret Data";
1346        let encoded = encode_with_ttl(original, 3600); // 1小时有效期
1347
1348        assert!(encoded.starts_with("dx"));
1349
1350        // 验证包含 TTL
1351        assert!(has_ttl(&encoded).unwrap());
1352
1353        // 验证能正确解码
1354        let decoded = decode(&encoded).unwrap();
1355        assert_eq!(decoded, original);
1356    }
1357
1358    #[test]
1359    fn test_encode_str_with_ttl() {
1360        let original = "临时令牌";
1361        let encoded = encode_str_with_ttl(original, 1800); // 30分钟
1362
1363        assert!(has_ttl(&encoded).unwrap());
1364
1365        let decoded = decode_str(&encoded).unwrap();
1366        assert_eq!(decoded, original);
1367    }
1368
1369    #[test]
1370    fn test_ttl_info() {
1371        let encoded = encode_with_ttl(b"Test", 3600);
1372
1373        let info = get_ttl_info(&encoded).unwrap().unwrap();
1374        assert_eq!(info.ttl_seconds, 3600);
1375        assert!(!info.is_expired);
1376        assert!(info.expires_at.is_some());
1377
1378        // created_at 应该在最近几秒内
1379        let now = std::time::SystemTime::now()
1380            .duration_since(std::time::UNIX_EPOCH)
1381            .unwrap()
1382            .as_secs();
1383        assert!(info.created_at <= now && info.created_at >= now - 5);
1384    }
1385
1386    #[test]
1387    fn test_ttl_zero_never_expires() {
1388        let encoded = encode_with_ttl(b"Forever", 0); // 永不过期
1389
1390        let info = get_ttl_info(&encoded).unwrap().unwrap();
1391        assert_eq!(info.ttl_seconds, 0);
1392        assert!(info.expires_at.is_none());
1393        assert!(!info.is_expired);
1394        assert!(!is_expired(&encoded).unwrap());
1395    }
1396
1397    #[test]
1398    fn test_no_ttl_returns_none() {
1399        let encoded = encode(b"No TTL");
1400
1401        assert!(!has_ttl(&encoded).unwrap());
1402        assert!(get_ttl_info(&encoded).unwrap().is_none());
1403        assert!(!is_expired(&encoded).unwrap());
1404    }
1405
1406    #[test]
1407    fn test_ttl_with_compression() {
1408        // 长数据应该被压缩
1409        let original = "Repeated data for compression test. ".repeat(50);
1410        let encoded = encode_str_with_ttl(&original, 7200);
1411
1412        // 验证 TTL 和压缩都存在
1413        assert!(has_ttl(&encoded).unwrap());
1414        assert!(is_compressed(&encoded).unwrap());
1415
1416        // 验证能正确解码
1417        let decoded = decode_str(&encoded).unwrap();
1418        assert_eq!(decoded, original);
1419    }
1420
1421    #[test]
1422    fn test_ttl_without_compression() {
1423        let original = "Short";
1424        let encoded = encode_with_ttl_and_options(original.as_bytes(), 3600, false);
1425
1426        assert!(has_ttl(&encoded).unwrap());
1427        assert!(!is_compressed(&encoded).unwrap());
1428
1429        let decoded = decode_str(&encoded).unwrap();
1430        assert_eq!(decoded, original);
1431    }
1432
1433    #[test]
1434    fn test_decode_skip_ttl_check() {
1435        // 即使 TTL 存在,也可以跳过检查
1436        let encoded = encode_with_ttl(b"Data", 1);
1437
1438        // 使用 decode_with_options 跳过 TTL 检查
1439        let decoded = decode_with_options(&encoded, false).unwrap();
1440        assert_eq!(decoded, b"Data");
1441    }
1442
1443    #[test]
1444    fn test_is_expired_function() {
1445        let encoded = encode_with_ttl(b"Data", 86400); // 1天有效期
1446        assert!(!is_expired(&encoded).unwrap());
1447    }
1448}