Announcement

Collapse
No announcement yet.

Case insensitive INSTR

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Case insensitive INSTR

    Does anyone have a fast case insensitive INSTR function already?

    I found this which is a binary search. I'll look at modifying it if there isn't one out there already.

    Source code was posted here zip available here.
    Last edited by Larry Charlton; 4 May 2014, 05:06 PM. Reason: Added links to code and zip.
    LarryC
    Website
    Sometimes life's a dream, sometimes it's a scream

  • #2
    Regxpr

    Comment


    • #3
      p = instr(ucase$(mainstring), ucase$(matchstring)) should do it.

      or use lcase$ if you want.
      There are no atheists in a fox hole or the morning of a math test.
      If my flag offends you, I'll help you pack.

      Comment


      • #4
        Larry,
        Some replacement ASM code discussed in this thread on INSTR.

        Comment


        • #5
          Only tested slightly, but looking pretty good so far. See notes in functions.
          Code:
          #COMPILE EXE
          #DIM ALL
          
          FUNCTION PBMAIN () AS LONG
             LOCAL ii, ii2, ii3, htxt AS LONG, line1, line2 AS STRING
             LOCAL t, t2 AS QUAD
          
             TXT.WINDOW("Bakerware", 150, 100) TO hTxt
             OPEN "filename.dat" FOR BINARY LOCK WRITE AS #1
          
             line1 = REPEAT$(10000 ,"QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./qwertyuiop[]asdfghjkl;'zxcvbnm,./1234567890-=") & "HeLLo THErE!"
             line2 = "Hello There!"
          
             TIX t
             ii = INSTR(UCASE$(line1), UCASE$(line2))
             TIX END t
             TXT.PRINT "PB UCASE$ tix","","", t; "pos found"; ii
          
             TIX t
             ii = caseFreeInstr(line1, line2, 0, 0)
             TIX END t
             TXT.PRINT
             TXT.PRINT "1st call tix:","","", t; "pos found"; ii
             TXT.PRINT
          
             TIX t
             ii = caseFreeInstr(line1, line2, 0, 0)
             TIX END t
             TXT.PRINT "2nd+ call tix:","", t; "pos found"; ii
             TXT.PRINT
             TIX t
          
             TIX t
             ii = caseFreeInstr(line1, line2, 1, 0)
             TIX END t
             TXT.PRINT "2nd+ call, mainStr already UCASE tix:", t; "pos found"; ii
             TXT.PRINT
          
             TIX t
             ii = caseFreeInstr(line1, line2, 1, -1)
             TIX END t
             TXT.PRINT "2nd+ reverse srch, already UCASE tix:", t; "pos found"; ii
             TXT.PRINT
             TXT.WAITKEY$
          
          END FUNCTION
          
          FUNCTION caseFreeInstr(mainStr AS STRING, matchStr AS STRING, mainStrIsUcase AS LONG, reverse AS LONG) AS LONG
             'note1: this fcn will cap mainStr and matchStr! This can good for multiple compares on the same mainStr.
             'note2: fcn allows to shortcut past UCASE'ing mainStr by setting mainStrIsUcase <> 0. If function has been called
             'already, and mainStr has been used previously as a parameter to this function (and has not been altered since),
             'then setting mainStrIsUcase to non-zero will speed function up many X.
             REGISTER ii AS LONG, ii2 AS LONG
             DIM sArr(LEN(mainStr)-1) AS BYTE AT STRPTR(mainStr)
             DIM sArr2(LEN(matchStr)-1) AS BYTE AT STRPTR(matchStr)
             DIM translateArr(255) AS STATIC BYTE
          
             IF translateArr(3) = 0 THEN CALL arrayAssign(translateArr())
          
             FOR ii = 0 TO LEN(matchStr)-1
                sArr2(ii) = translateArr(sArr2(ii)) 'make it uppercase
             NEXT
          
             IF mainStrIsUcase = 0 THEN
             FOR ii = 0 TO LEN(mainStr)-1
                sArr(ii) = translateArr(sArr(ii))   'make it uppercase
             NEXT
             END IF
          
             IF reverse = 0 THEN
                FUNCTION = INSTR(mainStr, matchStr)
             ELSE
                FUNCTION = INSTR(-1, mainStr, matchStr)
             END IF
          
          END FUNCTION
          
          SUB arrayAssign(translateArr() AS BYTE)
          ARRAY ASSIGN translateArr() = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, _
          21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, _
          41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60, _
          61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, _
          81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,65,66,67,68, _
          69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88, _
          89,90,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140, _
          141,142,143,144,145,146,147,148,149,150,151,152,153,138,155,140,157,142,159,160, _
          161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180, _
          181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, _
          201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, _
          221,222,223,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208, _
          209,210,211,212,213,214,247,216,217,218,219,220,221,222,159
          END SUB
          Typical results:
          Attached Files

          Comment


          • #6
            I tested post #5 extensively in automated loops verifying it against INSTR-using-UCASE$, and it gives identical results. It seems to be about 2.5x faster with longer main strings, but can exceed that by a bunch in repeated searches.

            Comment


            • #7
              John
              You are of course on the right track. Intel has provided a specific instruction to handle this XLAT. So if a large number of case insensitive INSTRs are to be done setting up the translation table in advance is a great speed saving.
              The PB INSTR function is actually quite complex.
              Consider INSTR(start, t1, t2). So either t1 or t2 or both may be the changing variables or both and of course there are the options on start.
              A specific case where either t1 or t2 is always constant and start is always 1 is a good case for using assembler particularly if the variable string remains unchanged.
              If the required filter was shown (as MCM didn't say "code not shown" ) then I suspect a number of BMs could post assembler to quickly handle a specific use.

              Comment


              • #8
                If string is ANSI you can create your 'upppercase' version by treating each char as a byte and AND'ing off bit 5.

                Code:
                  LOCAL pSrc, pTAR as BYTE PTR   
                  TargetString =     SPACE$(LEN(SourceString)) 
                   pSrc = STRPTR (SourceString)
                   pTar = STRPTR (TargetString)
                  
                  FOR registervar = 1 to LEN (SourceString)
                     @pTar = @pSrc AND &B11011111
                     INCR pTar
                     INCR pSrc
                  NEXT
                Don't know if there is a way to do this with Wide Chars or non-English ANSI.

                MCM

                Comment


                • #9
                  John P., I actually had tried some quick asm for the uppercasing trans table, but didn't see an improvement. So, I left it as is figuring it'd be more effort than payoff.

                  MCM, I also used the AND'ing off bit 5 technique at first, but because the string can be any of 256 chars, logic needed to be be invoked to avoid turning e.g. "8" into chr$(24).

                  Comment


                  • #10
                    So test for range of @pSRC between 97 and 122 (decimal) ( 'a' thru 'z') before ANDing.

                    Comment


                    • #11
                      >> test for range of @pSRC between 97 and 122

                      And don't forget 230-255 and four or five other scattered bytes.

                      I already feel bad enough that the algo is over 3x slower than INSTR alone. Make it 4 or 5x slower and I'd have to consider bumping the meds 50%, or maybe even switching to one of those experimental Swiss formulations currently banned in Michigan.

                      Comment


                      • #12
                        Here's my attempt, it appears a bit slower than instr but not a lot and it does the case insensitive for free. The Boyer Moore looked like a bit more work to make case insensitive. This was a really simple implementation but it did ok for now. Tomorrow I'll see whether I can filter 1,000,000 live.

                        Code:
                        #Compile Exe
                        #Dim All
                         Function PBMain () As Long
                          'Call CreateTable
                          Call DoTest()
                        End Function
                         Sub DoTest()
                          Local hWin As Dword
                          Local a As String
                           Txt.Window("Case Insensitive Test", 10, 10) To hWin
                          Local i As Long
                          Call TestSearch( "this is a super great test of the case insenstive isntri", "InS" )
                          Call TestSearch( "short run", "RUN" )
                          Call TestSearch( "Haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaneedleaaaaaaaay", "NEEDLE" )
                          Txt.Print "Finished"
                          Txt.Line.Input a
                        End Sub
                         Sub TestSearch( ByVal matchString As String, ByVal findString As String )
                          Local j As Long
                          Local st, en As Quad
                          Local s1#, e1#
                           Local msg As String
                          Local i, c As Long
                          s1 = Timer
                            For j=1 To 1000000
                              i = InstrI( matchString, findString )
                            Next
                          e1 = Timer
                          Txt.Print "InstrI. "; Format$( i, "0"); " in "; Format$( e1-s1, "#,##0.0000")
                           s1 = Timer
                            For j=1 To 1000000
                              i = InstrI2( matchString, findString )
                            Next
                          e1 = Timer
                          Txt.Print "InstrI2 "; Format$( i, "0"); " in "; Format$( e1-s1, "#,##0.0000")
                           matchString = UCase$( matchString )
                          findString = UCase$( findString )
                          s1 = Timer
                            For j=1 To 1000000
                              i = InStr( matchString, findString )
                            Next
                          e1 = Timer
                          Txt.Print "Instr.. "; Format$( i, "0"); " in ";  Format$( e1-s1, "#,##0.0000")
                          Txt.Print "-------------------------------------------------"
                          Txt.Print Format$( en-st, "#,##0" )
                         End Sub
                         Sub CreateTable()
                          Local i As Long
                          Local x As IStringBuilderA
                          x = Class "StringBuilderA"
                          x.Add( $Tab )
                          x.Add( $Tab )
                          x.Add( "DB " )
                          For i = 0 To 255
                            x.Add( Right$("  "+Str$(Asc(UCase$(Chr$(i)))), 3) )
                            If i Mod 16=15 Then
                              x.Add( $CrLf )
                              If i<255 Then
                                x.Add( $Tab )
                                x.Add( $Tab )
                                x.Add( "DB " )
                              End If
                            Else
                              x.Add( ", " )
                            End If
                          Next
                          Clipboard Set Text x.String
                        End Sub
                         ' Uses ucase collation
                        AsmData xlatTable
                          DB   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15
                          DB  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31
                          DB  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47
                          DB  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63
                          DB  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
                          DB  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95
                          DB  96,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
                          DB  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90, 123, 124, 125, 126, 127
                          DB 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143
                          DB 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 138, 155, 140, 157, 142, 159
                          DB 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175
                          DB 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191
                          DB 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207
                          DB 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223
                          DB 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207
                          DB 208, 209, 210, 211, 212, 213, 214, 247, 216, 217, 218, 219, 220, 221, 222, 159
                        End AsmData
                         Function InstrI2( mainStr As String, matchStr As String ) As Long
                          #Register None
                          ! MOV EDX, 1
                          ! MOV ESI, mainStr
                          ! MOV ESI, [ESI]           ; ESI = StrPtr( mainStr )
                          ! MOV EDI, matchStr
                          ! MOV EDI, [EDI]            ; EDI = StrPtr( matchStr )
                          ! LEA EBX, xlatTable        ; Translate table
                          ! XOR EAX, EAX              ; EAX = 0
                          ! MOV AL, BYTE PTR[EDI]     ; AL = @matchStr[0]
                          ! XLATB                     ; AL = UCase$( @matchStr[0] )
                          ! MOV ECX, EAX              ; ECX = UCase$( @matchStr[0] )
                        InstrI2_Loop:
                          ! MOV AL, BYTE PTR[ESI]     ; EAX = @a
                          ! OR AL, AL                 ; If @mainStr = 0
                          ! JZ InstrI2_NoMatch        ; Goto Exit
                          ! XLATB                     ; EAX = UCase$( @mainStr )
                          ! SUB EAX, ECX              ; if @mainStr = @matchStr
                          ! JZ InstrI2_Cmp            ; Goto Compare
                          ! XOR EAX, EAX              ; Clear upper registers of A
                          ! INC ESI                   ; Incr mainStr
                          ! INC EDX                   ; Incr Pos
                          ! JMP SHORT InstrI2_Loop    ; Try next byte
                        InstrI2_Cmp:
                          ! PUSH ESI                  ; Save mainStr
                          ! PUSH EDI                  ; Save findStr
                          ! PUSH ECX                  ; Save UCase$( @findStr )
                          ! INC ESI                   ; Already compared this byte
                          ! INC EDI                   ; Already compared this byte
                        InstrI2_CmpLoop:
                          ! MOV AL, BYTE PTR[EDI]     ; AL = @matchStr
                          ! OR AL, AL                 ; If @matchStr=0
                          ! JZ InstrI2_Match          ; Then Goto Match! yay
                          ! XLATB                     ; AL = UCase$( @matchStr )
                          ! MOV ECX, EAX              ; ECX = UCase$( @matchStr )
                          ! MOV AL, BYTE PTR[ESI]     ; EAX = @mainStr
                          ! OR AL, AL                 ; If @matchStr=0
                          ! JZ InstrI2_CmpEnd         ; Then we finish failed (no match)
                          ! XLATB                     ; EAX = UCase$( @mainStr )
                          ! SUB EAX, ECX              ; if @mainStr<>@matchStr
                          ! JNZ InstrI2_CmpFail       ; Then Goto Compare Failed
                          ! INC EDI                   ; Incr matchStr
                          ! INC ESI                   ; Incr mainStr
                          ! JMP SHORT InstrI2_CmpLoop ; Try next byte
                         InstrI2_CmpFail:
                          ! POP ECX                   ; Restore UCase$( @findStr )
                          ! POP EDI                   ; Restore findStr
                          ! POP ESI                   ; Restore mainStr
                          ! INC ESI                   ; Incr mainStr
                          ! INC EDX                   ; Incr pos
                          ! JMP SHORT InstrI2_Loop
                        InstrI2_Match:
                          ! POP ECX                   ; Restore UCase$( @findStr )
                          ! POP EDI                   ; Restore findStr
                          ! POP ESI                   ; Restore mainStr
                          ! JMP SHORT InstrI2_Finish  ; Return Position
                        InstrI2_CmpEnd:
                          ! POP ECX                   ; Restore UCase$( @findStr )
                          ! POP EDI                   ; Restore findStr
                          ! POP ESI                   ; Restore mainStr
                        InstrI2_NoMatch:
                          ! XOR EDX, EDX              ; Pos = 0 (No match)
                        InstrI2_Finish:
                          ! MOV FUNCTION, EDX         ; Function = pos
                        End Function
                         Function InstrI( mainStr As String, matchStr As String ) As Long
                          Register i As Long
                          Register j As Long
                          Local mainPtr, matchPtr, xptr As Byte Ptr
                          Local ch As Byte
                          mainPtr = StrPtr( mainStr )
                          matchPtr = StrPtr( matchStr )
                          xptr = CodePtr( xlatTable )
                          ch = @xptr[ @matchPtr ]
                           For i=1 To Len(mainStr)-Len(matchStr)+1
                            If @xptr[ @mainPtr ] = ch Then
                              j = 1
                              While @xptr[ @mainPtr[j] ] = @xptr[ @matchPtr[j] ] And @matchPtr[j] And @mainPtr[j]
                                Incr j
                              Wend
                              If @matchPtr[j]=0 Then
                                Function = i
                                Exit Function
                              End If
                            End If
                            Incr mainPtr
                          Next
                          Function = 0
                        End Function
                         
                        LarryC
                        Website
                        Sometimes life's a dream, sometimes it's a scream

                        Comment


                        • #13
                          Geez Larry, that's a pretty sweet "attempt"! My version only can time with instrI on longer strings, and gets whomped elsewhere. :shhh:

                          There are a couple mismatch glitches still tho somewhere. My quick glance at the code produced no joy, so maybe give it a perusal. (There's some dead code in there from other tests, so just ignore that.)

                          Added: Oops, changing 78000 to 7800 caused an error in my algo. For this mismatch demo, I've changed to all forward searching.

                          Code:
                          #COMPILE EXE
                          #DIM ALL
                          
                           FUNCTION PBMAIN () AS LONG
                              RANDOMIZE
                            'Call CreateTable
                            CALL DoTest()
                          END FUNCTION
                           SUB DoTest()
                            LOCAL hWin AS DWORD, ii2, ii3, r1,r2,r3,r4 AS LONG
                            LOCAL a, line1,line2,line3,exitStr AS STRING
                             TXT.WINDOW("Case Insensitive Test", 10, 10) TO hWin
                            LOCAL i AS LONG
                          
                             line1 = REPEAT$(100 ,"QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./qwertyuiop[]asdfghjkl;'zxcvbnm,./1234567890-=") & "HeLLo THErE!"
                             line3 = line1
                             line2 = "Hello There!"
                          
                          DO
                             line1 = line3
                             line2 = CHR$(RND(0,255),RND(0,255),RND(0,255),RND(0,255),RND(0,255))', _
                          
                             ii2 = RND(1, 7800): IF ii2 > 7800\2 THEN ii3 = -1 ELSE ii3 = 0
                             MID$(line1, ii2) = line2
                             r3 = INSTR(UCASE$(line1), UCASE$(line2))
                             r1 =        InstrI(line1, line2)
                             r2 =       InstrI2(line1, line2)
                             r4 = caseFreeInstr(line1, line2, 0, 0) 'has to be last because it caps line1 & line2
                          
                             IF r1 = r2 AND r1 = r3 AND r1 = r4 THEN ii3 = 0 ELSE TXT.PRINT "mismatch";r1;r2;r3;r4: TXT.WAITKEY$ TO exitStr: IF exitStr = "x" OR exitStr = CHR$(27) THEN EXIT SUB
                             ITERATE DO
                          
                             IF INSTR(line1, line2) = caseFreeInstr(line1, line2, 0, ii3) THEN
                              '  pcps(line1)
                              '  pcwait
                               ! nop
                          '      pcpss(line2)
                             ELSE
                                IF INSTR(line1, line2) = caseFreeInstr(line1, line2, 0, ii3) THEN
                                'pcpss(line2 & "ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok")
                                ELSE
                                   'pcps(line1)
                                   'pcps(line2)
                              '  ? "oooooo"
                                END IF
                             END IF
                          LOOP
                          
                            CALL TestSearch( "this is a super great test of the case insenstive isntri", "InS" )
                            CALL TestSearch( "short run", "RUN" )
                            CALL TestSearch(REPEAT$(20, "Haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay") & "needle", "NEEDLE" )
                            TXT.PRINT "Finished"
                            TXT.LINE.INPUT a
                          END SUB
                           SUB TestSearch( BYVAL matchString AS STRING, BYVAL findString AS STRING )
                            LOCAL j AS LONG
                            LOCAL st, en AS QUAD
                            LOCAL s1#, e1#
                             LOCAL msg AS STRING
                            LOCAL i, c AS LONG
                            s1 = TIMER
                              FOR j=1 TO 1000000
                                i = caseFreeInstr( matchString, findString, 0, 0 )
                              NEXT
                            e1 = TIMER
                            TXT.PRINT "cFInstr "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                            s1 = TIMER
                              FOR j=1 TO 1000000
                                i = InstrI( matchString, findString )
                              NEXT
                            e1 = TIMER
                            TXT.PRINT "InstrI. "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                             s1 = TIMER
                              FOR j=1 TO 1000000
                                i = InstrI2( matchString, findString )
                              NEXT
                            e1 = TIMER
                            TXT.PRINT "InstrI2 "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                             matchString = UCASE$( matchString )
                            findString = UCASE$( findString )
                            s1 = TIMER
                              FOR j=1 TO 1000000
                                i = INSTR( matchString, findString )
                              NEXT
                            e1 = TIMER
                            TXT.PRINT "Instr.. "; FORMAT$( i, "0"); " in ";  FORMAT$( e1-s1, "#,##0.0000")
                            TXT.PRINT "-------------------------------------------------"
                            TXT.PRINT FORMAT$( en-st, "#,##0" )
                           END SUB
                           SUB CreateTable()
                            LOCAL i AS LONG
                            LOCAL x AS ISTRINGBUILDERA
                            x = CLASS "StringBuilderA"
                            x.Add( $TAB )
                            x.Add( $TAB )
                            x.Add( "DB " )
                            FOR i = 0 TO 255
                              x.Add( RIGHT$("  "+STR$(ASC(UCASE$(CHR$(i)))), 3) )
                              IF i MOD 16=15 THEN
                                x.Add( $CRLF )
                                IF i<255 THEN
                                  x.Add( $TAB )
                                  x.Add( $TAB )
                                  x.Add( "DB " )
                                END IF
                              ELSE
                                x.Add( ", " )
                              END IF
                            NEXT
                            CLIPBOARD SET TEXT x.String
                          END SUB
                           ' Uses ucase collation
                          ASMDATA xlatTable
                            DB   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15
                            DB  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31
                            DB  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47
                            DB  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63
                            DB  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
                            DB  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95
                            DB  96,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
                            DB  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90, 123, 124, 125, 126, 127
                            DB 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143
                            DB 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 138, 155, 140, 157, 142, 159
                            DB 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175
                            DB 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191
                            DB 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207
                            DB 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223
                            DB 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207
                            DB 208, 209, 210, 211, 212, 213, 214, 247, 216, 217, 218, 219, 220, 221, 222, 159
                          END ASMDATA
                           FUNCTION InstrI2( mainStr AS STRING, matchStr AS STRING ) AS LONG
                            #REGISTER NONE
                            ! MOV EDX, 1
                            ! MOV ESI, mainStr
                            ! MOV ESI, [ESI]           ; ESI = StrPtr( mainStr )
                            ! MOV EDI, matchStr
                            ! MOV EDI, [EDI]            ; EDI = StrPtr( matchStr )
                            ! LEA EBX, xlatTable        ; Translate table
                            ! XOR EAX, EAX              ; EAX = 0
                            ! MOV AL, BYTE PTR[EDI]     ; AL = @matchStr[0]
                            ! XLATB                     ; AL = UCase$( @matchStr[0] )
                            ! MOV ECX, EAX              ; ECX = UCase$( @matchStr[0] )
                          InstrI2_Loop:
                            ! MOV AL, BYTE PTR[ESI]     ; EAX = @a
                            ! OR AL, AL                 ; If @mainStr = 0
                            ! JZ InstrI2_NoMatch        ; Goto Exit
                            ! XLATB                     ; EAX = UCase$( @mainStr )
                            ! SUB EAX, ECX              ; if @mainStr = @matchStr
                            ! JZ InstrI2_Cmp            ; Goto Compare
                            ! XOR EAX, EAX              ; Clear upper registers of A
                            ! INC ESI                   ; Incr mainStr
                            ! INC EDX                   ; Incr Pos
                            ! JMP SHORT InstrI2_Loop    ; Try next byte
                          InstrI2_Cmp:
                            ! PUSH ESI                  ; Save mainStr
                            ! PUSH EDI                  ; Save findStr
                            ! PUSH ECX                  ; Save UCase$( @findStr )
                            ! INC ESI                   ; Already compared this byte
                            ! INC EDI                   ; Already compared this byte
                          InstrI2_CmpLoop:
                            ! MOV AL, BYTE PTR[EDI]     ; AL = @matchStr
                            ! OR AL, AL                 ; If @matchStr=0
                            ! JZ InstrI2_Match          ; Then Goto Match! yay
                            ! XLATB                     ; AL = UCase$( @matchStr )
                            ! MOV ECX, EAX              ; ECX = UCase$( @matchStr )
                            ! MOV AL, BYTE PTR[ESI]     ; EAX = @mainStr
                            ! OR AL, AL                 ; If @matchStr=0
                            ! JZ InstrI2_CmpEnd         ; Then we finish failed (no match)
                            ! XLATB                     ; EAX = UCase$( @mainStr )
                            ! SUB EAX, ECX              ; if @mainStr<>@matchStr
                            ! JNZ InstrI2_CmpFail       ; Then Goto Compare Failed
                            ! INC EDI                   ; Incr matchStr
                            ! INC ESI                   ; Incr mainStr
                            ! JMP SHORT InstrI2_CmpLoop ; Try next byte
                           InstrI2_CmpFail:
                            ! POP ECX                   ; Restore UCase$( @findStr )
                            ! POP EDI                   ; Restore findStr
                            ! POP ESI                   ; Restore mainStr
                            ! INC ESI                   ; Incr mainStr
                            ! INC EDX                   ; Incr pos
                            ! JMP SHORT InstrI2_Loop
                          InstrI2_Match:
                            ! POP ECX                   ; Restore UCase$( @findStr )
                            ! POP EDI                   ; Restore findStr
                            ! POP ESI                   ; Restore mainStr
                            ! JMP SHORT InstrI2_Finish  ; Return Position
                          InstrI2_CmpEnd:
                            ! POP ECX                   ; Restore UCase$( @findStr )
                            ! POP EDI                   ; Restore findStr
                            ! POP ESI                   ; Restore mainStr
                          InstrI2_NoMatch:
                            ! XOR EDX, EDX              ; Pos = 0 (No match)
                          InstrI2_Finish:
                            ! MOV FUNCTION, EDX         ; Function = pos
                          END FUNCTION
                           FUNCTION InstrI( mainStr AS STRING, matchStr AS STRING ) AS LONG
                            REGISTER i AS LONG
                            REGISTER j AS LONG
                            LOCAL mainPtr, matchPtr, xptr AS BYTE PTR
                            LOCAL ch AS BYTE
                            mainPtr = STRPTR( mainStr )
                            matchPtr = STRPTR( matchStr )
                            xptr = CODEPTR( xlatTable )
                            ch = @xptr[ @matchPtr ]
                             FOR i=1 TO LEN(mainStr)-LEN(matchStr)+1
                              IF @xptr[ @mainPtr ] = ch THEN
                                j = 1
                                WHILE @xptr[ @mainPtr[j] ] = @xptr[ @matchPtr[j] ] AND @matchPtr[j] AND @mainPtr[j]
                                  INCR j
                                WEND
                                IF @matchPtr[j]=0 THEN
                                  FUNCTION = i
                                  EXIT FUNCTION
                                END IF
                              END IF
                              INCR mainPtr
                            NEXT
                            FUNCTION = 0
                          END FUNCTION
                          FUNCTION caseFreeInstr(mainStr AS STRING, matchStr AS STRING, mainStrIsUcase AS LONG, reverse AS LONG) AS LONG
                             'note1: this fcn will cap mainStr and matchStr! This can good for multiple compares on the same mainStr.
                             'note2: fcn allows to shortcut past UCASE'ing mainStr by setting mainStrIsUcase <> 0. If function has been called
                             'already, and mainStr has been used previously as a parameter to this function (and has not been altered since),
                             'then setting mainStrIsUcase to non-zero will speed function up many X.
                             REGISTER ii AS LONG
                             STATIC oneTime AS LONG
                             DIM sArr(LEN(mainStr)-1) AS BYTE AT STRPTR(mainStr)
                             DIM sArr2(LEN(matchStr)-1) AS BYTE AT STRPTR(matchStr)
                          
                             IF oneTime = 0 THEN
                                oneTime = 1
                                DIM translateArr(255) AS STATIC BYTE
                                CALL arrayAssign(translateArr())
                             END IF
                          
                             FOR ii = 0 TO LEN(matchStr)-1
                                sArr2(ii) = translateArr(sArr2(ii)) 'make it uppercase
                             NEXT
                          
                             IF mainStrIsUcase = 0 THEN
                             FOR ii = 0 TO LEN(mainStr)-1
                                sArr(ii) = translateArr(sArr(ii))   'make it uppercase
                             NEXT
                             END IF
                          
                             IF reverse = 0 THEN
                                FUNCTION = INSTR(mainStr, matchStr)
                             ELSE
                                FUNCTION = INSTR(-1, mainStr, matchStr)
                             END IF
                          
                          END FUNCTION
                          
                          SUB arrayAssign(translateArr() AS BYTE)
                          ARRAY ASSIGN translateArr() = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, _
                          21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, _
                          41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60, _
                          61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, _
                          81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,65,66,67,68, _
                          69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88, _
                          89,90,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140, _
                          141,142,143,144,145,146,147,148,149,150,151,152,153,138,155,140,157,142,159,160, _
                          161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180, _
                          181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, _
                          201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, _
                          221,222,223,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208, _
                          209,210,211,212,213,214,247,216,217,218,219,220,221,222,159
                          END SUB
                          Last edited by John Gleason; 1 May 2014, 12:52 PM. Reason: 78000 >> 7800

                          Comment


                          • #14
                            Larry, here is another problem with instrI2 demo'd. It seems to find the string even tho only a partial string is present. The previous mismatches mentioned in post #13 are ignored here.
                            Code:
                            #COMPILE EXE
                            #DIM ALL
                            
                             FUNCTION PBMAIN () AS LONG
                                RANDOMIZE
                              'Call CreateTable
                              CALL DoTest()
                            END FUNCTION
                             SUB DoTest()
                              LOCAL hWin AS DWORD, ii2, ii3, r1,r2,r3,r4,rlen AS LONG
                              LOCAL a, line1,line2,line3,exitStr AS STRING
                               TXT.WINDOW("Case Insensitive Test", 10, 10) TO hWin
                              LOCAL i AS LONG
                            
                               line1 = REPEAT$(100 ,"QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./qwertyuiop[]asdfghjkl;'zxcvbnm,./1234567890-=") & "HeLLo THErE!"
                               line3 = line1
                               line2 = "Hello There!"
                            
                            DO
                               line1 = line3
                               RESET line2
                               FOR rLen = 1 TO RND(1, 48)
                                  line2 &= CHR$(RND(0,255))',rnd(0,255),rnd(0,255),rnd(0,255),rnd(0,255))', _x
                               NEXT
                            
                               ii2 = RND(1, 7800): IF ii2 > 7800\2 THEN ii3 = -1 ELSE ii3 = 0
                               MID$(line1, ii2) = line2
                               r3 = INSTR(UCASE$(line1), UCASE$(line2))
                               r1 =        InstrI(line1, line2)
                               r2 =       InstrI2(line1, line2)
                               r4 = caseFreeInstr(line1, line2, 0, 0) 'has to be last because it caps line1 & line2
                            
                            '   IF r1 xor r2 xor r3 xor r4 THEN TXT.PRINT "mismatch";r1;r2;r3;r4: TXT.WAITKEY$ TO exitStr: IF exitStr = "x" OR exitStr = CHR$(27) THEN EXIT SUB
                            '!wait
                            '!wait
                            IF r2 = 0 THEN ITERATE DO
                            IF r1 = r2 AND r1 <> r3 THEN ITERATE DO
                                IF r1 = r2 AND r1 = r3 AND r1 = r4 THEN ii3 = 0 ELSE TXT.PRINT "mismatch";r1;r2;r3;r4: TXT.WAITKEY$ TO exitStr: IF exitStr = "x" OR exitStr = CHR$(27) THEN EXIT SUB
                               ITERATE DO
                            
                               IF INSTR(line1, line2) = caseFreeInstr(line1, line2, 0, ii3) THEN
                                '  pcps(line1)
                                '  pcwait
                                 ! nop
                            '      pcpss(line2)
                               ELSE
                                  IF INSTR(line1, line2) = caseFreeInstr(line1, line2, 0, ii3) THEN
                                  'pcpss(line2 & "ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok")
                                  ELSE
                                     'pcps(line1)
                                     'pcps(line2)
                                '  ? "oooooo"
                                  END IF
                               END IF
                            LOOP
                            
                              CALL TestSearch( "this is a super great test of the case insenstive isntri", "InS" )
                              CALL TestSearch( "short run", "RUN" )
                              CALL TestSearch(REPEAT$(20, "Haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay") & "needle", "NEEDLE" )
                              TXT.PRINT "Finished"
                              TXT.LINE.INPUT a
                            END SUB
                             SUB TestSearch( BYVAL matchString AS STRING, BYVAL findString AS STRING )
                              LOCAL j AS LONG
                              LOCAL st, en AS QUAD
                              LOCAL s1#, e1#
                               LOCAL msg AS STRING
                              LOCAL i, c AS LONG
                              s1 = TIMER
                                FOR j=1 TO 1000000
                                  i = caseFreeInstr( matchString, findString, 0, 0 )
                                NEXT
                              e1 = TIMER
                              TXT.PRINT "cFInstr "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                              s1 = TIMER
                                FOR j=1 TO 1000000
                                  i = InstrI( matchString, findString )
                                NEXT
                              e1 = TIMER
                              TXT.PRINT "InstrI. "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                               s1 = TIMER
                                FOR j=1 TO 1000000
                                  i = InstrI2( matchString, findString )
                                NEXT
                              e1 = TIMER
                              TXT.PRINT "InstrI2 "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                               matchString = UCASE$( matchString )
                              findString = UCASE$( findString )
                              s1 = TIMER
                                FOR j=1 TO 1000000
                                  i = INSTR( matchString, findString )
                                NEXT
                              e1 = TIMER
                              TXT.PRINT "Instr.. "; FORMAT$( i, "0"); " in ";  FORMAT$( e1-s1, "#,##0.0000")
                              TXT.PRINT "-------------------------------------------------"
                              TXT.PRINT FORMAT$( en-st, "#,##0" )
                             END SUB
                             SUB CreateTable()
                              LOCAL i AS LONG
                              LOCAL x AS ISTRINGBUILDERA
                              x = CLASS "StringBuilderA"
                              x.Add( $TAB )
                              x.Add( $TAB )
                              x.Add( "DB " )
                              FOR i = 0 TO 255
                                x.Add( RIGHT$("  "+STR$(ASC(UCASE$(CHR$(i)))), 3) )
                                IF i MOD 16=15 THEN
                                  x.Add( $CRLF )
                                  IF i<255 THEN
                                    x.Add( $TAB )
                                    x.Add( $TAB )
                                    x.Add( "DB " )
                                  END IF
                                ELSE
                                  x.Add( ", " )
                                END IF
                              NEXT
                              CLIPBOARD SET TEXT x.String
                            END SUB
                             ' Uses ucase collation
                            ASMDATA xlatTable
                              DB   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15
                              DB  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31
                              DB  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47
                              DB  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63
                              DB  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
                              DB  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95
                              DB  96,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
                              DB  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90, 123, 124, 125, 126, 127
                              DB 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143
                              DB 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 138, 155, 140, 157, 142, 159
                              DB 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175
                              DB 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191
                              DB 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207
                              DB 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223
                              DB 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207
                              DB 208, 209, 210, 211, 212, 213, 214, 247, 216, 217, 218, 219, 220, 221, 222, 159
                            END ASMDATA
                             FUNCTION InstrI2( mainStr AS STRING, matchStr AS STRING ) AS LONG
                              #REGISTER NONE
                              ! MOV EDX, 1
                              ! MOV ESI, mainStr
                              ! MOV ESI, [ESI]           ; ESI = StrPtr( mainStr )
                              ! MOV EDI, matchStr
                              ! MOV EDI, [EDI]            ; EDI = StrPtr( matchStr )
                              ! LEA EBX, xlatTable        ; Translate table
                              ! XOR EAX, EAX              ; EAX = 0
                              ! MOV AL, BYTE PTR[EDI]     ; AL = @matchStr[0]
                              ! XLATB                     ; AL = UCase$( @matchStr[0] )
                              ! MOV ECX, EAX              ; ECX = UCase$( @matchStr[0] )
                            InstrI2_Loop:
                              ! MOV AL, BYTE PTR[ESI]     ; EAX = @a
                              ! OR AL, AL                 ; If @mainStr = 0
                              ! JZ InstrI2_NoMatch        ; Goto Exit
                              ! XLATB                     ; EAX = UCase$( @mainStr )
                              ! SUB EAX, ECX              ; if @mainStr = @matchStr
                              ! JZ InstrI2_Cmp            ; Goto Compare
                              ! XOR EAX, EAX              ; Clear upper registers of A
                              ! INC ESI                   ; Incr mainStr
                              ! INC EDX                   ; Incr Pos
                              ! JMP SHORT InstrI2_Loop    ; Try next byte
                            InstrI2_Cmp:
                              ! PUSH ESI                  ; Save mainStr
                              ! PUSH EDI                  ; Save findStr
                              ! PUSH ECX                  ; Save UCase$( @findStr )
                              ! INC ESI                   ; Already compared this byte
                              ! INC EDI                   ; Already compared this byte
                            InstrI2_CmpLoop:
                              ! MOV AL, BYTE PTR[EDI]     ; AL = @matchStr
                              ! OR AL, AL                 ; If @matchStr=0
                              ! JZ InstrI2_Match          ; Then Goto Match! yay
                              ! XLATB                     ; AL = UCase$( @matchStr )
                              ! MOV ECX, EAX              ; ECX = UCase$( @matchStr )
                              ! MOV AL, BYTE PTR[ESI]     ; EAX = @mainStr
                              ! OR AL, AL                 ; If @matchStr=0
                              ! JZ InstrI2_CmpEnd         ; Then we finish failed (no match)
                              ! XLATB                     ; EAX = UCase$( @mainStr )
                              ! SUB EAX, ECX              ; if @mainStr<>@matchStr
                              ! JNZ InstrI2_CmpFail       ; Then Goto Compare Failed
                              ! INC EDI                   ; Incr matchStr
                              ! INC ESI                   ; Incr mainStr
                              ! JMP SHORT InstrI2_CmpLoop ; Try next byte
                             InstrI2_CmpFail:
                              ! POP ECX                   ; Restore UCase$( @findStr )
                              ! POP EDI                   ; Restore findStr
                              ! POP ESI                   ; Restore mainStr
                              ! INC ESI                   ; Incr mainStr
                              ! INC EDX                   ; Incr pos
                              ! JMP SHORT InstrI2_Loop
                            InstrI2_Match:
                              ! POP ECX                   ; Restore UCase$( @findStr )
                              ! POP EDI                   ; Restore findStr
                              ! POP ESI                   ; Restore mainStr
                              ! JMP SHORT InstrI2_Finish  ; Return Position
                            InstrI2_CmpEnd:
                              ! POP ECX                   ; Restore UCase$( @findStr )
                              ! POP EDI                   ; Restore findStr
                              ! POP ESI                   ; Restore mainStr
                            InstrI2_NoMatch:
                              ! XOR EDX, EDX              ; Pos = 0 (No match)
                            InstrI2_Finish:
                              ! MOV FUNCTION, EDX         ; Function = pos
                            END FUNCTION
                             FUNCTION InstrI( mainStr AS STRING, matchStr AS STRING ) AS LONG
                              REGISTER i AS LONG
                              REGISTER j AS LONG
                              LOCAL mainPtr, matchPtr, xptr AS BYTE PTR
                              LOCAL ch AS BYTE
                              mainPtr = STRPTR( mainStr )
                              matchPtr = STRPTR( matchStr )
                              xptr = CODEPTR( xlatTable )
                              ch = @xptr[ @matchPtr ]
                               FOR i=1 TO LEN(mainStr)-LEN(matchStr)+1
                                IF @xptr[ @mainPtr ] = ch THEN
                                  j = 1
                                  WHILE @xptr[ @mainPtr[j] ] = @xptr[ @matchPtr[j] ] AND @matchPtr[j] AND @mainPtr[j]
                                    INCR j
                                  WEND
                                  IF @matchPtr[j]=0 THEN
                                    FUNCTION = i
                                    EXIT FUNCTION
                                  END IF
                                END IF
                                INCR mainPtr
                              NEXT
                              FUNCTION = 0
                            END FUNCTION
                            FUNCTION caseFreeInstr(mainStr AS STRING, matchStr AS STRING, mainStrIsUcase AS LONG, reverse AS LONG) AS LONG
                               'note1: this fcn will cap mainStr and matchStr! This can good for multiple compares on the same mainStr.
                               'note2: fcn allows to shortcut past UCASE'ing mainStr by setting mainStrIsUcase <> 0. If function has been called
                               'already, and mainStr has been used previously as a parameter to this function (and has not been altered since),
                               'then setting mainStrIsUcase to non-zero will speed function up many X.
                               REGISTER ii AS LONG
                               STATIC oneTime AS LONG
                               DIM sArr(LEN(mainStr)-1) AS BYTE AT STRPTR(mainStr)
                               DIM sArr2(LEN(matchStr)-1) AS BYTE AT STRPTR(matchStr)
                            
                               IF oneTime = 0 THEN
                                  oneTime = 1
                                  DIM translateArr(255) AS STATIC BYTE
                                  CALL arrayAssign(translateArr())
                               END IF
                            
                               FOR ii = 0 TO LEN(matchStr)-1
                                  sArr2(ii) = translateArr(sArr2(ii)) 'make it uppercase
                               NEXT
                            
                               IF mainStrIsUcase = 0 THEN
                               FOR ii = 0 TO LEN(mainStr)-1
                                  sArr(ii) = translateArr(sArr(ii))   'make it uppercase
                               NEXT
                               END IF
                            
                               IF reverse = 0 THEN
                                  FUNCTION = INSTR(mainStr, matchStr)
                               ELSE
                                  FUNCTION = INSTR(-1, mainStr, matchStr)
                               END IF
                            
                            END FUNCTION
                            
                            SUB arrayAssign(translateArr() AS BYTE)
                            ARRAY ASSIGN translateArr() = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, _
                            21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, _
                            41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60, _
                            61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, _
                            81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,65,66,67,68, _
                            69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88, _
                            89,90,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140, _
                            141,142,143,144,145,146,147,148,149,150,151,152,153,138,155,140,157,142,159,160, _
                            161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180, _
                            181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, _
                            201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, _
                            221,222,223,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208, _
                            209,210,211,212,213,214,247,216,217,218,219,220,221,222,159
                            END SUB

                            Comment


                            • #15
                              You found an issue with embedded zero bytes Chr$(0). I use a Chr$(0) to indicate the end of string so InstrI will terminate earlier than a normal PB user might expect if you have these embedded.

                              That would make this unusable for things like UDT's with binary fields embedded. I might consider a different approach but for my current use case (scanning arrays of text strings), it doesn't present an issue.

                              If r1 = r2 And r1 = r3 And r1 = r4 Then
                              ii3 = 0
                              Else
                              Txt.Print "mismatch";r1;r2;r3;r4:
                              If InStr(line2, Chr$(0))>0 Then Txt.Print "ZERO"
                              Txt.WaitKey$ To exitStr:
                              If exitStr = "x" Or exitStr = Chr$(27) Then Exit Sub
                              End If
                              LarryC
                              Website
                              Sometimes life's a dream, sometimes it's a scream

                              Comment


                              • #16
                                This is code from my PwrDev tool, it results in:
                                (Is this fast?)

                                Code:
                                ---------------------------
                                PowerBASIC
                                ---------------------------
                                7800001
                                
                                .035
                                ---------------------------
                                OK   
                                ---------------------------
                                PBMain code, note the 100.000 and not 10.000x

                                Code:
                                FUNCTION PBMAIN
                                
                                Local line1 As String
                                    Local line2 As String
                                    Local result As Long
                                    Local tmr1 As Single
                                    Local tmr2 As Single
                                
                                    line1 = Repeat$(100000 ,"QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./qwertyuiop[]asdfghjkl;'zxcvbnm,./1234567890-=") & "HeLLo THErE!"
                                    line2 = "Hello There!"
                                    
                                    tmr1 = Timer
                                    result = InStr_ByMem( 0, StrPtr( line1 ), Len( line1 ), line2, 0 )
                                
                                    tmr2 = Timer
                                    
                                    ? Format$( result ) & $CrLf & Format$( Round( tmr2 - tmr1, 3 ) )
                                    
                                end function
                                Code:
                                Function InStr_ByMem( _
                                      ByVal nStartPos       As Long _
                                    , ByVal pData           As Long _
                                    , ByVal nDataLen        As Long _
                                    , ByVal sMatchString    As String _
                                    , ByVal bMatchCase      As Long _
                                    ) As Long
                                
                                    Local pBYT        As Byte Ptr
                                    Local pBYTASCII   As Byte Ptr
                                    Local nByte       As Long
                                    Local nLenMatch   As Long
                                    Local bStartByte  As Byte
                                    Local pBYTMatch   As Byte Ptr
                                    Local nByteMatch  As Long
                                    Local bFalse      As Long
                                    Static sAscii       As String
                                
                                    If pData < 0 Then Exit Function
                                    If nDataLen < 1 Then Exit Function
                                    If nDataLen < Len( sMatchString ) Then Exit Function
                                
                                    nLenMatch = Len( sMatchString )
                                    If nLenMatch = 0 Then Exit Function
                                
                                    nStartPos = Max( 1, nStartPos )
                                    If nStartPos + nLenMatch - 1 > nDataLen Then Exit Function
                                    Decr nStartPos
                                
                                    If Len( sAscii ) = 0 Then sAscii = Chr$( 0 To 255 ): CharUpperBuff ByVal StrPtr( sAscii ), Len( sAscii )
                                
                                    pBYTASCII = StrPtr( sAscii )
                                
                                    ' Make uppercase if case isn't important.
                                    If bMatchCase = 0 Then
                                        pBYT = StrPtr( sMatchString )
                                        For nByte = 0 To Len( sMatchString ) - 1: @pBYT[nByte] = @pBYTASCII[@pBYT[nByte]]: Next nByte
                                    End If
                                
                                    pBYT        = pData
                                    pBYTMatch   = StrPtr( sMatchString )
                                    bStartByte  = @pBYTMatch[0]
                                    If bMatchCase Then
                                        For nByte = nStartPos To nDataLen - nLenMatch - 0
                                    
                                            If @pBYT[nByte] = bStartByte Then
                                                bFalse = 0
                                                For nByteMatch = 1 To nLenMatch - 1
                                                    If @pBYT[nByte+nByteMatch] <> @pBYTMatch[nByteMatch] Then bFalse = 1: Exit For
                                                Next nByteMatch
                                                
                                                If bFalse = 0 Then Function = nByte + 1: Exit For
                                            End If
                                
                                        Next
                                
                                    Else
                                
                                        For nByte = nStartPos To nDataLen - nLenMatch - 0
                                            If @pBYTASCII[@pBYT[nByte]] = bStartByte Then
                                            bFalse = 0
                                            For nByteMatch = 1 To nLenMatch - 1
                                                If @pBYTASCII[@pBYT[nByte+nByteMatch]] <> @pBYTMatch[nByteMatch] Then bFalse = 1: Exit For
                                            Next nByteMatch
                                            If bFalse = 0 Then Function = nByte + 1: Exit For
                                            End If
                                        Next
                                        
                                    End If
                                    
                                End Function
                                (The term ascii used here is of course incorrect haha)
                                hellobasic

                                Comment


                                • #17
                                  I'm still getting an error even with strictly text string searches chrs 32-122 in InstrI2. Here it's being compared to the output from the *gold standard* INSTR(UCASE$(line1), UCASE$(line2)). InstrI so far looks good for text.
                                  Code:
                                  #COMPILE EXE
                                  #DIM ALL
                                  
                                   FUNCTION PBMAIN () AS LONG
                                      RANDOMIZE
                                    'Call CreateTable
                                    CALL DoTest()
                                  END FUNCTION
                                   SUB DoTest()
                                    LOCAL hWin AS DWORD, ii2, ii3, r1,r2,r3,r4,rlen AS LONG
                                    LOCAL a, line1,line2,line3,exitStr AS STRING
                                     TXT.WINDOW("Case Insensitive Test", 10, 10) TO hWin
                                    LOCAL i AS LONG
                                  
                                     line1 = REPEAT$(100 ,"QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./qwertyuiop[]asdfghjkl;'zxcvbnm,./1234567890-=") & "HeLLo THErE!"
                                     line3 = line1
                                     line2 = "Hello There!"
                                  
                                  DO
                                     line1 = line3
                                     RESET line2
                                     FOR rLen = 1 TO RND(1, 32)
                                        line2 &= CHR$(RND(32,122))'97,25))',rnd(0,255),rnd(0,255),rnd(0,255),rnd(0,255))', _x
                                     NEXT
                                  
                                     ii2 = RND(1, 7800): IF ii2 > 7800\2 THEN ii3 = -1 ELSE ii3 = 0
                                     MID$(line1, ii2) = line2
                                     r3 = INSTR(UCASE$(line1), UCASE$(line2))
                                     r1 =        InstrI(line1, line2)
                                     r2 =       InstrI2(line1, line2)
                                  '   r4 = caseFreeInstr(line1, line2, 0, 0) 'has to be last because it caps line1 & line2
                                  
                                  '   IF r1 xor r2 xor r3 xor r4 THEN TXT.PRINT "mismatch";r1;r2;r3;r4: TXT.WAITKEY$ TO exitStr: IF exitStr = "x" OR exitStr = CHR$(27) THEN EXIT SUB
                                  '!wait
                                  '!wait
                                  'IF r2 = 0 THEN ITERATE DO
                                  'IF r1 = r2 AND r1 <> r3 THEN ITERATE DO
                                       IF r1 = r2 AND r1 = r3 THEN ii3 = 0 ELSE TXT.PRINT "mismatch";r1;r2;r3: TXT.WAITKEY$ TO exitStr: IF exitStr = "x" OR exitStr = CHR$(27) THEN EXIT SUB
                                  '    IF r1 <> r3 then TXT.PRINT "mismatch";r1;r2;r3;r4: TXT.WAITKEY$ TO exitStr: IF exitStr = "x" OR exitStr = CHR$(27) THEN EXIT SUB
                                     ITERATE DO
                                  
                                     IF INSTR(line1, line2) = caseFreeInstr(line1, line2, 0, ii3) THEN
                                      '  pcps(line1)
                                      '  pcwait
                                       ! nop
                                  '      pcpss(line2)
                                     ELSE
                                        IF INSTR(line1, line2) = caseFreeInstr(line1, line2, 0, ii3) THEN
                                        'pcpss(line2 & "ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok")
                                        ELSE
                                           'pcps(line1)
                                           'pcps(line2)
                                      '  ? "oooooo"
                                        END IF
                                     END IF
                                  LOOP
                                  
                                    CALL TestSearch( "this is a super great test of the case insenstive isntri", "InS" )
                                    CALL TestSearch( "short run", "RUN" )
                                    CALL TestSearch(REPEAT$(20, "Haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay") & "needle", "NEEDLE" )
                                    TXT.PRINT "Finished"
                                    TXT.LINE.INPUT a
                                  END SUB
                                   SUB TestSearch( BYVAL matchString AS STRING, BYVAL findString AS STRING )
                                    LOCAL j AS LONG
                                    LOCAL st, en AS QUAD
                                    LOCAL s1#, e1#
                                     LOCAL msg AS STRING
                                    LOCAL i, c AS LONG
                                    s1 = TIMER
                                      FOR j=1 TO 1000000
                                        i = caseFreeInstr( matchString, findString, 0, 0 )
                                      NEXT
                                    e1 = TIMER
                                    TXT.PRINT "cFInstr "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                                    s1 = TIMER
                                      FOR j=1 TO 1000000
                                        i = InstrI( matchString, findString )
                                      NEXT
                                    e1 = TIMER
                                    TXT.PRINT "InstrI. "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                                     s1 = TIMER
                                      FOR j=1 TO 1000000
                                        i = InstrI2( matchString, findString )
                                      NEXT
                                    e1 = TIMER
                                    TXT.PRINT "InstrI2 "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                                     matchString = UCASE$( matchString )
                                    findString = UCASE$( findString )
                                    s1 = TIMER
                                      FOR j=1 TO 1000000
                                        i = INSTR( matchString, findString )
                                      NEXT
                                    e1 = TIMER
                                    TXT.PRINT "Instr.. "; FORMAT$( i, "0"); " in ";  FORMAT$( e1-s1, "#,##0.0000")
                                    TXT.PRINT "-------------------------------------------------"
                                    TXT.PRINT FORMAT$( en-st, "#,##0" )
                                   END SUB
                                   SUB CreateTable()
                                    LOCAL i AS LONG
                                    LOCAL x AS ISTRINGBUILDERA
                                    x = CLASS "StringBuilderA"
                                    x.Add( $TAB )
                                    x.Add( $TAB )
                                    x.Add( "DB " )
                                    FOR i = 0 TO 255
                                      x.Add( RIGHT$("  "+STR$(ASC(UCASE$(CHR$(i)))), 3) )
                                      IF i MOD 16=15 THEN
                                        x.Add( $CRLF )
                                        IF i<255 THEN
                                          x.Add( $TAB )
                                          x.Add( $TAB )
                                          x.Add( "DB " )
                                        END IF
                                      ELSE
                                        x.Add( ", " )
                                      END IF
                                    NEXT
                                    CLIPBOARD SET TEXT x.String
                                  END SUB
                                   ' Uses ucase collation
                                  ASMDATA xlatTable
                                    DB   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15
                                    DB  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31
                                    DB  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47
                                    DB  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63
                                    DB  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
                                    DB  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95
                                    DB  96,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
                                    DB  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90, 123, 124, 125, 126, 127
                                    DB 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143
                                    DB 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 138, 155, 140, 157, 142, 159
                                    DB 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175
                                    DB 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191
                                    DB 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207
                                    DB 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223
                                    DB 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207
                                    DB 208, 209, 210, 211, 212, 213, 214, 247, 216, 217, 218, 219, 220, 221, 222, 159
                                  END ASMDATA
                                   FUNCTION InstrI2( mainStr AS STRING, matchStr AS STRING ) AS LONG
                                    #REGISTER NONE
                                    ! MOV EDX, 1
                                    ! MOV ESI, mainStr
                                    ! MOV ESI, [ESI]           ; ESI = StrPtr( mainStr )
                                    ! MOV EDI, matchStr
                                    ! MOV EDI, [EDI]            ; EDI = StrPtr( matchStr )
                                    ! LEA EBX, xlatTable        ; Translate table
                                    ! XOR EAX, EAX              ; EAX = 0
                                    ! MOV AL, BYTE PTR[EDI]     ; AL = @matchStr[0]
                                    ! XLATB                     ; AL = UCase$( @matchStr[0] )
                                    ! MOV ECX, EAX              ; ECX = UCase$( @matchStr[0] )
                                  InstrI2_Loop:
                                    ! MOV AL, BYTE PTR[ESI]     ; EAX = @a
                                    ! OR AL, AL                 ; If @mainStr = 0
                                    ! JZ InstrI2_NoMatch        ; Goto Exit
                                    ! XLATB                     ; EAX = UCase$( @mainStr )
                                    ! SUB EAX, ECX              ; if @mainStr = @matchStr
                                    ! JZ InstrI2_Cmp            ; Goto Compare
                                    ! XOR EAX, EAX              ; Clear upper registers of A
                                    ! INC ESI                   ; Incr mainStr
                                    ! INC EDX                   ; Incr Pos
                                    ! JMP SHORT InstrI2_Loop    ; Try next byte
                                  InstrI2_Cmp:
                                    ! PUSH ESI                  ; Save mainStr
                                    ! PUSH EDI                  ; Save findStr
                                    ! PUSH ECX                  ; Save UCase$( @findStr )
                                    ! INC ESI                   ; Already compared this byte
                                    ! INC EDI                   ; Already compared this byte
                                  InstrI2_CmpLoop:
                                    ! MOV AL, BYTE PTR[EDI]     ; AL = @matchStr
                                    ! OR AL, AL                 ; If @matchStr=0
                                    ! JZ InstrI2_Match          ; Then Goto Match! yay
                                    ! XLATB                     ; AL = UCase$( @matchStr )
                                    ! MOV ECX, EAX              ; ECX = UCase$( @matchStr )
                                    ! MOV AL, BYTE PTR[ESI]     ; EAX = @mainStr
                                    ! OR AL, AL                 ; If @matchStr=0
                                    ! JZ InstrI2_CmpEnd         ; Then we finish failed (no match)
                                    ! XLATB                     ; EAX = UCase$( @mainStr )
                                    ! SUB EAX, ECX              ; if @mainStr<>@matchStr
                                    ! JNZ InstrI2_CmpFail       ; Then Goto Compare Failed
                                    ! INC EDI                   ; Incr matchStr
                                    ! INC ESI                   ; Incr mainStr
                                    ! JMP SHORT InstrI2_CmpLoop ; Try next byte
                                   InstrI2_CmpFail:
                                    ! POP ECX                   ; Restore UCase$( @findStr )
                                    ! POP EDI                   ; Restore findStr
                                    ! POP ESI                   ; Restore mainStr
                                    ! INC ESI                   ; Incr mainStr
                                    ! INC EDX                   ; Incr pos
                                    ! JMP SHORT InstrI2_Loop
                                  InstrI2_Match:
                                    ! POP ECX                   ; Restore UCase$( @findStr )
                                    ! POP EDI                   ; Restore findStr
                                    ! POP ESI                   ; Restore mainStr
                                    ! JMP SHORT InstrI2_Finish  ; Return Position
                                  InstrI2_CmpEnd:
                                    ! POP ECX                   ; Restore UCase$( @findStr )
                                    ! POP EDI                   ; Restore findStr
                                    ! POP ESI                   ; Restore mainStr
                                  InstrI2_NoMatch:
                                    ! XOR EDX, EDX              ; Pos = 0 (No match)
                                  InstrI2_Finish:
                                    ! MOV FUNCTION, EDX         ; Function = pos
                                  END FUNCTION
                                   FUNCTION InstrI( mainStr AS STRING, matchStr AS STRING ) AS LONG
                                    REGISTER i AS LONG
                                    REGISTER j AS LONG
                                    LOCAL mainPtr, matchPtr, xptr AS BYTE PTR
                                    LOCAL ch AS BYTE
                                    mainPtr = STRPTR( mainStr )
                                    matchPtr = STRPTR( matchStr )
                                    xptr = CODEPTR( xlatTable )
                                    ch = @xptr[ @matchPtr ]
                                     FOR i=1 TO LEN(mainStr)-LEN(matchStr)+1
                                      IF @xptr[ @mainPtr ] = ch THEN
                                        j = 1
                                        WHILE @xptr[ @mainPtr[j] ] = @xptr[ @matchPtr[j] ] AND @matchPtr[j] AND @mainPtr[j]
                                          INCR j
                                        WEND
                                        IF @matchPtr[j]=0 THEN
                                          FUNCTION = i
                                          EXIT FUNCTION
                                        END IF
                                      END IF
                                      INCR mainPtr
                                    NEXT
                                    FUNCTION = 0
                                  END FUNCTION
                                  FUNCTION caseFreeInstr(mainStr AS STRING, matchStr AS STRING, mainStrIsUcase AS LONG, reverse AS LONG) AS LONG
                                     'note1: this fcn will cap mainStr and matchStr! This can good for multiple compares on the same mainStr.
                                     'note2: fcn allows to shortcut past UCASE'ing mainStr by setting mainStrIsUcase <> 0. If function has been called
                                     'already, and mainStr has been used previously as a parameter to this function (and has not been altered since),
                                     'then setting mainStrIsUcase to non-zero will speed function up many X.
                                     REGISTER ii AS LONG
                                     STATIC oneTime AS LONG
                                     DIM sArr(LEN(mainStr)-1) AS BYTE AT STRPTR(mainStr)
                                     DIM sArr2(LEN(matchStr)-1) AS BYTE AT STRPTR(matchStr)
                                  
                                     IF oneTime = 0 THEN
                                        oneTime = 1
                                        DIM translateArr(255) AS STATIC BYTE
                                        CALL arrayAssign(translateArr())
                                     END IF
                                  
                                     FOR ii = 0 TO LEN(matchStr)-1
                                        sArr2(ii) = translateArr(sArr2(ii)) 'make it uppercase
                                     NEXT
                                  
                                     IF mainStrIsUcase = 0 THEN
                                     FOR ii = 0 TO LEN(mainStr)-1
                                        sArr(ii) = translateArr(sArr(ii))   'make it uppercase
                                     NEXT
                                     END IF
                                  
                                     IF reverse = 0 THEN
                                        FUNCTION = INSTR(mainStr, matchStr)
                                     ELSE
                                        FUNCTION = INSTR(-1, mainStr, matchStr)
                                     END IF
                                  
                                  END FUNCTION
                                  
                                  SUB arrayAssign(translateArr() AS BYTE)
                                  ARRAY ASSIGN translateArr() = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, _
                                  21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, _
                                  41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60, _
                                  61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, _
                                  81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,65,66,67,68, _
                                  69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88, _
                                  89,90,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140, _
                                  141,142,143,144,145,146,147,148,149,150,151,152,153,138,155,140,157,142,159,160, _
                                  161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180, _
                                  181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, _
                                  201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, _
                                  221,222,223,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208, _
                                  209,210,211,212,213,214,247,216,217,218,219,220,221,222,159
                                  END SUB

                                  Comment


                                  • #18
                                    re the UCASE/LCASE thing...

                                    You could always test "as is" and see if you get a hit, then only if that fails you UCASE$ it and test.

                                    Saves a UCASE operation (=create string + move data (converting) + destroy string) whenever there is a 'natural' hit.

                                    Might produce some interesting results.

                                    Comment


                                    • #19
                                      > *gold standard* INSTR(UCASE$(line1), UCASE$(line2)).

                                      How do you know that is the "gold standard" using "pure" PowerBASIC statements?

                                      REGEXPR might be the true gold standard for "case insensitive string search usng simple PowerBASIC syntax."

                                      Comment


                                      • #20
                                        Originally posted by John Gleason View Post
                                        I'm still getting an error even with strictly text string searches chrs 32-122 in InstrI2. Here it's being compared to the output from the *gold standard* INSTR(UCASE$(line1), UCASE$(line2)). InstrI so far looks good for text.
                                        Code:
                                        #COMPILE EXE
                                        #DIM ALL
                                         
                                         FUNCTION PBMAIN () AS LONG
                                            RANDOMIZE
                                          'Call CreateTable
                                          CALL DoTest()
                                        END FUNCTION
                                         SUB DoTest()
                                          LOCAL hWin AS DWORD, ii2, ii3, r1,r2,r3,r4,rlen AS LONG
                                          LOCAL a, line1,line2,line3,exitStr AS STRING
                                           TXT.WINDOW("Case Insensitive Test", 10, 10) TO hWin
                                          LOCAL i AS LONG
                                         
                                           line1 = REPEAT$(100 ,"QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./qwertyuiop[]asdfghjkl;'zxcvbnm,./1234567890-=") & "HeLLo THErE!"
                                           line3 = line1
                                           line2 = "Hello There!"
                                         
                                        DO
                                           line1 = line3
                                           RESET line2
                                           FOR rLen = 1 TO RND(1, 32)
                                              line2 &= CHR$(RND(32,122))'97,25))',rnd(0,255),rnd(0,255),rnd(0,255),rnd(0,255))', _x
                                           NEXT
                                         
                                           ii2 = RND(1, 7800): IF ii2 > 7800\2 THEN ii3 = -1 ELSE ii3 = 0
                                           MID$(line1, ii2) = line2
                                           r3 = INSTR(UCASE$(line1), UCASE$(line2))
                                           r1 =        InstrI(line1, line2)
                                           r2 =       InstrI2(line1, line2)
                                        '   r4 = caseFreeInstr(line1, line2, 0, 0) 'has to be last because it caps line1 & line2
                                         
                                        '   IF r1 xor r2 xor r3 xor r4 THEN TXT.PRINT "mismatch";r1;r2;r3;r4: TXT.WAITKEY$ TO exitStr: IF exitStr = "x" OR exitStr = CHR$(27) THEN EXIT SUB
                                        '!wait
                                        '!wait
                                        'IF r2 = 0 THEN ITERATE DO
                                        'IF r1 = r2 AND r1 <> r3 THEN ITERATE DO
                                             IF r1 = r2 AND r1 = r3 THEN ii3 = 0 ELSE TXT.PRINT "mismatch";r1;r2;r3: TXT.WAITKEY$ TO exitStr: IF exitStr = "x" OR exitStr = CHR$(27) THEN EXIT SUB
                                        '    IF r1 <> r3 then TXT.PRINT "mismatch";r1;r2;r3;r4: TXT.WAITKEY$ TO exitStr: IF exitStr = "x" OR exitStr = CHR$(27) THEN EXIT SUB
                                           ITERATE DO
                                         
                                           IF INSTR(line1, line2) = caseFreeInstr(line1, line2, 0, ii3) THEN
                                            '  pcps(line1)
                                            '  pcwait
                                             ! nop
                                        '      pcpss(line2)
                                           ELSE
                                              IF INSTR(line1, line2) = caseFreeInstr(line1, line2, 0, ii3) THEN
                                              'pcpss(line2 & "ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok")
                                              ELSE
                                                 'pcps(line1)
                                                 'pcps(line2)
                                            '  ? "oooooo"
                                              END IF
                                           END IF
                                        LOOP
                                         
                                          CALL TestSearch( "this is a super great test of the case insenstive isntri", "InS" )
                                          CALL TestSearch( "short run", "RUN" )
                                          CALL TestSearch(REPEAT$(20, "Haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay") & "needle", "NEEDLE" )
                                          TXT.PRINT "Finished"
                                          TXT.LINE.INPUT a
                                        END SUB
                                         SUB TestSearch( BYVAL matchString AS STRING, BYVAL findString AS STRING )
                                          LOCAL j AS LONG
                                          LOCAL st, en AS QUAD
                                          LOCAL s1#, e1#
                                           LOCAL msg AS STRING
                                          LOCAL i, c AS LONG
                                          s1 = TIMER
                                            FOR j=1 TO 1000000
                                              i = caseFreeInstr( matchString, findString, 0, 0 )
                                            NEXT
                                          e1 = TIMER
                                          TXT.PRINT "cFInstr "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                                          s1 = TIMER
                                            FOR j=1 TO 1000000
                                              i = InstrI( matchString, findString )
                                            NEXT
                                          e1 = TIMER
                                          TXT.PRINT "InstrI. "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                                           s1 = TIMER
                                            FOR j=1 TO 1000000
                                              i = InstrI2( matchString, findString )
                                            NEXT
                                          e1 = TIMER
                                          TXT.PRINT "InstrI2 "; FORMAT$( i, "0"); " in "; FORMAT$( e1-s1, "#,##0.0000")
                                           matchString = UCASE$( matchString )
                                          findString = UCASE$( findString )
                                          s1 = TIMER
                                            FOR j=1 TO 1000000
                                              i = INSTR( matchString, findString )
                                            NEXT
                                          e1 = TIMER
                                          TXT.PRINT "Instr.. "; FORMAT$( i, "0"); " in ";  FORMAT$( e1-s1, "#,##0.0000")
                                          TXT.PRINT "-------------------------------------------------"
                                          TXT.PRINT FORMAT$( en-st, "#,##0" )
                                         END SUB
                                         SUB CreateTable()
                                          LOCAL i AS LONG
                                          LOCAL x AS ISTRINGBUILDERA
                                          x = CLASS "StringBuilderA"
                                          x.Add( $TAB )
                                          x.Add( $TAB )
                                          x.Add( "DB " )
                                          FOR i = 0 TO 255
                                            x.Add( RIGHT$("  "+STR$(ASC(UCASE$(CHR$(i)))), 3) )
                                            IF i MOD 16=15 THEN
                                              x.Add( $CRLF )
                                              IF i<255 THEN
                                                x.Add( $TAB )
                                                x.Add( $TAB )
                                                x.Add( "DB " )
                                              END IF
                                            ELSE
                                              x.Add( ", " )
                                            END IF
                                          NEXT
                                          CLIPBOARD SET TEXT x.String
                                        END SUB
                                         ' Uses ucase collation
                                        ASMDATA xlatTable
                                          DB   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15
                                          DB  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31
                                          DB  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47
                                          DB  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63
                                          DB  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
                                          DB  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95
                                          DB  96,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
                                          DB  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90, 123, 124, 125, 126, 127
                                          DB 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143
                                          DB 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 138, 155, 140, 157, 142, 159
                                          DB 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175
                                          DB 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191
                                          DB 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207
                                          DB 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223
                                          DB 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207
                                          DB 208, 209, 210, 211, 212, 213, 214, 247, 216, 217, 218, 219, 220, 221, 222, 159
                                        END ASMDATA
                                         FUNCTION InstrI2( mainStr AS STRING, matchStr AS STRING ) AS LONG
                                          #REGISTER NONE
                                          ! MOV EDX, 1
                                          ! MOV ESI, mainStr
                                          ! MOV ESI, [ESI]           ; ESI = StrPtr( mainStr )
                                          ! MOV EDI, matchStr
                                          ! MOV EDI, [EDI]            ; EDI = StrPtr( matchStr )
                                          ! LEA EBX, xlatTable        ; Translate table
                                          ! XOR EAX, EAX              ; EAX = 0
                                          ! MOV AL, BYTE PTR[EDI]     ; AL = @matchStr[0]
                                          ! XLATB                     ; AL = UCase$( @matchStr[0] )
                                          ! MOV ECX, EAX              ; ECX = UCase$( @matchStr[0] )
                                        InstrI2_Loop:
                                          ! MOV AL, BYTE PTR[ESI]     ; EAX = @a
                                          ! OR AL, AL                 ; If @mainStr = 0
                                          ! JZ InstrI2_NoMatch        ; Goto Exit
                                          ! XLATB                     ; EAX = UCase$( @mainStr )
                                          ! SUB EAX, ECX              ; if @mainStr = @matchStr
                                          ! JZ InstrI2_Cmp            ; Goto Compare
                                          ! XOR EAX, EAX              ; Clear upper registers of A
                                          ! INC ESI                   ; Incr mainStr
                                          ! INC EDX                   ; Incr Pos
                                          ! JMP SHORT InstrI2_Loop    ; Try next byte
                                        InstrI2_Cmp:
                                          ! PUSH ESI                  ; Save mainStr
                                          ! PUSH EDI                  ; Save findStr
                                          ! PUSH ECX                  ; Save UCase$( @findStr )
                                          ! INC ESI                   ; Already compared this byte
                                          ! INC EDI                   ; Already compared this byte
                                        InstrI2_CmpLoop:
                                          ! MOV AL, BYTE PTR[EDI]     ; AL = @matchStr
                                          ! OR AL, AL                 ; If @matchStr=0
                                          ! JZ InstrI2_Match          ; Then Goto Match! yay
                                          ! XLATB                     ; AL = UCase$( @matchStr )
                                          ! MOV ECX, EAX              ; ECX = UCase$( @matchStr )
                                          ! MOV AL, BYTE PTR[ESI]     ; EAX = @mainStr
                                          ! OR AL, AL                 ; If @matchStr=0
                                          ! JZ InstrI2_CmpEnd         ; Then we finish failed (no match)
                                          ! XLATB                     ; EAX = UCase$( @mainStr )
                                          ! SUB EAX, ECX              ; if @mainStr<>@matchStr
                                          ! JNZ InstrI2_CmpFail       ; Then Goto Compare Failed
                                          ! INC EDI                   ; Incr matchStr
                                          ! INC ESI                   ; Incr mainStr
                                          ! JMP SHORT InstrI2_CmpLoop ; Try next byte
                                         InstrI2_CmpFail:
                                          ! POP ECX                   ; Restore UCase$( @findStr )
                                          ! POP EDI                   ; Restore findStr
                                          ! POP ESI                   ; Restore mainStr
                                          ! INC ESI                   ; Incr mainStr
                                          ! INC EDX                   ; Incr pos
                                          ! JMP SHORT InstrI2_Loop
                                        InstrI2_Match:
                                          ! POP ECX                   ; Restore UCase$( @findStr )
                                          ! POP EDI                   ; Restore findStr
                                          ! POP ESI                   ; Restore mainStr
                                          ! JMP SHORT InstrI2_Finish  ; Return Position
                                        InstrI2_CmpEnd:
                                          ! POP ECX                   ; Restore UCase$( @findStr )
                                          ! POP EDI                   ; Restore findStr
                                          ! POP ESI                   ; Restore mainStr
                                        InstrI2_NoMatch:
                                          ! XOR EDX, EDX              ; Pos = 0 (No match)
                                        InstrI2_Finish:
                                          ! MOV FUNCTION, EDX         ; Function = pos
                                        END FUNCTION
                                         FUNCTION InstrI( mainStr AS STRING, matchStr AS STRING ) AS LONG
                                          REGISTER i AS LONG
                                          REGISTER j AS LONG
                                          LOCAL mainPtr, matchPtr, xptr AS BYTE PTR
                                          LOCAL ch AS BYTE
                                          mainPtr = STRPTR( mainStr )
                                          matchPtr = STRPTR( matchStr )
                                          xptr = CODEPTR( xlatTable )
                                          ch = @xptr[ @matchPtr ]
                                           FOR i=1 TO LEN(mainStr)-LEN(matchStr)+1
                                            IF @xptr[ @mainPtr ] = ch THEN
                                              j = 1
                                              WHILE @xptr[ @mainPtr[j] ] = @xptr[ @matchPtr[j] ] AND @matchPtr[j] AND @mainPtr[j]
                                                INCR j
                                              WEND
                                              IF @matchPtr[j]=0 THEN
                                                FUNCTION = i
                                                EXIT FUNCTION
                                              END IF
                                            END IF
                                            INCR mainPtr
                                          NEXT
                                          FUNCTION = 0
                                        END FUNCTION
                                        FUNCTION caseFreeInstr(mainStr AS STRING, matchStr AS STRING, mainStrIsUcase AS LONG, reverse AS LONG) AS LONG
                                           'note1: this fcn will cap mainStr and matchStr! This can good for multiple compares on the same mainStr.
                                           'note2: fcn allows to shortcut past UCASE'ing mainStr by setting mainStrIsUcase <> 0. If function has been called
                                           'already, and mainStr has been used previously as a parameter to this function (and has not been altered since),
                                           'then setting mainStrIsUcase to non-zero will speed function up many X.
                                           REGISTER ii AS LONG
                                           STATIC oneTime AS LONG
                                           DIM sArr(LEN(mainStr)-1) AS BYTE AT STRPTR(mainStr)
                                           DIM sArr2(LEN(matchStr)-1) AS BYTE AT STRPTR(matchStr)
                                         
                                           IF oneTime = 0 THEN
                                              oneTime = 1
                                              DIM translateArr(255) AS STATIC BYTE
                                              CALL arrayAssign(translateArr())
                                           END IF
                                         
                                           FOR ii = 0 TO LEN(matchStr)-1
                                              sArr2(ii) = translateArr(sArr2(ii)) 'make it uppercase
                                           NEXT
                                         
                                           IF mainStrIsUcase = 0 THEN
                                           FOR ii = 0 TO LEN(mainStr)-1
                                              sArr(ii) = translateArr(sArr(ii))   'make it uppercase
                                           NEXT
                                           END IF
                                         
                                           IF reverse = 0 THEN
                                              FUNCTION = INSTR(mainStr, matchStr)
                                           ELSE
                                              FUNCTION = INSTR(-1, mainStr, matchStr)
                                           END IF
                                         
                                        END FUNCTION
                                         
                                        SUB arrayAssign(translateArr() AS BYTE)
                                        ARRAY ASSIGN translateArr() = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, _
                                        21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, _
                                        41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60, _
                                        61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, _
                                        81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,65,66,67,68, _
                                        69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88, _
                                        89,90,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140, _
                                        141,142,143,144,145,146,147,148,149,150,151,152,153,138,155,140,157,142,159,160, _
                                        161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180, _
                                        181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, _
                                        201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, _
                                        221,222,223,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208, _
                                        209,210,211,212,213,214,247,216,217,218,219,220,221,222,159
                                        END SUB
                                        Yep, the version you're using has a bug in it. I'm away from my computer atm, but that's the only thing I noticed. The issue is caused whenever ECX is > EAX, EAX becomes a negative number, throwing off all subsequent comparisons. Not sure how you got that version of code, thought it was briefly lived as I was trying some options to get rid of the XOR. CMP seemed to have a similar issue. XOR EAX, ECX didn't seem to work either.
                                        Code:
                                          ! SUB EAX, ECX              ; if @mainStr<>@matchStr
                                          ! JNZ InstrI2_CmpFail       ; Then Goto Compare Failed
                                          ! XOR EAX, EAX
                                          ! INC EDI                   ; Incr matchStr
                                          ! INC ESI                   ; Incr mainStr
                                          ! JMP SHORT InstrI2_CmpLoop ; Try next byte
                                         InstrI2_CmpFail:
                                        LarryC
                                        Website
                                        Sometimes life's a dream, sometimes it's a scream

                                        Comment

                                        Working...
                                        X