News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Using scasb and cmpsb

Started by Ryan, June 04, 2012, 08:28:22 AM

Previous topic - Next topic

Ryan

Hi everyone,

I'm trying to create a search routine that looks for a string through itemdata that is tied to each entry in a ListBox.  I think scasb and cmpsb fit my needs, but I can't get it to work correctly, so I'm not so sure.  This is for Win32.  I've searched for scasb and cmpsb, and it seems the examples I find are for DOS.  Is that my problem?

Have a look at the routine.  I'll comment on what everything is along the way.

DoFind proc uses edi ebx esi
    local numEntries:dword
   
    invoke SendDlgItemMessage,g_hWnd,lstEntries,LB_GETCOUNT,0,0
    mov numEntries,eax
    xor ebx,ebx
    .while ebx!=numEntries
        invoke SendDlgItemMessage,g_hWnd,lstEntries,LB_GETITEMDATA,ebx,0
        cld ; Clearing the direction flag after the API call in case it changes it?
        xor ecx,ecx
        mov cx,[eax] ; The itemdata in the ListBox is a pointer from HeapAlloc that starts with a 16-bit integer that specifies the length of the text immediately after it.  I am able to use it without problems when selecting an item in the ListBox and populate an Edit control accordingly.
        sub ecx,findStrLen ; Subtracting the length of the search string because I only initially scan for the first character
        add eax,2 ; Move the pointer to the start of the text.
        mov edi,eax ; For scasb
        mov edx,findStrPtr ; Load first character into al
        mov al,[edx] ; For scasb
        repnz scasb ; Find a match
        .if ZERO?
            mov esi,findStrPtr ; Match found.  Point to the search string for cmpsb.
            inc esi ; First character already checked.  Don't need to check it again.
            mov ecx,findStrLen ; Search the remainder of the string.
            dec ecx ; minus the first character.
            repz cmpsb ; While it's a match, continue checking subsequent characters.
            .if ZERO?
                ; Found the string
                sub edi,findStrLen ; Debugging purposes.  Trying to see if the string was really a match.
                invoke MessageBoxA,0,edi,0,0
                invoke SendDlgItemMessage,g_hWnd,lstEntries,LB_SETCURSEL,ebx,0 ; Select the item in the ListBox
                .break
            .endif
        .endif
        inc ebx
    .endw
    ret
DoFind endp


Thank you!

dedndave

first, you shouldn't have to use CLD
it is a requirement that the direction flag remained cleared when calling system functions
and - system functions that set it will clear it when done

next - we can't tell what lstEntries is - hopefully, it's the ID for the listbox
most of us would prefer to store the handle of the listbox and use SendMessage, instead

