My approach is less portable, but much flexible and faster...
program ByteSearch;
uses SysUtils;
function RDTSC: Int64; // dirty counter...
var
TSC: record
case Byte of
1: (Ticks: Int64);
2: (LO, HI: LongWord);
end;
begin
TSC.Ticks:=0;
{$ASMMODE INTEL}
asm
db $0F, $31;
{$IFDEF CPU386}
MOV [TSC.LO], EAX
MOV [TSC.HI], EDX
{$ELSE}
NOP
{$FATAL This mode of the CPU isn't supported currently!}
{$ENDIF}
end;
Result:=TSC.Ticks;
end;
function GizmoSearch(By1,By2,By3: Byte; Buf: array of byte):LongInt;
var I: Longword;
begin
for I := 0 to High(Buf) do
if Buf[I] = By1 then
if Buf[I+1] = By2 then
if Buf[I+2] = By3 then
begin
Result:=I;
Break;
end;
end;
function ByteSearch (const Haystack: array of byte; const Needle: array of byte): LongInt;
var
LenBuf, LenPat: LongInt;
PB, PP: Pointer;
begin
PB:=@Haystack; //for testing purposes (there is no need to hold locally)
PP:=@Needle; //for testing purposes (there is no need to hold locally)
LenBuf:=Length(Haystack);
LenPat:=Length(Needle);
if (LenPat=0) or (LenBuf=0) or (LenPat>LenBuf) then Exit(-2);
{$ASMMODE INTEL}
asm
{$IFDEF CPU386}
CLD
MOV ECX, DWORD(LenBuf)
MOV EDX, DWORD(LenPat)
MOV EDI, DWORD PTR PB //MOV EDI, DWORD PTR Haystack
MOV ESI, DWORD PTR PP //MOV ESI, DWORD PTR Needle
MOV EAX, [ESI]
AND EAX, $000000FF
@SEARCH: REPNE SCASB
JECXZ @NOT_FOUND
PUSH ECX
PUSH ESI
PUSH EDI
LEA ECX, [EDX-1]
MOV ESI, DWORD PTR PP //MOV ESI, DWORD PTR Needle
INC ESI
REPE CMPSB
JZ @FOUND
POP EDI
POP ESI
POP ECX
JMP @SEARCH
@FOUND: POP EDI
POP ESI
POP ECX
INC ECX
MOV EAX, DWORD(LenBuf)
SUB EAX, ECX
JMP @EXIT
@NOT_FOUND: XOR EAX,EAX // will return -1
NOT EAX
@EXIT: MOV @RESULT, EAX
{$ELSE} // not written yet...
NOP
{$FATAL This mode of the CPU isn't supported currently!}
{$ENDIF}
end;
end;
type
DInt64 = record
TSC1:Int64;
TSC2:Int64;
end;
const
Zero = 0;
MaxArr = 1000;
ScaleFactor = 1.0E6;
var
Len_P, Len_B, I, J: Integer;
Offset: LongWord;
Buffer, Pattern: array of byte;
T1, T2: Int64;
Data: array [1..MaxArr] of DInt64;
Ave1: Double = Zero;
Ave2: Double = Zero;
begin
Len_B:=32*1024; // 32KB
SetLength(Buffer, Len_B);
// create pattern
Len_P:=3;
SetLength(Pattern, Len_P);
Pattern[0]:=$20;
Pattern[1]:=$21;
Pattern[2]:=$23;
Writeln('Estimation will take several minutes...');
for J:=1 to MaxArr do
begin
// fill buffer
Randomize;
for I:=0 to (Len_B-1) do Buffer[I]:=Byte(Random(256)); //Buffer[I]:=$20
Offset:=0; //Offset:=Len_B-3;
Buffer[Len_B-Offset-3]:=$20;
Buffer[Len_B-Offset-2]:=$21;
Buffer[Len_B-Offset-1]:=$23;
T1:=RDTSC;
for I:=1 to 1000 do GizmoSearch ($20, $21, $23, Buffer);
T2:=RDTSC;
Data[J].TSC1:=T2-T1;
T1:=RDTSC;
for I:=1 to 1000 do ByteSearch (Buffer, Pattern);
T2:=RDTSC;
Data[J].TSC2:=T2-T1;
end;
// estimate averages
for J:=1 to MaxArr do
begin
Ave1:=Ave1+(Data[J].TSC1/(MaxArr*ScaleFactor));
Ave2:=Ave2+(Data[J].TSC2/(MaxArr*ScaleFactor));
end;
Writeln('GizmoSearch took on the average ' + FloatToStrF(Ave1*ScaleFactor, ffFixed, 0, 3)+' CPU ticks');
Writeln('ByteSearch took on the average ' + FloatToStrF(Ave2*ScaleFactor, ffFixed, 0, 3)+' CPU ticks');
Writeln('...');
Writeln ('GizmoSearch/ByteSearch ratio = '+FloatToStrF((Ave1/Ave2), ffFixed, 0, 3));
while true do
begin
;
end;
end.