RUST 调用 C++的 lib 请教

45 天前
 hkhk366

新人学习 rust ffi ,实在搞不定,特来请教一下 下面段代码主要是实现一个简单的字符串然后通过 FFI 调用 hyperscan (这是一个 C++写的库,我通过 lib 调用,完全静态编译)的正则表达式同时匹配多个 pattern ,然后打印每一个 pattern 出现的第一个位置即可,如果没出现打印-1 。 但是这个代码我怎么改都是-1 或者 0 ,就是不能有正确结果,我问了多个 AI ,但是都始终无法解决这个问题,所以想向大神请教一下,非常感谢。

运行结果如下:

Hyperscan 版本: 5.4.2 2024-10-06
模式 "test" 未出现,位置: -1
模式 "string" 未出现,位置: -1
模式 "example" 未出现,位置: -1
模式 "中文" 未出现,位置: -1

完整代码如下:

use std::ffi::{CStr, CString};
use std::os::raw::{c_int, c_uint, c_void};
use std::ptr;

const HS_MODE_BLOCK: c_uint = 1;
const HS_FLAG_LITERAL: c_uint = 1 << 10; // 添加 HS_FLAG_LITERAL 常量

#[link(name = "hs")]
extern "C" {
    fn hs_version() -> *const i8;

    pub fn hs_compile_multi(
        expressions: *const *const i8,
        flags: *const c_uint,
        ids: *const c_uint,
        elements: c_uint,
        mode: c_uint,
        platform: *const c_void,
        db: *mut *mut hs_database_t,
        compile_err: *mut *mut hs_compile_error_t,
    ) -> c_int;

    pub fn hs_alloc_scratch(
        db: *const hs_database_t,
        scratch: *mut *mut hs_scratch_t,
    ) -> c_int;

    pub fn hs_free_scratch(
        scratch: *mut hs_scratch_t,
    ) -> c_int;

    pub fn hs_scan(
        db: *const hs_database_t,
        data: *const i8,
        length: c_uint,
        flags: c_uint,
        scratch: *mut hs_scratch_t,
        match_event_handler: Option<
            extern "C" fn(
                id: c_uint,
                from: u64,
                to: u64,
                flags: c_uint,
                context: *mut c_void,
            ) -> c_int,
        >,
        context: *mut c_void,
    ) -> c_int;

    pub fn hs_free_database(db: *mut hs_database_t) -> c_int;

    pub fn hs_free_compile_error(error: *mut hs_compile_error_t);
}

pub enum hs_database_t {}
pub enum hs_scratch_t {}

#[repr(C)]
pub struct hs_compile_error_t {
    pub message: *const i8,
    pub expression: c_int,
}

const HS_SUCCESS: c_int = 0;

extern "C" fn event_handler(
    id: c_uint,
    from: u64,
    _to: u64,
    _flags: c_uint,
    context: *mut c_void,
) -> c_int {
    unsafe {
        let positions = context as *mut u64;
        let pos_ptr = positions.add(id as usize);
        if *pos_ptr == u64::MAX {
            *pos_ptr = from;
        }
    }
    0
}

fn main() {
    unsafe {
        // 获取并打印 Hyperscan 版本
        let version = hs_version();
        let c_str = CStr::from_ptr(version);
        let str_slice = c_str.to_str().unwrap();
        println!("Hyperscan 版本: {}", str_slice);

        // 定义要匹配的模式列表
        let patterns = vec!["test", "string", "example", "中文"];

        // 将模式转换为 CString
        let c_patterns: Vec<CString> = patterns
            .iter()
            .map(|s| CString::new(*s).unwrap())
            .collect();

        // 创建 expressions 、flags 、ids 数组
        let expressions: Vec<*const i8> = c_patterns.iter().map(|s| s.as_ptr()).collect();

        // 使用 HS_FLAG_LITERAL 标志
        let flags: Vec<c_uint> = vec![HS_FLAG_LITERAL; patterns.len()];

        let ids: Vec<c_uint> = (0..patterns.len() as c_uint).collect();

        // 编译模式
        let mut db: *mut hs_database_t = ptr::null_mut();
        let mut compile_err: *mut hs_compile_error_t = ptr::null_mut();

        let compile_result = hs_compile_multi(
            expressions.as_ptr(),
            flags.as_ptr(),
            ids.as_ptr(),
            patterns.len() as c_uint,
            HS_MODE_BLOCK,
            ptr::null(),
            &mut db,
            &mut compile_err,
        );

        if compile_result != HS_SUCCESS {
            if !compile_err.is_null() {
                let err = &*compile_err;
                let message = CStr::from_ptr(err.message).to_string_lossy();
                println!("编译错误: {}", message);
                hs_free_compile_error(compile_err);
            } else {
                println!("未知的编译错误");
            }
            return;
        }

        // 分配 scratch 空间
        let mut scratch: *mut hs_scratch_t = ptr::null_mut();
        let alloc_result = hs_alloc_scratch(db, &mut scratch);
        if alloc_result != HS_SUCCESS {
            println!("hs_alloc_scratch 失败");
            hs_free_database(db);
            return;
        }

        // 定义输入字符串
        let input = "This is a test string for example purposes 中文测试.";

        // 初始化匹配位置数组
        let mut match_positions: Vec<u64> = vec![u64::MAX; patterns.len()];

        // 执行扫描
        let scan_result = hs_scan(
            db,
            input.as_ptr() as *const i8,
            input.len() as c_uint,
            0,
            scratch,
            Some(event_handler),
            match_positions.as_mut_ptr() as *mut c_void,
        );

        if scan_result != HS_SUCCESS {
            println!("hs_scan 失败,错误代码: {}", scan_result);
            hs_free_scratch(scratch);
            hs_free_database(db);
            return;
        }

        // 输出结果
        for (i, pattern) in patterns.iter().enumerate() {
            let pos = match_positions[i];
            if pos != u64::MAX {
                println!("模式 \"{}\" 首次出现位置: {}", pattern, pos);
            } else {
                println!("模式 \"{}\" 未出现,位置: -1", pattern);
            }
        }

        // 释放资源
        hs_free_scratch(scratch);
        hs_free_database(db);
    }
}

1115 次点击
所在节点    Rust
5 条回复
PTLin
45 天前
调用 cpp 的库用 https://cxx.rs/ ,cpp 的 api 和 c 不一样,用 extern "C"未必好使。
gwy15
45 天前
const HS_FLAG_LITERAL: c_uint = 1 << 10; // 添加 HS_FLAG_LITERAL 常量

这一行的问题,换成其他的可以正常匹配
gwy15
45 天前
* Compile flag: Don't do any match reporting.
*
* This flag instructs Hyperscan to ignore match reporting for this expression.
* It is designed to be used on the sub-expressions in logical combinations.
*/
#define HS_FLAG_QUIET 1024

你给的 flag 会导致 onEvent 不被调用,自然也就不会写入结果
hkhk366
44 天前
@gwy15 谢谢回复,但是我把 HS_FLAG_LITERAL 改成了 0 或者其他值后,输出结果是下面,还是不对,头疼
Hyperscan 版本: 5.4.2 2024-10-06
模式 "test" 首次出现位置: 0
模式 "string" 首次出现位置: 0
模式 "example" 首次出现位置: 0
模式 "中文" 首次出现位置: 0
gwy15
44 天前
@hkhk366 你自己再研究下你的 flag ,应该是你的 event handler 没写对。你在 event handler 里面打印 to 参数是能发现是正确传递了的。这个库我也没用过,你仔细读文档吧

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1078028

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX