Tvůrce webu je i pro tebe! Postav třeba web. Bez grafika. Bez kodéra. Hned.
wz

Obsah / Utility / TEXT / TextFindFirst, TextFind

Zdrojový kód: INCLUDE\UTIL\TEXT.INC, UTIL\TEXT.ASM


TextFindFirst, TextFind - Vyhledání prvního/dalšího řetězce v textu

Funkce TextFindFirst vyhledá v textu první výskyt daného textového řetězce. Funkce TextFind vyhledá první výskyt textového řetězce od dané pozice v textu (včetně).


; -----------------------------------------------------------------------------
;                       Find text in forward direction
; -----------------------------------------------------------------------------
; INPUT:	EAX = pointer to TEXT to find
;		EBX = pointer to TEXT where to search
;		EDX = start offset (it can be out of range)
; OUTPUT:	EDX = offset of found text (or EDX = -1 if text not found)
;		CY = text not found (EDX = -1)
; NOTES:	Set EDX to 0 to find first occurrence of the text.
; -----------------------------------------------------------------------------
; TEXT_Ref of text to find may be invalid (see TextFindChar, TextFindCharRev).

; ------------- Find first occurrence of the text

TextFindFirst:	xor	edx,edx		; EDX <- 0, find first occurrence

Na vstupu funkce obsahuje registr EAX ukazatel na textovou proměnnou s hledaným textovým řetězcem. Registr EBX obsahuje ukazatel na textovou proměnnou s textem k prohledání. Registr EDX obsahuje počáteční offset v textu, od kterého bude textový řetězec vyhledáván. V případě funkce TextFindFirst je obsah registru EDX nejdříve vynulován, čímž se zajistí nastavení ukazatele na začátek textu.

Referenční čítač TEXT_Ref hledaného textového řetězce se ve funkci ignoruje a to je využito v některých funkcích které tuto funkci volají - např. TextFindChar a TextFindCharRev.

Je-li textový řetězec v textu nalezen, je navrácena v registru EDX pozice (offset) řetězce v textu a příznak CF je vynulován. Není-li textový řetězec nalezen, je v registru EDX navrácena hodnota -1 a je navrácen příznak chyby CY.


; ------------- Push registers 1

TextFind:	push	eax		; push EAX
		push	esi		; push ESI
		push	ebp		; push EBP

; ------------- Use faster method

		mov	esi,[eax]	; ESI <- source data buffer
		mov	ebp,[esi+TEXT_Length] ; EBP <- text length
		or	ebp,ebp		; zero length?
		jz	short TextFind9	; zero length, always return NC
		mov	al,[esi+TEXT_Text] ; AL <- first byte
		dec	ebp		; length 1?
		jz	TextFind2	; text is one byte long
		mov	ah,[esi+TEXT_Text+1] ; AH <- second byte
		dec	ebp		; length 2?
		jnz	TextFind3	; text is too long
		call	TextFindWord	; find two bytes
		jmp	short TextFind9
TextFind2:	call	TextFindByte	; find one byte
		jmp	short TextFind9

Po úschově první části registrů se funkce pokusí provést hledání zrychleně. Do registru EBP si připraví délku hledaného textu. Pokud má hledaný řetězec nulovou délku, funkce se ihned ukončí s vynulovaným příznakem chyby NC znamenajícím, že nulový řetězec je nalezen vždy. Pro délku řetězce 1 se do registru AL připraví první bajt řetězce a hledání se provede funkcí TextFindByte. Pro délku řetězce 2 se do registru AH navíc připraví druhý bajt řetězce a hledání se provede funkcí TextFindWord. Jinak se použije pomalé hledání.


; ------------- Push registers 2

TextFind3:	push	ebx		; push EBX
		push	ecx		; push ECX
		push	edi		; push EDI

; ------------- Limit minimal offset to 0 (-> EDX)

		or	edx,edx		; is start offset negative?
		sets	cl		; CL <- 0 if EDX >= 0, 1 if EDX < 0
		movzx	ecx,cl		; ECX <- 0 if EDX >= 0, 1 if EDX < 0
		dec	ecx		; ECX <- -1 if EDX >= 0, 0 if EDX < 0
		and	edx,ecx		; EDX <- 0 if EDX < 0

