hyperscan 作为和intel深度绑定的政策匹配工具,提供了及其强大的正则匹配功能,并提供接口,方便对匹配结果进行二次操作。
我在学习hyperscan时,在各大论坛并没有找到比较完备并且详细讲解,很多只是提到了函数库大概时干什么的,索性将自己了解到的东西,详细的记录下来,也算是自己对hyperscan的一次巩固
hyperscan 是英特尔开源的一款正则匹配工具,与intel芯片深度绑定,intel对其有专门的优化,因此在整体的匹配效率上,相较于其他正则方案,处于一个较为领先的位置。 hyperscan支持pcre 正则规则,现有的主要应用方案大都是基于pcre做的匹配。
样例代码放在了github上,后面会贴出来。
注: 本文演示环境为 ubuntu 22.04
hyperscan下载安装
- 下载
intel在github上开放了源码,通过git即可下载,由于github,在国内访问不友好,在gitee上也有相关开源代码
github
git clone https://github.com/intel/hyperscan.git
gitee
git clone https://gitee.com/tzichengchew/hyperscan.git
- 依赖
hyperscan 使用cmake编译cmake等基础库安装不再赘述, 需要依赖 - boost
Boost Downloads
git clone https://github.com/adrian-thurston/ragel.git
* colm
git clone https://github.com/adrian-thurston/colm.git
其中ragel的安装需要clom支持在官网均能找到下载地址。或者git地址
- 安装
安装boost
bash bootstrap.sh && ./b2 && ./b2 install
安装ragel
不建议编译安装,坑很多
sudo apt install ragel
编译安装hyperscan
创建一个目录
mkdir hyperscan_comp
执行编译命令
cmake $HYPERSCAN_PATH
make && make install
需要蛮久的。
遇到一个非常棘手的问题:
undefined reference to `avx2_memset'
undefined reference to `corei7_memset'
…………
https://github.com/intel/hyperscan/issues/292#issuecomment-762635447
亲测有效。
我在另外一台设备上测试发现并没有报错,估计可能是虚拟机cpu配置问题,前面提到了,hyperscan与intel深度绑定。
修改过cmake/build_wrapper.sh脚本后,就可以执行cmake命令了
为防止产生的cmake文件搞混了源文件,这里建议建立一个新的目录,执行cmake
需要注意的是,在使用hyperscan时,动态库要比静态库使用相对方便,修改CMakeCache.txt文件,根据版本不同,所在行数也不同。
BUILD_STATIC_AND_SHARED:BOOL=OFF
->
BUILD_STATIC_AND_SHARED:BOOL=ON
修改过后即可执行make && make install
hyperscan 简单使用
hyperscan整体的使用流程主要分为编译器和运行期
编译期
编译器主要是将编辑好的正则,编译成数据库供后续使用,
主要包括三个函数,分别适用不同的使用场景
- hs_compile() 将单个规则编译成数据库。
- hs_compile_multi() 将一组规则编译成数据库。
- hs_compile_ext_multi() 将一组规则编译成数据库,并支持扩展参数。
具体细节可以参考《深入浅出 Hyperscan: 高性能正则表达式算法原理与设计》
本例设计使用hs_compile_multi() 应该是最常用的函数,普适性较高
定义宏,方便后续使用
enum { HYPERSCAN_ID_NONE = 0, HYPERSCAN_ID_TEST1, HYPERSCAN_ID_TEST2, HYPERSCAN_ID_MAX };
定义数据结构
/** * 将数据库中待编译的信息保存到这个数组里面 */ typedef struct CompilePatternArray { uint32_t ids[20]; /**< id */ uint32_t flag[20]; /**< 编译参数 */ const char *exps[20]; /**< 正则 */ } CompilePatternArray_t; /** * 编译数据结构 */ typedef struct CompilePattern { uint32_t ids; const char *exps; } CompilePattern_t;
使用数组定义一组正则规则
/** * 编译到数据库中的匹配字段, * hyperscan编译阶段有一个特殊字符转义的过程 */ static const CompilePattern_t gCompilePattern[] = { {HYPERSCAN_ID_NONE, " "}, {HYPERSCAN_ID_TEST1, "123.{1,3}abc"}, {HYPERSCAN_ID_TEST2, "max"} };
编译
/** * * @param CompPatt * 待编译信息 * @param count * 数据条数 * @param pDatabase * 数据库 * @return * 返回值,暂时没定义 */ static int CompilePatterns(CompilePattern_t *CompPatt, uint32_t count, hs_database_t **pDatabase) { hs_error_t error; hs_compile_error_t *ComError; CompilePatternArray_t compilePattern; // 一对一存储,转存 int i; for (i = 0; i < count; i++) { compilePattern.ids[i] = CompPatt[i].ids; compilePattern.exps[i] = CompPatt[i].exps; compilePattern.flag[i] = HS_FLAG_CASELESS; // 忽略大小写 } // 编译 /** * 参数说明 * 1. 正则 * 2. 正则匹配标签 * 3. id * 4. 数量 * 5. 匹配模式 * 6. platform 需要深入去了解一下,目前置NULL * 7. 数据库指针 * 8. errorInfo */ error = hs_compile_multi(compilePattern.exps, compilePattern.flag, compilePattern.ids, count, HS_MODE_BLOCK, NULL, pDatabase, (hs_compile_error_t **) &ComError); if (error != HS_SUCCESS) { // 失败的话给出相应的提示信息 std::cout << "compile failed error code is " << error << std::endl; } return 0; }
运行期
hypersacn 运行过程中,进行正则匹配会有一个中间状态暂存匹配信息,需要创建一个scratch空间
hs_alloc_scratch(HsDatabase, (hs_scratch_t **) &HsStra);
运行期分为块模式,流模式,向量模式,块模式可以简单的理解为就一次数据输入,输出结果,流模式主要针对网络字节流,但相较于块模式,增加了流开启与关闭的过程,在编译期需要编译的内容也有所区别,本例中主要是块模式样例,流模式,有兴趣可以自行研究。
使用
hs_scan()进行匹配,调用时需要规定回调函数
/** 参数说明
* 1. 数据库
* 2. 待匹配字符串
* 3. 字符串长度
* 4. flag, 未定义,官方说明待用
* 5. scratch
* 6. 回调函数
* 7. 回调函数传参
*/
hs_scan(HsDatabase, tmpString, strlen(tmpString), 0, HsStra, onMatch, tmpString);
回调函数
/**
* 回调函数
* @param id
* 匹配字段的 id
* @param from
* 从哪里还是匹配的
* @param to
* 匹配到哪里结束(指向尾部)
* @param flags
* 根本没用, hyperscan 官方明确说明没有使用的参数。
* @param ctx
* 回调参数
* @return
* 暂时没用
*/
static int onMatch(unsigned int id, unsigned long long from, unsigned long long to, unsigned int flags, void *ctx) {
char *tmp = (char *) ctx;
std::cout << "id is " << id << std::endl;
switch (id) {
case HYPERSCAN_ID_NONE:
std::cout << std::endl;
break;
case HYPERSCAN_ID_TEST1:
std::cout << "form:" << tmp + from << std::endl;
std::cout << "to:" << tmp + to << std::endl;
std::cout << std::endl;
break;
case HYPERSCAN_ID_TEST2:
std::cout << "form:" << tmp + from << std::endl;
std::cout << "to:" << tmp + to << std::endl;
std::cout << std::endl;
break;
default:
std::cout << "can not found any subString" << std::endl;
break;
}
return 0;
}
传入字符串
abc123123123abcmax123123bacbacabc\0
执行效果
后记
hyperscan作为一个高性能正则匹配工具,应用场景很多,王翔先生等用几百页文字详细介绍了hyperscan基本原理,算法过程与高级特性,但真正使用还需要真正去实践才能真正理解,并且用好这个工具
完整代码放在了gitee上,欢迎大家补充,指正。