Added: optimized Split function
This commit is contained in:
parent
bc366730a2
commit
72f1763ea1
35
Test/X2UtStringsTest.cfg
Normal file
35
Test/X2UtStringsTest.cfg
Normal file
@ -0,0 +1,35 @@
|
||||
-$A8
|
||||
-$B-
|
||||
-$C+
|
||||
-$D+
|
||||
-$E-
|
||||
-$F-
|
||||
-$G+
|
||||
-$H+
|
||||
-$I+
|
||||
-$J+
|
||||
-$K-
|
||||
-$L+
|
||||
-$M-
|
||||
-$N+
|
||||
-$O+
|
||||
-$P+
|
||||
-$Q-
|
||||
-$R+
|
||||
-$S-
|
||||
-$T-
|
||||
-$U-
|
||||
-$V+
|
||||
-$W-
|
||||
-$X+
|
||||
-$YD
|
||||
-$Z1
|
||||
-cg
|
||||
-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
|
||||
-H+
|
||||
-W+
|
||||
-M
|
||||
-$M16384,1048576
|
||||
-K$00400000
|
||||
-LE"c:\delphi6\Projects\Bpl"
|
||||
-LN"c:\delphi6\Projects\Bpl"
|
167
Test/X2UtStringsTest.dof
Normal file
167
Test/X2UtStringsTest.dof
Normal file
@ -0,0 +1,167 @@
|
||||
[FileVersion]
|
||||
Version=6.0
|
||||
[Compiler]
|
||||
A=8
|
||||
B=0
|
||||
C=1
|
||||
D=1
|
||||
E=0
|
||||
F=0
|
||||
G=1
|
||||
H=1
|
||||
I=1
|
||||
J=1
|
||||
K=0
|
||||
L=1
|
||||
M=0
|
||||
N=1
|
||||
O=1
|
||||
P=1
|
||||
Q=0
|
||||
R=1
|
||||
S=0
|
||||
T=0
|
||||
U=0
|
||||
V=1
|
||||
W=0
|
||||
X=1
|
||||
Y=1
|
||||
Z=1
|
||||
ShowHints=1
|
||||
ShowWarnings=1
|
||||
UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
|
||||
[Linker]
|
||||
MapFile=0
|
||||
OutputObjs=0
|
||||
ConsoleApp=1
|
||||
DebugInfo=0
|
||||
RemoteSymbols=0
|
||||
MinStackSize=16384
|
||||
MaxStackSize=1048576
|
||||
ImageBase=4194304
|
||||
ExeDescription=
|
||||
[Directories]
|
||||
OutputDir=
|
||||
UnitOutputDir=
|
||||
PackageDLLOutputDir=
|
||||
PackageDCPOutputDir=
|
||||
SearchPath=
|
||||
Packages=vcl;rtl;dbrtl;vcldb;vclx;dss;dsnapcrba;dsnapcon;inetdb;webdsnap;websnap;dbxcds;Irc;parsdpk;hotspotter
|
||||
Conditionals=
|
||||
DebugSourceDirs=
|
||||
UsePackages=0
|
||||
[Parameters]
|
||||
RunParams=ip-to-country.csv countries.csv geo.db
|
||||
HostApplication=
|
||||
Launcher=
|
||||
UseLauncher=0
|
||||
DebugCWD=
|
||||
[Version Info]
|
||||
IncludeVerInfo=1
|
||||
AutoIncBuild=0
|
||||
MajorVer=1
|
||||
MinorVer=0
|
||||
Release=0
|
||||
Build=0
|
||||
Debug=0
|
||||
PreRelease=0
|
||||
Special=0
|
||||
Private=0
|
||||
DLL=0
|
||||
Locale=1043
|
||||
CodePage=1252
|
||||
[Version Info Keys]
|
||||
CompanyName=
|
||||
FileDescription=
|
||||
FileVersion=1.0.0.0
|
||||
InternalName=
|
||||
LegalCopyright=
|
||||
LegalTrademarks=
|
||||
OriginalFilename=
|
||||
ProductName=
|
||||
ProductVersion=1.0.0.0
|
||||
Comments=
|
||||
[Excluded Packages]
|
||||
c:\delphi6\Bin\dcl31w60.bpl=Delphi 1.0 Compatibility Components
|
||||
C:\Delphi6\Bin\dcldb60.bpl=Borland Database Components
|
||||
c:\delphi6\Bin\dclact60.bpl=Borland ActionBar Components
|
||||
C:\Program Files\madCollection\madBasic\Delphi 6\madHelp_.bpl=madHelp 1.1 · www.madshi.net
|
||||
c:\delphi6\Bin\applet60.bpl=Borland Control Panel Applet Package
|
||||
c:\delphi6\Projects\Bpl\X2CompsD6.bpl=X²Software Components - Designtime
|
||||
C:\Delphi6\Bin\dbx60.bpl=Borland SQL Explorer UI Package
|
||||
C:\Delphi6\Projects\Bpl\tb2k_d6.bpl=Toolbar2000 Components (Jordan Russell)
|
||||
c:\delphi6\Projects\Bpl\tb2kdsgn_d6.bpl=Toolbar2000 Design Package (Jordan Russell)
|
||||
c:\delphi6\Projects\Bpl\VirtualTreesD6D.bpl=Virtual Treeview
|
||||
c:\delphi6\Bin\DCLNMF60.bpl=NetMasters Fastnet Tools
|
||||
c:\delphi6\Bin\dclado60.bpl=Borland ADO DB Components
|
||||
c:\delphi6\Bin\dclclxdb60.bpl=Borland CLX Database Components
|
||||
C:\Delphi6\Bin\dclclxstd60.bpl=Borland CLX Standard Components
|
||||
c:\delphi6\Bin\dclie60.bpl=Internet Explorer Components
|
||||
C:\Delphi6\Projects\Bpl\aSQLitepkg.bpl=Aducom Software -- SQLite RunTime Components
|
||||
c:\delphi6\Projects\Bpl\asqlite.bpl=Aducom Software -- SQLite Design Time Components
|
||||
c:\delphi6\Projects\Bpl\PCtrlExd6.bpl=PageControlEx
|
||||
c:\delphi6\Projects\Bpl\JvCoreD6D.bpl=JVCL Core Components
|
||||
c:\delphi6\Projects\Bpl\JvSystemD6D.bpl=JVCL System Components
|
||||
c:\delphi6\Projects\Bpl\JvStdCtrlsD6D.bpl=JVCL Standard Controls
|
||||
c:\delphi6\Projects\Bpl\JvCtrlsD6D.bpl=JVCL Visual Controls
|
||||
c:\delphi6\Projects\Bpl\JvCmpD6D.bpl=JVCL Non-Visual Components
|
||||
c:\delphi6\Projects\Bpl\JvCustomD6D.bpl=JVCL Custom Controls
|
||||
c:\delphi6\Projects\Bpl\JvDlgsD6D.bpl=JVCL Dialog Components
|
||||
c:\delphi6\Projects\Bpl\JvCryptD6D.bpl=JVCL Encryption and Compression Components
|
||||
c:\delphi6\Projects\Bpl\JvMMD6D.bpl=JVCL Multimedia and Image Components
|
||||
c:\delphi6\Projects\Bpl\JvNetD6D.bpl=JVCL Network Components
|
||||
c:\delphi6\Projects\Bpl\JvAppFrmD6D.bpl=JVCL Application and Form Components
|
||||
c:\delphi6\Projects\Bpl\JvDBD6D.bpl=JVCL Database Components
|
||||
c:\delphi6\Projects\Bpl\JvBDED6D.bpl=JVCL BDE Components
|
||||
c:\delphi6\Projects\Bpl\JvInterpreterD6D.bpl=JVCL Interpreter Components
|
||||
c:\delphi6\Projects\Bpl\JvBandsD6D.bpl=JVCL Band Objects
|
||||
c:\delphi6\Projects\Bpl\JvPluginD6D.bpl=JVCL Plugin Components
|
||||
c:\delphi6\Projects\Bpl\JvJansD6D.bpl=JVCL Jans Components
|
||||
c:\delphi6\Projects\Bpl\JvGlobusD6D.bpl=JVCL Globus Components
|
||||
c:\delphi6\Projects\Bpl\JvPrintPreviewD6D.bpl=JVCL Print Preview Components
|
||||
c:\delphi6\Projects\Bpl\JvPageCompsD6D.bpl=JVCL Page Style Components
|
||||
c:\delphi6\Projects\Bpl\JvValidatorsD6D.bpl=JVCL Validators and Error Provider Components
|
||||
c:\delphi6\Projects\Bpl\JvUIBD6D.bpl=JVCL Unified Interbase Components
|
||||
c:\delphi6\Projects\Bpl\JvWizardD6D.bpl=JVCL Wizard Design Time Package
|
||||
c:\delphi6\Projects\Bpl\JvTimeFrameworkD6D.bpl=JVCL Time Framework
|
||||
c:\delphi6\Projects\Bpl\JvHMID6D.bpl=JVCL HMI Controls design time unit
|
||||
c:\delphi6\Projects\Bpl\JvManagedThreadsD6D.bpl=JVCL Managed Threads
|
||||
c:\delphi6\Projects\Bpl\JvXPCtrlsD6D.bpl=JVCL XP Controls
|
||||
c:\delphi6\Projects\Bpl\JvDockingD6D.bpl=JVCL Docking Components
|
||||
c:\delphi6\Projects\Bpl\JvDotNetCtrlsD6D.bpl=JVCL DotNet Controls
|
||||
c:\delphi6\Projects\Bpl\dclIndyCore60.bpl=Indy 10 Core Design Time
|
||||
c:\delphi6\Projects\Bpl\dclIndyProtocols60.bpl=Indy 10 Protocols Design Time
|
||||
c:\delphi6\Projects\Bpl\SysILS.bpl=System ImageList
|
||||
c:\delphi6\Projects\Bpl\DragDropD6.bpl=Drag and Drop Component Suite
|
||||
C:\Projects\Components\DevExpress\OrgChart Suite\Lib\dcldxOrgCD6.bpl=ExpressOrgChart by Developer Express Inc.
|
||||
C:\Projects\Components\DevExpress\OrgChart Suite\Lib\dcldxDBOrD6.bpl=ExpressDBOrgChart by Developer Express Inc.
|
||||
c:\delphi6\Projects\Bpl\BalloonD6.bpl=Balloon 2.0
|
||||
c:\delphi6\Projects\Bpl\DIPasDocD6.bpl=DiPasDoc - Designtime
|
||||
C:\Delphi6\Projects\Bpl\DIContainers_D6.bpl=The Delphi Inspiration -- DIContainers
|
||||
c:\delphi6\Bin\dclshlctrls60.bpl=Shell Control Property and Component Editors
|
||||
c:\delphi6\Bin\dclsmp60.bpl=Borland Sample Components
|
||||
c:\delphi6\Bin\dclbde60.bpl=Borland BDE DB Components
|
||||
c:\delphi6\Bin\dclcds60.bpl=Borland Base Cached ClientDataset Component
|
||||
C:\Delphi6\Bin\dclmid60.bpl=Borland MyBase DataAccess Components
|
||||
c:\delphi6\Bin\dclbdecds60.bpl=Borland Local BDE ClientDataset Components
|
||||
c:\delphi6\Bin\dclib60.bpl=InterBase Data Access Components
|
||||
c:\delphi6\Bin\DBWEBXPRT.BPL=Borland Web Wizard Package
|
||||
c:\delphi6\Bin\dcloffice2k60.bpl=Microsoft Office 2000 Sample Automation Server Wrapper Components
|
||||
c:\delphi6\Bin\dcltee60.bpl=TeeChart Components
|
||||
c:\delphi6\Bin\dcltqr60.bpl=TeeChart for QuickReport Components
|
||||
c:\delphi6\Bin\dclnet60.bpl=Borland Internet Components
|
||||
c:\delphi6\Bin\dclite60.bpl=Borland Integrated Translation Environment
|
||||
c:\delphi6\Bin\dcldbx60.bpl=Borland dbExpress Components
|
||||
c:\delphi6\Bin\dclsoap60.bpl=Borland SOAP Components
|
||||
c:\delphi6\Bin\dclocx60.bpl=Borland Sample Imported ActiveX Controls
|
||||
c:\delphi6\Bin\dcldbxcds60.bpl=Borland Local DBX ClientDataset Components
|
||||
C:\Program Files\madCollection\madRemote\Delphi 6\madRemote_.bpl=madRemote 1.1b · www.madshi.net
|
||||
C:\Program Files\madCollection\madKernel\Delphi 6\madKernel_.bpl=madKernel 1.3 · www.madshi.net
|
||||
C:\Program Files\madCollection\madCodeHook\Delphi 6\madCodeHook_.bpl=madCodeHook 2.1b · www.madshi.net
|
||||
C:\Program Files\madCollection\madSecurity\Delphi 6\madSecurity_.bpl=madSecurity 1.1n · www.madshi.net
|
||||
C:\Program Files\madCollection\madShell\Delphi 6\madShell_.bpl=madShell 1.3k · www.madshi.net
|
||||
C:\WINDOWS\System32\ibevnt60.bpl=Borland Interbase Event Alerter Component
|
||||
c:\delphi6\Projects\Bpl\PsychoTidyD6.bpl=PsychoTidy IDE Expert
|
||||
[HistoryLists\hlUnitAliases]
|
||||
Count=1
|
||||
Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
|
144
Test/X2UtStringsTest.dpr
Normal file
144
Test/X2UtStringsTest.dpr
Normal file
@ -0,0 +1,144 @@
|
||||
program X2UtStringsTest;
|
||||
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
uses
|
||||
SysUtils,
|
||||
Windows,
|
||||
|
||||
FastStrings,
|
||||
|
||||
X2UtStrings;
|
||||
|
||||
var
|
||||
GFreq: Int64;
|
||||
GStart: Int64;
|
||||
|
||||
procedure TimeStart();
|
||||
begin
|
||||
QueryPerformanceFrequency(GFreq);
|
||||
QueryPerformanceCounter(GStart);
|
||||
end;
|
||||
|
||||
procedure TimeEnd();
|
||||
var
|
||||
iEnd: Int64;
|
||||
|
||||
begin
|
||||
QueryPerformanceCounter(iEnd);
|
||||
WriteLn(Format('%.6f seconds', [(iEnd - GStart) / GFreq]));
|
||||
end;
|
||||
|
||||
|
||||
procedure OldSplit(const ASource, ADelimiter: String; out ADest: TSplitArray);
|
||||
var
|
||||
iCount: Integer;
|
||||
iPos: Integer;
|
||||
iLength: Integer;
|
||||
sTemp: String;
|
||||
|
||||
begin
|
||||
sTemp := ASource;
|
||||
iCount := 0;
|
||||
iLength := Length(ADelimiter) - 1;
|
||||
|
||||
repeat
|
||||
iPos := Pos(ADelimiter, sTemp);
|
||||
|
||||
if iPos = 0 then
|
||||
break
|
||||
else begin
|
||||
Inc(iCount);
|
||||
SetLength(ADest, iCount);
|
||||
ADest[iCount - 1] := Copy(sTemp, 1, iPos - 1);
|
||||
Delete(sTemp, 1, iPos + iLength);
|
||||
end;
|
||||
until False;
|
||||
|
||||
if Length(sTemp) > 0 then begin
|
||||
Inc(iCount);
|
||||
SetLength(ADest, iCount);
|
||||
ADest[iCount - 1] := sTemp;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure OldFastStringsSplit(const ASource, ADelimiter: String; out ADest: TSplitArray);
|
||||
const
|
||||
BufferSize = 50;
|
||||
|
||||
var
|
||||
iCount: Integer;
|
||||
iSize: Integer;
|
||||
iPos: Integer;
|
||||
iDelimLength: Integer;
|
||||
iLength: Integer;
|
||||
iLastPos: Integer;
|
||||
|
||||
begin
|
||||
iCount := 0;
|
||||
iDelimLength := Length(ADelimiter);
|
||||
iLength := Length(ASource);
|
||||
iPos := 1;
|
||||
iLastPos := 1;
|
||||
iSize := BufferSize;
|
||||
SetLength(ADest, iSize);
|
||||
|
||||
repeat
|
||||
iPos := FastPos(ASource, ADelimiter, iLength, iDelimLength, iPos);
|
||||
|
||||
if iPos = 0 then
|
||||
break
|
||||
else begin
|
||||
ADest[iCount] := Copy(ASource, iLastPos, iPos - iLastPos);
|
||||
Inc(iPos, iDelimLength);
|
||||
iLastPos := iPos;
|
||||
|
||||
Inc(iCount);
|
||||
if iCount >= iSize then begin
|
||||
Inc(iSize, BufferSize);
|
||||
SetLength(ADest, iSize);
|
||||
end;
|
||||
end;
|
||||
until False;
|
||||
|
||||
|
||||
if iLastPos <= iLength then begin
|
||||
ADest[iCount] := Copy(ASource, iLastPos, iLength - iLastPos + 1);
|
||||
Inc(iCount);
|
||||
end;
|
||||
|
||||
if iSize <> iCount then
|
||||
SetLength(ADest, iCount);
|
||||
end;
|
||||
|
||||
|
||||
var
|
||||
sTest: String;
|
||||
iCount: Integer;
|
||||
aSplit: TSplitArray;
|
||||
|
||||
begin
|
||||
sTest := 'this|isateststring||';
|
||||
for iCount := 0 to 7 do
|
||||
sTest := sTest + sTest;
|
||||
|
||||
TimeStart();
|
||||
Write('10.000 iterations of OldSplit: ');
|
||||
for iCount := 0 to 9999 do
|
||||
OldSplit(sTest, '|', aSplit);
|
||||
TimeEnd();
|
||||
|
||||
TimeStart();
|
||||
Write('10.000 iterations of OldFastStringsSplit: ');
|
||||
for iCount := 0 to 9999 do
|
||||
OldFastStringsSplit(sTest, '|', aSplit);
|
||||
TimeEnd();
|
||||
|
||||
TimeStart();
|
||||
Write('10.000 iterations of Split: ');
|
||||
for iCount := 0 to 9999 do
|
||||
Split(sTest, '||', aSplit);
|
||||
TimeEnd();
|
||||
|
||||
ReadLn;
|
||||
end.
|
128
X2UtStrings.pas
128
X2UtStrings.pas
@ -8,6 +8,9 @@
|
||||
unit X2UtStrings;
|
||||
|
||||
interface
|
||||
type
|
||||
TSplitArray = array of String;
|
||||
|
||||
//:$ Formats the specified size
|
||||
//:: If KeepBytes is true, the size will be formatted for decimal separators
|
||||
//:: and 'bytes' will be appended. If KeepBytes is false the best suitable
|
||||
@ -30,6 +33,9 @@ interface
|
||||
//:$ Compares AMatch against AAgainst using AAgainst's length.
|
||||
function SameTextS(const AMatch, AAgainst: String): Boolean;
|
||||
|
||||
//:$ Splits a string on the specified delimiter
|
||||
procedure Split(const ASource, ADelimiter: String; out ADest: TSplitArray);
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils;
|
||||
@ -101,5 +107,127 @@ begin
|
||||
Result := SameTextL(AMatch, AAgainst, Length(AAgainst));
|
||||
end;
|
||||
|
||||
|
||||
procedure Split;
|
||||
// StrPos is slow. Sloooooow slow. This function may not be advanced or
|
||||
// the fastest one around, but it sure kicks StrPos' ass.
|
||||
// 11.5 vs 1.7 seconds on a 2.4 Ghz for 10.000 iterations, baby!
|
||||
function StrPosEx(const ASource, ASearch: PChar): PChar;
|
||||
var
|
||||
pPos: PChar;
|
||||
pSub: PChar;
|
||||
|
||||
begin
|
||||
Result := nil;
|
||||
|
||||
// Search for the first character
|
||||
pPos := ASource;
|
||||
|
||||
while pPos^ <> #0 do
|
||||
begin
|
||||
if pPos^ = ASearch^ then
|
||||
begin
|
||||
// Found the first character, match the rest
|
||||
pSub := ASearch;
|
||||
Result := pPos;
|
||||
Inc(pSub);
|
||||
Inc(pPos);
|
||||
|
||||
|
||||
while pSub^ <> #0 do
|
||||
begin
|
||||
if pPos^ <> pSub^ then
|
||||
begin
|
||||
// No match, resume as normal
|
||||
Result := nil;
|
||||
break;
|
||||
end;
|
||||
|
||||
Inc(pSub);
|
||||
Inc(pPos);
|
||||
end;
|
||||
|
||||
// If still assigned, all characters matched
|
||||
if Assigned(Result) then
|
||||
exit;
|
||||
end else
|
||||
Inc(pPos);
|
||||
end;
|
||||
end;
|
||||
|
||||
const
|
||||
GrowStart = 32;
|
||||
GrowMax = 256;
|
||||
|
||||
var
|
||||
iCapacity: Integer;
|
||||
iCount: Integer;
|
||||
iDelimLen: Integer;
|
||||
iLength: Integer;
|
||||
iPos: Integer;
|
||||
iSize: Integer;
|
||||
pDelimiter: PChar;
|
||||
pLast: PChar;
|
||||
pPos: PChar;
|
||||
|
||||
begin
|
||||
// Reserve some space
|
||||
iCapacity := GrowStart;
|
||||
iCount := 0;
|
||||
SetLength(ADest, iCapacity);
|
||||
|
||||
iDelimLen := Length(ADelimiter);
|
||||
iLength := Length(ASource);
|
||||
iPos := -1;
|
||||
pDelimiter := PChar(ADelimiter);
|
||||
pPos := PChar(ASource);
|
||||
|
||||
repeat
|
||||
// Find delimiter
|
||||
pLast := pPos;
|
||||
pPos := StrPosEx(pPos, pDelimiter);
|
||||
|
||||
if pPos <> nil then
|
||||
begin
|
||||
// Make space
|
||||
Inc(iCount);
|
||||
if iCount > iCapacity then
|
||||
begin
|
||||
if iCapacity < GrowMax then
|
||||
Inc(iCapacity, iCapacity)
|
||||
else
|
||||
Inc(iCapacity, GrowMax);
|
||||
|
||||
SetLength(ADest, iCapacity);
|
||||
end;
|
||||
|
||||
// Copy substring
|
||||
iSize := Integer(pPos) - Integer(pLast);
|
||||
SetString(ADest[iCount - 1], pLast, iSize);
|
||||
|
||||
// Move pointer
|
||||
Inc(pPos, iDelimLen);
|
||||
Inc(iPos, iSize + iDelimLen);
|
||||
end else
|
||||
begin
|
||||
if iPos < iLength then
|
||||
begin
|
||||
// Copy what's left
|
||||
Inc(iCount);
|
||||
if iCount > iCapacity then
|
||||
SetLength(ADest, iCount);
|
||||
|
||||
ADest[iCount - 1] := pLast;
|
||||
end;
|
||||
|
||||
if iCount <> iCapacity then
|
||||
// Shrink array
|
||||
SetLength(ADest, iCount);
|
||||
|
||||
break;
|
||||
end;
|
||||
until False;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user