173 lines
7.8 KiB
Markdown
173 lines
7.8 KiB
Markdown
------------------------ --------------------------------- --------------------
|
||
[Previous](14-03.html) [Table of Contents](index.html) [Next](14-05.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
**LISTING 14.1 L14-1.C**
|
||
|
||
/* 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;
|
||
}
|
||
}
|
||
|
||
**LISTING 14.2 L14-2.C**
|
||
|
||
/* 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);
|
||
}
|
||
|
||
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 **REPNZ SCASB** from the original brute-force
|
||
approach of **REPZ CMPSB** 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 **QuickSearchLoop**, much as **REPNZ SCASB** 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 **QuickSearchLoop** could boost performance by up to 10
|
||
percent more). Listing 14.3 is also *four times* faster than **REPNZ
|
||
SCASB** in one case.
|
||
|
||
------------------------ --------------------------------- --------------------
|
||
[Previous](14-03.html) [Table of Contents](index.html) [Next](14-05.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
* * * * *
|
||
|
||
Graphics Programming Black Book © 2001 Michael Abrash
|