心澄

命由己造,相由心生,世间万物皆是化相,心不动,万物皆不动,心不变,万物皆不变
Program Languagerusting... going...
Email insunsgmail.com
Country China
LocationHangZhou, ZheJiang

rust连接ctp的数据交互处理


生成绑定

rust调用ctp的步骤:

  1. 利用python自动生成wrapper.cppwrapper.hpp文件;
  2. 用rust的bindgen 生成rust可调用的接口;
  3. 引入生成后的文件,引入的方式有两种:pub use crate::ctp::generated::*;导入和
include!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/src/ctp/generated/mod.rs"
));
include 模式,vscode代码导航只能到引入的文件,所以本人更推荐用use的形式

ctp生成rust绑定可以参考这个开源项目:ctp-rs

c的char类型与rust的i8类型

ctp头文件中所有定义为char类型的,通过rustbindgen将自动转为i8类型,比如下图的登录参数中的MacAddress在CTP头文件定义为char的21个字节定长类型,转为rust则变成[i8;21]定长数组:

ctp头文件中MacAddress定义

Rust中的MacAddress定义

rust 调用ctp时定长数据的处理

rust与ctp进行交互比较不好处理的是大量参数必须转换成[i8;N]定长数组类型。

在rust中,字符串转bytes后就是 &[u8]类型,通过map就可以很容易就转成 Vec<i8>类型:

let b: Vec<i8> = "Hello, world!"
            .as_bytes()
            .iter()
            .map(|x| *x as i8)
            .collect();

Vec<i8>类型作为引用参数传入时,系统将自动转换为&[i8]类型。如下代码:

fn main() {
    let b: Vec<i8> = "Hello, world!"
        .as_bytes()
        .iter()
        .map(|x| *x as i8)
        .collect();
    print_sice(&b)
}
fn print_sice(s: &[i8]) {
    println!("{:?}", s)
}

输出:

================ STANDARD OUTPUT ================

[72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]

因此不管是[u8]还是[i8]slice类型的数据都很容易转换获得。但是如果大量的定长数组就比较不好处理。有人可能觉得写一个方法将 String[i8;N]即可。要知道在rust中[i8;1],[i8;2]...[i8;N]都是不同的类型。因此必须实现一个 String -> [i8;N]的转换函数。在rust1.51之前基本不可能实现这个功能,除非你从String->[i8;1]String->[i8;N]每个手写一遍。rust的Default::default()看提示应该是用宏实现了数组,数组的最大长度为32(截止rustc 1.57.0),不信试试let b: [i8; 33] = Default::default();
宏实现了数组

rust1.51的版本引入了很重要的一个特性:const泛型,相关的知识点可以通过Rust语言圣经了解:const泛型

下列给出&str -> [i8;N]的转换函数:

/// 将给定的字符串转换为定长的cstr
/// 注意:如果长度超过将被截断,不足部分用`�`补足
pub fn str_to_cstr<const N: usize>(text: &str) -> [i8; N] {
    if N < 1 {
        panic!("数组长度必须大于0!")
    }
    let buffer = &mut [0u8; N];
    for (place, data) in buffer[..N - 1].iter_mut().zip(text.as_bytes().iter()) {
        *place = *data;
    }
    let d: Vec<i8> = (*buffer).iter().map(|x| *x as i8).collect();

    let mut arr: [i8; N] = [0i8; N];
    arr.copy_from_slice(&d);
    arr
}

Example:

// "password" -> [i8;41]
let cs = to_cstr::<41>("password");
println!("{:?}",cs);

其中 to_cstr::<41>尖括号中的数字代表转换后的数组长度

ctp返回数据处理

rust默认采用的是utf-8字符,特别对于非windows平台一般不使用gbk相关的字符集。

但是,ctp返回的是gbk字符集,如果不进行处理,将出现乱码。下列提供rust版本的gbk->utf-8

use encoding::{all::GB18030, DecoderTrap, Encoding};

/// ctp发送过来的中文信息是gbk格式的,这里需要转换为utf-8格式
/// rust的字符串是存储为`[u8]`格式,但是ctp包装后,接口过来的数据是 `[i8]`格式,因此
/// 在数据转换之前需先将 `[u8]`转为`[i8]`
/// 如果数据转换错误,将直接返回 `""` 空字符串`String`
pub fn gbk_cstr_to_str(v: &[i8]) -> String {
    let vi: Vec<u8> = v.iter().map(|x| *x as u8).collect();
    let slice = vi.split(|c| *c == 0u8).next().unwrap();
    if slice.is_ascii() {
        unsafe {
            return std::str::from_utf8_unchecked(slice).to_owned();
        };
    }
    match GB18030.decode(slice, DecoderTrap::Replace) {
        Ok(s) => s,
        Err(e) => {
            log::error!("GBK转UTF-8错误:{}", e.to_string());
            return "".to_owned();
        }
    }
}

调用方法前须先引入依赖包

[dependencies]
encoding = "0.2"
  • 分享:
评论

    • 博主

    说点什么