鱼C论坛

 找回密码
 立即注册
查看: 1716|回复: 0

[技术交流] 【老刘谈算法002】反向快速比较——instring函数的汇编实现分析

[复制链接]
发表于 2019-2-24 09:39:53 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
挂下代码先,
  1. ;注释修改&中文注释添加 by 老刘

  2. ; #########################################################################

  3. ;       这个算法的子循环代码被EKO重新设计来反向执行比较,
  4. ;       这种比较方法通过分支比较减少了需要执行的指令数。

  5. ; #########################################################################

  6.     .486
  7.     .model flat, stdcall  ; 32 bit memory model
  8.     option casemap :none  ; case sensitive

  9.     StrLen PROTO :DWORD

  10.     .code

  11. ; ########################################################################

  12. InString proc startpos:DWORD,lpSource:DWORD,lpPattern:DWORD

  13.   ; ------------------------------------------------------------------
  14.   ; InString函数在一个更大的“源字符串”中寻找“匹配字符串”,
  15.   ; 如果找到了的话,“匹配字符串”在“源字符串”的位置会被放在eax中。
  16.   ;
  17.   ; "StartPos"即“起始位”参数和返回值均使用1来作为首个字符的索引。
  18.   ; (第一个字符是1,第二个字符是2,等等……)
  19.   ;
  20.   ;
  21.   ; 返回值:
  22.   ; 如果函数执行成功,它将会返回“源字符串”的位置。
  23.   ;  0 = 没找着
  24.   ; -1 = “匹配字符串”的长度不短于“源字符串”
  25.   ; -2 = "StartPos"参数越界。
  26.   ; ------------------------------------------------------------------

  27.     LOCAL sLen:DWORD
  28.     LOCAL pLen:DWORD

  29.     push ebx
  30.     push esi
  31.     push edi

  32.     invoke StrLen,lpSource
  33.     mov sLen, eax           ;源字符串长度
  34.     invoke StrLen,lpPattern
  35.     mov pLen, eax           ;匹配字符串长度

  36.     cmp startpos, 1
  37.     jge @F
  38.     mov eax, -2
  39.     jmp isOut               ;如果“起始位”参数为0则退出。
  40.   @@:

  41.     dec startpos            ;改成0为第一个字符的索引。

  42.     cmp  eax, sLen                        ;这里的eax依旧=pLen
  43.     jl @F
  44.     mov eax, -1
  45.     jmp isOut               ;匹配字符串不短于源字符串,退出。
  46.   @@:

  47.     sub sLen, eax           ;sLen-=pLen源字符串去尾
  48.     inc sLen

  49.     mov ecx, sLen
  50.     cmp ecx, startpos
  51.     jg @F
  52.     mov eax, -2
  53.     jmp isOut               ;不可能匹配上,退出
  54.   @@:

  55.   ; ----------------
  56.   ; setup loop code
  57.   ; ----------------
  58.     mov esi, lpSource
  59.     mov edi, lpPattern
  60.     mov al, [edi]           ;al=匹配字符串中的第一个字符

  61.     add esi, ecx            ;源字符串地址加上源字符串长(使esi指向源字符串末尾,以便倒序比较)
  62.     neg ecx                 ;ecx=-ecx
  63.     add ecx, startpos       ;加上起始位参数

  64.     jmp Scan_Loop

  65.     align 16                ;使下条指令的地址从0x10倍数处开始,提升速度

  66.   ; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

  67.   Pre_Scan:
  68.     inc ecx

  69.   Scan_Loop:
  70.     cmp al, [esi+ecx]       ;判断esi+ecx指向的第一个字符是否=匹配字符串中的第一个字符
  71.     je Pre_Match            ;如果匹配,则跳到Pre_Match继续判断
  72.     inc ecx
  73.     js Scan_Loop            ;ecx为负时继续循环

  74.     jmp No_Match

  75.   Pre_Match:
  76.     lea ebx, [esi+ecx]      ;ebx=第一个匹配的字符的地址
  77.     mov edx, pLen

  78.   Test_Match:
  79.     mov ah, [ebx+edx-1]     ;ah=ebx+edx-1指向的字符
  80.     cmp ah, [edi+edx-1]     ;与匹配字符串中对应的字符比较
  81.     jne Pre_Scan            ;不匹配,跳到Pre_Scan
  82.     dec edx
  83.     jnz Test_Match          ;edx非0时继续倒序循环判断

  84.   ; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

  85.   Match:
  86.     add ecx, sLen
  87.     mov eax, ecx
  88.     inc eax
  89.     jmp isOut
  90.    
  91.   No_Match:
  92.     xor eax, eax

  93.   isOut:
  94.     pop edi
  95.     pop esi
  96.     pop ebx

  97.     ret

  98. InString endp

  99. ; ########################################################################

  100.     end
复制代码

那么什么条件下可以直接判断不可能匹配呢?
我们设
a=startpos
b=sLen
c=pLen
令d=b-(a-1)
则d即为实际需要进行查找的字符串的长度,
那么当被查找的字符串长小于需要查找的字符串长度的时候,即
当(a-1)+c>b的时候,
显然不可能匹配上,
作者可谓考虑的十分周到了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-1 12:45

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表