ctp_rust/
encoding.rs

1//! 编码转换模块
2//!
3//! 处理GB18030和UTF-8之间的编码转换
4
5use crate::error::{encoding_error, CtpResult};
6use encoding::all::GB18030;
7use encoding::{DecoderTrap, EncoderTrap, Encoding};
8use std::ffi::{CStr, CString};
9
10// GB18030编码转换器
11pub struct GbkConverter;
12
13impl GbkConverter {
14    // 将GB18030编码的字节转换为UTF-8字符串
15    //
16    // # 参数
17    // * `bytes` - GB18030编码的字节数组
18    //
19    // # 返回值
20    // UTF-8字符串
21    pub fn gb18030_to_utf8(bytes: &[u8]) -> CtpResult<String> {
22        // 移除末尾的空字节
23        let trimmed_bytes = Self::trim_null_bytes(bytes);
24
25        if trimmed_bytes.is_empty() {
26            return Ok(String::new());
27        }
28
29        GB18030
30            .decode(trimmed_bytes, DecoderTrap::Replace)
31            .map_err(|e| encoding_error(&format!("GB18030解码失败: {}", e)))
32    }
33
34    // 将UTF-8字符串转换为GB18030编码的字节
35    //
36    // # 参数
37    // * `utf8_str` - UTF-8字符串
38    //
39    // # 返回值
40    // GB18030编码的字节数组
41    pub fn utf8_to_gb18030(utf8_str: &str) -> CtpResult<Vec<u8>> {
42        GB18030
43            .encode(utf8_str, EncoderTrap::Replace)
44            .map_err(|e| encoding_error(&format!("UTF-8编码失败: {}", e)))
45    }
46
47    // 将UTF-8字符串转换为以null结尾的GB18030 C字符串
48    //
49    // # 参数
50    // * `utf8_str` - UTF-8字符串
51    //
52    // # 返回值
53    // GB18030编码的CString
54    pub fn utf8_to_gb18030_cstring(utf8_str: &str) -> CtpResult<CString> {
55        let gb_bytes = Self::utf8_to_gb18030(utf8_str)?;
56        CString::new(gb_bytes).map_err(|e| encoding_error(&format!("创建CString失败: {}", e)))
57    }
58
59    // 从C字符串指针读取GB18030编码并转换为UTF-8
60    //
61    // # 安全性
62    // 此函数是unsafe的,因为它解引用原始指针
63    //
64    // # 参数
65    // * `ptr` - 指向GB18030编码C字符串的指针
66    //
67    // # 返回值
68    // UTF-8字符串
69    pub unsafe fn cstring_to_utf8(ptr: *const i8) -> CtpResult<String> {
70        if ptr.is_null() {
71            return Ok(String::new());
72        }
73
74        let c_str = CStr::from_ptr(ptr);
75        let bytes = c_str.to_bytes();
76        Self::gb18030_to_utf8(bytes)
77    }
78
79    // 移除字节数组末尾的空字节
80    fn trim_null_bytes(bytes: &[u8]) -> &[u8] {
81        bytes
82            .iter()
83            .rposition(|&b| b != 0)
84            .map_or(&[], |pos| &bytes[..=pos])
85    }
86
87    // 将固定长度的字节数组转换为UTF-8字符串
88    //
89    // # 参数
90    // * `bytes` - 固定长度的字节数组
91    //
92    // # 返回值
93    // UTF-8字符串
94    pub fn fixed_bytes_to_utf8<const N: usize>(bytes: &[u8; N]) -> CtpResult<String> {
95        Self::gb18030_to_utf8(bytes)
96    }
97
98    // 将UTF-8字符串转换为固定长度的GB18030字节数组
99    //
100    // # 参数
101    // * `utf8_str` - UTF-8字符串
102    //
103    // # 返回值
104    // 固定长度的GB18030字节数组
105    pub fn utf8_to_fixed_bytes<const N: usize>(utf8_str: &str) -> CtpResult<[u8; N]> {
106        let gb_bytes = Self::utf8_to_gb18030(utf8_str)?;
107        let mut result = [0u8; N];
108
109        if gb_bytes.len() >= N {
110            result.copy_from_slice(&gb_bytes[..N]);
111        } else {
112            result[..gb_bytes.len()].copy_from_slice(&gb_bytes);
113        }
114
115        Ok(result)
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn test_utf8_to_gb18030_and_back() {
125        let original = "测试字符串";
126        let gb_bytes = GbkConverter::utf8_to_gb18030(original).unwrap();
127        let converted = GbkConverter::gb18030_to_utf8(&gb_bytes).unwrap();
128        assert_eq!(original, converted);
129    }
130
131    #[test]
132    fn test_empty_string() {
133        let result = GbkConverter::gb18030_to_utf8(&[]).unwrap();
134        assert_eq!(result, "");
135    }
136
137    #[test]
138    fn test_null_terminated_bytes() {
139        let bytes = b"test\0\0\0";
140        let result = GbkConverter::gb18030_to_utf8(bytes).unwrap();
141        assert_eq!(result, "test");
142    }
143
144    #[test]
145    fn test_fixed_bytes_conversion() {
146        let original = "测试";
147        let fixed_bytes: [u8; 20] = GbkConverter::utf8_to_fixed_bytes(original).unwrap();
148        let converted = GbkConverter::fixed_bytes_to_utf8(&fixed_bytes).unwrap();
149        assert_eq!(converted.trim_end_matches('\0'), original);
150    }
151}