[原]深析DELPHI in 操作实现

Reverse.King Reverse.King 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区
群友探讨到这个问题顺便有空分析了下delphi编译器的实现方式,有错误地方敬请指出。
function AnsiStrInCharSet(ch: char; CharSet: TSysCharSet): boolean;
begin
Result := ch in CharSet;
end;
测试调用:if AnsiStrInCharSet('a', ['a'..'z']) then ...

实际编译器翻译为以下代码:
0045BF7A |. F3:A5 rep movs dword ptr es:[edi], dword ptr [esi] ;
复制ESI( ['a'..'z']) 到BT指令标志判断dest操作数,也就是CharSet: TSysCharSet 的内容由编译器固定编译为一个位信息常量,该常量长度为32byte,定义结构为:

TBitInfoField =array [0..7] of dword;

暂称该常量为BitInfoField:TBitInfoField 具体规则后面讲述。

0045BF7C |. 0FB6C0 movzx eax, al ;
赋值到BT指令标志判断src操作数,也就是ch: char

0045BF7F |. 0FA345 E0 bt dword ptr [ebp-20], eax ;
[ebp-20] 实际指向BitInfoField,eax是位数,作用就是把BitInfoField的第eax位送CF标志

比如['a'..'z']编译器编译的BitInfoField为:
0012F588 00 00 00 00 00 00 00 00 00 00 00 00 FE FF FF 07 ............?
0012F598 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

[#0..#255]编译器编译的BitInfoField为:
0012F588 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0012F598 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 

为什么编译器会这么编译呢?因为bt指令是一个标志测试指令具体语法格式为:
BT r/m16,r16
将所选的位存储到 CF 标志

BT r/m32,r32
将所选的位存储到 CF 标志

BT r/m16,imm8
将所选的位存储到 CF 标志

BT r/m32,imm8
将所选的位存储到 CF 标志
这里有两个公式确定位值:

1. BIdx= BitOffset div 32

2. BBit= BitOffset mod 32




我们这里的in编译器使用的是 BT m32,r32格式,它的作用就是把m32内存中BitInfoField[BIdx(r32)]常量的数据的第BitInfoField[BBit(r32)]位送CF标志.
假设枚举范围是['a'..'z'] 则BitInfoField为:

00 00 00 00 00 00 00 00 00 00 00 00 FE FF FF 07
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00



'a' in ['a'..'z'] 最后转换成的CPU指令应该是:

bt dword ptr [BitInfoField] , eax ; eax= $61



因为 'a'的Ascll码为:$61 根据公式: BIdx = $61 div 32 = 3 , BBit= BitOffset mod 32 =1

BitInfoField[BIdx=3 ] = $7FFFFFE 转换为二进制为:00000111111111111111111111111110

BBit = 1

00000111111111111111111111111110

31-------------------------------0

那么 bt dword ptr [BitInfoField] , $61 就是把下面标志传送到CF标志寄存器


000001111111111111111111111111[1]0

31-------------------------------0

所以CF =1


这样就完成了'a' in ['a'..'z'] 的判断。

根据此原理可以推导出 delphi的 char in ['a'..'z'] 和 char in ['a'..'z','1'..'9'] 执行效率是一样的

因为['a'..'z','1'..'9'] 的BitInfoField为:

00 00 00 00 00 00 FE 03 00 00 00 00 FE FF FF 07

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00



当判断任意字符 bt dword ptr [BitInfoField] , BitOffset

通过公式:


1. BIdx= BitOffset div 32

2. BBit= BitOffset mod 32


计算出BIdx 和BBit 只需把相应标志位传送到CF即完成。

总结DELPHI in 操作效率比pos操作效率要高很多,并且效率不被枚举元素个数和多少影响,缺点是只能判断0-255范围的数据。
...全文

相关推荐

评论 86
收藏 已收藏
分享
返回顶部