
 ; ------------------------------------------------------------------
 ;
 ; Function InPosBMHR_T(sStr As String, sFind As String, _
 ;                  Optional ByVal lStartPos As Long = 1, _
 ;                 Optional ByVal lRightLimit As Long = 0) As Long
 ;
 ; InPosBMHR_T searches for a substring in another string in text
 ; compare mode, and if found returns the position of the first match
 ; as the function result.
 ;
 ; It uses a one based character index (1st character is 1, 2nd is 2 etc)
 ; for both the lStartPos parameter and the returned character position.
 ;
 ; If the optional start position is less than one it defaults to one.
 ;
 ; The optional right limit specifies the one based position in the
 ; string of the last possible pattern match. If omitted/less than one
 ; the string is searched from start pos to string end. This parameter
 ; specifies the limit of matching the last search pattern character.
 ;
 ; If the right limit parameter is beyond the length of the string
 ; the string is searched from start pos to string end.
 ; ------------------------------------------------------------------
 ; If the function succeeds, it returns the one based character index
 ; of the start of the first substring match, or:
 ;  0 = no match found
 ;  0 = substring longer than main string
 ;  0 = startpos out of range (startpos > len str - len substr)
 ;  0 = rightlimit out of range (0 < rightlimit < len substr)
 ; ------------------------------------------------------------------
 ; InPosBMHR_T uses a Boyer-Moore-Horspool-Raita algorithm to optimize
 ; the search operation, and requires a shift table initialized with
 ; 65536 16-bit integers, and populated with character offsets of the
 ; current pattern (note this table is different than for InPosBMHR).
 ;
 ; This version of the BMHR algorithm has self-updating of the shift
 ; table character offsets performed only when the pattern changes.
 ; ------------------------------------------------------------------
 ;  Private iaTableT() As Integer
 ;  Private lpTableT As Long
 ;  ReDim iaTableT(0 To 65535) As Integer ' Class Init before patch
 ;  lpTableT = SafeArrayPtr(iaTableT)
 ;  Private sPrevPatT As String ' Don't modify
 ;  Private lpPrevPatT As Long
 ;  lpPrevPatT = VarPtr(sPrevPatT)
 ; ------------------------------------------------------------------
 ; This procedure requires a module level integer array to be
 ; initialized by calling the UnsBuildTextCompTable sub-routine.
 ; ------------------------------------------------------------------
 ;  Private lpTxtCmp As Long
 ;  lpTxtCmp = UnsBuildTextCompTable ' Class Init before patch
 ; ------------------------------------------------------------------
 ; These unicode BMHR algorithms use 5 tables containing 65536 integers,
 ; one each for InPosBMHR, InPosBMHR_R, InPosBMHR_T, InPosBMHR_RT, as
 ; well as the Text Compare table for a total of 640 KB (655,360 bytes).
 ; ------------------------------------------------------------------

 .486             ; 32-bit instruction set
 .model flat      ; Protected memory, 4GB limit
 .code            ; Start of code

