
 ; ------------------------------------------------------------------
 ;
 ; Function InPos(sStr As String, sFind As String, _
 ;                Optional ByVal lStartPos As Long = 1, _
 ;                Optional ByVal lRightLimit As Long = 0) As Long
 ;
 ; InPos searches for a substring in a unicode string 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)
 ; ------------------------------------------------------------------

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

InPos proc near   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

   string equ [esp+24]
   find   equ [esp+28]
   start  equ [esp+32] ; friendly param names
   limit  equ [esp+36]
   return equ [esp+40]

   push ebp
   push esi          ; preserve registers
   push edi
   push ebx

   xor eax, eax      ; zero default return
   mov esi, string   ; copy str descriptor
   mov edi, find     ; copy find descriptor
   mov edx, limit    ; get right limit param
   mov esi,[esi]     ; extract str pointer
   mov edi,[edi]     ; extract find pointer

   test esi, esi     ; validate str pointer
   jz _exitp         ; exit null str pointer
   test edi, edi     ; validate find pointer
   jz _exitp         ; exit null str pointer

   mov ecx,[esi-4]   ; extract str byte length
   mov ebx,[edi-4]   ; extract find byte length

   test ecx, ecx     ; validate string length
   jz _exitp         ; exit empty string passed
   test ebx, ebx     ; validate pattern length
   jz _exitp         ; exit empty string passed

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

   mov edx, start    ; copy start position parameter
   sub ecx, ebx      ; calc last pos find can occur
   js _exitp         ; exit if str shorter than find

   dec edx           ; convert start pos to zero base
   jz _start         ; default start first character
   jns _tobytes      ; start pos greater than zero
   mov edx, eax      ; default first char 0 offset
   jmp _start        ; default start first character

  _tobytes:
   shl edx, 1        ; convert search start to bytes
   sub ecx, edx      ; is start pos within str len
   js _exitp         ; hit cannot occur after ecx

  _start:
   mov ebp, esi      ; cache string base address
   mov bx,[edi]      ; prep bx for 1st char search
   shr ecx, 1        ; convert counter to chars
   add esi, edx      ; set search start address
   inc ecx           ; offset cntr before start
   sub esi, 2        ; offset ptr before start

  _chr:              ; walk up str unicode chars
   add esi, 2        ; offset ptr to next wchar
   dec ecx           ; reduce counter by 1 wchar
   js _exitp         ; reached search scope limit
   cmp bx,[esi]      ; compare first find character
   jne _chr          ; continue search for 1st char

  _nextchr:          ; compare the rest of pattern
   add eax, 2        ; offset to next wchars
   mov bx,[edi+eax]  ; assign next find char
   test bx, bx       ; test for null terminator
   jz _match         ; reached the end of find
   cmp bx,[esi+eax]  ; compare next characters
   je _nextchr       ; if match continue compare
   mov bx,[edi]      ; prep bx 1st find char
   xor eax, eax      ; zero char offset index
   jmp _chr          ; continue search

  _match:
   sub esi, ebp      ; subtract string base addr
   mov eax, esi      ; assign hit position to eax
   shr eax, 1        ; convert result to char cnt
   inc eax           ; convert result to one base

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

   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)

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

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

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