Po úschově druhé části registrů bude omezen minimální počáteční offset v textu (aby neukazoval pod hodnotu 0). Pro zrychlení kódu se nepoužije instrukce skoku, ale využijí se operace s registry. Po testu obsahu registru EDX se nastaví obsah registru CL na hodnotu 0, pokud byl offset nezáporný, nebo 1, pokud byl offset záporný. Po rozšíření hodnoty do registru ECX a dekrementaci bude registr ECX obsahovat hodnotu -1 (tj. 0FFFFFFFFh) pokud byl offset nezáporný, nebo hodnotu 0, pokud byl offset záporný. Zamaskováním offsetu v registru EDX registrem ECX se zajistí vynulování obsahu registru EDX v případě, že offset byl záporný.


; ------------- Get text length (-> ECX) and text address (-> EDI)

		mov	edi,[ebx]	; EDI <- data buffer
		mov	ecx,[edi+TEXT_Length] ; ECX <- length of text
		sub	ecx,ebp		; ECX <- without text length - 2
		dec	ecx		; ECX <- without first byte
		lea	edi,[edi+TEXT_Text+edx] ; EDI <- start pointer
		sub	ecx,edx		; ECX <- remaining data
		jle	short TextFind7	; data not found

Do registru ECX se připraví celková délka prohledávaného textu (v bajtech) snížená o délku řetězce a snížená o 1 (což je rezerva pro zbytek řetězce bez prvního bajtu) a do registru EDI ukazatel na začátek prohledávaného textu (posunutý na počáteční pozici k prohledání). Odečtením pozice počátku textu zůstane v registru ECX počet zbylých pozic k prohledání - při podtečení se funkce ukončí s navrácením příznaku chyby CY a s hodnotou -1 v registru EDX.


; ------------- Find begin of data

		mov	edx,[ebx]	; EDX <- data buffer
		add	esi,TEXT_Text+2	; ESI <- start of text to find
TextFind4:	repne	scasb		; find data
		jne	short TextFind7	; data not found
		cmp	ah,[edi]	; check second byte
		jne	TextFind4	; text not equal

Je-li počet bajtů kladný, vyhledá se první bajt řetězce v textu - je obsažen v registru AL. Nebyl-li nalezen (instrukce SCASB navrátí příznak NZ) funkce se opět ukončí s příznakem chyby CY a hodnotou -1 v registru EDX. Pokud byl první bajt nalezen, otestuje se druhý bajt - ten se nachází v registru AH. Jestliže druhý bajt nesouhlasí, pokračuje se vyhledáváním prvního bajtu od další pozice v textu.


; ------------- Compare rest of text

		push	esi		; push ESI
		push	edi		; push EDI
		mov	ebx,ecx		; EBX <- push ECX

		inc	edi		; EDI <- start of text
		mov	ecx,ebp		; ECX <- text length - 2
		shr	ecx,2		; ECX <- text length in DWORDs
		repe	cmpsd		; compare texts in DWORDs
		jne	TextFind6	; texts not equal
		mov	ecx,ebp		; ECX <- text length
		and	ecx,byte 3	; ECX <- rest of text in last DWORD
		repe	cmpsb		; compare bytes in last DWORD

TextFind6:	mov	ecx,ebx		; pop ECX
		pop	edi		; pop EDI
		pop	esi		; pop ESI
		jne	TextFind4	; text not equal

V případě nalezení prvních 2 bajtů hledaného řetězce se provede porovnání zbytku řetězce. Uchovají se ukazatele pozice v textu a porovná se zbytek řezězce - pro zrychlení se porovnání provádí nejdříve po dvojslovech (instrukce cmpsd) a nakonec se porovnají zbylé bajty v posledním dvojslově řetězce. Navrátí se registry a pokud řetězec nesouhlasí, pokračuje se v hledání další pozicí v textu.


; ------------- Get offset of data

		lea	ecx,[ebp+ecx+2]	; ECX <- return found data
		mov	edx,[edx+TEXT_Length] ; EDX <- text length
		sub	edx,ecx		; EDX <- data offset (it sets NC)
		jmp	short TextFind8

; ------------- Text not found

TextFind7:	xor	edx,edx		; EDX <- 0
		dec	edx		; EDX <- -1, data not found
		stc			; set error flag

; ------------- Pop registers 2

TextFind8:	pop	edi		; pop EDI
		pop	ecx		; pop ECX
		pop	ebx		; pop EBX

; ------------- Pop registers 1

TextFind9:	pop	ebp		; pop EBP
		pop	esi		; pop ESI
		pop	eax		; pop EAX
		ret

Je-li řetězec úspěšně nalezen, obnoví se v registru ECX počet zbylých bajtů do konce textu, odečtením od délky textu se v registru EDX obdrží offset nalezené pozice v textu a funkce se ukončí s vynulovaným příznakem chyby NC.


Obsah / Utility / TEXT / TextFindFirst, TextFind