i am not so sure about what is returned in EAX after LB_GETITEMDATA
(i don't have a lot of experience with listboxes - other than trivials)
if you have a buffer address load that into EDI
the address of the string sent to the listbox may be old news   :biggrin:

        xor ecx,ecx
        mov cx,[eax]

can be replaced with
        movzx   ecx,word ptr [eax]

we can't tell what the values of findStrLen or findStrPtr are   :P

generally speaking, to compare the first byte, then compare strings, you might do something like...
        mov     esi,offset TestString
        mov     edi,offset buffer
        mov     al,[esi]
        .if al==[edi]
            mov     ecx,SizeOfTestString
            inc     esi
            inc     edi
            dec     ecx
            repz    cmpsb
            .if (ZERO?)
                ;do strings equal stuff
            .endif
        .endif

there are faster ways than using REPZ CMPSB
but - it does work under windows
in many cases, speed isn't that critical because you are not comparing thousands of strings with thousands of characters in each string

Ryan

Quote from: dedndave on June 04, 2012, 12:13:40 PM
first, you shouldn't have to use CLD
it is a requirement that the direction flag remained cleared when calling system functions
and - system functions that set it will clear it when done
Good to know.
next - we can't tell what lstEntries is - hopefully, it's the ID for the listbox
most of us would prefer to store the handle of the listbox and use SendMessage, instead
Correct.  lstEntries is the ID for the ListBox.
i am not so sure about what is returned in EAX after LB_GETITEMDATA
if you have a buffer address load that into EDI
the address of the string sent to the listbox may be old news   :biggrin:
I use HeapAlloc to get the pointer.  MSDN states: Memory allocated by HeapAlloc is not movable. The address returned by HeapAlloc is valid until the memory block is freed or reallocated; the memory block does not need to be locked. Because the system cannot compact a private heap, it can become fragmented.

I assume that's what you mean by old news.  I am not calling HeapFree until the entries are removed from the ListBox.  I load the pointer to the string to search into EDI.

        xor ecx,ecx
        mov cx,[eax]

can be replaced with
        movzx   ecx,word ptr [eax]

we can't tell what the values of findStrLen or findStrPtr are   :P
They are global variables that are set before I call the routine.  I have verified through message boxes that they are correct.
generally speaking, to compare the first byte, then compare strings, you might do something like...
        mov     esi,offset TestString
        mov     edi,offset buffer
        mov     al,[esi]
        .if al==[edi]
            mov     ecx,SizeOfTestString
            inc     esi
            inc     edi
            dec     ecx
            repz    cmpsb
            .if (ZERO?)
                ;do strings equal stuff
            .endif
        .endif

Not sure how that would work in a loop.  Since I set EDI to point to the start of the buffer, it should increment with every iteration of repnz scasb, no?

dedndave

QuoteI think scasb and cmpsb fit my needs, but I can't get it to work correctly, so I'm not so sure.

could you tell us what it does or doesn't do ?
does the program crash with an exception error ?
can you post a more complete test-piece so we don't have to write a listbox window to test it   :P

EDIT - i don't know if it's the site or my provider, but i am experiencing long delays - lol
it makes it hard to have a back-and-forth convo, at the moment

hutch--

Ryan,

Do yourself a favour, perform scans and compares in direct mnemonics, its generally faster and gives you more control. Its only special case circuitry combined with REP(E) that get the old string instructions up to speed in some tasks over a certain size.

Ryan

Dave,

It doesn't crash or hang.  Sometimes it works; sometimes it doesn't work.  Not working is one of two scenarios: either it's a false positive, or it misses what I know it should find.

One thing that occurred to me is that there are entries that are blank.  In my case, blank means that the listbox itemdata is a pointer to 3 bytes, all zeros.  If testing ecx happens after it is decremented, things may become unpredictable.  I tried testing ecx for zero before the scan.  It seems to take away the false positive (it was finding the blank entry), but it doesn't help any for finding what it should.

Hutch,

I think I'm going to do that.  I thought it would be faster using scasb and cmpsb, but if that's not the case, I'd rather write it out and take control back, especially considering the scenario I outlined in the previous paragraph.  Thank you for the insight.

jj2007

Try this:
mov al,[edx] ; For scasb
inc ecx
TryAgain: repnz scasb ; Find a match
.if ZERO?
mov esi,findStrPtr ; Match found. Point to the search string for cmpsb.
inc esi ; First character already checked. Don't need to check it again.
push eax
push ecx
push edi
mov ecx,findStrLen ; Search the remainder of the string.
dec ecx ; minus the first character.
repz cmpsb ; While it's a match, continue checking subsequent characters.
xchg edx, edi
pop edi
pop ecx
pop eax
jnz TryAgain
xchg edx, edi
; Found the string
sub edi,findStrLen ; Debugging purposes. Trying to see if the string was really a match.
invoke MessageBoxA,0,edi,0,0
; invoke SendDlgItemMessage,g_hWnd,lstEntries,LB_SETCURSEL,ebx,0 ; Select the item in the ListBox
.break
.endif
inc ebx
.endw

Ryan

Well I was working on converting the routine to direct mnemonics.  It occurred to me as I was doing it that I completely left out an else condition for the inner test.  I think I was skeptical about using scasb and cmpsb that I wanted to get it to a point where I could test the usage without finishing it.  I'm rather ashamed to admit it, but I completely dropped the ball on this one.  It was all me.

I see that jj caught my incompleteness.  Thank you for that.  You are all top-notch.  I am just in awe.

jj2007

Quote from: Ryan on June 05, 2012, 09:24:34 AM
I'm rather ashamed to admit it, ... I am just in awe.

Cool down, quickly  :biggrin:
We all started where you are now, and your start is among the better ones :t
Oh, and by the way: Welcome to the Forum :icon14: