213 lines
9 KiB
HTML
213 lines
9 KiB
HTML
<HTML>
|
|
<HEAD>
|
|
<META name=vsisbn content="1576101746">
|
|
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
|
<META name=vsauthor content="Michael Abrash">
|
|
<META name=vspublisher content="The Coriolis Group">
|
|
<META name=vspubdate content="07/01/97">
|
|
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: Boyer-Moore String Searching</TITLE>
|
|
|
|
<!-- HEADER -->
|
|
<!-- Empty Reference Subhead -->
|
|
|
|
<!--ISBN=1576101746//-->
|
|
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
|
<!--AUTHOR=Michael Abrash//-->
|
|
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
|
<!--CHAPTER=14//-->
|
|
<!--PAGES=268-271//-->
|
|
<!--UNASSIGNED1//-->
|
|
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
|
|
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="14-03.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="14-05.html">Next</A></TD>
|
|
</TR>
|
|
</TABLE>
|
|
</CENTER>
|
|
<P><BR></P>
|
|
<P><B>LISTING 14.1 L14-1.C</B></P>
|
|
<!-- CODE //-->
|
|
<PRE>
|
|
/* Searches a buffer for a specified pattern. In case of a mismatch,
|
|
uses the value of the mismatched byte to skip across as many
|
|
potential match locations as possible (partial Boyer-Moore).
|
|
Returns start offset of first match searching forward, or NULL if
|
|
no match is found.
|
|
Tested with Borland C++ in C mode and the small model. */
|
|
|
|
#include <stdio.h>
|
|
|
|
unsigned char * FindString(unsigned char * BufferPtr,
|
|
unsigned int BufferLength, unsigned char * PatternPtr,
|
|
unsigned int PatternLength)
|
|
{
|
|
unsigned char * WorkingPatternPtr, * WorkingBufferPtr;
|
|
unsigned int CompCount, SkipTable[256], Skip, DistanceMatched;
|
|
int i;
|
|
|
|
/* Reject if the buffer is too small */
|
|
if (BufferLength < PatternLength) return(NULL);
|
|
|
|
/* Return an instant match if the pattern is 0-length */
|
|
if (PatternLength == 0) return(BufferPtr);
|
|
|
|
/* Create the table of distances by which to skip ahead on
|
|
mismatches for every possible byte value */
|
|
/* Initialize all skips to the pattern length; this is the skip
|
|
distance for bytes that don’t appear in the pattern */
|
|
for (i = 0; i < 256; i++) SkipTable[i] = PatternLength;
|
|
/*Set the skip values for the bytes that do appear in the pattern
|
|
to the distance from the byte location to the end of the
|
|
pattern. When there are multiple instances of the same byte,
|
|
the rightmost instance’s skip value is used. Note that the
|
|
rightmost byte of the pattern isn’t entered in the skip table;
|
|
if we get that value for a mismatch, we know for sure that the
|
|
right end of the pattern has already passed the mismatch
|
|
location, so this is not a relevant byte for skipping purposes */
|
|
for (i = 0; i < (PatternLength - 1); i++)
|
|
SkipTable[PatternPtr[i]] = PatternLength - i - 1;
|
|
|
|
/* Point to rightmost byte of the pattern */
|
|
PatternPtr += PatternLength - 1;
|
|
/* Point to last (rightmost) byte of the first potential pattern
|
|
match location in the buffer */
|
|
BufferPtr += PatternLength - 1;
|
|
/* Count of number of potential pattern match locations in
|
|
buffer */
|
|
BufferLength -= PatternLength - 1;
|
|
|
|
/* Search the buffer */
|
|
while (1) {
|
|
/* See if we have a match at this buffer location */
|
|
WorkingPatternPtr = PatternPtr;
|
|
WorkingBufferPtr = BufferPtr;
|
|
CompCount = PatternLength;
|
|
/* Compare the pattern and the buffer location, searching from
|
|
high memory toward low (right to left) */
|
|
while (*WorkingPatternPtr— == *WorkingBufferPtr—) {
|
|
/* If we’ve matched the entire pattern, it’s a match */
|
|
if (–CompCount == 0)
|
|
/* Return a pointer to the start of the match location */
|
|
return(BufferPtr - PatternLength + 1);
|
|
}
|
|
/* It’s a mismatch; let’s see what we can learn from it */
|
|
WorkingBufferPtr++; /* point back to the mismatch location */
|
|
/* # of bytes that did match */
|
|
DistanceMatched = BufferPtr - WorkingBufferPtr;
|
|
/*If, based on the mismatch character, we can’t even skip ahead
|
|
as far as where we started this particular comparison, then
|
|
just advance by 1 to the next potential match; otherwise,
|
|
skip ahead from the mismatch location by the skip distance
|
|
for the mismatch character */
|
|
if (SkipTable[*WorkingBufferPtr] <= DistanceMatched)
|
|
Skip = 1; /* skip doesn’t do any good, advance by 1 */
|
|
else
|
|
/* Use skip value, accounting for distance covered by the
|
|
partial match */
|
|
Skip = SkipTable[*WorkingBufferPtr] - DistanceMatched;
|
|
/* If skipping ahead would exhaust the buffer, we’re done
|
|
without a match */
|
|
if (Skip >= BufferLength) return(NULL);
|
|
/* Skip ahead and perform the next comparison */
|
|
BufferLength -= Skip;
|
|
BufferPtr += Skip;
|
|
}
|
|
}
|
|
</PRE>
|
|
<!-- END CODE //-->
|
|
<P><B>LISTING 14.2 L14-2.C</B></P>
|
|
<!-- CODE //-->
|
|
<PRE>
|
|
/* Program to exercise buffer-search routines in Listings 14.1 & 14.3.
|
|
(Must be modified to put copy of pattern as sentinel at end of the
|
|
search buffer in order to be used with Listing 14.4.) */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
|
|
#define DISPLAY_LENGTH 40
|
|
#define BUFFER_SIZE 0x8000
|
|
|
|
extern unsigned char * FindString(unsigned char *, unsigned int,
|
|
unsigned char *, unsigned int);
|
|
void main(void);
|
|
|
|
void main() {
|
|
unsigned char TempBuffer[DISPLAY_LENGTH+1];
|
|
unsigned char Filename[150], Pattern[150], *MatchPtr, *TestBuffer;
|
|
int Handle;
|
|
unsigned int WorkingLength;
|
|
|
|
printf(“File to search:”);
|
|
gets(Filename);
|
|
printf(“Pattern for which to search:”);
|
|
gets(Pattern);
|
|
|
|
if ( (Handle = open(Filename, O_RDONLY | O_BINARY)) == -1 ) {
|
|
printf(“Can’t open file: %s\n”, Filename); exit(1);
|
|
}
|
|
/* Get memory in which to buffer the data */
|
|
if ( (TestBuffer=(unsigned char *)malloc(BUFFER_SIZE+1)) == NULL) {
|
|
printf(“Can’t get enough memory\n”); exit(1);
|
|
}
|
|
/* Process a BUFFER_SIZE chunk */
|
|
if ( (int)(WorkingLength =
|
|
read(Handle, TestBuffer, BUFFER_SIZE)) == -1 ) {
|
|
printf(“Error reading file %s\n”, Filename); exit(1);
|
|
}
|
|
TestBuffer[WorkingLength] = 0; /* 0-terminate buffer for printf */
|
|
/* Search for the pattern and report the results */
|
|
if ((MatchPtr = FindString(TestBuffer, WorkingLength, Pattern,
|
|
(unsigned int) strlen(Pattern))) == NULL) {
|
|
/* Pattern wasn’t found */
|
|
printf(“\“%s\” not found\n”, Pattern);
|
|
} else {
|
|
/* Pattern was found. Zero-terminate TempBuffer; strncpy
|
|
won’t do it if DISPLAY_LENGTH characters are copied */
|
|
TempBuffer[DISPLAY_LENGTH] = 0;
|
|
printf(“\“%s\” found. Next %d characters at match:\n\”%s\“\n”,
|
|
Pattern, DISPLAY_LENGTH,
|
|
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
|
}
|
|
exit(0);
|
|
}
|
|
</PRE>
|
|
<!-- END CODE //-->
|
|
<P>Well, architecture carries a lot of weight, but it sure as heck isn’t destiny. I had simply fallen into the trap of figuring that the algorithm was so clever that I didn’t have to do any thinking myself. The path leading to <B>REPNZ SCASB</B> from the original brute-force approach of <B>REPZ CMPSB</B> at every location had been based on my observation that the first character comparison at each buffer location usually fails. Why not apply the same concept to Boyer-Moore? Listing 14.3 is just like the standard implementation—except that it’s optimized to handle a first-comparison mismatch as quickly as possible in the loop at <B>QuickSearchLoop</B>, much as <B>REPNZ SCASB</B> optimizes first-comparison mismatches for the brute-force approach. The results in Table 14.1 speak for themselves; Listing 14.3 is more than twice as fast as what I assure you was already a nice, tight assembly implementation (and unrolling <B>QuickSearchLoop</B> could boost performance by up to 10 percent more). Listing 14.3 is also <I>four times</I> faster than <B>REPNZ SCASB</B> in one case.</P><P><BR></P>
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="14-03.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="14-05.html">Next</A></TD>
|
|
</TR>
|
|
</TABLE>
|
|
</CENTER>
|
|
|
|
<hr width="90%" size="1" noshade>
|
|
<div align="center">
|
|
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book © 2001 Michael Abrash</font>
|
|
</div>
|
|
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
|
<!-- reference_subfoot = footer -->
|
|
<!-- reference_footer = subfoot -->
|
|
|
|
<!-- BEGIN SUB FOOTER -->
|
|
</BODY>
|
|
</HTML>
|
|
|
|
<!-- END FOOTER -->
|
|
|
|
|