如何通过逻辑运算高效检测通用寄存器中的零字节
通用寄存器零字节高效检测方法
针对存储多字节ASCII序列的通用寄存器,无需逐位移位、逐字节遍历,仅用3次基础位运算即可完成零字节存在性判定,所有操作均为单CPU周期级别,性能远高于逐字节检查逻辑。
核心实现逻辑
整个检测流程无分支、无循环,步骤如下:
- 根据寄存器位宽准备两个固定魔数:
- 减法魔数:每个字节均为
0x01,即n位寄存器对应值为0x0101...01(总长度为n/8个0x01) - 掩码魔数:每个字节最高位为1、其余位为0,即n位寄存器对应值为
0x8080...80(总长度为n/8个0x80)
- 减法魔数:每个字节均为
- 对寄存器存储的目标值
x做运算:(x - 减法魔数) & (~x) & 掩码魔数 - 判定运算结果:
- 结果为
0:不存在零字节 - 结果非
0:存在零字节
- 结果为
不同常见位宽寄存器对应的魔数参考:
- 16位寄存器:减法魔数
0x0101,掩码魔数0x8080 - 32位寄存器(如eax):减法魔数
0x01010101,掩码魔数0x80808080 - 64位寄存器(如rax):减法魔数
0x0101010101010101,掩码魔数0x8080808080808080
效果验证
用题目给出的两个32位场景示例验证:
示例1:待检测值为
0x7172706b,无零字节
- 计算
x - 0x01010101 = 0x7172706b - 0x01010101 = 0x70716f6a - 计算
~x = ~0x7172706b = 0x8e8d8f94 - 做与运算:
0x70716f6a & 0x8e8d8f94 & 0x80808080 = 0,结果为0,判定无零字节,符合预期。
示例2:待检测值为
0x7172006b,存在零字节
- 计算
x - 0x01010101 = 0x7172006b - 0x01010101 = 0x7171ff6a - 计算
~x = ~0x7172006b = 0x8e8dff94 - 做与运算:
0x7171ff6a & 0x8e8dff94 & 0x80808080 = 0x00008000,结果非0,判定存在零字节,符合预期。
原理说明
对任意单字节值:
- 如果字节值在
0x01~0xFF(非零),减1操作产生的借位不会传递到该字节的最高位(即0x80对应的比特位),和~x、掩码魔数做与运算后,该字节最高位结果一定为0 - 如果字节值为
0x00,减1操作会产生连续借位,最终将该字节的最高位置为1,和~x、掩码魔数做与运算后,该字节最高位结果为1
最终只要提取到任意一个字节的最高位为1,就说明对应位置原本是零字节。
汇编实现参考(64位场景)
; 入参:rax存储待检测的ASCII字节序列 ; 出参:ZF标志位为1表示无零字节,ZF为0表示存在零字节 mov rbx, rax mov rcx, 0x0101010101010101 mov rdx, 0x8080808080808080 sub rbx, rcx not rax test rbx, rax test rbx, rdx ; 后续可直接通过jz/jnz指令跳转处理对应逻辑
内容的提问来源于stack exchange,提问作者Ben Heckmann
相关产品推荐
相关产品推荐