InPosBMHR_T proc near ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

   string equ [esp+40]
   find   equ [esp+44]
   start  equ [esp+48] ; friendly param names
   limit  equ [esp+52]
   return equ [esp+56]

   pvData equ [ecx+12] ; safearray pointers
   cElems equ [ecx+16]

   base   equ [esp]
   ccPat  equ [esp+4]  ; set up stack variables
   lptabl equ [esp+8]
   lpfind equ [esp+12]

   push ebp
   push esi           ; preserve registers
   push edi
   push ebx
   sub esp, 16        ; offset stack variables

   mov ecx, 5876543h  ; tableT SA patched on init
   test ecx, ecx      ; test shift table pointer
   jz _abort          ; exit if null table pointer
   mov edx, cElems    ; get count of table elements
   mov ebx, pvData    ; get first shift table addr

   cmp edx, 10000h    ; check if count is valid
   jne _abort         ; exit if not correctly init
   mov lptabl, ebx    ; save shift table base addr

   mov ecx, 2876543h  ; txtcmp SA patched on init
   test ecx, ecx      ; test header for zero ptr
   jz _abort          ; exit if null SA pointer
   mov edx, cElems    ; get cnt of lookup elements
   mov ebx, pvData    ; get first lookup address

   cmp edx, 10000h    ; check if count is valid
   jne _abort         ; exit if not correctly init

   mov ecx, find      ; copy pattern descriptor
   mov edi, [ecx]     ; extract pattern pointer
   test edi, edi      ; test pattern string pointer
   jz _abort          ; exit if null string pointer

   mov ebp,[edi-4]    ; extract byte length of find
   test ebp, ebp      ; test pattern string length
   jz _abort          ; exit if empty pattern passed

   mov ecx,16876543h  ; prev pat addr patched on init
   mov esi, [ecx]     ; dereference sPrevPatT pointer
   test esi, esi      ; test previous pattern pointer
   jz _alloc          ; allocate if null str pointer

   mov eax,[esi-4]    ; retrieve previous byte count
   cmp ebp, eax       ; cmp prev pattern byte count
   jne _alloc         ; different length new pattern

  _cmp:
   sub eax, 2
   js _search         ; skip update same pattern
   mov dx,[esi+eax]   ; get previous pattern char
   cmp dx,[edi+eax]   ; compare with pattern char
   je _cmp

  _alloc:             ; patterns are different
   mov eax, ebp       ; assign pattern byte count
   mov edx,7876543h   ; api address patched on init
   shr eax, 1         ; convert to character count
   push eax           ; SysReAllocStringLen cclen
   push edi           ; pointer to string to copy
   push ecx           ; string descriptor to update
   call edx           ; call SysReAllocStringLen

   mov eax, ebp       ; init to new pattern length
   mov esi, lptabl    ; retrieve shift table addr
   mov ecx, 0ffffh    ; i = 65535
  _init:              ; Do
   mov [esi+ecx*2],ax ;   aShiftTableT(i) = PatLen
   dec ecx            ;   i = i - 1
   jns _init          ; Loop Until i = -1

   xor ecx, ecx       ; i = 0
   sub eax, 2         ; j = PatLen - 1
   jz _search         ; pattern len = 1
   mov edx, ecx       ; clear char buffer/idx

  _update:            ; assign current pattern offsets
   mov dx,[edi+ecx*2] ; Do While j > 0
   mov dx,[ebx+edx*2] ;   iChr = iaTxtCmp(saPattern(i))
   mov [esi+edx*2],ax ;   aShiftTableT(iChr) = j
   inc ecx            ;   i = i + 1
   sub eax, 2         ;   j = j - 1
   jnz _update        ; Loop

  _search:
   mov ecx,  ebp      ; retrieve find byte count
   mov esi, string    ; copy string descriptor
   neg ebp            ; fix for [ptr-idx] error
   mov esi, [esi]     ; extract string pointer
   mov ccPat,ebp      ; store negated pat count
   test esi, esi      ; validate string pointer
   jz _abort          ; exit null string pointer

   mov ebp,[esi-4]    ; extract byte length of str
   mov edx, limit     ; copy right limit parameter
   test ebp, ebp      ; test string for zero length
   jz _abort          ; exit if empty string passed

   shl edx, 1         ; convert opt param to bytes
   jbe _start         ; opt param less than one (jcz)
   cmp edx, ebp       ; validate optional limit param
   jae _start         ; limit range to string length
   mov ebp, edx       ; set string search right limit
  _start:             ; default search entire string

   mov eax, start     ; copy start position parameter
   xor edx, edx       ; clear wchar buffer/table idx
   mov base, esi      ; cache string start address
   dec eax            ; convert start to zero base
   jns _over          ; opt start greater than zero
   mov eax, edx       ; default to start first wchar

  _over:
   sub ecx, 2         ; convert byte len to zero base
   shl eax, 1         ; convert search start to bytes
   sub ebp, 2         ; convert input len to zero base

   add eax, ecx       ; offset start to end of pattern
   add ebp, esi       ; calc input string end address
   add edi, ecx       ; set pointer to end of pattern
   add esi, eax       ; offset input string start addr

   mov eax, edx       ; clear wchar buffer/table idx
   mov dx, [edi]      ; prep with last pattern char
   mov lpfind, edi    ; preserve find pointer end
   mov dx,[ebx+edx*2] ; get lookup wchar from table
   mov edi, lptabl    ; extract shift table base

  _bmhr:              ; walk up str unicode chars
   cmp esi, ebp       ; test for end of input string
   ja _abort          ; reached search scope limit (jncz)
   mov ax, [esi]      ; extract current input wchar
   mov ax,[ebx+eax*2] ; cache lookup wchar from table
   cmp dx, ax         ; compare w/ last pattern wchar
   je _compare        ; continue search pattern chars

  _shift:             ; esi += ShiftTable([esi])
   mov ax,[edi+2*eax] ; extract occurrence offset
   add esi, eax       ; shift esi to next offset
   jmp _bmhr          ; continue BMHR search

  _compare:
   mov ecx, ccPat     ; reset negated pattern length
  _nextchr:           ; test the rest of find in str
   add ecx, 2         ; offset to next aligned chars
   jz _match          ; reached the end of pattern
   mov edi, lpfind    ; retrieve find pointer base
   mov dx,[edi+ecx]   ; assign next find wchar
   mov ax,[esi+ecx]   ; get current string wchar
   mov dx,[ebx+edx*2] ; assign lookup wchar to dx
   cmp dx,[ebx+eax*2] ; compare next text characters
   je _nextchr        ; if match continue compare
   mov dx,[edi]       ; reset last pattern char
   mov ax,[esi]       ; reset current string char
   mov dx,[ebx+edx*2] ; get lookup wchar from table
   mov ax,[ebx+eax*2] ; get lookup wchar from table
   mov edi, lptabl    ; reset shift table base ptr
   jmp _shift         ; shift current position

  _abort:
   xor eax, eax     ; zero return value
   jmp _exitp

  _match:
   sub esi, base    ; subtract start address
   add esi, ccPat   ; subtract pat len (negated)
   mov eax, esi     ; assign hit position to eax
   shr eax, 1       ; convert eax to char count
   add eax, 2       ; correct result to one base

  _exitp:
   mov ebx, return  ; get address of return value
   mov [ebx],eax    ; store the function result

   add esp, 16      ; reset stack variable offset
   pop ebx
   pop edi          ; restore registers
   pop esi
   pop ebp

   xor eax, eax  ; clear eax to tell VB all is okay
   ret 24        ; cleanup (lpO, p1, p2, p3, p4, rc)

 ; ------------------------------------------------------------------

InPosBMHR_T endp ; end of procedure
end InPosBMHR_T  ; end of code, indicate entry point

 ; ------------------------------------------------------------------
 ;
 ;      If sPattern = sPrevPat Then Skip
 ;      iPatLen = Len(sPattern)
 ;
 ;      ' Init to new pattern length
 ;      i = 65536
 ;      Do Until i = 0
 ;        i = i - 1
 ;        aShiftTableT(i) = iPatLen
 ;      Loop
 ;
 ;      ' Assign current pattern offsets
 ;      i = 1
 ;      Do While i < iPatLen
 ;        iChr = AscW(iaTxtCmp(Mid$(sPattern, i, 1)))
 ;        aShiftTableT(iChr) = iPatLen - i
 ;        i = i + 1
 ;      Loop
 ;
 ; ------------------------------------------------------------------
