From 65ae15f8a31ef727e5cfa91f7500fc4e44c152ea Mon Sep 17 00:00:00 2001 From: bergmann Date: Tue, 29 Nov 2016 21:20:08 +0100 Subject: [PATCH] * removed unneeded and old files --- Tests/UtilsTests.ico | Bin 137040 -> 0 bytes Tests/UtilsTests.lpi | 90 -- Tests/UtilsTests.lpr | 23 - Tests/UtilsTests.lps | 110 -- Tests/UtilsTests.res | Bin 138128 -> 0 bytes Tests/uGenericsTests.pas | 1252 --------------------- uutlConsoleHelper.pas | 2128 ------------------------------------ uutlConversion.pas | 66 -- uutlGraph.pas | 413 ------- uutlLocalization.pas | 244 ----- uutlMcfHelper.pas | 100 -- uutlMessageThread.pas | 396 ------- uutlMessages.pas | 171 --- uutlObservableGenerics.pas | 369 ------- uutlPlatform.pas | 93 -- uutlSerialization.pas | 126 --- uutlSetHelper.inc | 88 -- uutlSettings.pas | 406 ------- uutlSystemInfo.pas | 523 --------- uutlTiming.pas | 90 -- 20 files changed, 6688 deletions(-) delete mode 100644 Tests/UtilsTests.ico delete mode 100644 Tests/UtilsTests.lpi delete mode 100644 Tests/UtilsTests.lpr delete mode 100644 Tests/UtilsTests.lps delete mode 100644 Tests/UtilsTests.res delete mode 100644 Tests/uGenericsTests.pas delete mode 100644 uutlConsoleHelper.pas delete mode 100644 uutlConversion.pas delete mode 100644 uutlGraph.pas delete mode 100644 uutlLocalization.pas delete mode 100644 uutlMcfHelper.pas delete mode 100644 uutlMessageThread.pas delete mode 100644 uutlMessages.pas delete mode 100644 uutlObservableGenerics.pas delete mode 100644 uutlPlatform.pas delete mode 100644 uutlSerialization.pas delete mode 100644 uutlSetHelper.inc delete mode 100644 uutlSettings.pas delete mode 100644 uutlSystemInfo.pas delete mode 100644 uutlTiming.pas diff --git a/Tests/UtilsTests.ico b/Tests/UtilsTests.ico deleted file mode 100644 index 0341321b5d952e1662a3d9444a73cf9f42a7db37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 137040 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 G`+orYGy**U diff --git a/Tests/UtilsTests.lpi b/Tests/UtilsTests.lpi deleted file mode 100644 index 92f2f27..0000000 --- a/Tests/UtilsTests.lpi +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - <ResourceType Value="res"/> - <UseXPManifest Value="True"/> - <Icon Value="0"/> - </General> - <i18n> - <EnableI18N LFM="False"/> - </i18n> - <VersionInfo> - <StringTable ProductVersion=""/> - </VersionInfo> - <BuildModes Count="1"> - <Item1 Name="Default" Default="True"/> - </BuildModes> - <PublishOptions> - <Version Value="2"/> - </PublishOptions> - <RunParams> - <local> - <FormatVersion Value="1"/> - </local> - </RunParams> - <RequiredPackages Count="3"> - <Item1> - <PackageName Value="FPCUnitTestRunner"/> - </Item1> - <Item2> - <PackageName Value="LCL"/> - </Item2> - <Item3> - <PackageName Value="FCL"/> - </Item3> - </RequiredPackages> - <Units Count="2"> - <Unit0> - <Filename Value="UtilsTests.lpr"/> - <IsPartOfProject Value="True"/> - </Unit0> - <Unit1> - <Filename Value="uGenericsTests.pas"/> - <IsPartOfProject Value="True"/> - <UnitName Value="uGenericsTests"/> - </Unit1> - </Units> - </ProjectOptions> - <CompilerOptions> - <Version Value="11"/> - <PathDelim Value="\"/> - <Target> - <Filename Value="UtilsTests"/> - </Target> - <SearchPaths> - <IncludeFiles Value="$(ProjOutDir)"/> - <OtherUnitFiles Value=".."/> - <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> - </SearchPaths> - <CodeGeneration> - <Optimizations> - <OptimizationLevel Value="0"/> - </Optimizations> - </CodeGeneration> - <Linking> - <Debugging> - <UseHeaptrc Value="True"/> - <UseExternalDbgSyms Value="True"/> - </Debugging> - </Linking> - </CompilerOptions> - <Debugging> - <Exceptions Count="3"> - <Item1> - <Name Value="EAbort"/> - </Item1> - <Item2> - <Name Value="ECodetoolError"/> - </Item2> - <Item3> - <Name Value="EFOpenError"/> - </Item3> - </Exceptions> - </Debugging> -</CONFIG> diff --git a/Tests/UtilsTests.lpr b/Tests/UtilsTests.lpr deleted file mode 100644 index a51e236..0000000 --- a/Tests/UtilsTests.lpr +++ /dev/null @@ -1,23 +0,0 @@ -program UtilsTests; - -{$mode objfpc}{$H+} - -uses - sysutils, Interfaces, Forms, GuiTestRunner, uGenericsTests; - -{$R *.res} - -var - heaptrcFile: String; - -begin - heaptrcFile := ChangeFileExt(Application.ExeName, '.heaptrc'); - if (FileExists(heaptrcFile)) then - DeleteFile(heaptrcFile); - SetHeapTraceOutput(heaptrcFile); - - Application.Initialize; - Application.CreateForm(TGuiTestRunner, TestRunner); - Application.Run; -end. - diff --git a/Tests/UtilsTests.lps b/Tests/UtilsTests.lps deleted file mode 100644 index a732cdf..0000000 --- a/Tests/UtilsTests.lps +++ /dev/null @@ -1,110 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<CONFIG> - <ProjectSession> - <PathDelim Value="\"/> - <Version Value="9"/> - <BuildModes Active="Default"/> - <Units Count="7"> - <Unit0> - <Filename Value="UtilsTests.lpr"/> - <IsPartOfProject Value="True"/> - <EditorIndex Value="-1"/> - <CursorPos Y="22"/> - <UsageCount Value="30"/> - </Unit0> - <Unit1> - <Filename Value="uGenericsTests.pas"/> - <IsPartOfProject Value="True"/> - <UnitName Value="uGenericsTests"/> - <TopLine Value="1127"/> - <CursorPos X="73" Y="1148"/> - <UsageCount Value="30"/> - <Loaded Value="True"/> - </Unit1> - <Unit2> - <Filename Value="C:\Zusatzprogramme\Lazarus\fpc\2.7.1\source\packages\fcl-fpcunit\src\fpcunit.pp"/> - <UnitName Value="fpcunit"/> - <IsVisibleTab Value="True"/> - <EditorIndex Value="1"/> - <UsageCount Value="12"/> - <Loaded Value="True"/> - </Unit2> - <Unit3> - <Filename Value="..\uutlGenerics.pas"/> - <UnitName Value="uutlGenerics"/> - <EditorIndex Value="-1"/> - <TopLine Value="1952"/> - <CursorPos Y="1969"/> - <UsageCount Value="14"/> - </Unit3> - <Unit4> - <Filename Value="C:\Zusatzprogramme\Lazarus\fpc\2.7.1\source\rtl\inc\wstringh.inc"/> - <EditorIndex Value="-1"/> - <CursorPos X="11" Y="30"/> - <UsageCount Value="10"/> - </Unit4> - <Unit5> - <Filename Value="C:\Zusatzprogramme\Lazarus\components\fpcunit\guitestrunner.pas"/> - <ComponentName Value="GUITestRunner"/> - <HasResources Value="True"/> - <ResourceBaseClass Value="Form"/> - <EditorIndex Value="-1"/> - <TopLine Value="78"/> - <CursorPos X="3" Y="41"/> - <UsageCount Value="10"/> - </Unit5> - <Unit6> - <Filename Value="C:\Zusatzprogramme\Lazarus\fpc\2.7.1\source\rtl\inc\objpas.inc"/> - <EditorIndex Value="-1"/> - <UsageCount Value="10"/> - </Unit6> - </Units> - <JumpHistory Count="8" HistoryIndex="7"> - <Position1> - <Filename Value="uGenericsTests.pas"/> - <Caret Line="1145" TopLine="1122"/> - </Position1> - <Position2> - <Filename Value="uGenericsTests.pas"/> - <Caret Line="1140" Column="19" TopLine="1122"/> - </Position2> - <Position3> - <Filename Value="uGenericsTests.pas"/> - <Caret Line="1139" TopLine="1122"/> - </Position3> - <Position4> - <Filename Value="uGenericsTests.pas"/> - <Caret Line="1143" TopLine="1122"/> - </Position4> - <Position5> - <Filename Value="uGenericsTests.pas"/> - <Caret Line="1148" Column="59" TopLine="1122"/> - </Position5> - <Position6> - <Filename Value="uGenericsTests.pas"/> - <Caret Line="1143" TopLine="1125"/> - </Position6> - <Position7> - <Filename Value="uGenericsTests.pas"/> - <Caret Line="133" Column="15" TopLine="117"/> - </Position7> - <Position8> - <Filename Value="uGenericsTests.pas"/> - <Caret Line="1146" Column="33" TopLine="1127"/> - </Position8> - </JumpHistory> - </ProjectSession> - <Debugging> - <Watches Count="3"> - <Item1> - <Expression Value="aCount"/> - </Item1> - <Item2> - <Expression Value="aConsumer"/> - </Item2> - <Item3> - <Expression Value="PChar(@ReadPage^.Data[1023])"/> - </Item3> - </Watches> - </Debugging> -</CONFIG> diff --git a/Tests/UtilsTests.res b/Tests/UtilsTests.res deleted file mode 100644 index 7c6cf3e4be6fa881cb2f2caa9bda693328155c28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138128 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 S)M$%T8;H+2FPFK8vi}DltTdMZ diff --git a/Tests/uGenericsTests.pas b/Tests/uGenericsTests.pas deleted file mode 100644 index 8fb2a21..0000000 --- a/Tests/uGenericsTests.pas +++ /dev/null @@ -1,1252 +0,0 @@ -unit uGenericsTests; - -{$mode objfpc}{$H+} -{$modeswitch nestedprocvars} - -interface - -uses - Classes, SysUtils, fpcunit, testregistry, - uutlGenerics; - -type -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TTestObject = class - private - fData: Integer; - fOnDestroy: TNotifyEvent; - public - property Data: Integer read fData; - constructor Create(const aData: Integer; const aOnDestroy: TNotifyEvent); - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlListTest = class(TTestCase) - private type - TTestList = specialize TutlList<TTestObject>; - private - fList: TTestList; - fTestObjs: array[0..9] of TTestObject; - procedure TestObjectDestroy(aSender: TObject); - protected - procedure SetUp; override; - procedure TearDown; override; - published - procedure GetItem; - procedure SetItem; - - procedure Add; - procedure Insert; - procedure IndexOf; - - procedure Exchange; - procedure Move; - - procedure Delete; - procedure Extract; - procedure Remove; - procedure Clear; - - procedure First; - procedure PushFirst; - procedure PopFirst; - - procedure Last; - procedure PushLast; - procedure PopLast; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlHashSetTest = class(TTestCase) - private type - TTestObjComparer = specialize TutlEventComparer<TTestObject>; - TTestHashSet = specialize TutlCustomHashSet<TTestObject>; - private - fHashSet: TTestHashSet; - fTestObjs: array[0..9] of TTestObject; - procedure TestObjectDestroy(aSender: TObject); - protected - procedure SetUp; override; - procedure TearDown; override; - public - function CompareTestObjects(const i1, i2: TTestObject): Integer; - published - procedure Add; - procedure Contains; - procedure IndexOf; - procedure Remove; - procedure Delete; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlMapTest = class(TTestCase) - private type - TTestMap = specialize TutlMap<Integer, TTestObject>; - private - fMap: TTestMap; - fTestObjs: array[0..9] of TTestObject; - fLastRemovedIndex: Integer; - procedure TestObjectDestroy(aSender: TObject); - function Key(const aIndex: Integer): Integer; - function CreateObj: TTestObject; - protected - procedure SetUp; override; - procedure TearDown; override; - - procedure AddExistingKey; - published - procedure GetValue; - procedure SetValue; - procedure GetValueAt; - procedure SetValueAt; - procedure GetKey; - procedure Add; - procedure IndexOf; - procedure Delete; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlPagedDataFiFoTest = class(TTestCase) - private type - TTestFiFo = specialize TutlPagedDataFiFo<Byte>; - private - fFiFo: TTestFiFo; - protected - procedure SetUp; override; - procedure TearDown; override; - published - procedure WriteReadSinglePage; - procedure WriteReadMultiPage; - procedure WritePeekReadSinglePage; - procedure WritePeekReadMultiPage; - procedure WriteDiscardReadSinglePage; - procedure WriteDiscardReadMultiPage; - procedure NestedProvider; - procedure NestedConsumer; - procedure StreamProvider; - procedure StreamConsumer; - procedure RandomSizeTest; - procedure BigDataBlocks; - procedure WriteSmallBlocks; - procedure ReadSmallBlocks; - procedure WriteHalfPageReadAll; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlEnumHelperTest = class(TTestCase) - private type - TBasicEnum = (test1,test2,test3); - TBasicEnumH = specialize TutlEnumHelper<TBasicEnum>; - TOffsetEnum = ( - rtNone = -1, - rtJanaru = 0, - rtTambinor = 1, - rtBendalir = 2, - rtCogadh = 3); - TOffsetEnumH = specialize TutlEnumHelper<TOffsetEnum>; - TSparseEnum = ( - pmFoo = $00f0, - pmPoint = $1b01, - pmLine = $1b02, - pmFill = $1b03); - TSparseEnumH = specialize TutlEnumHelper<TSparseEnum>; - TDisorder = (teNone = -1, teOne = 200, teTwo = 10); - TDisorderH = specialize TutlEnumHelper<TDisorder>; - {$push}{$ScopedEnums on} - TScoped = (tefoo, uGenericsTests); - TScopedH = specialize TutlEnumHelper<TScoped>; - {$pop} - published - procedure BasicConvert; - procedure OffsetEnum; - procedure SparseEnum; - procedure DisorderedEnum; - procedure ScopedEnum; - end; - - - -implementation - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TTestObject/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TTestObject.Create(const aData: Integer; const aOnDestroy: TNotifyEvent); -begin - inherited Create; - fData := aData; - fOnDestroy := aOnDestroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TTestObject.Destroy; -begin - if Assigned(fOnDestroy) then - fOnDestroy(self); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlListTest////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.TestObjectDestroy(aSender: TObject); -var - i: Integer; -begin - for i := Low(fTestObjs) to High(fTestObjs) do - if (fTestObjs[i] = aSender) then - fTestObjs[i] := nil; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.SetUp; -var - i: Integer; -begin - inherited SetUp; - fList := TTestList.Create(true); - for i := Low(fTestObjs) to High(fTestObjs) do begin - fTestObjs[i] := TTestObject.Create(i, @TestObjectDestroy); - fList.Add(fTestObjs[i]); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.TearDown; -begin - FreeAndNil(fList); - inherited TearDown; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.GetItem; -var - i: Integer; -begin - for i := Low(fTestObjs) to High(fTestObjs) do - AssertTrue(fTestObjs[i] = fList[i - Low(fTestObjs)]); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.SetItem; -var - o1, o2: TTestObject; -begin - o1 := fList[3]; - o2 := fList[6]; - fList[3] := o2; - fList[6] := o1; - AssertTrue(fList[6] = o1); - AssertTrue(fList[3] = o2); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.Add; -var - t: TTestObject; - c: Integer; -begin - t := TTestObject.Create(123456, @TestObjectDestroy); - c := fList.Count; - fList.Add(t); - AssertEquals(c+1, fList.Count); - AssertTrue(fList[c] = t); - AssertTrue(fList.Last = t); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.Insert; -var - t: TTestObject; - c: Integer; -begin - t := TTestObject.Create(123456, @TestObjectDestroy); - c := fList.Count; - fList.Insert(3, t); - AssertEquals(c+1, fList.Count); - AssertTrue(fList[3] = t); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.IndexOf; -var - i: Integer; -begin - for i := Low(fTestObjs) to High(fTestObjs) do - AssertEquals(i - Low(fTestObjs), fList.IndexOf(fTestObjs[i])); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.Exchange; -var - o1, o2: TTestObject; -begin - o1 := fList[3]; - o2 := fList[7]; - fList.Exchange(3, 7); - AssertTrue(fList[3] = o2); - AssertTrue(fList[7] = o1); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.Move; -begin - fList.Move(3, 6); - AssertTrue(fList[3] = fTestObjs[4]); - AssertTrue(fList[4] = fTestObjs[5]); - AssertTrue(fList[5] = fTestObjs[6]); - AssertTrue(fList[6] = fTestObjs[3]); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.Delete; -begin - fList.Delete(3); - AssertTrue(fTestObjs[3] = nil); - AssertEquals(Length(fTestObjs)-1, fList.Count); - - fList.OwnsObjects := false; - fList.Delete(4); - AssertTrue(fTestObjs[5] <> nil); - AssertEquals(Length(fTestObjs)-2, fList.Count); - AssertTrue(fList[4] = fTestObjs[6]); - FreeAndNil(fTestObjs[5]); - fList.OwnsObjects := true; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.Extract; -var - o1, o2, o3: TTestObject; -begin - o1 := fList[1]; - o2 := TTestObject.Create(1234, @TestObjectDestroy); - o3 := fList.Extract(o1, o2); - try - AssertTrue(o1 = o3); - AssertEquals(Length(fTestObjs)-1, fList.Count); - AssertTrue(fTestObjs[1] <> nil); - finally - FreeAndNil(o1); - FreeAndNil(o2); - end; - - o1 := fList[1]; - o2 := TTestObject.Create(1234, @TestObjectDestroy); - o3 := fList.Extract(o2, o1); - try - AssertTrue(o1 = o3); - AssertEquals(Length(fTestObjs)-1, fList.Count); - finally - FreeAndNil(o2); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.Remove; -var - o1: TTestObject; - i: Integer; -begin - o1 := fList[3]; - i := fList.Remove(o1); - AssertEquals(3, i); - AssertEquals(Length(fTestObjs)-1, fList.Count); - AssertTrue(fTestObjs[3] = nil); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.Clear; -var - o: TTestObject; -begin - fList.Clear; - AssertEquals(0, fList.Count); - for o in fTestObjs do - AssertTrue(o = nil); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.First; -begin - AssertTrue(fTestObjs[Low(fTestObjs)] = fList.First); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.PushFirst; -var - o1: TTestObject; -begin - o1 := TTestObject.Create(1234, @TestObjectDestroy); - fList.PushFirst(o1); - AssertEquals(Length(fTestObjs)+1, fList.Count); - AssertTrue(fList.First = o1); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.PopFirst; -var - o1: TTestObject; -begin - o1 := fList.PopFirst; - AssertEquals(Length(fTestObjs)-1, fList.Count); - AssertTrue(o1 = fTestObjs[0]); - FreeAndNil(o1); - - o1 := fList.PopFirst(true); - AssertEquals(Length(fTestObjs)-2, fList.Count); - AssertTrue(o1 = nil); - AssertTrue(fTestObjs[1] = nil); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.Last; -begin - AssertTrue(fTestObjs[High(fTestObjs)] = fList.Last); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.PushLast; -var - o1: TTestObject; -begin - o1 := TTestObject.Create(1234, @TestObjectDestroy); - fList.PushLast(o1); - AssertEquals(Length(fTestObjs)+1, fList.Count); - AssertTrue(fList.Last = o1); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlListTest.PopLast; -var - o1: TTestObject; -begin - o1 := fList.PopLast; - AssertEquals(Length(fTestObjs)-1, fList.Count); - AssertTrue(o1 = fTestObjs[High(fTestObjs)]); - FreeAndNil(o1); - - o1 := fList.PopLast(true); - AssertEquals(Length(fTestObjs)-2, fList.Count); - AssertTrue(o1 = nil); - AssertTrue(fTestObjs[High(fTestObjs)-1] = nil); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlHashSetTest/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlHashSetTest.TestObjectDestroy(aSender: TObject); -var - i: Integer; -begin - for i := Low(fTestObjs) to High(fTestObjs) do - if (fTestObjs[i] = aSender) then - fTestObjs[i] := nil; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlHashSetTest.SetUp; -var - i: Integer; -begin - inherited SetUp; - fHashSet := TTestHashSet.Create(TTestObjComparer.Create(@CompareTestObjects), true); - for i := Low(fTestObjs) to High(fTestObjs) do begin - fTestObjs[i] := TTestObject.Create(i, @TestObjectDestroy); - fHashSet.Add(fTestObjs[i]); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlHashSetTest.TearDown; -begin - FreeAndNil(fHashSet); - inherited TearDown; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlHashSetTest.CompareTestObjects(const i1, i2: TTestObject): Integer; -begin - if (i1.Data < i2.Data) then - result := -1 - else if (i1.Data > i2.Data) then - result := 1 - else - result := 0; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlHashSetTest.Add; -var - o1: TTestObject; - b: Boolean; -begin - o1 := TTestObject.Create(1234, @TestObjectDestroy); - b := fHashSet.Add(o1); - AssertTrue(b); - AssertEquals(Length(fTestObjs)+1, fHashSet.Count); - - b := fHashSet.Add(o1); - AssertFalse(b); - AssertEquals(Length(fTestObjs)+1, fHashSet.Count); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlHashSetTest.Contains; -var - o1: TTestObject; - b: Boolean; -begin - o1 := TTestObject.Create(1234, @TestObjectDestroy); - try - b := fHashSet.Contains(fTestObjs[0]); - AssertTrue(b); - - b := fHashSet.Contains(o1); - AssertFalse(b); - finally - FreeAndNil(o1); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlHashSetTest.IndexOf; -var - o1: TTestObject; - i: Integer; -begin - o1 := TTestObject.Create(1234, @TestObjectDestroy); - try - i := fHashSet.IndexOf(fTestObjs[4]); - AssertEquals(4, i); - - i := fHashSet.IndexOf(o1); - AssertEquals(-1, i); - finally - FreeAndNil(o1); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlHashSetTest.Remove; -var - b: Boolean; -begin - b := fHashSet.Remove(fTestObjs[5]); - AssertTrue(fTestObjs[5] = nil); - AssertTrue(b); - AssertEquals(Length(fTestObjs)-1, fHashSet.Count); - - fHashSet.OwnsObjects := false; - try - b := fHashSet.Remove(fTestObjs[0]); - AssertTrue(fTestObjs[0] <> nil); - AssertEquals(Length(fTestObjs)-2, fHashSet.Count); - AssertTrue(b); - - b := fHashSet.Remove(fTestObjs[0]); - AssertFalse(b); - AssertEquals(Length(fTestObjs)-2, fHashSet.Count); - finally - FreeAndNil(fTestObjs[0]); - fHashSet.OwnsObjects := true; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlHashSetTest.Delete; -begin - fHashSet.Delete(0); - AssertEquals(Length(fTestObjs)-1, fHashSet.Count); - AssertTrue(fTestObjs[0] = nil); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMapTest/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.TestObjectDestroy(aSender: TObject); -var - i: Integer; -begin - for i := Low(fTestObjs) to High(fTestObjs) do - if (fTestObjs[i] = aSender) then begin - fLastRemovedIndex := i; - fTestObjs[i] := nil; - exit; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMapTest.Key(const aIndex: Integer): Integer; -begin - result := fTestObjs[aIndex].Data; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMapTest.CreateObj: TTestObject; -var - k: Integer; -begin - repeat - k := random(10000); - until not fMap.Contains(k); - result := TTestObject.Create(k, @TestObjectDestroy); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.SetUp; -var - i: Integer; - o: TTestObject; -begin - inherited SetUp; - fMap := TTestMap.Create(true); - Randomize; - for i := Low(fTestObjs) to High(fTestObjs) do begin - o := CreateObj; - fTestObjs[i] := o; - fMap.Add(o.Data, o); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.TearDown; -begin - FreeAndNil(fMap); - inherited TearDown; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.AddExistingKey; -var - o1: TTestObject; -begin - o1 := TTestObject.Create(fTestObjs[0].Data, @TestObjectDestroy); - try - fMap.Add(o1.Data, o1); - finally - FreeAndNil(o1); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.GetValue; -var - i: Integer; -begin - for i := Low(fTestObjs) to High(fTestObjs) do - AssertTrue(fMap[Key(i)] = fTestObjs[i]); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.SetValue; -var - o1, o2: TTestObject; -begin - o1 := fMap[Key(2)]; - o2 := CreateObj; - fMap[Key(2)] := o2; - try - AssertTrue(fMap[Key(2)] = o2); - finally - FreeAndNil(o1); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.GetValueAt; -type - TIntList = specialize TutlList<Integer>; - TIntComparer = specialize TutlComparer<Integer>; -var - o: TTestObject; - l: TIntList; - i: Integer; -begin - l := TIntList.Create; - try - for o in fTestObjs do - l.Add(o.Data); - l.Sort(TIntComparer.Create); - - for i := 0 to l.Count-1 do - AssertEquals(l[i], fMap.ValueAt[i].Data); - finally - FreeAndNil(l); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.SetValueAt; -var - o1, o2: TTestObject; -begin - o1 := fMap.ValueAt[4]; - o2 := TTestObject.Create(o1.Data, @TestObjectDestroy); - fMap.ValueAt[4] := o2; - try - AssertTrue(fMap.ValueAt[4] = o2); - finally - FreeAndNil(o1); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.GetKey; -type - TIntList = specialize TutlList<Integer>; - TIntComparer = specialize TutlComparer<Integer>; -var - o: TTestObject; - l: TIntList; - i: Integer; -begin - l := TIntList.Create; - try - for o in fTestObjs do - l.Add(o.Data); - l.Sort(TIntComparer.Create); - - for i := 0 to l.Count-1 do - AssertEquals(l[i], fMap.Keys[i]); - finally - FreeAndNil(l); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.Add; -var - o1: TTestObject; -begin - o1 := CreateObj; - fMap.Add(o1.Data, o1); - AssertEquals(Length(fTestObjs)+1, fMap.Count); - - AssertException(EutlMapKeyAlreadyExists, @AddExistingKey); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.IndexOf; -type - TIntList = specialize TutlList<Integer>; - TIntComparer = specialize TutlComparer<Integer>; -var - o: TTestObject; - l: TIntList; -begin - l := TIntList.Create; - try - for o in fTestObjs do - l.Add(o.Data); - l.Sort(TIntComparer.Create); - - for o in fTestObjs do - AssertEquals(l.IndexOf(o.Data), fMap.IndexOf(o.Data)); - finally - FreeAndNil(l); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMapTest.Delete; -var - i: Integer; -begin - for i := Low(fTestObjs) to High(fTestObjs) do begin - fMap.Delete(Key(i)); - AssertNull(fTestObjs[i]); - AssertEquals('Count', Length(fTestObjs)-i-1, fMap.Count); - AssertEquals('Index', fLastRemovedIndex, i); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlPagedDataFiFoTest///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.SetUp; -begin - inherited SetUp; - fFiFo := TTestFiFo.Create; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.TearDown; -begin - FreeAndNil(fFiFo); - inherited TearDown; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.WriteReadSinglePage; -var - buf1, buf2: String; - l: Integer; -begin - buf1 := 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis a'; - l := Length(buf1); - AssertEquals(l, fFiFo.Write(PByte(@buf1[1]), l)); - AssertEquals(l, fFiFo.Size); - - SetLength(buf2, l); - AssertEquals(l, fFiFo.Read(PByte(@buf2[1]), l)); - AssertEquals(0, fFiFo.Size); - AssertEquals('Data', 0, CompareStr(buf1, buf2)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.WriteReadMultiPage; -var - buf1, buf2: String; - l: Integer; -begin - buf1 := 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptu'; - l := Length(buf1); - AssertEquals(l, fFiFo.Write(PByte(@buf1[1]), l)); - AssertEquals(l, fFiFo.Size); - - SetLength(buf2, l); - AssertEquals(l, fFiFo.Read(PByte(@buf2[1]), l)); - AssertEquals(0, fFiFo.Size); - AssertEquals('Data', 0, CompareStr(buf1, buf2)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.WritePeekReadSinglePage; -var - buf1, buf2, buf3: String; - l: Integer; -begin - buf1 := 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis a'; - l := Length(buf1); - AssertEquals(l, fFiFo.Write(PByte(@buf1[1]), l)); - AssertEquals(l, fFiFo.Size); - - SetLength(buf2, l); - AssertEquals(l, fFiFo.Peek(PByte(@buf2[1]), l)); - AssertEquals(l, fFiFo.Size); - Assert(buf1 = buf2); - - SetLength(buf3, l); - AssertEquals(l, fFiFo.Read(PByte(@buf3[1]), l)); - AssertEquals(0, fFiFo.Size); - AssertEquals('Data', 0, CompareStr(buf1, buf3)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.WritePeekReadMultiPage; -var - buf1, buf2, buf3: String; - l: Integer; -begin - buf1 := 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptu'; - l := Length(buf1); - AssertEquals(l, fFiFo.Write(PByte(@buf1[1]), l)); - AssertEquals(l, fFiFo.Size); - - SetLength(buf2, l); - AssertEquals(l, fFiFo.Peek(PByte(@buf2[1]), l)); - AssertEquals(l, fFiFo.Size); - Assert(buf1 = buf2); - - SetLength(buf3, l); - AssertEquals(l, fFiFo.Read(PByte(@buf3[1]), l)); - AssertEquals(0, fFiFo.Size); - AssertEquals('Data', 0, CompareStr(buf1, buf3)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.WriteDiscardReadSinglePage; -var - buf1, buf2: String; - l: Integer; -begin - buf1 := 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptu'; - l := Length(buf1); - AssertEquals(l, fFiFo.Write(PByte(@buf1[1]), l)); - AssertEquals(l, fFiFo.Size); - - AssertEquals(512, fFiFo.Discard(512)); - AssertEquals(l - 512, fFiFo.Size); - - buf1 := 't clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptu'; - l := Length(buf1); - SetLength(buf2, l); - AssertEquals(l, fFiFo.Read(PByte(@buf2[1]), l)); - AssertEquals(0, fFiFo.Size); - AssertEquals('Data', 0, CompareStr(buf1, buf2)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.WriteDiscardReadMultiPage; -var - buf1, buf2: String; - l: Integer; -begin - buf1 := 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptu'; - l := Length(buf1); - AssertEquals(l, fFiFo.Write(PByte(@buf1[1]), l)); - AssertEquals(l, fFiFo.Size); - - AssertEquals(3000, fFiFo.Discard(3000)); - AssertEquals(l - 3000, fFiFo.Size); - - buf1 := 'tur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptu'; - l := Length(buf1); - SetLength(buf2, l); - AssertEquals(l, fFiFo.Read(PByte(@buf2[1]), l)); - AssertEquals(0, fFiFo.Size); - AssertEquals('Data', 0, CompareStr(buf1, buf2)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.NestedProvider; -var - pos: Integer; - len: Integer; - data: String; - tmp: String; - - function GiveData(const aBuffer: PByte; aCount: Integer): Integer; - begin - result := aCount; - if (result > len - pos) then - result := len - pos; - move(data[pos+1], aBuffer^, result); - inc(pos, result); - end; - -var - provider: TTestFiFo.IDataProvider; -begin - data := 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab'; - len := Length(data); - pos := 0; - - provider := TTestFiFo.TNestedDataProvider.Create(@GiveData); - AssertEquals(len, fFiFo.Write(provider, len)); - provider := nil; - - SetLength(tmp, len); - AssertEquals(len, fFiFo.Read(PByte(@tmp[1]), len)); - AssertEquals(0, fFiFo.Size); - AssertEquals('Data', 0, CompareStr(data, tmp)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.NestedConsumer; -var - pos: Integer; - len: Integer; - data: String; - tmp: String; - - function TakeData(const aBuffer: PByte; aCount: Integer): Integer; - begin - result := aCount; - if (result > len - pos) then - result := len - pos; - move(aBuffer^, data[pos+1], result); - inc(pos, result); - end; - -var - consumer: TTestFiFo.IDataConsumer; -begin - tmp := 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab'; - len := Length(tmp); - pos := 0; - AssertEquals(len, fFiFo.Write(@tmp[1], len)); - - SetLength(data, len); - consumer := TTestFiFo.TNestedDataConsumer.Create(@TakeData); - AssertEquals(len, fFiFo.Read(consumer, len)); - consumer := nil; - - AssertEquals(0, fFiFo.Size); - AssertEquals('Data', 0, CompareStr(data, tmp)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.StreamProvider; -var - s: TMemoryStream; - len: Integer; - buf1, buf2: String; - provider: TTestFiFo.IDataProvider; -begin - buf1 := 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab'; - len := Length(buf1); - - s := TMemoryStream.Create; - try - s.Write(buf1[1], len); - s.Position := 0; - provider := TTestFiFo.TStreamDataProvider.Create(s); - AssertEquals(len, fFiFo.Write(provider, len)); - provider := nil; - - SetLength(buf2, len); - AssertEquals(len, fFiFo.Read(@buf2[1], len)); - AssertEquals('Data', 0, CompareStr(buf1, buf2)); - finally - FreeAndNil(s); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.StreamConsumer; -var - s: TMemoryStream; - len: Integer; - buf1, buf2: String; - consumer: TTestFiFo.IDataConsumer; -begin - buf1 := 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab'; - len := Length(buf1); - - AssertEquals(len, fFiFo.Write(@buf1[1], len)); - - s := TMemoryStream.Create; - try - consumer := TTestFiFo.TStreamDataConsumer.Create(s); - AssertEquals(len, fFiFo.Read(consumer, len)); - consumer := nil; - - SetLength(buf2, len); - s.Position := 0; - s.Read(buf2[1], len); - AssertEquals('Data', 0, CompareStr(buf1, buf2)); - finally - FreeAndNil(s); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.RandomSizeTest; -const - MAX_LEN = 128; - MAX_REPEAT = 1024; - -var - rBuf, wBuf: Word; - buf, tmp: array[0..MAX_LEN-1] of Word; - len, i, j: Integer; -begin - rBuf := 0; - wBuf := 0; - len := 1; - for i := 0 to MAX_REPEAT-1 do begin - for j := 0 to len-1 do begin - buf[j] := wBuf; - inc(wBuf); - end; - AssertEquals(SizeOf(Word) * len, fFiFo.Write(@buf[0], SizeOf(Word) * len)); - - AssertEquals(SizeOf(Word) * len, fFiFo.Peek(@tmp[0], SizeOf(Word) * len)); - for j := 0 to len-1 do begin - AssertEquals(rBuf, tmp[j]); - inc(rBuf); - end; - AssertEquals(SizeOf(Word) * len, fFiFo.Discard(SizeOf(Word) * len)); - AssertEquals(0, fFiFo.Size); - - inc(len); - if (len >= MAX_LEN) then - len := 1; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.BigDataBlocks; -const - BUFFER_LEN = 1000; - REPEAT_COUNT = 50; -var - buf1, buf2: array[0..BUFFER_LEN-1] of Integer; - i, j, sz: Integer; -begin - for i := 0 to BUFFER_LEN-1 do - buf1[i] := i; - - sz := 0; - for i := 0 to REPEAT_COUNT + 4 do begin - if (i < REPEAT_COUNT) then begin - AssertEquals(SizeOf(Integer) * BUFFER_LEN, fFiFo.Write(@buf1[0], SizeOf(Integer) * BUFFER_LEN)); - inc(sz, SizeOf(Integer) * BUFFER_LEN); - AssertEquals(sz, fFiFo.Size); - end; - - if (i >= 5) then begin - AssertEquals(SizeOf(Integer) * BUFFER_LEN, fFiFo.Read(@buf2[0], SizeOf(Integer) * BUFFER_LEN)); - dec(sz, SizeOf(Integer) * BUFFER_LEN); - AssertEquals(sz, fFiFo.Size); - for j := 0 to BUFFER_LEN-1 do - AssertEquals(j, buf2[j]); - end; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.WriteSmallBlocks; -var - pos: Integer; - len: Integer; - data: String; - tmp: String; - - function GiveData(const aBuffer: PByte; aCount: Integer): Integer; - begin - result := aCount; - if (result > len - pos) then - result := len - pos; - if (result > 10) then - result := 10; - move(data[pos+1], aBuffer^, result); - inc(pos, result); - end; - -var - provider: TTestFiFo.IDataProvider; -begin - data := 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab'; - len := Length(data); - pos := 0; - - provider := TTestFiFo.TNestedDataProvider.Create(@GiveData); - AssertEquals(len, fFiFo.Write(provider, len)); - provider := nil; - - SetLength(tmp, len); - AssertEquals(len, fFiFo.Read(PByte(@tmp[1]), len)); - AssertEquals(0, fFiFo.Size); - AssertEquals('Data', 0, CompareStr(data, tmp)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.ReadSmallBlocks; -var - pos: Integer; - len: Integer; - data: String; - tmp: String; - - function TakeData(const aBuffer: PByte; aCount: Integer): Integer; - begin - result := aCount; - if (result > len - pos) then - result := len - pos; - if (result > 10) then - result := 10; - move(aBuffer^, data[pos+1], result); - inc(pos, result); - end; - -var - consumer: TTestFiFo.IDataConsumer; -begin - tmp := 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab'; - len := Length(tmp); - pos := 0; - AssertEquals(len, fFiFo.Write(@tmp[1], len)); - - SetLength(data, len); - consumer := TTestFiFo.TNestedDataConsumer.Create(@TakeData); - AssertEquals(len, fFiFo.Read(consumer, len)); - consumer := nil; - - AssertEquals(0, fFiFo.Size); - AssertEquals('Data', 0, CompareStr(data, tmp)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlPagedDataFiFoTest.WriteHalfPageReadAll; -const - REPEAT_COUNT = 10; -var - buf1: String; - i, sz: Integer; - p: Pointer; -begin - buf1 := 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab'; - for i := 1 to REPEAT_COUNT do begin - AssertEquals('Write' + IntToStr(i), fFiFo.PageSize div 2, fFiFo.Write(@buf1[1], fFiFo.PageSize div 2)); - sz := fFiFo.Size; - AssertEquals('Size' + IntToStr(i), fFiFo.PageSize div 2, sz); - p := GetMem(fFiFo.PageSize); - try - AssertEquals('Read' + IntToStr(i), sz, fFiFo.Read(p, fFiFo.PageSize)); - Assert(CompareMem(p, @buf1[1], sz), 'Data' + IntToStr(i)); - finally - Freemem(p); - end; - end; -end; - - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlEnumHelperTest//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlEnumHelperTest.BasicConvert; -var - en: TBasicEnum; -begin - AssertEquals('test1', TBasicEnumH.ToString(test1)); - AssertEquals(Ord(test2), Ord(TBasicEnumH.ToEnum('test2'))); - en:= test1; - AssertEquals(false, TBasicEnumH.TryToEnum('NotInList',en)); - AssertEquals(ord(test1), ord(en)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlEnumHelperTest.OffsetEnum; -begin - AssertEquals('rtJanaru', TOffsetEnumH.ToString(rtJanaru)); - AssertEquals(Ord(rtJanaru), Ord(TOffsetEnumH.ToEnum('rtJanaru'))); - AssertEquals(5, Length(TOffsetEnumH.Values)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlEnumHelperTest.SparseEnum; -begin - AssertEquals('pmPoint', TSparseEnumH.ToString(pmPoint)); - AssertEquals(Ord(pmPoint), Ord(TSparseEnumH.ToEnum('pmPoint'))); - AssertEquals(4, Length(TSparseEnumH.Values)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlEnumHelperTest.DisorderedEnum; -var - s: string; - en: TDisorder; -begin - AssertEquals(3, Length(TDisorderH.Values)); - s:= ''; - for en in TDisorderH.Values do - s += TDisorderH.ToString(en) + '|'; - AssertEquals('teNone|teOne|teTwo|', s); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlEnumHelperTest.ScopedEnum; -var - s: string; - en: TScoped; -begin - AssertEquals(2, Length(TScopedH.Values)); - s:= ''; - for en in TScopedH.Values do - s += TScopedH.ToString(en) + '|'; - AssertEquals('tefoo|uGenericsTests|', s); -end; - -initialization - RegisterTest(TutlListTest); - RegisterTest(TutlHashSetTest); - RegisterTest(TutlMapTest); - RegisterTest(TutlPagedDataFiFoTest); - RegisterTest(TutlEnumHelperTest); - -end. - diff --git a/uutlConsoleHelper.pas b/uutlConsoleHelper.pas deleted file mode 100644 index c90fa06..0000000 --- a/uutlConsoleHelper.pas +++ /dev/null @@ -1,2128 +0,0 @@ -unit uutlConsoleHelper; - -{ Package: Utils - Prefix: utl - UTiLs - Beschreibung: diese Unit implementiert Helper Klassen für Consolen Ein- und Ausgaben, - sowie Menüführung und Autovervollständigung } - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils, fgl, uutlMCF, uutlCommon, uutlGenerics, syncobjs; - -type -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlParameterStringFlag = (psfBrackets); - TutlParameterStringFlags = set of TutlParameterStringFlag; - TutlMenuItem = class; - TutlMenuParameter = class - private - fParent: TutlMenuItem; - fOptional: Boolean; - fName, fDescription, fValue: String; - public - property Parent: TutlMenuItem read fParent; - property Optional: Boolean read fOptional; - property Value: String read fValue; - property Name: String read fName; - property Description: String read fDescription; - - procedure WriteConfig(const aMCF: TutlMCFSection); virtual; - procedure ReadConfig(const aMCF: TutlMCFSection); virtual; - - procedure GetAutoCompleteStrings(const aStrings: TStrings); virtual; abstract; - function SetValue(const aValue: String): Boolean; virtual; - function GetString(const aOptions: TutlParameterStringFlags = [psfBrackets]): String; virtual; - - constructor Create(const aOptional: Boolean; const aName, aDescription: String); - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlMenuParameterListBase = specialize TFPGObjectList<TutlMenuParameter>; - TutlMenuParameterList = class(TutlMenuParameterListBase) - public - function HasParameter(const aName: String): Boolean; - function FindParameter(const aName: String): TutlMenuParameter; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlMenuParameterStack = class(TutlMenuParameterList) - public - procedure Push(const aValue: TutlMenuParameter); - function Seek: TutlMenuParameter; - function Pop: TutlMenuParameter; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlParameterType = (ptString = 0, ptInteger, ptBoolean, ptHex, ptPreset); - TutlParameterTypeH = specialize TutlEnumHelper<TutlParameterType>; - TutlMenuParameterSingle = class(TutlMenuParameter) - private - fType: TutlParameterType; - public - property ParamType: TutlParameterType read fType; - - procedure WriteConfig(const aMCF: TutlMCFSection); override; - procedure ReadConfig(const aMCF: TutlMCFSection); override; - - procedure GetAutoCompleteStrings(const aStrings: TStrings); override; - function SetValue(const aValue: String): Boolean; override; - function GetString(const aOptions: TutlParameterStringFlags = [psfBrackets]): String; override; - - constructor Create(const aOptional: Boolean; const aName, aDescription: String; const aType: TutlParameterType); - destructor Destroy; override; - end; - TutlMenuParameterSingleList = specialize TFPGObjectList<TutlMenuParameterSingle>; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlMenuParameterGroup = class(TutlMenuParameter) - private - fParameters: TutlMenuParameterSingleList; - function GetCount: Integer; - function GetParameter(const aIndex: Integer): TutlMenuParameterSingle; - public - property Count: Integer read GetCount; - property Parameter[const aIndex: Integer]: TutlMenuParameterSingle read GetParameter; default; - - procedure WriteConfig(const aMCF: TutlMCFSection); override; - procedure ReadConfig(const aMCF: TutlMCFSection); override; - - procedure GetAutoCompleteStrings(const aStrings: TStrings); override; - function SetValue(const aValue: String): Boolean; override; - function GetString(const aOptions: TutlParameterStringFlags = [psfBrackets]): String; override; - - function AddParameter(const aName, aDescription: String; - const aType: TutlParameterType): TutlMenuParameterSingle; overload; - function AddParameter(const aParameter: TutlMenuParameterSingle): TutlMenuParameterSingle; overload; - procedure DelParameter(const aIndex: Integer); - - constructor Create(const aOptional: Boolean; const aName, aDescription: String); - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlCallback = procedure(aSender: TObject) of object; - TutlMenuItemList = specialize TFPGObjectList<TutlMenuItem>; - TutlMenuItem = class(TObject) - private - fCommand: String; - fDescription: String; - fCallback: TutlCallback; - fExecutable: Boolean; - fParent: TutlMenuItem; - fHelpItem: TutlMenuItem; - - function GetCount: Integer; - function GetItems(const aIndex: Integer): TutlMenuItem; - function GetMenuPath: String; - function GetParamCount: Integer; - function GetParameters(const aIndex: Integer): TutlMenuParameter; - function GetParameterString: String; - procedure SetCallback(aValue: TutlCallback); - protected - fItems: TutlMenuItemList; - fParameters: TutlMenuParameterList; - - procedure WriteConfig(const aMCF: TutlMCFSection); virtual; - procedure ReadConfig(const aMCF: TutlMCFSection); virtual; - public - property Command: String read fCommand write fCommand; - property Description: String read fDescription write fDescription; - property Callback: TutlCallback read fCallback write SetCallback; - property Executable: Boolean read fExecutable; - property MenuPath: String read GetMenuPath; - property ParameterString: String read GetParameterString; - property ParamCount: Integer read GetParamCount; - property Count: Integer read GetCount; - property Parent: TutlMenuItem read fParent; - property Items[const aIndex: Integer]: TutlMenuItem read GetItems; default; - property Parameters[const aIndex: Integer]: TutlMenuParameter read GetParameters; - - procedure GetAutoCompleteStrings(const aList: TStrings; const aParameter: TutlMenuParameterList); virtual; - function GetString: String; - function AddItem(const aCmd, aDesc: String; const aCallback: TutlCallback): TutlMenuItem; overload; - function AddItem(const aItem: TutlMenuItem): TutlMenuItem; overload; - procedure DelItem(const aIndex: Integer); - function AddParameter(const aParameter: TutlMenuParameter): TutlMenuParameter; - procedure DelParameter(const aIndex: Integer); - - procedure LoadFromStream(const aStream: TStream); - procedure SaveToStream(const aStream: TStream); - - constructor Create(const aParent: TutlMenuItem; const aCmd, aDesc: String; const aCallback: TutlCallback); - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlParseResult = (prUnknownCommand, prInvalidParam, prInvalidParamCount, prSuccess, prIncompleteCmd); - TutlHelpOption = (hoNone, hoAll, hoDetail); - TutlCommandMenu = class(TutlMenuItem) - private - fInvalidParam: String; - fUnknownCmd: String; - fLastCmd: String; - fCurrentParamCount: Integer; - function GetCmdParameter: TutlMenuParameterList; - protected - fCmdParameter: TutlMenuParameterStack; - fCmdStack: TutlStringStack; - fCurrentMenu: TutlMenuItem; - procedure SplitCmdString(const aText: String; const aChar: Char); - function ParseCommand: TutlParseResult; - public - property CmdParameter: TutlMenuParameterList read GetCmdParameter; - property LastCmd: String read fLastCmd; - - procedure ExecuteCommand(const aCmd: String); virtual; - - procedure DisplayHelp(const aRefMenu: TutlMenuItem = nil; const aOption: TutlHelpOption = hoNone); - procedure DisplayIncompleteCommand; - procedure DisplayUnknownCommand(const aCmd: String = ''); - procedure DisplayInvalidParamCount(const aParamCount: Integer = -1); - procedure DisplayInvalidParam(const aParam: String = ''); - - constructor Create(const aHelp: String); - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlInputEvent = procedure(aSender: TObject; const aInput: String) of Object; - TutlAutoCompleteEvent = function(aSender: TObject; const aInput: String; const aDisplayPossibilities: Boolean): String of Object; - TutlCommandPrompt = class(TObject) - private - fCurrent: String; - fInput: String; - fPrefix: String; - fHistoryBackup: String; - fCurID: Integer; - fStartIndex: Integer; - fHistoryID: Integer; - fHistoryEnabled: Boolean; - fRunning: Boolean; - fHiddenChar: Char; - fConsoleCS: TCriticalSection; - fHistory: TStringList; - - fOnInput: TutlInputEvent; - fOnAutoComplete: TutlAutoCompleteEvent; - - function GetCurrent: String; - procedure SetCurrent(aValue: String); - procedure SetHiddenChar(aValue: Char); - procedure SetPrefix(aValue: String); - - procedure CursorToStart; - procedure CursorToEnd; - procedure DelInput(const aAll: Boolean = false); - procedure RestoreInput; - procedure CursorRight; - procedure CursorLeft; - procedure DelChar(const aBeforeCursor: Boolean = false); - procedure WriteChar(const c: Char); - - function ReadLnEx: String; - function AutoComplete(const aInput: String; const aDisplayPossibilities: Boolean): String; - procedure AddHistory(const aInput: String); - - procedure DoInput; - public - property Prefix: String read fPrefix write SetPrefix; - property Current: String read GetCurrent write SetCurrent; - property HistoryEnabled: Boolean read fHistoryEnabled write fHistoryEnabled; - property HiddenChar: Char read fHiddenChar write SetHiddenChar; - property OnInput: TutlInputEvent read fOnInput write fOnInput; - property OnAutoComplete: TutlAutoCompleteEvent read fOnAutoComplete write fOnAutoComplete; - - procedure Start; - procedure Stop; - procedure Reset; - procedure Clear; //löscht nur die Ausgabe und hält die Eingabe intern - procedure Restore; //stellt die Ausgabe wieder her - - constructor Create(const aConsoleCS: TCriticalSection); - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlConsoleMenu = class(TutlCommandMenu) - private - fExitMenu: TutlMenuItem; - fCommandPrompt: TutlCommandPrompt; - fIsAsking: Boolean; - fInputBackup: String; - - fOnAnswer: TutlInputEvent; - - procedure CommandInput(aSender: TObject; const aCmd: String); - function AutoComplete(aSender: TObject; const aCmd: String; - const aDisplayPossibilities: Boolean): String; - - procedure DoAnswer(const aInput: String); - public - property CommandPrompt: TutlCommandPrompt read fCommandPrompt; - property OnAnswer: TutlInputEvent read fOnAnswer; - - procedure ExecuteCommand(const aCmd: String); override; - procedure StartMenu; - procedure ExitMenu; - procedure Ask(const aQuestion: String; const aHidden: Boolean = false; const aOnAnswer: TutlInputEvent = nil); - - constructor Create(const aHelp: String; const aConsoleCS: TCriticalSection); - destructor Destroy; override; - end; - -implementation - -uses - strutils, - uutlLogger, - uutlKeyCodes, - {$IFDEF WINDOWS} - windows - {$ELSE} - crt - {$ENDIF}; - -const - COMMAND_PROMPT_PREFIX = '> '; - -{$IFDEF WINDOWS} -var - ScanCode : char; - SpecialKey : boolean; - DoingNumChars: Boolean; - DoingNumCode: Byte; - -Function RemapScanCode (ScanCode: byte; CtrlKeyState: byte; keycode:longint): byte; - { Several remappings of scancodes are necessary to comply with what - we get with MSDOS. Special Windows keys, as Alt-Tab, Ctrl-Esc etc. - are excluded } -var - AltKey, CtrlKey, ShiftKey: boolean; -const - { - Keypad key scancodes: - - Ctrl Norm - - $77 $47 - Home - $8D $48 - Up arrow - $84 $49 - PgUp - $8E $4A - - - $73 $4B - Left Arrow - $8F $4C - 5 - $74 $4D - Right arrow - $4E $4E - + - $75 $4F - End - $91 $50 - Down arrow - $76 $51 - PgDn - $92 $52 - Ins - $93 $53 - Del - } - CtrlKeypadKeys: array[$47..$53] of byte = - ($77, $8D, $84, $8E, $73, $8F, $74, $4E, $75, $91, $76, $92, $93); - -begin - AltKey := ((CtrlKeyState AND - (RIGHT_ALT_PRESSED OR LEFT_ALT_PRESSED)) > 0); - CtrlKey := ((CtrlKeyState AND - (RIGHT_CTRL_PRESSED OR LEFT_CTRL_PRESSED)) > 0); - ShiftKey := ((CtrlKeyState AND SHIFT_PRESSED) > 0); - - if AltKey then - begin - case ScanCode of - // Digits, -, = - $02..$0D: inc(ScanCode, $76); - // Function keys - $3B..$44: inc(Scancode, $2D); - $57..$58: inc(Scancode, $34); - // Extended cursor block keys - $47..$49, $4B, $4D, $4F..$53: - inc(Scancode, $50); - // Other keys - $1C: Scancode := $A6; // Enter - $35: Scancode := $A4; // / (keypad and normal!) - end - end - else if CtrlKey then - case Scancode of - // Tab key - $0F: Scancode := $94; - // Function keys - $3B..$44: inc(Scancode, $23); - $57..$58: inc(Scancode, $32); - // Keypad keys - $35: Scancode := $95; // \ - $37: Scancode := $96; // * - $47..$53: Scancode := CtrlKeypadKeys[Scancode]; - //Enter on Numpad - $1C: - begin - Scancode := $0A; - SpecialKey := False; - end; - end - else if ShiftKey then - case Scancode of - // Function keys - $3B..$44: inc(Scancode, $19); - $57..$58: inc(Scancode, $30); - //Enter on Numpad - $1C: - begin - Scancode := $0D; - SpecialKey := False; - end; - end - else - case Scancode of - // Function keys - $57..$58: inc(Scancode, $2E); // F11 and F12 - //Enter on NumPad - $1C: - begin - Scancode := $0D; - SpecialKey := False; - end; - end; - RemapScanCode := ScanCode; -end; - - -function KeyPressed : boolean; -var - nevents,nread : dword; - buf : TINPUTRECORD; - AltKey: Boolean; - c : longint; -begin - KeyPressed := FALSE; - if ScanCode <> #0 then - KeyPressed := TRUE - else - begin - GetNumberOfConsoleInputEvents(TextRec(input).Handle,nevents{%H-}); - while nevents>0 do - begin - ReadConsoleInputA(TextRec(input).Handle,buf{%H-},1,nread{%H-}); - if buf.EventType = KEY_EVENT then - if buf.Event.KeyEvent.bKeyDown then - begin - { Alt key is VK_MENU } - { Capslock key is VK_CAPITAL } - - AltKey := ((Buf.Event.KeyEvent.dwControlKeyState AND - (RIGHT_ALT_PRESSED OR LEFT_ALT_PRESSED)) > 0); - if not(Buf.Event.KeyEvent.wVirtualKeyCode in [VK_SHIFT, VK_MENU, VK_CONTROL, - VK_CAPITAL, VK_NUMLOCK, - VK_SCROLL]) then - begin - keypressed:=true; - - if (ord(buf.Event.KeyEvent.AsciiChar) = 0) or - (buf.Event.KeyEvent.dwControlKeyState and (LEFT_ALT_PRESSED or ENHANCED_KEY) > 0) then - begin - SpecialKey := TRUE; - ScanCode := Chr(RemapScanCode(Buf.Event.KeyEvent.wVirtualScanCode, Buf.Event.KeyEvent.dwControlKeyState, - Buf.Event.KeyEvent.wVirtualKeyCode)); - end - else - begin - { Map shift-tab } - if (buf.Event.KeyEvent.AsciiChar=#9) and - (buf.Event.KeyEvent.dwControlKeyState and SHIFT_PRESSED > 0) then - begin - SpecialKey := TRUE; - ScanCode := #15; - end - else - begin - SpecialKey := FALSE; - ScanCode := Chr(Ord(buf.Event.KeyEvent.AsciiChar)); - end; - end; - - if AltKey then - begin - case Buf.Event.KeyEvent.wVirtualScanCode of - 71 : c:=7; - 72 : c:=8; - 73 : c:=9; - 75 : c:=4; - 76 : c:=5; - 77 : c:=6; - 79 : c:=1; - 80 : c:=2; - 81 : c:=3; - 82 : c:=0; - else - break; - end; - DoingNumChars := true; - DoingNumCode := Byte((DoingNumCode * 10) + c); - Keypressed := false; - Specialkey := false; - ScanCode := #0; - end - else - break; - end; - end - else - begin - if (Buf.Event.KeyEvent.wVirtualKeyCode in [VK_MENU]) then - if DoingNumChars then - if DoingNumCode > 0 then - begin - ScanCode := Chr(DoingNumCode); - Keypressed := true; - - DoingNumChars := false; - DoingNumCode := 0; - break - end; { if } - end; - { if we got a key then we can exit } - if keypressed then - exit; - GetNumberOfConsoleInputEvents(TextRec(input).Handle,nevents); - end; - end; -end; - -function ReadKey: char; -begin - while (not KeyPressed) do - Sleep(1); - if SpecialKey then begin - ReadKey := #0; - SpecialKey := FALSE; - end else begin - ReadKey := ScanCode; - ScanCode := #0; - end; -end; -{$ENDIF} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMenuParameter//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuParameter.WriteConfig(const aMCF: TutlMCFSection); -begin - with aMCF do begin - SetString('classname', self.ClassName); - SetBool ('optional', fOptional); - SetString('name', fName); - SetString('description', fDescription); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuParameter.ReadConfig(const aMCF: TutlMCFSection); -begin - with aMCF do begin - fOptional := GetBool ('optional', true); - fName := GetString('name', fName); - fDescription := GetString('description', fDescription); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameter.SetValue(const aValue: String): Boolean; -begin - fValue := aValue; - result := true; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMenuParameter//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameter.GetString(const aOptions: TutlParameterStringFlags - ): String; -begin - if fOptional then - result := '(' + fName + ')' - else - result := '[' + fName + ']' -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlMenuParameter.Create(const aOptional: Boolean; const aName, aDescription: String); -begin - inherited Create; - fOptional := aOptional; - fName := aName; - fDescription := aDescription; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlMenuParameter.Destroy; -begin - inherited Destroy; -end; - - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMenuParameterList//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterList.HasParameter(const aName: String): Boolean; -begin - result := Assigned(FindParameter(aName)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterList.FindParameter(const aName: String): TutlMenuParameter; -var - i: Integer; -begin - for i := 0 to Count-1 do begin - result := Items[i]; - if (result.Name = aName) then - exit; - end; - result := nil -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMenuParameterStack/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuParameterStack.Push(const aValue: TutlMenuParameter); -begin - Add(aValue); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterStack.Seek: TutlMenuParameter; -begin - if (Count > 0) then - result := Items[Count-1] - else - result := nil; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterStack.Pop: TutlMenuParameter; -begin - if (Count > 0) then begin - result := Items[Count-1]; - Delete(Count-1); - end else - result := nil; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuParameterSingle.WriteConfig(const aMCF: TutlMCFSection); -begin - inherited WriteConfig(aMCF); - aMCF.SetString('type', TutlParameterTypeH.ToString(fType)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuParameterSingle.ReadConfig(const aMCF: TutlMCFSection); -begin - inherited ReadConfig(aMCF); - fType := TutlParameterTypeH.ToEnum(aMCF.GetString('type', TutlParameterTypeH.ToString(ptPreset))); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMenuParameterSingle////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuParameterSingle.GetAutoCompleteStrings(const aStrings: TStrings); -begin - case fType of - ptBoolean: begin - aStrings.Add('true'); - aStrings.Add('false'); - end; - ptInteger: begin - aStrings.Add('[integer]'); - end; - ptHex: begin - aStrings.Add('[hex]'); - end; - ptString: begin - aStrings.Add('[string]'); - end; - ptPreset: begin - aStrings.Add(fName); - end; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterSingle.SetValue(const aValue: String): Boolean; -const - BOOL_ARR: array[0..7] of String = ('y', 'n', 't', 'f', 'yes', 'no', 'true', 'false'); - - function IsInArr: Boolean; - var - i: Integer; - s: String; - begin - s := LowerCase(aValue); - result := true; - for i := 0 to high(BOOL_ARR) do - if (BOOL_ARR[i] = s) then - exit; - result := false; - end; - -var - i: Integer; - c: QWord; -begin - result := false; - case fType of - ptBoolean: - if IsInArr then - result := true; - ptInteger: - result := TryStrToInt(aValue, i); - ptHex: - result := AnsiStartsStr('0x', aValue) and TryStrToQWord('$' + AnsiRightStr(aValue, Length(aValue)-2), c); - ptPreset: - result := (aValue = fName); - ptString: - result := true; - end; - if result then - result := inherited SetValue(aValue); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMenuParameterSingle////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterSingle.GetString(const aOptions: TutlParameterStringFlags): String; -begin - result := fName; - case fType of - ptBoolean: - result := result+':b'; - ptHex: - result := result+':h'; - ptInteger: - result := result+':i'; - ptString: - result := result+':s'; - ptPreset: - result := '''' + result + ''''; - end; - if (psfBrackets in aOptions) then begin - if fOptional then - result := '(' + result + ')' - else - result := '[' + result + ']'; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlMenuParameterSingle.Create(const aOptional: Boolean; - const aName, aDescription: String; const aType: TutlParameterType); -begin - inherited Create(aOptional, aName, aDescription); - fType := aType; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlMenuParameterSingle.Destroy; -begin - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMenuParameterGroup/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterGroup.GetCount: Integer; -begin - result := fParameters.Count; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterGroup.GetParameter(const aIndex: Integer): TutlMenuParameterSingle; -begin - if (aIndex >= 0) and (aIndex < fParameters.Count) then - result := fParameters[aIndex] - else - raise Exception.Create(format('TMenuParameterGroup.GetParameter - index out of bounds (%d)', [aIndex])); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuParameterGroup.WriteConfig(const aMCF: TutlMCFSection); -var - i: Integer; -begin - inherited WriteConfig(aMCF); - with aMCF.Sections['parameters'] do begin - SetInt('count', fParameters.Count); - for i := 0 to fParameters.Count-1 do - fParameters[i].WriteConfig(Sections[IntToStr(i)]); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuParameterGroup.ReadConfig(const aMCF: TutlMCFSection); -var - c, i: Integer; -begin - inherited ReadConfig(aMCF); - with aMCF.Sections['parameters'] do begin - c := GetInt('count', 0); - for i := 0 to c-1 do - AddParameter('', '', ptPreset).ReadConfig(Sections[IntToStr(i)]); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuParameterGroup.GetAutoCompleteStrings(const aStrings: TStrings); -var - i: Integer; -begin - for i := 0 to fParameters.Count-1 do - fParameters[i].GetAutoCompleteStrings(aStrings); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterGroup.SetValue(const aValue: String): Boolean; -var - i: Integer; -begin - for i := 0 to fParameters.Count-1 do - if fParameters[i].SetValue(aValue) then begin - result := inherited SetValue(aValue); - break; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterGroup.GetString(const aOptions: TutlParameterStringFlags): String; -var - i: Integer; - s: String; -begin - result := ''; - for i := 0 to fParameters.Count-1 do begin - if result <> '' then - result := result + '|'; - s := fParameters[i].GetString; - s := copy(s, 2, Length(s)-2); - result := result + s; - end; - result := fName + ':' + result; - if (psfBrackets in aOptions) then begin - if (fOptional) then - result := '(' + result + ')' - else - result := '[' + result + ']'; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterGroup.AddParameter(const aName, aDescription: String; const aType: TutlParameterType): TutlMenuParameterSingle; -begin - result := TutlMenuParameterSingle.Create(false, aName, aDescription, aType); - fParameters.Add(result); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuParameterGroup.AddParameter(const aParameter: TutlMenuParameterSingle): TutlMenuParameterSingle; -begin - result := aParameter; - fParameters.Add(result); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuParameterGroup.DelParameter(const aIndex: Integer); -begin - if (aIndex >= 0) and (aIndex < fParameters.Count) then - fParameters.Delete(aIndex) - else - raise Exception.Create(format('TMenuParameterGroup.DelParameter - index out of bounds (%d)', [aIndex])); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlMenuParameterGroup.Create(const aOptional: Boolean; const aName, aDescription: String); -begin - inherited Create(aOptional, aName, aDescription); - fParameters := TutlMenuParameterSingleList.Create(true); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlMenuParameterGroup.Destroy; -begin - FreeAndNil(fParameters); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMenuItem///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuItem.GetCount: Integer; -begin - result := fItems.Count; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuItem.GetItems(const aIndex: Integer): TutlMenuItem; -begin - if (aIndex >= 0) and (aIndex < fItems.Count) then - result := fItems[aIndex] - else - raise Exception.Create(format('TMenuItem.GetItems - index out of bounds (%d)', [aIndex])); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuItem.GetMenuPath: String; -var - m: TutlMenuItem; -begin - result := ''; - m := self; - while Assigned(m) do begin - if Length(result) > 0 then - result := ' ' + result; - result := m.GetString + result; //m.Command + result; - if (m <> m.Parent) then - m := m.Parent - else - m := nil; - end; - result := Trim(result); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuItem.GetParamCount: Integer; -begin - result := fParameters.Count; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuItem.GetParameters(const aIndex: Integer): TutlMenuParameter; -begin - if (aIndex >= 0) and (aIndex < fParameters.Count) then - result := fParameters[aIndex] - else - raise Exception.Create(format('TMenuItem.GetParameters - index out of bounds', [aIndex])); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuItem.GetParameterString: String; -var - i: Integer; -begin - result := ''; - for i := 0 to fParameters.Count-1 do begin - if result <> '' then - result := result + ' '; - result := result + fParameters[i].GetString; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuItem.SetCallback(aValue: TutlCallback); -begin - if fCallback = aValue then - exit; - fCallback := aValue; - fExecutable := Assigned(fCallback); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuItem.WriteConfig(const aMCF: TutlMCFSection); -var - i: Integer; -begin - with aMCF do begin - SetString('command', fCommand); - SetString('description', fDescription); - SetBool ('executable', fExecutable); - with Sections['parameters'] do begin - SetInt('count', fParameters.Count); - for i := 0 to fParameters.Count-1 do - fParameters[i].WriteConfig(Sections[IntToStr(i)]); - end; - with Sections['menus'] do begin - SetInt('count', fItems.Count); - for i := 0 to fItems.Count-1 do - fItems[i].WriteConfig(Sections[IntToStr(i)]); - end; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuItem.ReadConfig(const aMCF: TutlMCFSection); -var - c, i: Integer; - s: String; - p: TutlMenuParameter; -begin - fItems.Clear; - fParameters.Clear; - with aMCF do begin - fCommand := GetString('command', ''); - fDescription := GetString('description', ''); - fExecutable := GetBool ('executable', false); - with Sections['parameters'] do begin - c := GetInt('count', 0); - for i := 0 to c-1 do begin - s := Sections[IntToStr(i)].GetString('classname', ''); - p := nil; - if (s = TutlMenuParameterSingle.ClassName) then - p := TutlMenuParameterSingle.Create(true, '', '', ptPreset) - else if (s = TutlMenuParameterGroup.ClassName) then - p := TutlMenuParameterGroup.Create(true, '', ''); - if Assigned(p) then begin - fParameters.Add(p); - p.ReadConfig(Sections[IntToStr(i)]); - end; - end; - end; - with Sections['menus'] do begin - c := GetInt('count', 0); - for i := 0 to c-1 do - AddItem('', '', nil).ReadConfig(Sections[IntToStr(i)]); - end; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuItem.GetAutoCompleteStrings(const aList: TStrings; const aParameter: TutlMenuParameterList); -var - hasAllParam: Boolean; - i: Integer; - p: TutlMenuParameter; -begin - hasAllParam := true; - for i := 0 to fParameters.Count-1 do begin - p := fParameters[i]; - if not p.Optional then begin - if not aParameter.HasParameter(p.Name) then begin - p.GetAutoCompleteStrings(aList); - hasAllParam := false; - break; - end; - end else begin - if not aParameter.HasParameter(p.Name) then - p.GetAutoCompleteStrings(aList); - end; - end; - if hasAllParam then begin - for i := 0 to fItems.Count-1 do - aList.Add(fItems[i].Command); - if Command <> 'help' then - aList.Add('help'); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuItem.GetString: String; -var - s: String; -begin - result := Command; - s := ParameterString; - if (Command <> '') and (s <> '') then - result := result + ' '; - result := result + s; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuItem.AddItem(const aCmd, aDesc: String; const aCallback: TutlCallback): TutlMenuItem; -begin - result := TutlMenuItem.Create(self, aCmd, aDesc, aCallback); - fItems.Add(result); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuItem.AddItem(const aItem: TutlMenuItem): TutlMenuItem; -begin - result := aItem; - fItems.Add(result); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuItem.DelItem(const aIndex: Integer); -begin - if (aIndex >= 0) and (aIndex < fItems.Count) then - fItems.Delete(aIndex) - else - raise Exception.Create(format('TMenuItem.DelItem - index out of bounds (%d)', [aIndex])); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMenuItem.AddParameter(const aParameter: TutlMenuParameter): TutlMenuParameter; -begin - result := aParameter; - result.fParent := self; - fParameters.Add(result); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuItem.DelParameter(const aIndex: Integer); -begin - if (aIndex >= 0) and (aIndex < fParameters.Count) then - fParameters.Delete(aIndex) - else - raise Exception.Create(format('TMenuItem.DelParameter - index out of bounds (%d)', [aIndex])); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlMenuItem.Create(const aParent: TutlMenuItem; const aCmd, aDesc: String; const aCallback: TutlCallback); -begin - inherited Create; - fParent := aParent; - fCommand := aCmd; - fDescription := aDesc; - fCallback := aCallback; - fExecutable := Assigned(fCallback); - fItems := TutlMenuItemList.Create(true); - fParameters := TutlMenuParameterList.Create(true); - - if aCmd <> 'help' then begin - fHelpItem := TutlMenuItem.Create(self, 'help', 'shows the help. use ''all'' to display the menu tree. use ''detail'' to display command details', nil); - with fHelpItem.AddParameter(TutlMenuParameterGroup.Create(true, 'mode', 'specify how to display the help menu')) as TutlMenuParameterGroup do begin - AddParameter('all', 'displays the complete menu tree', ptPreset); - AddParameter('detail', 'displays detailed information', ptPreset); - end; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlMenuItem.Destroy; -begin - FreeAndNil(fItems); - FreeAndNil(fParameters); - FreeAndNil(fHelpItem); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlCommandMenu////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlCommandMenu.GetCmdParameter: TutlMenuParameterList; -begin - result := fCmdParameter; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandMenu.SplitCmdString(const aText: String; const aChar: Char); -var - i: Integer; - Buffer: String; - Quote: Boolean; -begin - fCmdStack.Clear; - Buffer := ''; - Quote := false; - for i := 1 to Length(aText) do begin - if (aText[i] = aChar) and not Quote then begin - Buffer := Trim(Buffer); - fCmdStack.Add(Buffer); - Buffer := ''; - end else if (aText[i] = '"') then - Quote := not Quote - else - Buffer := Buffer + aText[i]; - end; - if Buffer <> '' then begin - Buffer := Trim(Buffer); - fCmdStack.Add(Buffer); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlCommandMenu.ParseCommand: TutlParseResult; - - procedure RestoreParameterStack(const aMenu: TutlMenuItem); - var - p: TutlMenuParameter; - begin - p := fCmdParameter.Seek; - while Assigned(p) and (p.Parent = aMenu) do begin - fCmdStack.Push(p.Value); - fCmdParameter.Pop; - p := fCmdParameter.Seek; - end; - end; - - function BackTrack(const aMenu: TutlMenuItem): TutlParseResult; - var - s, cmd: String; - OptionalParamCount: Integer; - i, c: Integer; - m: TutlMenuItem; - p: TutlMenuParameter; - begin - //Stack is empty - if (fCmdStack.Count = 0) then begin - if Assigned(aMenu.Callback) or (aMenu = fHelpItem) then - result := prSuccess - else - result := prIncompleteCmd; - fCurrentMenu := aMenu; - exit; - end; - s := fCmdStack.Pop; - cmd := LowerCase(s); - - //find command - m := nil; - for i := 0 to aMenu.Count-1 do - if (LowerCase(aMenu[i].Command) = cmd) then begin - m := aMenu[i]; - break; - end; - if not Assigned(m) and (cmd = 'help') then begin - m := fHelpItem; - m.fParent := aMenu; - end; - - if Assigned(m) then begin - //count optional parameters - c := 0; - for i := 0 to m.ParamCount-1 do - if (m.Parameters[i].Optional) then - inc(c); - OptionalParamCount := (1 shl c) - 1; - - //backtrack optional parameters - while (OptionalParamCount >= 0) do begin - result := prSuccess; - fCurrentParamCount := 0; - c := 0; - for i := 0 to m.ParamCount-1 do begin - p := m.Parameters[i]; - if not p.Optional or (((OptionalParamCount shr c) and 1) = 1) then begin - if (fCmdStack.Count <= 0) then begin - result := prInvalidParamCount; - RestoreParameterStack(m); - fCurrentMenu := m; - break; - end else if (LowerCase(fCmdStack.Seek) = 'help') then begin - break; - end else if not (p.SetValue(fCmdStack.Seek)) then begin - result := prInvalidParam; - RestoreParameterStack(m); - fCurrentMenu := m; - break; - end else begin - inc(fCurrentParamCount); - fCmdParameter.Push(p); - fCmdStack.Pop; - end; - if p.Optional then - inc(c); - end; - end; - - if (result = prSuccess) then begin - result := BackTrack(m); - if result = prUnknownCommand then - fCurrentMenu := aMenu; - end; - if result <> prSuccess then - dec(OptionalParamCount) - else - OptionalParamCount := -1; - end; - end else begin - fCmdStack.Push(s); - fUnknownCmd := s; - result := prUnknownCommand; - end; - end; - -begin - fCmdParameter.Clear; - fCurrentParamCount := 0; - fInvalidParam := ''; - fUnknownCmd := ''; - fCurrentMenu := nil; - result := BackTrack(self); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandMenu.ExecuteCommand(const aCmd: String); -var - r: TutlParseResult; - p: TutlMenuParameter; -begin - fLastCmd := aCmd; - SplitCmdString(aCmd, ' '); - r := ParseCommand; - case r of - prSuccess: - if (fCurrentMenu = fHelpItem) then begin - p := nil; - if (fCmdParameter.Count > 0) and (fCmdParameter[fCmdParameter.Count-1].Parent = fHelpItem) then - p := fCmdParameter[fCmdParameter.Count-1]; - if not Assigned(p) then - DisplayHelp(fHelpItem.Parent) - else if (p.Value = 'all') then - DisplayHelp(fHelpItem.Parent, hoAll) - else if (p.Value = 'detail') then - DisplayHelp(fHelpItem.Parent, hoDetail) - else - DisplayHelp(fHelpItem.Parent); - end else - fCurrentMenu.Callback(self); - prInvalidParam: - DisplayInvalidParam(fInvalidParam); - prInvalidParamCount: - DisplayInvalidParamCount(fCurrentParamCount); - prIncompleteCmd: - DisplayIncompleteCommand; - prUnknownCommand: - DisplayUnknownCommand(fUnknownCmd); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandMenu.DisplayHelp(const aRefMenu: TutlMenuItem; const aOption: TutlHelpOption); -var - maxLength: Integer; - - function AddStr(const aOld, aNew, aPostfix: String): String; - begin - result := aOld; - if (aOld <> '') and (aNew <> '') then - result := result + aPostfix; - result := result + aNew; - end; - - function GetMaxLength(const aItem: TutlMenuItem): Integer; - var - i, l: Integer; - m: TutlMenuItem; - begin - result := 0; - for i := 0 to aItem.Count-1 do begin - m := aItem[i]; - l := Length(m.GetString); - if l > result then - result := l; - if (aOption = hoAll) then begin - l := GetMaxLength(m) + 2; - if l > result then - result := l; - end; - end; - end; - - function FillStr(const aStr: String; const aChar: Char; const aLength: Integer): String; - begin - result := aStr + StringOfChar(aChar, aLength-Length(aStr)); - end; - - procedure AddHelpText(var aMsg: String; const aPrefix: String; const aItem: TutlMenuItem); - begin - if (aMsg <> '') then - aMsg := aMsg + sLineBreak; - aMsg := aMsg + FillStr(aPrefix + aItem.GetString, ' ', maxLength) + ' ' + aItem.Description; - end; - - procedure AddHelpItems(var aMsg: String; const aPrefix: String; const aItem: TutlMenuItem; const aDisplayHelpItem: Boolean = false); - var - i: Integer; - m: TutlMenuItem; - begin - for i := 0 to aItem.Count-1 do begin - m := aItem[i]; - AddHelpText(aMsg, aPrefix, m); - if (aOption = hoAll) then - AddHelpItems(aMsg, aPrefix+' ', m); - end; - if aDisplayHelpItem then - AddHelpText(aMsg, aPrefix, fHelpItem); - end; - - procedure DisplayGroupParameters(var aMsg: String; const aParamGroup: TutlMenuParameterGroup); - var - i, maxLen, l: Integer; - p: TutlMenuParameter; - begin - maxLen := 0; - for i := 0 to aParamGroup.Count-1 do begin - l := Length(aParamGroup[i].GetString([])); - if l > maxLen then - maxLen := l; - end; - inc(maxLen, 3); - - for i := 0 to aParamGroup.Count-1 do begin - p := aParamGroup[i]; - aMsg := aMsg + sLineBreak + ' ' + - FillStr(p.GetString([]), ' ', maxLen) + - p.Description; - end; - end; - - procedure DisplayParameters(var aMsg: String); - var - i, maxLen, l: Integer; - p: TutlMenuParameter; - begin - maxLen := 0; - for i := 0 to aRefMenu.ParamCount-1 do begin - l := Length(aRefMenu.Parameters[i].GetString); - if (l > maxLen) then - maxLen := l; - end; - inc(maxLen, 3); - - aMsg := aMsg + sLineBreak + 'Parameters:'; - if (aRefMenu.ParamCount > 0) then begin - for i := 0 to aRefMenu.ParamCount-1 do begin - p := aRefMenu.Parameters[i]; - aMsg := aMsg + sLineBreak + ' ' + FillStr(p.GetString, ' ', maxLen); - if p.Optional then - aMsg := aMsg + '(optional) ' - else - aMsg := aMsg + ' '; - aMsg := aMsg + p.Description; - if (p is TutlMenuParameterGroup) then - DisplayGroupParameters(aMsg, p as TutlMenuParameterGroup); - end; - end else - aMsg := aMsg + sLineBreak + ' [no Parameters]'; - end; - -var - menu: TutlMenuItem; - msg: String; -begin - if Assigned(aRefMenu) then - menu := aRefMenu - else - menu := self; - - msg := AddStr(menu.MenuPath, menu.ParameterString, ' '); - msg := AddStr(msg, menu.Description, ' - '); - fHelpItem.fParent := nil; - if (aOption = hoDetail) then - msg := msg + sLineBreak + 'Submenus / Commands:'; - maxLength := max(GetMaxLength(menu), Length(fHelpItem.GetString)) + 2; - if (msg = '') then - msg := sLineBreak; - AddHelpItems(msg, ' ', menu, true); - if (aRefMenu is TutlConsoleMenu) then - AddHelpText(msg, ' ', (self as TutlConsoleMenu).fExitMenu); - if (aOption = hoDetail) then - DisplayParameters(msg); - utlLogger.Log(Self, msg, []); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandMenu.DisplayIncompleteCommand; - - function GetMenuPath: String; - var - s: String; - begin - s := ''; - if Assigned(fCurrentMenu) then - s := fCurrentMenu.MenuPath; - if (s <> '') then - result := s+' ' - else - result := ''; - end; - -begin - utlLogger.Error(Self, 'incomplete command! type "%shelp" to get further information.', [GetMenuPath]); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandMenu.DisplayUnknownCommand(const aCmd: String); - - function GetMenuPath: String; - var - s: String; - begin - s := ''; - if Assigned(fCurrentMenu) then - s := fCurrentMenu.MenuPath; - if (s <> '') then - result := s+' ' - else - result := ''; - end; - - function GetCommand: String; - begin - if (aCmd <> '') then - result := ' "'+aCmd+'"' - else - result := ''; - end; - -begin - utlLogger.Error(Self, 'unknown command%s! type "%shelp" to get further information.', [GetCommand, GetMenuPath]); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandMenu.DisplayInvalidParamCount(const aParamCount: Integer); - - function GetMenuPath: String; - var - s: String; - begin - s := ''; - if Assigned(fCurrentMenu) then - s := fCurrentMenu.MenuPath; - if (s <> '') then - result := s+' ' - else - result := ''; - end; - - function GetParamCount: String; - var - i, c: Integer; - begin - result := ' ('; - if (aParamCount >= 0) then begin - result := result + IntToStr(aParamCount); - end; - if Assigned(fCurrentMenu) then begin - c := 0; - for i := 0 to fCurrentMenu.ParamCount-1 do - if not fCurrentMenu.Parameters[i].Optional then - inc(c); - result := result + ', expected '+IntToStr(c); - end; - result := result + ')'; - if (result = ' ()') then - result := ''; - end; - -begin - utlLogger.Log(Self, 'invalid parameter count%s! type "%shelp" to get further information.', [GetParamCount, GetMenuPath]); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandMenu.DisplayInvalidParam(const aParam: String); - - function GetMenuPath: String; - var - s: String; - begin - s := ''; - if Assigned(fCurrentMenu) then - s := fCurrentMenu.MenuPath; - if (s <> '') then - result := s+' ' - else - result := ''; - end; - - function GetParam: String; - begin - if (aParam <> '') then - result := ' "'+aParam+'"' - else - result := ''; - end; - -begin - utlLogger.Log(Self, 'invalid parameter%s! type "%shelp" to get further information.', [GetParam, GetMenuPath]); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuItem.LoadFromStream(const aStream: TStream); -var - mcf: TutlMCFFile; -begin - mcf := TutlMCFFile.Create(nil); - try - mcf.LoadFromStream(aStream); - ReadConfig(mcf); - finally - mcf.Free; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMenuItem.SaveToStream(const aStream: TStream); -var - mcf: TutlMCFFile; -begin - mcf := TutlMCFFile.Create(nil); - try - WriteConfig(mcf); - mcf.SaveToStream(aStream); - finally - mcf.Free; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlCommandMenu.Create(const aHelp: String); -begin - inherited Create(nil, '', aHelp, nil); - fCmdStack := TutlStringStack.Create; - fCmdParameter := TutlMenuParameterStack.Create(False); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlCommandMenu.Destroy; -begin - FreeAndNil(fCmdStack); - FreeAndNil(fCmdParameter); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlCommandPrompt///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.SetPrefix(aValue: String); -begin - if fPrefix = aValue then - exit; - DelInput(true); - delete(fCurrent, 1, Length(fPrefix)); - if fHistoryID >= 0 then - delete(fHistoryBackup, 1, Length(fPrefix)); - fPrefix := aValue; - fStartIndex := Length(fPrefix)+1; - fCurrent := fPrefix + fCurrent; - if fHistoryID >= 0 then - fHistoryBackup := fPrefix + fHistoryBackup; - RestoreInput; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlCommandPrompt.GetCurrent: String; -begin - if fHistoryID = -1 then - result := copy(fCurrent, fStartIndex, MaxInt) - else - result := copy(fHistoryBackup, fStartIndex, MaxInt); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.SetCurrent(aValue: String); -begin - DelInput; - fCurrent := fPrefix + aValue; - fHistoryID := -1; - RestoreInput; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.SetHiddenChar(aValue: Char); -begin - if fHiddenChar = aValue then - exit; - DelInput(true); - fHiddenChar := aValue; - RestoreInput; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.CursorToStart; -begin - Write(StringOfChar(#8, fCurID-fStartIndex)); - fCurID := fStartIndex; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.CursorToEnd; -begin - Write(Copy(fCurrent, fCurID, MaxInt)); - fCurID := Length(fCurrent)+1; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.DelInput(const aAll: Boolean); -var - l: Integer; -begin - if not aAll then - l := fCurID - fStartIndex - else - l := fCurID - 1; - Write(StringOfChar(#08, l)); - Write(StringOfChar(' ', l)); - Write(StringOfChar(#08, l)); - dec(fCurID, l); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.RestoreInput; -begin - if (fHiddenChar <> #0) then begin - while fCurID < fStartIndex do begin - Write(fCurrent[fCurID]); - inc(fCurID); - end; - Write(StringOfChar(fHiddenChar, Length(fCurrent) - fCurID + 1)) - end else - Write(Copy(fCurrent, fCurID, MaxInt)); - fCurID := Length(fCurrent)+1; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.CursorRight; -begin - if fCurID <= Length(fCurrent) then begin - if (fHiddenChar <> #0) then - Write(fHiddenChar) - else - Write(fCurrent[fCurID]); - inc(fCurID); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.CursorLeft; -begin - if fCurID > fStartIndex then begin - Write(#8); - dec(fCurID, 1); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.DelChar(const aBeforeCursor: Boolean); -begin - if not aBeforeCursor and (fCurID <= Length(fCurrent)) then begin - Delete(fCurrent, fCurID, 1); - Write(copy(fCurrent, fCurID, MaxInt), ' '); - Write(StringOfChar(#8, Length(fCurrent)-fCurID+2)); - end else if aBeforeCursor and (fCurID > fStartIndex) then begin - Delete(fCurrent, fCurID-1, 1); - Write(#8, copy(fCurrent, fCurID-1, MaxInt), ' '); - Write(StringOfChar(#8, Length(fCurrent)-fCurID+3)); - dec(fCurID); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.WriteChar(const c: Char); -begin - if (fCurID <= Length(fCurrent)) then begin - Write('#', copy(fCurrent, fCurID, MaxInt)); - Write(StringOfChar(#8, Length(fCurrent)-fCurID+2)); - end; - Insert(c, fCurrent, fCurID); - inc(fCurID, 1); - if (fHiddenChar <> #0) then - Write(fHiddenChar) - else - Write(c); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlCommandPrompt.ReadLnEx: String; -var - c: Byte; - key: Char; - CtrlKey: Boolean; - s: String; - tabPressed: Boolean; -begin - fHistoryID := -1; - CtrlKey := false; - tabPressed := false; - - fConsoleCS.Enter; - try - DelInput(true); - RestoreInput; - finally - fConsoleCS.Leave; - end; - - while fRunning do begin - key := ReadKey; - c := Ord(key) and $FF; - fConsoleCS.Enter; - try - if (CtrlKey) then begin - CtrlKey := false; - case c of - 72: begin //KEY_UP - if (fHistoryID+1 < fHistory.Count) then begin - if (fHistoryID = -1) then - fHistoryBackup := fCurrent; - CursorToEnd; - DelInput; - inc(fHistoryID); - fCurrent := fPrefix + fHistory[fHistoryID]; - RestoreInput; - end; - end; - 80: begin //KEY_DOWN - if (fHistoryID >= 0) then begin - CursorToEnd; - DelInput; - dec(fHistoryID); - if (fHistoryID >= 0) then - fCurrent := fPrefix + fHistory[fHistoryID] - else - fCurrent := fHistoryBackup; - RestoreInput; - end; - end; - 75: begin //KEY_LEFT - CursorLeft; - end; - 77: begin //KEY_RIGHT - CursorRight; - end; - 82: begin //KEY_INSERT - end; - 83: begin //KEY_DELETE - DelChar(false); - end; - 71: begin //KEY_HOME - CursorToStart; - end; - 79: begin //KEY_END - CursorToEnd; - end; - 73: begin //KEY_PGUP - end; - 81: begin //KEY_PGDOWN - end; - end; - end else begin - if (tabPressed) then - tabPressed := (c = VK_TAB); - case c of - VK_UNKNOWN: begin - CtrlKey := true; - end; - VK_BACK: begin - DelChar(true); - end; - VK_TAB: begin - CursorToEnd; - DelInput; - s := fCurrent; - fCurrent := fPrefix + - AutoComplete(copy(fCurrent, fStartIndex, MaxInt), tabPressed); - RestoreInput; - if s <> fCurrent then - tabPressed := false - else - tabPressed := not tabPressed; - end; - VK_RETURN: begin //RETURN - break; - end; - VK_ESCAPE: begin - Reset; - end; - else - WriteChar(Chr(c)); - end; - end; - finally - fConsoleCS.Leave; - end; - end; - - fConsoleCS.Enter; - try - WriteLn; - finally - fConsoleCS.Leave; - end; - result := copy(fCurrent, Length(fPrefix)+1, MaxInt); - Reset; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlCommandPrompt.AutoComplete(const aInput: String; const aDisplayPossibilities: Boolean): String; -begin - result := aInput; - if Assigned(fOnAutoComplete) then - result := fOnAutoComplete(self, aInput, aDisplayPossibilities); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.AddHistory(const aInput: String); -begin - if fHistoryEnabled and (fHiddenChar = #0) and - ((fHistory.Count <= 0) or (fHistory[0] <> aInput)) then - fHistory.Insert(0, aInput); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.DoInput; -begin - if Assigned(fOnInput) then - fOnInput(self, fInput); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.Start; -begin - Reset; - fRunning := true; - while fRunning do begin - fInput := ReadLnEx; - AddHistory(fInput); - DoInput; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.Stop; -begin - fRunning := false; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.Reset; -begin - CursorToEnd; - DelInput(true); - fCurrent := fPrefix; - fCurID := 1; - Restore; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.Clear; -begin - fConsoleCS.Enter; - try - DelInput(true); - finally - fConsoleCS.Leave; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCommandPrompt.Restore; -begin - fConsoleCS.Enter; - try - RestoreInput; - finally - fConsoleCS.Leave; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlCommandPrompt.Create(const aConsoleCS: syncobjs.TCriticalSection); -begin - inherited Create; - fConsoleCS := aConsoleCS; - fPrefix := COMMAND_PROMPT_PREFIX; - fHistory := TStringList.Create; - fHistoryEnabled := true; - fStartIndex := Length(fPrefix)+1; -end; - -destructor TutlCommandPrompt.Destroy; -begin - FreeAndNil(fHistory); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlConsoleMenu////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlConsoleMenu.CommandInput(aSender: TObject; const aCmd: String); -begin - if fIsAsking then begin - fIsAsking := false; - fCommandPrompt.Current := fInputBackup; - fCommandPrompt.Prefix := COMMAND_PROMPT_PREFIX; - fCommandPrompt.OnAutoComplete := @AutoComplete; - fCommandPrompt.HiddenChar := #0; - fCommandPrompt.HistoryEnabled := true; - DoAnswer(aCmd); - end else begin - utlLogger.Debug(Self, 'CMD: ' + aCmd, []); - ExecuteCommand(aCmd); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlConsoleMenu.AutoComplete(aSender: TObject; const aCmd: String; - const aDisplayPossibilities: Boolean): String; -var - r: TutlParseResult; - s, cmd: String; - c: Char; - cmdList: TStringList; - i, CharIndex, MaxLength: Integer; - - function TestParam(const aParam: String): Boolean; - var - i: Integer; - c: QWord; - begin - if aParam = '[integer]' then - result := TryStrToInt(s, i) - else if aParam = '[hex]' then - result := AnsiStartsStr('0x', s) and TryStrToQWord('$' + copy(s, 3, MaxInt), c) - else - result := true; - end; - - function NextChar: Char; - var - i: Integer; - c: String; - begin - result := #0; - try - for i := 0 to cmdList.Count-1 do begin - c := cmdList[i]; - if (Length(c) > 0) and (c[1] <> '[') then begin - if (Length(c) >= CharIndex) then begin - if (result = #0) then - result := c[CharIndex] - else if (result <> c[CharIndex]) then begin - result := #0; - exit; - end; - end; - end; - end; - finally - inc(CharIndex); - end; - end; - - function GetMaxLength: Integer; - var - i, l: Integer; - begin - result := 0; - for i := 0 to cmdList.Count-1 do begin - l := Length(cmdList[i]); - if l > result then - result := l; - end; - end; - -begin - SplitCmdString(aCmd, ' '); - s := ''; - if (Length(aCmd) > 0) and (aCmd[Length(aCmd)] <> ' ') then begin - s := fCmdStack[fCmdStack.Count-1]; - fCmdStack.Delete(fCmdStack.Count-1); - end; - r := ParseCommand; - result := aCmd; - if (r in [prSuccess, prIncompleteCmd, prInvalidParamCount]) and Assigned(fCurrentMenu) then begin - cmdList := TStringList.Create; - try - fCurrentMenu.GetAutoCompleteStrings(cmdList, fCmdParameter); - if (s <> '') then begin - for i := cmdList.Count-1 downto 0 do begin - cmd := cmdList[i]; - if (Length(cmd) > 0) and (cmd[1] = '[') then begin - if not TestParam(cmd) then - cmdList.Delete(i); - end else if not AnsiStartsStr(s, cmd) then - cmdList.Delete(i); - end; - end; - - CharIndex := Length(s)+1; - c := NextChar; - while c <> #0 do begin - result := result + c; - c := NextChar; - end; - if (cmdList.Count = 1) then - result := result + ' '; - - if aDisplayPossibilities then begin - WriteLn(''); - s := ''; - MaxLength := GetMaxLength+5; - for i := 0 to cmdList.Count-1 do begin - cmd := cmdList[i]; - s := s + cmd + StringOfChar(' ', MaxLength-Length(cmd)); - if ((i+1) mod 5) = 0 then begin - writeln(s); - s := ''; - end; - end; - if (s <> '') then - WriteLn(s); - if (cmdList.Count = 0) then - WriteLn('[no possible commands]'); - end; - finally - cmdList.Free; - end; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlConsoleMenu.DoAnswer(const aInput: String); -begin - if Assigned(fOnAnswer) then - fOnAnswer(self, aInput); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlConsoleMenu.ExecuteCommand(const aCmd: String); -begin - if (LowerCase(AnsiLeftStr(trim(aCmd), 4)) = 'exit') then - ExitMenu - else - inherited ExecuteCommand(aCmd); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlConsoleMenu.StartMenu; -begin - fCommandPrompt.Start; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlConsoleMenu.ExitMenu; -begin - fCommandPrompt.Stop; -end; - -procedure TutlConsoleMenu.Ask(const aQuestion: String; const aHidden: Boolean; const aOnAnswer: TutlInputEvent); -begin - if Assigned(aOnAnswer) then - fOnAnswer := aOnAnswer; - fIsAsking := true; - fInputBackup := fCommandPrompt.Current; - if aHidden then - fCommandPrompt.HiddenChar := '*' - else - fCommandPrompt.HiddenChar := #0; - fCommandPrompt.HistoryEnabled := false; - fCommandPrompt.OnAutoComplete := nil; - fCommandPrompt.Prefix := aQuestion; - fCommandPrompt.Current := ''; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlConsoleMenu.Create(const aHelp: String; const aConsoleCS: syncobjs.TCriticalSection); -begin - inherited Create(aHelp); - fExitMenu := TutlMenuItem.Create(self, 'exit', 'exit programm', nil); - fCommandPrompt := TutlCommandPrompt.Create(aConsoleCS); - fCommandPrompt.OnAutoComplete := @AutoComplete; - fCommandPrompt.OnInput := @CommandInput; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlConsoleMenu.Destroy; -begin - FreeAndNil(fCommandPrompt); - FreeAndNil(fExitMenu); - inherited Destroy; -end; - -end. - diff --git a/uutlConversion.pas b/uutlConversion.pas deleted file mode 100644 index b30c8b5..0000000 --- a/uutlConversion.pas +++ /dev/null @@ -1,66 +0,0 @@ -unit uutlConversion; - -{ Package: Utils - Prefix: utl - UTiLs - Beschreibung: diese Unit stellt Methoden für Konvertierung verschiedener Datentypen zur Verfügung } - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils; - -function Supports(const aInstance: TObject; const aClass: TClass; out aObj): Boolean; overload; -function HexToBinary(HexValue: PChar; BinValue: PByte; BinBufSize: Integer): Integer; - -implementation - -function Supports(const aInstance: TObject; const aClass: TClass; out aObj): Boolean; -begin - result := Assigned(aInstance) and aInstance.InheritsFrom(aClass); - if result then - TObject(aObj) := aInstance - else - TObject(aObj) := nil; -end; - - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//wandelt einen Hex-String in einen Blob um -//@hexvalue: Hex-String -//@binvalue: Zeiger auf einen Speicherbereich -//@binbufsize: maximale größe die geschrieben werden darf -//@result: gelesene bytes -function HexToBinary(HexValue: PChar; BinValue: PByte; BinBufSize: Integer): Integer; -var i,j,h,l : integer; - -begin - i:=binbufsize; - while (i>0) do - begin - if hexvalue^ IN ['A'..'F','a'..'f'] then - h:=((ord(hexvalue^)+9) and 15) - else if hexvalue^ IN ['0'..'9'] then - h:=((ord(hexvalue^)) and 15) - else - break; - inc(hexvalue); - if hexvalue^ IN ['A'..'F','a'..'f'] then - l:=(ord(hexvalue^)+9) and 15 - else if hexvalue^ IN ['0'..'9'] then - l:=(ord(hexvalue^)) and 15 - else - break; - j := l + (h shl 4); - inc(hexvalue); - binvalue^:=j; - inc(binvalue); - dec(i); - end; - result:=binbufsize-i; -end; - - -end. - diff --git a/uutlGraph.pas b/uutlGraph.pas deleted file mode 100644 index f1b7495..0000000 --- a/uutlGraph.pas +++ /dev/null @@ -1,413 +0,0 @@ -unit uutlGraph; - -{ Package: Utils - Prefix: utl - UTiLs - Beschreibung: diese Unit implementiert einen generischen Graphen } - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils, contnrs, uutlCommon; - -type - TutlGraph = class; - TutlGraphNodeData = class(TObject) - public - constructor Create; virtual; - end; - TutlGraphNodeDataClass = class of TutlGraphNodeData; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlGraphNode = class; - TutlGraphNodeClass = class of TutlGraphNode; - TutlGraphNode = class(TutlInterfaceNoRefCount) - private type - TNodeEnumerator = class(TObject) - private - fOwner: TutlGraphNode; - fPos: Integer; - function GetCurrent: TutlGraphNode; - public - property Current: TutlGraphNode read GetCurrent; - function MoveNext: Boolean; - constructor Create(const aOwner: TutlGraphNode); - end; - protected - fParent: TutlGraphNode; - fOwner: TutlGraph; - fData: TutlGraphNodeData; - fItems: TObjectList; - - class function GetDataClass: TutlGraphNodeDataClass; virtual; - - function GetCount: Integer; virtual; - function GetItems(const aIndex: Integer): TutlGraphNode; virtual; - function AttachNode(const aNode: TutlGraphNode): Boolean; virtual; - function DetachNode(const aNode: TutlGraphNode): Boolean; virtual; - public - property Parent: TutlGraphNode read fParent; - property Owner: TutlGraph read fOwner; - property Data: TutlGraphNodeData read fData; - property Count: Integer read GetCount; - property Items[const aIndex: Integer]: TutlGraphNode read GetItems; default; - - function AddItem: TutlGraphNode; - function IndexOf(const aItem: TutlGraphNode): Integer; - procedure DelItem(const aIndex: Integer); - procedure Clear; - function IsParent(const aNode: TutlGraphNode): Boolean; - function Move(const aParent: TutlGraphNode): Boolean; - - function GetEnumerator: TNodeEnumerator; - - constructor Create(const aParent: TutlGraphNode; const aOwner: TutlGraph); - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlGraph = class(TutlInterfaceNoRefCount) - protected - fRootNode: TutlGraphNode; - - class function GetItemClass: TutlGraphNodeClass; virtual; - public - property RootNode: TutlGraphNode read fRootNode; - - constructor Create; - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - generic TutlGenericGraphNode<GData: TutlGraphNodeData; GNode, GOwner> = class(TutlGraphNode) - private type - TGenericNodeEnumerator = class(TObject) - private - fOwner: TutlGraphNode; - fPos: Integer; - function GetCurrent: GNode; - public - property Current: GNode read GetCurrent; - function MoveNext: Boolean; - constructor Create(const aOwner: TutlGraphNode); - end; - private - function GetParent: GNode; - function GetOwner: GOwner; - function GetData: GData; - function GetItemsGeneric(const aIndex: Integer): GNode; - public - property Parent: GNode read GetParent; - property Owner: GOwner read GetOwner; - property Data: GData read GetData; - property Items[const aIndex: Integer]: GNode read GetItemsGeneric; default; - - function AddItem: GNode; - function IndexOf(const aItem: GNode): Integer; - function IsParent(const aNode: GNode): Boolean; - function Move(const aParent: GNode): Boolean; - - function GetEnumerator: TGenericNodeEnumerator; - - constructor Create(const aParent: GNode; const aOwner: GOwner); - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - generic TutlGenericGraph<T: TutlGraphNode> = class(TutlGraph) - private - function GetRootNode: T; - public - property RootNode: T read GetRootNode; - end; - -implementation - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlGraphNodeData///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlGraphNodeData.Create; -begin - inherited Create; - //nothing to do here -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlGraphNode.TNodeEnumerator////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.TNodeEnumerator.GetCurrent: TutlGraphNode; -begin - result := fOwner.Items[fPos]; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.TNodeEnumerator.MoveNext: Boolean; -begin - inc(fPos); - result := (fPos < fOwner.Count); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlGraphNode.TNodeEnumerator.Create(const aOwner: TutlGraphNode); -begin - inherited Create; - fPos := -1; - fOwner := aOwner; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlGraphNode///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class function TutlGraphNode.GetDataClass: TutlGraphNodeDataClass; -begin - result := TutlGraphNodeData; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.GetCount: Integer; -begin - result := fItems.Count; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.GetItems(const aIndex: Integer): TutlGraphNode; -begin - if (aIndex >= 0) and (aIndex < Count) then - result := (fItems[aIndex] as TutlGraphNode) - else - raise Exception.Create(Format('index (%d) is out of Range (%d - %d)', [aIndex, 0, Count-1])); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.AttachNode(const aNode: TutlGraphNode): Boolean; -begin - result := true; - fItems.Add(aNode); - aNode.fParent := self; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.DetachNode(const aNode: TutlGraphNode): Boolean; -var - i: Integer; -begin - result := false; - i := fItems.IndexOf(aNode); - if (i < 0) then - exit; - try - fItems.OwnsObjects := false; - fItems.Delete(i); - aNode.fParent := nil; - finally - fItems.OwnsObjects := true; - end; - result := true; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.AddItem: TutlGraphNode; -begin - if Assigned(Owner) then - result := Owner.GetItemClass().Create(self, Owner) - else - result := TutlGraphNode.Create(self, Owner); - fItems.Add(result); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.IndexOf(const aItem: TutlGraphNode): Integer; -begin - result := fItems.IndexOf(aItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlGraphNode.DelItem(const aIndex: Integer); -begin - if (aIndex >= 0) and (aIndex < Count) then begin - fItems.Delete(aIndex); - end else - raise Exception.Create(Format('index (%d) is out of Range (%d - %d)', [aIndex, 0, Count-1])); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlGraphNode.Clear; -begin - fItems.Clear; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.IsParent(const aNode: TutlGraphNode): Boolean; -var - n: TutlGraphNode; -begin - n := self; - result := true; - while Assigned(n.Parent) do begin - if (aNode = n.Parent) then - exit; - n := n.Parent; - end; - result := false; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.Move(const aParent: TutlGraphNode): Boolean; -var - oldParent: TutlGraphNode; -begin - result := false; - if (aParent.IsParent(self)) then - exit; - oldParent := Parent; - if Assigned(oldParent) and not oldParent.DetachNode(self) then - exit; - if not aParent.AttachNode(self) then begin - if Assigned(oldParent) then - oldParent.AttachNode(self); - exit; - end; - result := true; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGraphNode.GetEnumerator: TNodeEnumerator; -begin - result := TNodeEnumerator.Create(self); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlGraphNode.Create(const aParent: TutlGraphNode; const aOwner: TutlGraph); -begin - inherited Create; - fParent := aParent; - fOwner := aOwner; - fData := GetDataClass().Create(); - fItems := TObjectList.create(true); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlGraphNode.Destroy; -begin - FreeAndNil(fData); - FreeAndNil(fItems); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlGraph///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class function TutlGraph.GetItemClass: TutlGraphNodeClass; -begin - result := TutlGraphNode; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlGraph.Create; -begin - inherited Create; - fRootNode := GetItemClass().Create(nil, self); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlGraph.Destroy; -begin - FreeAndNil(fRootNode); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlGenericGraphNode.TGenericNodeEnumerator/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.TGenericNodeEnumerator.GetCurrent: GNode; -begin - result := GNode(fOwner.Items[fPos]); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.TGenericNodeEnumerator.MoveNext: Boolean; -begin - inc(fPos); - result := (fPos < fOwner.Count); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlGenericGraphNode.TGenericNodeEnumerator.Create(const aOwner: TutlGraphNode); -begin - inherited Create; - fPos := -1; - fOwner := aOwner; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlGenericGraphNode////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.GetParent: GNode; -begin - result := GNode(fParent); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.GetOwner: GOwner; -begin - result := GOwner(fOwner); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.GetData: GData; -begin - result := GData(fData); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.GetItemsGeneric(const aIndex: Integer): GNode; -begin - result := GNode(inherited GetItems(aIndex)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.AddItem: GNode; -begin - result := GNode(inherited AddItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.IndexOf(const aItem: GNode): Integer; -begin - result := inherited IndexOf(aItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.IsParent(const aNode: GNode): Boolean; -begin - result := inherited IsParent(aNode); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.Move(const aParent: GNode): Boolean; -begin - result := inherited Move(aParent); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraphNode.GetEnumerator: TGenericNodeEnumerator; -begin - result := TGenericNodeEnumerator.Create(self); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlGenericGraphNode.Create(const aParent: GNode; const aOwner: GOwner); -begin - inherited Create(TutlGraphNode(aParent), TutlGraph(aOwner)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlGenericGraph////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlGenericGraph.GetRootNode: T; -begin - result := (fRootNode as T); -end; - -end. - diff --git a/uutlLocalization.pas b/uutlLocalization.pas deleted file mode 100644 index b18e8db..0000000 --- a/uutlLocalization.pas +++ /dev/null @@ -1,244 +0,0 @@ -unit uutlLocalization; - -{ Package: Utils - Prefix: utl - UTiLs - Beschreibung: diese Unit stellt Mechanismen zur Übersetzung von Texten mit Hilfe von PO/MO-Files zur Verfügung } - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils, gettext, - uutlCommon, uutlGenerics, uutlStreamHelper; - -type -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlLocalizationItem = class(TObject) - Name, Comment: String; - constructor Create; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlLocalizationDatabase = class(TObject) - private type - TStringObjMap = specialize TutlMap<String, TutlLocalizationItem>; - private - fStringList: TStringObjMap; - fLangFile: TMOFile; - - function GetCount: Integer; - function GetObject(Index: Integer): TutlLocalizationItem; - public - property Count : Integer read GetCount; - property Objects[Index: Integer]: TutlLocalizationItem read GetObject; - function AddName(const Name, Comment: string): TutlLocalizationItem; - function RemoveName(const Name: string): boolean; - - procedure LoadFromStream(const aStream: TStream); - procedure SaveToStream(const aStream: TStream); - - procedure LoadLanguage(const aStream: TStream); - function Translate(const Name: string): string; - - constructor Create; - destructor Destroy; override; - end; - -function utlLocalizationDatabase: TutlLocalizationDatabase; -function __(Name: string; Default: string = #0): string; overload; - -implementation - -uses - Dialogs, uvfsManager; - -var - Entity: TutlLocalizationDatabase; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function utlLocalizationDatabase: TutlLocalizationDatabase; -var - str: IStreamHandle; -begin - if not Assigned(Entity) then begin - Entity := TutlLocalizationDatabase.Create; - if vfsManager.ReadFile('lang/strings', str) then - Entity.LoadFromStream(str.GetStream); - end; - result := Entity; -end; - - -function __(Name: string; Default: string): string; -begin - if Default=#0 then - Default := Name; - Result := utlLocalizationDatabase.Translate(Name); - if (Result = '') then - Result := Default; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlLocalizationItem//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//erstellt das Objekt -constructor TutlLocalizationItem.Create; -begin - inherited Create; - Name := ''; - Comment := ''; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlLocalizationDatabase/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//Get-Methode der Count-Eigenschaft -function TutlLocalizationDatabase.GetCount: Integer; -begin - result := fStringList.Count; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//Get-Methode der Value-Eigenschaft -function TutlLocalizationDatabase.GetObject(Index: Integer): TutlLocalizationItem; -begin - if (Index >= 0) and (Index < fStringList.Count) then - result := fStringList.ValueAt[Index] - else - result := nil; -end; - -function TutlLocalizationDatabase.AddName(const Name, Comment: string): TutlLocalizationItem; -var - e: TutlLocalizationItem; - i: Integer; -begin - i := fStringList.IndexOf(Name); - if i >= 0 then - Result:= Objects[i] - else begin - e := TutlLocalizationItem.Create; - e.Name := Name; - e.Comment := Comment; - fStringList.Add(e.Name, e); - result := e; - end; -end; - -function TutlLocalizationDatabase.RemoveName(const Name: string): boolean; -begin - Result:= fStringList.IndexOf(Name) >= 0; - if Result then - fStringList.Delete(Name); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//läd die Datenbank aus einem Stream -//@aStream: Stream aus der geladen werden soll; -procedure TutlLocalizationDatabase.LoadFromStream(const aStream: TStream); -const - HEADER = 'StringDatabase'; -var - rd: TutlStreamReader; - csv: TutlCSVList; - co: string; -begin - rd:= TutlStreamReader.Create(aStream); - try - if HEADER <> rd.ReadLine then - raise Exception.Create('TStringDatabase.LoadFromStream - invalid Stream'); - fStringList.Clear; - csv:= TutlCSVList.Create; - try - csv.Delimiter:= ';'; - csv.StrictDelimitedText:= rd.ReadLine; - while csv.Count>=1 do begin - co:= ''; - if csv.Count>1 then - co:= csv[1]; - AddName(csv[0],co); - // next line - csv.StrictDelimitedText:= rd.ReadLine; - end; - finally - FreeAndNil(csv); - end; - finally - FreeAndNil(rd); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//speichert die Datenbank in einem Stream -//@aStream: Stream in dem gespeichert werden soll; -procedure TutlLocalizationDatabase.SaveToStream(const aStream: TStream); -const - HEADER = 'StringDatabase'; -var - i: Integer; - wr: TutlStreamWriter; - csv: TutlCSVList; - o: TutlLocalizationItem; -begin - wr:= TutlStreamWriter.Create(aStream); - try - wr.WriteLine(HEADER); - csv:= TutlCSVList.Create; - try - csv.Delimiter:= ';'; - for i := 0 to fStringList.Count-1 do begin - csv.Clear; - o:= Objects[i]; - csv.Add(o.Name); - csv.Add(o.Comment); - wr.WriteLine(csv.StrictDelimitedText); - end; - finally - FreeAndNil(csv); - end; - finally - FreeAndNil(wr); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlLocalizationDatabase.LoadLanguage(const aStream: TStream); -begin - fLangFile.Free; - fLangFile := nil; - fLangFile := TMOFile.Create(aStream); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlLocalizationDatabase.Translate(const Name: string): string; -begin - if Assigned(fLangFile) then - Result := UTF8Encode(fLangFile.Translate(Name)) - else - Result := ''; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//erstellt das Objekt -constructor TutlLocalizationDatabase.Create; -begin - inherited Create; - fStringList := TStringObjMap.Create(True); - fLangFile := nil; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//gibt das Objekt frei -destructor TutlLocalizationDatabase.Destroy; -begin - fLangFile.Free; - fStringList.Free; - inherited Destroy; -end; - -finalization - FreeAndNil(Entity); - -end. - diff --git a/uutlMcfHelper.pas b/uutlMcfHelper.pas deleted file mode 100644 index 5495dd2..0000000 --- a/uutlMcfHelper.pas +++ /dev/null @@ -1,100 +0,0 @@ -unit uutlMcfHelper; - -{$mode objfpc}{$H+} - -interface - -uses - ugluMatrix, ugluVector, uutlMCF, uglcLight; - -procedure utlWriteMatrix4f(const aSection: TutlMCFSection; const aMatrix: TgluMatrix4f); -function utlReadMatrix4f(const aSection: TutlMCFSection): TgluMatrix4f; -procedure utlWriteMaterial(const aSection: TutlMCFSection; const aMaterial: TglcMaterialRec); -function utlReadMaterial(const aSection: TutlMCFSection): TglcMaterialRec; -procedure utlWriteLight(const aSection: TutlMCFSection; const aLight: TglcLightRec); -function utlReadLight(const aSection: TutlMCFSection): TglcLightRec; - -implementation - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure utlWriteMatrix4f(const aSection: TutlMCFSection; const aMatrix: TgluMatrix4f); -begin - with aSection do begin - SetString('AxisX', gluVector4fToStr(aMatrix[maAxisX])); - SetString('AxisY', gluVector4fToStr(aMatrix[maAxisY])); - SetString('AxisZ', gluVector4fToStr(aMatrix[maAxisZ])); - SetString('Pos', gluVector4fToStr(aMatrix[maPos])); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function utlReadMatrix4f(const aSection: TutlMCFSection): TgluMatrix4f; -begin - with aSection do begin - result[maAxisX] := gluStrToVector4f(GetString('AxisX', '1; 0; 0; 0;')); - result[maAxisY] := gluStrToVector4f(GetString('AxisY', '0; 1; 0; 0;')); - result[maAxisZ] := gluStrToVector4f(GetString('AxisZ', '0; 0; 1; 0;')); - result[maPos] := gluStrToVector4f(GetString('Pos', '0; 0; 0; 1;')); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure utlWriteMaterial(const aSection: TutlMCFSection; const aMaterial: TglcMaterialRec); -begin - with aSection do begin - SetString('Ambient', gluVector4fToStr(aMaterial.Ambient)); - SetString('Diffuse', gluVector4fToStr(aMaterial.Diffuse)); - SetString('Specular', gluVector4fToStr(aMaterial.Specular)); - SetString('Emission', gluVector4fToStr(aMaterial.Emission)); - SetFloat ('Shininess', aMaterial.Shininess); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function utlReadMaterial(const aSection: TutlMCFSection): TglcMaterialRec; -begin - with aSection do begin - result.Ambient := gluStrToVector4f(GetString('Ambient', gluVector4fToStr(MAT_DEFAULT_AMBIENT))); - result.Diffuse := gluStrToVector4f(GetString('Diffuse', gluVector4fToStr(MAT_DEFAULT_DIFFUSE))); - result.Specular := gluStrToVector4f(GetString('Specular', gluVector4fToStr(MAT_DEFAULT_SPECULAR))); - result.Emission := gluStrToVector4f(GetString('Emission', gluVector4fToStr(MAT_DEFAULT_EMISSION))); - result.Shininess := GetFloat('Shininess', MAT_DEFAULT_SHININESS); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure utlWriteLight(const aSection: TutlMCFSection; const aLight: TglcLightRec); -begin - with aSection do begin - SetString('Ambient', gluVector4fToStr(aLight.Ambient)); - SetString('Diffuse', gluVector4fToStr(aLight.Diffuse)); - SetString('Specular', gluVector4fToStr(aLight.Specular)); - SetString('Position', gluVector4fToStr(aLight.Position)); - SetString('SpotDirection', gluVector3fToStr(aLight.SpotDirection)); - SetFloat ('SpotExponent', aLight.SpotExponent); - SetFloat ('SpotCutoff', aLight.SpotCutoff); - SetFloat ('ConstantAtt', aLight.ConstantAtt); - SetFloat ('LinearAtt', aLight.LinearAtt); - SetFloat ('QuadraticAtt', aLight.QuadraticAtt); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function utlReadLight(const aSection: TutlMCFSection): TglcLightRec; -begin - with aSection do begin - result.Ambient := gluStrToVector4f(GetString('Ambient', gluVector4fToStr(LIGHT_DEFAULT_AMBIENT))); - result.Diffuse := gluStrToVector4f(GetString('Diffuse', gluVector4fToStr(LIGHT_DEFAULT_DIFFUSE))); - result.Specular := gluStrToVector4f(GetString('Specular', gluVector4fToStr(LIGHT_DEFAULT_SPECULAR))); - result.Position := gluStrToVector4f(GetString('Position', gluVector4fToStr(LIGHT_DEFAULT_POSITION))); - result.SpotDirection := gluStrToVector3f(GetString('SpotDirection', gluVector3fToStr(LIGHT_DEFAULT_SPOT_DIRECTION))); - result.SpotExponent := GetFloat ('SpotExponent', LIGHT_DEFAULT_SPOT_EXPONENT); - result.SpotCutoff := GetFloat ('SpotCutoff', LIGHT_DEFAULT_SPOT_CUTOFF); - result.ConstantAtt := GetFloat ('ConstantAtt', LIGHT_DEFAULT_CONSTANT_ATT); - result.LinearAtt := GetFloat ('LinearAtt', LIGHT_DEFAULT_LINEAR_ATT); - result.QuadraticAtt := GetFloat ('QuadraticAtt', LIGHT_DEFAULT_QUADRATIC_ATT); - end; -end; - -end. - diff --git a/uutlMessageThread.pas b/uutlMessageThread.pas deleted file mode 100644 index 21a04a3..0000000 --- a/uutlMessageThread.pas +++ /dev/null @@ -1,396 +0,0 @@ -unit uutlMessageThread; - -{ Package: Utils - Prefix: utl - UTiLs - Beschreibung: diese Unit definiert einen Thread, der mit Hilfe von Messages Daten synchronisiert - mit anderen Threads austauschen kann } - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils, syncobjs, uutlMessages, uutlGenerics; - -type -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlMessageThread = class(TThread, IUnknown) - public type - TMessageProgressCallback = procedure(const aMsg: TutlMessage) of Object; - TMessageQueue = class(specialize TutlSyncQueue<TutlMessage>) - private - fEvent: TSimpleEvent; - public - procedure Push(const aItem: TutlMessage); override; - function Pop(out aItem: TutlMessage): Boolean; override; - - function WaitForMessages(const aWaitTime: Cardinal = INFINITE): Boolean; - function ProcessMessages(const aProgressCallback: TMessageProgressCallback): Boolean; - - constructor Create(const aOwnsObjects: Boolean = true); - destructor Destroy; override; - end; - protected - fMessages: TMessageQueue; - fRefCount : longint; - { implement methods of IUnknown } - function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} iid : tguid;out obj) : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; - function _AddRef : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual; - function _Release : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual; - protected - function CreateMessageQueue: TMessageQueue; virtual; - function WaitForMessages(const aWaitTime: Cardinal): Boolean; - function ProcessMessages: Boolean; virtual; - procedure ProcessMessage(const {%H-}aMessage: TutlMessage); virtual; - public - //Messages Objects passed to PostMessage will be freed automatically - procedure PostMessage(const aID: Cardinal; const aWParam, aLParam: PtrInt); overload; - procedure PostMessage(const aID: Cardinal; const aArgs: TObject); overload; - procedure PostMessage(const aMsg: TutlMessage); virtual; overload; - - //Messages Objects passed to SendMessage must be freed by user when WaitResult is wrSignaled (otherwise the thread will handle it) - function SendMessage(const aID: Cardinal; const aWParam, aLParam: PtrInt; - const aWaitTime: Cardinal = INFINITE): TWaitResult; overload; - function SendMessage(const aID: Cardinal; const aArgs: TObject; - const aWaitTime: Cardinal = INFINITE): TWaitResult; overload; - function SendMessage(const aMsg: TutlSynchronousMessage; - const aWaitTime: Cardinal = INFINITE): TWaitResult; virtual; overload; - - constructor Create(CreateSuspended: Boolean; const StackSize: SizeUInt=DefaultStackSize); - destructor Destroy; override; - end; - - //Messages Objects passed to PostMessage will be freed automatically - function utlPostMessage(const aThreadID: TThreadID; const aID: Cardinal; const aWParam, aLParam: PtrInt): Boolean; overload; - function utlPostMessage(const aThreadID: TThreadID; const aID: Cardinal; const aArgs: TObject): Boolean; overload; - function utlPostMessage(const aThreadID: TThreadID; const aMsg: TutlMessage): Boolean; overload; - - //Messages Objects passed to SendMessage must be freed by user when WaitResult is wrSignaled (otherwise the thread will handle it) - function utlSendMessage(const aThreadID: TThreadID; const aID: Cardinal; const aWParam, aLParam: PtrInt; - const aWaitTime: Cardinal = INFINITE): TWaitResult; overload; - function utlSendMessage(const aThreadID: TThreadID; const aID: Cardinal; const aArgs: TObject; - const aWaitTime: Cardinal = INFINITE): TWaitResult; overload; - function utlSendMessage(const aThreadID: TThreadID; const aMsg: TutlSynchronousMessage; - const aWaitTime: Cardinal = INFINITE): TWaitResult; overload; - -implementation - -uses - uutlLogger, uutlExceptions; - -type - TutlMessageThreadMap = class(specialize TutlMap<TThreadID, TutlMessageThread>) - private - fCS: TCriticalSection; - public - procedure Lock; - procedure Release; - constructor Create(const aOwnsObjects: Boolean = true); - destructor Destroy; override; - end; - -var - Threads: TutlMessageThreadMap; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function utlPostMessage(const aThreadID: TThreadID; const aID: Cardinal; const aWParam, aLParam: PtrInt): Boolean; -begin - result := utlPostMessage(aThreadID, TutlMessage.Create(aID, aWParam, aLParam)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function utlPostMessage(const aThreadID: TThreadID; const aID: Cardinal; const aArgs: TObject): Boolean; -begin - result := utlPostMessage(aThreadID, TutlMessage.Create(aID, aArgs)); -end; - -function utlPostMessage(const aThreadID: TThreadID; const aMsg: TutlMessage): Boolean; -var - t: TutlMessageThread; -begin - Threads.Lock; - try - t := Threads[aThreadID]; - finally - Threads.Release; - end; - result := Assigned(t); - if (result) then - t.PostMessage(aMsg) - else - aMsg.Free; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function utlSendMessage(const aThreadID: TThreadID; const aID: Cardinal; const aWParam, aLParam: PtrInt; const aWaitTime: Cardinal): TWaitResult; -begin - result := utlSendMessage(aThreadID, TutlSynchronousMessage.Create(aID, aWParam, aLParam), aWaitTime); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function utlSendMessage(const aThreadID: TThreadID; const aID: Cardinal; const aArgs: TObject; const aWaitTime: Cardinal): TWaitResult; -begin - result := utlSendMessage(aThreadID, TutlSynchronousMessage.Create(aID, aArgs), aWaitTime); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function utlSendMessage(const aThreadID: TThreadID; const aMsg: TutlSynchronousMessage; const aWaitTime: Cardinal): TWaitResult; -var - t: TutlMessageThread; -begin - Threads.Lock; - try - t := Threads[aThreadID]; - finally - Threads.Release; - end; - if Assigned(t) then - result := t.SendMessage(aMsg) - else begin - result := wrError; - aMsg.Free; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMessageThread.TMessageQueue/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMessageThread.TMessageQueue.Push(const aItem: TutlMessage); -begin - inherited Push(aItem); - fEvent.SetEvent; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread.TMessageQueue.Pop(out aItem: TutlMessage): Boolean; -begin - result := inherited Pop(aItem); - if (Count <= 0) then - fEvent.ResetEvent; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread.TMessageQueue.WaitForMessages(const aWaitTime: Cardinal): Boolean; -var - wr: TWaitResult; -begin - wr := fEvent.WaitFor(aWaitTime); - result := (wr = wrSignaled); - if not result and (wr <> wrTimeout) then - raise EWait.Create('Error while waiting for messages', wr); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread.TMessageQueue.ProcessMessages(const aProgressCallback: TMessageProgressCallback): Boolean; -var - m: TutlMessage; - empty: Boolean; -begin - empty := false; - result := false; - if not Assigned(aProgressCallback) then - exit; - repeat - try - if Pop(m) then begin - result := true; - try - aProgressCallback(m); - finally - if (m is TutlSynchronousMessage) then - (m as TutlSynchronousMessage).Finish - else - FreeAndNil(m); - end; - end else - empty := true; - except - on e: Exception do begin - utlLogger.Error(self, 'error while progressing message: %s - %s', [e.ClassName, e.Message]); - end; - end; - until empty; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlMessageThread.TMessageQueue.Create(const aOwnsObjects: Boolean); -begin - inherited Create(aOwnsObjects); - fEvent := TSimpleEvent.Create; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlMessageThread.TMessageQueue.Destroy; -begin - inherited Destroy; - FreeAndNil(fEvent); // do not free event before all messages has been deleted -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMessageThreadMap////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMessageThreadMap.Lock; -begin - fCS.Acquire; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMessageThreadMap.Release; -begin - fCS.Release; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlMessageThreadMap.Create(const aOwnsObjects: Boolean); -begin - inherited; - fCS:= TCriticalSection.Create; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlMessageThreadMap.Destroy; -begin - fCS.Acquire; - try - inherited Destroy; - finally - fCS.Release; - end; - FreeAndNil(fCS); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMessageThread///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread.QueryInterface(constref iid: tguid; out obj): longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; -begin - if getinterface(iid,obj) then - result := S_OK - else - result := longint(E_NOINTERFACE); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread._AddRef: longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; -begin - result := InterLockedIncrement(fRefCount); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread._Release: longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; -begin - result := InterLockedDecrement(fRefCount); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread.CreateMessageQueue: TMessageQueue; -begin - result := TMessageQueue.Create(true); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread.WaitForMessages(const aWaitTime: Cardinal): Boolean; -begin - result := fMessages.WaitForMessages(aWaitTime); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread.ProcessMessages: Boolean; -begin - result := fMessages.ProcessMessages(@ProcessMessage); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMessageThread.ProcessMessage(const aMessage: TutlMessage); -begin - case aMessage.ID of - MSG_CALLBACK: - (aMessage as TutlCallbackMsg).ExecuteCallback; - MSG_SYNC_CALLBACK: - (aMessage as TutlSyncCallbackMsg).ExecuteCallback; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMessageThread.PostMessage(const aID: Cardinal; const aWParam, aLParam: PtrInt); -begin - fMessages.Push(TutlMessage.Create(aID, aWParam, aLParam)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMessageThread.PostMessage(const aID: Cardinal; const aArgs: TObject); -begin - fMessages.Push(TutlMessage.Create(aID, aArgs)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlMessageThread.PostMessage(const aMsg: TutlMessage); -begin - fMessages.Push(aMsg); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread.SendMessage(const aID: Cardinal; const aWParam, aLParam: PtrInt; const aWaitTime: Cardinal): TWaitResult; -begin - result := SendMessage(TutlSynchronousMessage.Create(aID, aWParam, aLParam), aWaitTime); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread.SendMessage(const aID: Cardinal; const aArgs: TObject; const aWaitTime: Cardinal): TWaitResult; -begin - result := SendMessage(TutlSynchronousMessage.Create(aID, aArgs), aWaitTime); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlMessageThread.SendMessage(const aMsg: TutlSynchronousMessage; const aWaitTime: Cardinal): TWaitResult; -begin - fMessages.Push(aMsg); - result := aMsg.WaitFor(aWaitTime); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlMessageThread.Create(CreateSuspended: Boolean; const StackSize: SizeUInt); -begin - inherited Create(CreateSuspended, StackSize); - fMessages := CreateMessageQueue; - Threads.Lock; - try - Threads.Add(ThreadID, self); - finally - Threads.Release; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlMessageThread.Destroy; -begin - Threads.Lock; - try - Threads.Delete(ThreadID); - finally - Threads.Release; - end; - FreeAndNil(fMessages); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -initialization - Threads := TutlMessageThreadMap.Create(false); - -finalization - Threads.Lock; - try - while (Threads.Count > 0) do - Threads.ValueAt[Threads.Count-1].Free; - finally - Threads.Release; - end; - FreeAndNil(Threads); - -end. - diff --git a/uutlMessages.pas b/uutlMessages.pas deleted file mode 100644 index 3f5ab93..0000000 --- a/uutlMessages.pas +++ /dev/null @@ -1,171 +0,0 @@ -unit uutlMessages; - -{ Package: Utils - Prefix: utl - UTiLs - Beschreibung: diese Unit enthält verschiedene Klassen, die Messages definieren, - die zwischen utlMessageThreads ausgetauscht werden können } - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils, syncobjs; - -const - //General - MSG_CALLBACK = $00010001; //TutlCallbackMsg - - MSG_SYNC_CALLBACK = $00010002; //TutlSyncCallbackMsg - - //User - MSG_USER = $F0000000; - -type -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlMessage = class(TObject) - protected - fID: Cardinal; - fWParam: PtrInt; - fLParam: PtrInt; - fArgs: TObject; - fOwnsObjects: Boolean; - public - property ID: Cardinal read fID; - property WParam: PtrInt read fWParam; - property LParam: PtrInt read fLParam; - property Args: TObject read fArgs; - property OwnsObjects: Boolean read fOwnsObjects write fOwnsObjects; - - constructor Create(const aID: Cardinal; const aWParam, aLParam: PtrInt); overload; - constructor Create(const aID: Cardinal; const aArgs: TObject; const aOwnsObjects: Boolean = true); overload; - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlSynchronousMessage = class(TutlMessage) - private - fEvent: TEvent; - public - procedure Finish; - function WaitFor(const aTimeout: Cardinal): TWaitResult; - - constructor Create(const aID: Cardinal; const aWParam, aLParam: PtrInt); overload; - constructor Create(const {%H-}aID: Cardinal; const aArgs: TObject; const aOwnsObjects: Boolean = true); overload; - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlCallbackMsg = class(TutlMessage) - public - procedure ExecuteCallback; virtual; - constructor Create; overload; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlSyncCallbackMsg = class(TutlSynchronousMessage) - public - procedure ExecuteCallback; virtual; - constructor Create; overload; - end; - -implementation - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlMessage/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlMessage.Create(const aID: Cardinal; const aWParam, aLParam: PtrInt); -begin - inherited Create; - fID := aID; - fWParam := aWParam; - fLParam := aLParam; - fArgs := nil; - fOwnsObjects := true; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlMessage.Create(const aID: Cardinal; const aArgs: TObject; const aOwnsObjects: Boolean); -begin - inherited Create; - fID := aID; - fWParam := 0; - fLParam := 0; - fArgs := aArgs; - fOwnsObjects := aOwnsObjects; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlMessage.Destroy; -begin - if Assigned(fArgs) and fOwnsObjects then - FreeAndNil(fArgs); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlSynchronousMessage////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlSynchronousMessage.Finish; -begin - fEvent.SetEvent; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlSynchronousMessage.WaitFor(const aTimeout: Cardinal): TWaitResult; -begin - result := fEvent.WaitFor(aTimeout); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlSynchronousMessage.Create(const aID: Cardinal; const aWParam, aLParam: PtrInt); -begin - inherited Create(aID, aWParam, aLParam); - fEvent := TEvent.Create(nil, true, false, ''); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlSynchronousMessage.Create(const aID: Cardinal; const aArgs: TObject; const aOwnsObjects: Boolean); -begin - inherited Create(ID, aArgs, aOwnsObjects); - fEvent := TEvent.Create(nil, true, false, ''); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlSynchronousMessage.Destroy; -begin - fEvent.SetEvent; - FreeAndNil(fEvent); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlCallbackMsg/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlCallbackMsg.ExecuteCallback; -begin - //DUMMY -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlCallbackMsg.Create; -begin - inherited Create(MSG_CALLBACK, 0, 0); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlSyncCallbackMsg/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlSyncCallbackMsg.ExecuteCallback; -begin - //DUMMY -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlSyncCallbackMsg.Create; -begin - inherited Create(MSG_SYNC_CALLBACK, 0, 0); -end; - -end. - diff --git a/uutlObservableGenerics.pas b/uutlObservableGenerics.pas deleted file mode 100644 index f89ea03..0000000 --- a/uutlObservableGenerics.pas +++ /dev/null @@ -1,369 +0,0 @@ -unit uutlObservableGenerics; - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils, - uutlGenerics, uutlInterfaces; - -type -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - generic TutlEventList<T> = class(specialize TutlHashSetBase<T>) - private type - TComparer = class(TInterfacedObject, IComparer) - public - function Compare(const i1, i2: T): Integer; - end; - - public - function RegisterEvent(const aEvent: T): Boolean; - function UnregisterEvent(const aEvent: T): Boolean; - - constructor Create; - end; - TutlNotifyEventList = specialize TutlEventList<TNotifyEvent>; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - generic TutlObservableCustomList<T> = class(specialize TutlCustomList<T>) - public type - TEventList = specialize TutlEventList<TItemEvent>; - private - fOnAddItem: TEventList; - fOnRemoveItem: TEventList; - protected - procedure InsertIntern(const aIndex: Integer; const aItem: T); override; - procedure DeleteIntern(const aIndex: Integer; const aFreeItem: Boolean = true); override; - - procedure DoAddItem(const aIndex: Integer; const aItem: T); virtual; - procedure DoRemoveItem(const aIndex: Integer; const aItem: T); virtual; - public - property OnAddItem: TEventList read fOnAddItem; - property OnRemoveItem: TEventList read fOnRemoveItem; - - constructor Create(aEqualityComparer: IEqualityComparer; const aOwnsObjects: Boolean = true); - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - generic TutlObservableList<T> = class(specialize TutlObservableCustomList<T>) - public type - TEqualityComparer = specialize TutlEqualityComparer<T>; - public - constructor Create(const aOwnsObjects: Boolean = true); - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - generic TutlObservableCustomHashSet<T> = class(specialize TutlCustomHashSet<T>) - public type - TEventList = specialize TutlEventList<THashItemEvent>; - private - fOnAddItem: TEventList; - fOnRemoveItem: TEventList; - protected - procedure InsertIntern(const aIndex: Integer; const aItem: T); override; - procedure DeleteIntern(const aIndex: Integer; const aFreeItem: Boolean = true); override; - - procedure DoAddItem(const aItem: T); virtual; - procedure DoRemoveItem(const aItem: T); virtual; - public - property OnAddItem: TEventList read fOnAddItem; - property OnRemoveItem: TEventList read fOnRemoveItem; - - constructor Create(aComparer: IComparer; const aOwnsObjects: Boolean = true); - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - generic TutlObservableHashSet<T> = class(specialize TutlObservableCustomHashSet<T>) - public type - TComparer = specialize TutlComparer<T>; - public - constructor Create(const aOwnsObjects: Boolean = true); - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - generic TutlObservableCustomMap<TKey, TValue> = class(specialize TutlMapBase<TKey, TValue>) - public type - TEventList = specialize TutlEventList<TKeyValuePairEvent>; - TObservableHashSet = class(THashSet) - private - fOwner: TutlObservableCustomMap; - protected - procedure InsertIntern(const aIndex: Integer; const aItem: TKeyValuePair); override; - procedure DeleteIntern(const aIndex: Integer; const aFreeItem: Boolean = true); override; - public - constructor Create(const aOwner: TutlObservableCustomMap; const aComparer: IComparer; const aOwnsObjects: Boolean = true); - end; - - private - fHashSetImpl: TObservableHashSet; - fOnAddItem: TEventList; - fOnRemoveItem: TEventList; - protected - procedure DoAddItem(const aKey: TKey; const aValue: TValue); virtual; - procedure DoRemoveItem(const aKey: TKey; const aValue: TValue); virtual; - public - property OnAddItem: TEventList read fOnAddItem; - property OnRemoveItem: TEventList read fOnRemoveItem; - - constructor Create(const aComparer: IComparer; const aOwnsObjects: Boolean = true); - destructor Destroy; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - generic TutlObservableMap<TKey, TValue> = class(specialize TutlObservableCustomMap<TKey, TValue>) - public type - TComparer = specialize TutlComparer<TKey>; - public - constructor Create(const aOwnsObjects: Boolean = true); - end; - -implementation - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlEventList///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlEventList.TComparer.Compare(const i1, i2: T): Integer; -var - m1, m2: TMethod; -begin - m1 := TMethod(i1); - m2 := TMethod(i2); - if (m1.Data < m2.Data) then - result := -1 - else if (m1.Data > m2.Data) then - result := 1 - else if (m1.Code < m2.Code) then - result := -1 - else if (m1.Code > m2.Code) then - result := 1 - else - result := 0; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlEventList.RegisterEvent(const aEvent: T): Boolean; -var - i: Integer; -begin - result := (SearchItem(0, List.Count-1, aEvent, i) < 0); - if result then - InsertIntern(i, aEvent); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlEventList.UnregisterEvent(const aEvent: T): Boolean; -var - i, tmp: Integer; -begin - i := SearchItem(0, List.Count-1, aEvent, tmp); - result := (i >= 0); - if result then - DeleteIntern(i); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlEventList.Create; -begin - inherited Create(TComparer.Create, true); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlObservableCustomList////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomList.InsertIntern(const aIndex: Integer; const aItem: T); -begin - inherited InsertIntern(aIndex, aItem); - DoAddItem(aIndex, aItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomList.DeleteIntern(const aIndex: Integer; const aFreeItem: Boolean); -begin - DoRemoveItem(aIndex, aIndex); - inherited DeleteIntern(aIndex, aFreeItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomList.DoAddItem(const aIndex: Integer; const aItem: T); -var - e: TItemEvent; -begin - if Assigned(fOnAddItem) then - for e in fOnAddItem do - e(self, aIndex, aItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomList.DoRemoveItem(const aIndex: Integer; const aItem: T); -var - e: TItemEvent; -begin - if Assigned(fOnRemoveItem) then - for e in fOnRemoveItem do - e(self, aIndex, GetItem(aIndex)); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlObservableCustomList.Create(aEqualityComparer: IEqualityComparer; const aOwnsObjects: Boolean); -begin - inherited Create(aEqualityComparer, aOwnsObjects); - fOnAddItem := TEventList.Create; - fOnRemoveItem := TEventList.Create; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlObservableCustomList.Destroy; -begin - FreeAndNil(fOnRemoveItem); - FreeAndNil(fOnAddItem); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlObservableList//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlObservableList.Create(const aOwnsObjects: Boolean); -begin - inherited Create(TEqualityComparer.Create, aOwnsObjects); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlObservableCustomHashSet/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomHashSet.InsertIntern(const aIndex: Integer; const aItem: T); -begin - inherited InsertIntern(aIndex, aItem); - DoAddItem(aItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomHashSet.DeleteIntern(const aIndex: Integer; const aFreeItem: Boolean); -begin - DoRemoveItem(GetItem(aIndex)); - inherited DeleteIntern(aIndex, aFreeItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomHashSet.DoAddItem(const aItem: T); -var - e: THashItemEvent; -begin - if Assigned(fOnAddItem) then - for e in fOnAddItem do - e(self, aItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomHashSet.DoRemoveItem(const aItem: T); -var - e: THashItemEvent; -begin - if Assigned(fOnRemoveItem) then - for e in fOnRemoveItem do - e(self, aItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlObservableCustomHashSet.Create(aComparer: IComparer; const aOwnsObjects: Boolean); -begin - inherited Create(aComparer, aOwnsObjects); - fOnAddItem := TEventList.Create; - fOnRemoveItem := TEventList.Create; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlObservableCustomHashSet.Destroy; -begin - inherited Destroy; // calls clear -> Free EventLists after - FreeAndNil(fOnAddItem); - FreeAndNil(fOnRemoveItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlObservableHashSet///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlObservableHashSet.Create(const aOwnsObjects: Boolean); -begin - inherited Create(TComparer.Create, aOwnsObjects); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlObservableCustomMap/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomMap.TObservableHashSet.InsertIntern(const aIndex: Integer; const aItem: TKeyValuePair); -begin - inherited InsertIntern(aIndex, aItem); - fOwner.DoAddItem(aItem.Key, aItem.Value); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomMap.TObservableHashSet.DeleteIntern(const aIndex: Integer; const aFreeItem: Boolean); -var - kvp: TKeyValuePair; -begin - kvp := GetItem(aIndex); - fOwner.DoRemoveItem(kvp.Key, kvp.Value); - inherited DeleteIntern(aIndex, aFreeItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlObservableCustomMap.TObservableHashSet.Create( - const aOwner: TutlObservableCustomMap; const aComparer: IComparer; const aOwnsObjects: Boolean); -begin - inherited Create(aComparer, aOwnsObjects); - fOwner := aOwner; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomMap.DoAddItem(const aKey: TKey; const aValue: TValue); -var - e: TKeyValuePairEvent; -begin - if not Assigned(fOnAddItem) then - exit; - for e in fOnAddItem do - e(self, aKey, aValue); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlObservableCustomMap.DoRemoveItem(const aKey: TKey; const aValue: TValue); -var - e: TKeyValuePairEvent; -begin - if not Assigned(fOnRemoveItem) then - exit; - for e in fOnRemoveItem do - e(self, aKey, aValue); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlObservableCustomMap.Create(const aComparer: IComparer; const aOwnsObjects: Boolean); -begin - fOnAddItem := TEventList.Create; - fOnRemoveItem := TEventList.Create; - fHashSetImpl := TObservableHashSet.Create(self, TKeyValuePairComparer.Create(aComparer), aOwnsObjects); - inherited Create(fHashSetImpl); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlObservableCustomMap.Destroy; -begin - inherited Destroy; - FreeAndNil(fHashSetImpl); - FreeAndNil(fOnAddItem); - FreeAndNil(fOnRemoveItem); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlObservableMap///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlObservableMap.Create(const aOwnsObjects: Boolean); -begin - inherited Create(TComparer.Create, aOwnsObjects); -end; - -end. - diff --git a/uutlPlatform.pas b/uutlPlatform.pas deleted file mode 100644 index 2426798..0000000 --- a/uutlPlatform.pas +++ /dev/null @@ -1,93 +0,0 @@ -unit uutlPlatform; - -{ Package: Utils - Prefix: utl - UTiLs - Beschreibung: diese Unit implementiert Methoden mit denen ein String generiert werden kann, - welcher das System auf dem die Anwendung läuft identifiziert } - - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils; - -function GetPlatformIdentitfier: string; - -implementation - -uses - {$ifdef WINDOWS} - Windows - {$endif} - ; - -{$ifdef WINDOWS} -function GetWindowsVersionStr(const aDefault: String): string; -var - osv: TOSVERSIONINFO; - ver: cardinal; -begin - Result:= aDefault; - osv.dwOSVersionInfoSize:= SizeOf(osv); - if GetVersionEx(osv) then begin - ver:= MAKELONG(osv.dwMinorVersion, osv.dwMajorVersion); - // positive overflow: if system is newer, always detect as newest we knew instead of failing - if ver >= $00060003 then - Result:= '8_1' - else - if ver >= $00060002 then - Result:= '8' - else - if ver >= $00060001 then - Result:= '7' - else - if ver >= $00060000 then - Result:= 'Vista' - else - if ver >= $00050002 then - Result:= '2003' - else - if ver >= $00050001 then - Result:= 'XP' - else - if ver >= $00050000 then - Result:= '2000' - else - if ver >= $00040000 then - Result:= 'NT4'; - // ignore NT3, hmkay?; - end; -end; -{$endif} - -function GetPlatformIdentitfier: string; -var - os,ver,arch: string; -begin - Result:= ''; - os:= ''; - ver:= 'generic'; - arch:= ''; - {$if defined(WINDOWS)} - os:= 'mswin'; - ver:= GetWindowsVersionStr(ver); - {$elseif defined(LINUX)} - os:= 'linux'; - {$Warning System Version String missing!} - {$endif} - - {$if defined(CPUX86)} - arch:= 'x86'; - {$elseif defined(cpux86_64)} - arch:= 'x64'; - {$else} - {$Error Unknown Architecture!} - {$endif} - Result:= format('%s-%s-%s', [os, ver, arch]); -end; - - -end. - diff --git a/uutlSerialization.pas b/uutlSerialization.pas deleted file mode 100644 index 81a740f..0000000 --- a/uutlSerialization.pas +++ /dev/null @@ -1,126 +0,0 @@ -unit uutlSerialization; - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils; - -type -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - IutlFileReader = interface - ['{3A9C3AE3-CAEE-44C9-85BE-0BCAA5C1BE7A}'] - function LoadStream(const aFilename: String; const aStream: TStream): Boolean; - end; - - IutlFileWriter = interface - ['{3DF84644-9FC4-4A8A-88C2-73F13E72B1ED}'] - procedure SaveStream(const aFilename: String; const aStream: TStream); - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlSimpleFileReader = class(TInterfacedObject, IutlFileReader) - public - function LoadStream(const aFilename: String; const aStream: TStream): Boolean; - end; - - TutlSimpleFileWriter = class(TInterfacedObject, IutlFileWriter) - public - procedure SaveStream(const aFilename: String; const aStream: TStream); - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlFileReaderProxy = class(TInterfacedObject, IutlFileReader) - private - fReader: IutlFileReader; - fPrefix: String; - public - function LoadStream(const aFilename: String; const aStream: TStream): Boolean; - constructor Create(const aReader: IutlFileReader; const aPrefix: String); - end; - - TutlFileWriterProxy = class(TInterfacedObject, IutlFileWriter) - private - fWriter: IutlFileWriter; - fPrefix: String; - public - procedure SaveStream(const aFilename: String; const aStream: TStream); - constructor Create(const aWriter: IutlFileWriter; const aPrefix: String); - end; - -implementation - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlSimpleFileReader////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlSimpleFileReader.LoadStream(const aFilename: String; const aStream: TStream): Boolean; -var - fs: TFileStream; -begin - result := FileExists(aFilename); - if result then begin - fs := TFileStream.Create(aFilename, fmOpenRead); - try - aStream.CopyFrom(fs, fs.Size - fs.Position); - finally - FreeAndNil(fs); - end; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlSimpleFileWriter////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlSimpleFileWriter.SaveStream(const aFilename: String; const aStream: TStream); -var - fs: TFileStream; -begin - fs := TFileStream.Create(aFilename, fmCreate); - try - fs.CopyFrom(aStream, aStream.Size - aStream.Position); - finally - FreeAndNil(fs); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlFileReaderProxy/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlFileReaderProxy.LoadStream(const aFilename: String; const aStream: TStream): Boolean; -var - dir, name: String; -begin - dir := ExtractFilePath(aFilename); - name := ExtractFileName(aFilename); - result := fReader.LoadStream(dir + fPrefix + name, aStream); -end; - -constructor TutlFileReaderProxy.Create(const aReader: IutlFileReader; const aPrefix: String); -begin - inherited Create; - fReader := aReader; - fPrefix := aPrefix; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlFileWriterProxy/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlFileWriterProxy.SaveStream(const aFilename: String; const aStream: TStream); -var - dir, name: String; -begin - dir := ExtractFilePath(aFilename); - name := ExtractFileName(aFilename); - fWriter.SaveStream(dir + fPrefix + name, aStream); -end; - -constructor TutlFileWriterProxy.Create(const aWriter: IutlFileWriter; const aPrefix: String); -begin - inherited Create; - fWriter := aWriter; - fPrefix := aPrefix; -end; - -end. - diff --git a/uutlSetHelper.inc b/uutlSetHelper.inc deleted file mode 100644 index 136b0f5..0000000 --- a/uutlSetHelper.inc +++ /dev/null @@ -1,88 +0,0 @@ -{$IF defined(__SET_INTERFACE)} - -type __SET_HELPER = class -public - class function {%H}ToString(const Value: __SET_TYPE): String; reintroduce; - class function TryToSet(const Str: String; out Value: __SET_TYPE): boolean; overload; - class function ToSet(const Str: String; const aDefault: __SET_TYPE): __SET_TYPE; overload; - class function ToSet(const Str: String): __SET_TYPE; overload; - class function Compare(const aSet1, aSet2: __SET_TYPE): Integer; -end; -{$ELSEIF defined (__SET_IMPLEMENTATION)} - -class function __SET_HELPER.ToString(const Value: __SET_TYPE): String; -var - m: __ENUM_TYPE; -begin - Result:= ''; - for m in __ENUM_HELPER.Values do - if m in Value then begin - if Result > '' then - Result:= Result + ', '; - Result:= Result + __ENUM_HELPER.ToString(m); - end; -end; - -class function __SET_HELPER.ToSet(const Str: String): __SET_TYPE; -begin - if not TryToSet(Str, Result) then - raise SysUtils.EConvertError.CreateFmt('"%s" is an invalid value',[Str]); -end; - -class function __SET_HELPER.ToSet(const Str: String; const aDefault: __SET_TYPE): __SET_TYPE; -begin - if not TryToSet(Str, Result) then - Result:= aDefault; -end; - -class function __SET_HELPER.TryToSet(const Str: String; out Value: __SET_TYPE): boolean; -var - i, j: Integer; - s: String; - m: __ENUM_TYPE; -begin - Result:= true; - Value := []; - i := 1; - j := 1; - while (i <= Length(Str)) do begin - if (Str[i] = ',') then begin - s := Trim(copy(Str, j, i-j)); - Result:= Result and __ENUM_HELPER.TryToEnum(s, m); - if not Result then - Exit; - Include(Value, m); - j := i+1; - end; - inc(i); - end; - s := Trim(copy(Str, j, i-j)); - if (s <> '') then begin - Result:= Result and __ENUM_HELPER.TryToEnum(s, m); - if not Result then - Exit; - Include(Value, m); - end; -end; - -class function __SET_HELPER.Compare(const aSet1, aSet2: __SET_TYPE): Integer; -var - i: __ENUM_TYPE; -begin - result := 0; - for i := High(i) downto Low(i) do begin - if (i in aSet1) and not (i in aSet2) then begin - result := 1; - break; - end else if not (i in aSet1) and (i in aSet2) then begin - result := -1; - break; - end; - end; -end; - -{$ENDIF} -{$undef __SET_HELPER} -{$undef __SET_TYPE} -{$undef __ENUM_TYPE} -{$undef __ENUM_HELPER} diff --git a/uutlSettings.pas b/uutlSettings.pas deleted file mode 100644 index dd8bfa7..0000000 --- a/uutlSettings.pas +++ /dev/null @@ -1,406 +0,0 @@ -unit uutlSettings; - -{ Package: Utils - Prefix: utl - UTiLs - Beschreibung: diese Unit stellt ein Framework zur Verfügung mit dessen Hilfe Einstellungs-Blöcke - in ein MCF File geladen und geschreieben werden können } - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils, - uutlMCF, uutlGenerics, uutlMessageThread; - -type - TutlSettingsBlock = class - constructor Create; virtual; - - procedure LoadDefaults; virtual; abstract; - procedure LoadFromConfig(const aMcf: TutlMCFSection); virtual; abstract; - procedure SaveToConfig(const aMcf: TutlMCFSection); virtual; abstract; - end; - TutlSettingsBlockClass = class of TutlSettingsBlock; - - TutlSettingsUpdateOp = (opInstanceChanged, opDataChanged); - TutlSettingsUpdateEvent = procedure (const aUpdateOp: TutlSettingsUpdateOp; const aOld, aNew: TutlSettingsBlock) of object; - TutlSettingsUpdateEventCntr = packed record - Callback: TutlSettingsUpdateEvent; - ThreadID: TThreadID; - end; - TutlSettingsUpdateEventCntrEqComp = class(TInterfacedObject, specialize IutlEqualityComparer<TutlSettingsUpdateEventCntr>) - public - function EqualityCompare(const i1, i2: TutlSettingsUpdateEventCntr): Boolean; - end; - - TutlSettings = class - private type - TutlSettingsUpdateEventList = specialize TutlCustomList<TutlSettingsUpdateEventCntr>; - TBlockData = class - Instance, OldInstance: TutlSettingsBlock; - Events: TutlSettingsUpdateEventList; - procedure CallEvents(const aOp: TutlSettingsUpdateOp; const aOld, aNew: TutlSettingsBlock); - constructor Create; - destructor Destroy; override; - end; - - TBlockList = specialize TutlMap<String, TBlockData>; - private - fBlocks: TBlockList; - fRaiseChangedEventOnLoad: Boolean; - procedure CopyInstance(O, N: TutlSettingsBlock); - public - property RaiseChangedEventOnLoad: Boolean read fRaiseChangedEventOnLoad write fRaiseChangedEventOnLoad; - - function RegisterBlock(const aName: String; const aClass: TutlSettingsBlockClass; const aOnUpdateEvent: TutlSettingsUpdateEvent): TutlSettingsBlock; - procedure UnregisterBlockCallback(const aName: String; const aOnUpdateEvent: TutlSettingsUpdateEvent); - procedure UnregisterBlockCallbacks(const aObj: TObject); - - function Block(const aName: String; out aBlock): boolean; - procedure Changed(const aBlock: TutlSettingsBlock); - - procedure LoadFromConfig(const aMcf: TutlMCFSection); - procedure SaveToConfig(const aMcf: TutlMCFSection); - - procedure LoadFromFile(const aFile: string); - procedure SaveToFile(const aFile: string); - - constructor Create; - destructor Destroy; override; - end; - -operator = (const i1, i2: TutlSettingsUpdateEventCntr): Boolean; inline; - -var - utlSettings: TutlSettings; - -implementation - -uses - uutlExceptions, Forms{$IFDEF USE_VFS}, uvfsManager{$ENDIF}, uutlMessages, syncobjs; - -const - SETTINGS_MSG_WAIT_TIME = 1000; //ms - -type - TSettingsBlockChangedMsg = class(TutlSyncCallbackMsg) - private - fCallback: TutlSettingsUpdateEvent; - fOperation: TutlSettingsUpdateOp; - fOld: TutlSettingsBlock; - fNew: TutlSettingsBlock; - public - procedure ExecuteCallback; override; - constructor Create(const aCallback: TutlSettingsUpdateEvent; const aOp: TutlSettingsUpdateOp; - const aOld, aNew: TutlSettingsBlock); - end; - -operator = (const i1, i2: TutlSettingsUpdateEventCntr): Boolean; -begin - result := - (i1.Callback = i2.Callback) and - (i2.ThreadID = i2.ThreadID); -end; - -function TutlSettingsUpdateEventCntrEqComp.EqualityCompare(const i1, i2: TutlSettingsUpdateEventCntr): Boolean; -begin - result := (i1 = i2); -end; - -{ TSettingsBlockChangedMsg } - -procedure TSettingsBlockChangedMsg.ExecuteCallback; -begin - fCallback(fOperation, fOld, fNew); -end; - -constructor TSettingsBlockChangedMsg.Create(const aCallback: TutlSettingsUpdateEvent; - const aOp: TutlSettingsUpdateOp; const aOld, aNew: TutlSettingsBlock); -begin - inherited Create; - fCallback := aCallback; - fOperation := aOp; - fOld := aOld; - fNew := aNew; -end; - -{ TutlSettings.TBlockData } - -procedure TutlSettings.TBlockData.CallEvents(const aOp: TutlSettingsUpdateOp; - const aOld, aNew: TutlSettingsBlock); -var - current: TThreadID; - cntr: TutlSettingsUpdateEventCntr; - msg: TSettingsBlockChangedMsg; -begin - current := GetCurrentThreadId; - for cntr in Events do begin - if (cntr.ThreadID <> current) then begin - msg := TSettingsBlockChangedMsg.Create(cntr.Callback, aOp, aOld, aNew); - if utlSendMessage(cntr.ThreadID, msg, SETTINGS_MSG_WAIT_TIME) = wrSignaled then - msg.Free; - end else - cntr.Callback(aOp, aOld, aNew); - end; -end; - -constructor TutlSettings.TBlockData.Create; -begin - inherited; - Events:= TutlSettingsUpdateEventList.Create(TutlSettingsUpdateEventCntrEqComp.Create); -end; - -destructor TutlSettings.TBlockData.Destroy; -begin - FreeAndNil(Events); - FreeAndNil(Instance); - FreeAndNil(OldInstance); - inherited Destroy; -end; - -{ TutlSettingsBlock } - -constructor TutlSettingsBlock.Create; -begin - inherited; - LoadDefaults; -end; - -{ TutlSettings } - -function TutlSettings.RegisterBlock(const aName: String; const aClass: TutlSettingsBlockClass; - const aOnUpdateEvent: TutlSettingsUpdateEvent): TutlSettingsBlock; -var - i: integer; - bd: TBlockData; - cntr: TutlSettingsUpdateEventCntr; -begin - Result:= nil; - - if aName = '' then - raise EInvalidOperation.Create('Empty Settings section name.'); - - i:= fBlocks.IndexOf(aName); - if i>=0 then begin - bd:= fBlocks.ValueAt[i]; - // gleicher name, instance ist gleiche oder spezifischere klasse - if bd.Instance is aClass then begin - if Assigned(aOnUpdateEvent) then begin - cntr.Callback := aOnUpdateEvent; - cntr.ThreadID := GetCurrentThreadId; - bd.Events.Add(cntr); - end; - Exit(bd.Instance) - end else - // gleicher name, neue klasse ist spezifischer - if aClass.InheritsFrom(bd.Instance.ClassType) then begin - Result:= aClass.Create; - CopyInstance(bd.Instance, Result); - bd.CallEvents(opInstanceChanged, bd.Instance, Result); - bd.Instance.Free; - bd.OldInstance.Free; - bd.Instance:= aClass.Create; - bd.OldInstance:= aClass.Create; - if Assigned(aOnUpdateEvent) then begin - cntr.Callback := aOnUpdateEvent; - cntr.ThreadID := GetCurrentThreadId; - bd.Events.Add(cntr); - end; - Exit; - end - // gleicher name, aber komplett andere klasse - else - raise EInvalidOperation.CreateFmt('Duplicate Settings entry: %s', [aName]); - end; - - for bd in fBlocks do - // verwandte klasse aber anderer name (wäre es der gleiche wäre das schon oben abgefangen) - if (bd.Instance is aClass) or (aClass.InheritsFrom(bd.Instance.ClassType)) then - raise EInvalidOperation.CreateFmt('Reused Settings class: %s', [aClass.ClassName]); - - // neuer name, neue klasse - bd:= TBlockData.Create; - bd.Instance:= aClass.Create; - bd.OldInstance:= aClass.Create; - if Assigned(aOnUpdateEvent) then begin - cntr.Callback := aOnUpdateEvent; - cntr.ThreadID := GetCurrentThreadId; - bd.Events.Add(cntr); - end; - - fBlocks.Add(aName, bd); - Result:= bd.Instance; -end; - -procedure TutlSettings.UnregisterBlockCallback(const aName: String; const aOnUpdateEvent: TutlSettingsUpdateEvent); -var - i: integer; - bd: TBlockData; -begin - i:= fBlocks.IndexOf(aName); - if i >= 0 then begin - bd := fBlocks.ValueAt[i]; - for i := bd.Events.Count-1 downto 0 do - if (bd.Events.Items[i].Callback = aOnUpdateEvent) then - bd.Events.Delete(i); - end; -end; - -procedure TutlSettings.UnregisterBlockCallbacks(const aObj: TObject); -var - bd: TBlockData; - i: integer; -begin - for bd in fBlocks do - for i:= bd.Events.Count-1 downto 0 do - if TMethod(bd.Events[i].Callback).Data = Pointer(aObj) then - bd.Events.Delete(i); -end; - -procedure TutlSettings.CopyInstance(O, N: TutlSettingsBlock); -var - tmp: TutlMCFSection; -begin - tmp:= TutlMCFSection.Create; - try - O.SaveToConfig(tmp); - N.LoadFromConfig(tmp); - finally - FreeAndNil(tmp); - end; -end; - -function TutlSettings.Block(const aName: String; out aBlock): boolean; -var - i: integer; - bd: TBlockData; -begin - i := fBlocks.IndexOf(aName); - Result := (i >= 0); - if Result then begin - bd:= fBlocks.ValueAt[i]; - CopyInstance(bd.Instance, bd.OldInstance); - TutlSettingsBlock(aBlock):= bd.Instance; - end; -end; - -procedure TutlSettings.Changed(const aBlock: TutlSettingsBlock); -var - bd: TBlockData; -begin - for bd in fBlocks do - if bd.Instance = aBlock then begin - bd.CallEvents(opDataChanged, bd.OldInstance, bd.Instance); - exit; - end; -end; - -procedure TutlSettings.LoadFromConfig(const aMcf: TutlMCFSection); -var - i: Integer; - b: TBlockData; -begin - for i := 0 to fBlocks.Count-1 do begin - b := fBlocks.ValueAt[i]; - b.Instance.LoadFromConfig(aMcf.Section(fBlocks.Keys[i])); - if fRaiseChangedEventOnLoad then - Changed(b.Instance); - end; -end; - -procedure TutlSettings.SaveToConfig(const aMcf: TutlMCFSection); -var - i: integer; -begin - for i:= 0 to fBlocks.Count-1 do - fBlocks.ValueAt[i].Instance.SaveToConfig(aMcf.Section(fBlocks.Keys[i])); -end; - -{$IFDEF USE_VFS} -procedure TutlSettings.LoadFromFile(const aFile: string); -var - sh: IStreamHandle; - mcf: TutlMCFFile; -begin - if vfsManager.ReadFile(aFile, sh) then begin - mcf:= TutlMCFFile.Create(sh); - try - LoadFromConfig(mcf); - finally - FreeAndNil(mcf); - end; - end; -end; -{$ELSE} -procedure TutlSettings.LoadFromFile(const aFile: string); -var - fs: TFileStream; - mcf: TutlMCFFile; -begin - fs := TFileStream.Create(aFile, fmOpenRead); - mcf := TutlMCFFile.Create(nil); - try - mcf.LoadFromStream(fs); - LoadFromConfig(mcf); - finally - FreeAndNil(fs); - end; -end; -{$ENDIF} - -{$IFDEF USE_VFS} -procedure TutlSettings.SaveToFile(const aFile: string); -var - sh: IStreamHandle; - mcf: TutlMCFFile; -begin - if vfsManager.CreateFile(aFile, sh) then begin - mcf:= TutlMCFFile.Create(nil); - try - SaveToConfig(mcf); - mcf.SaveToStream(sh); - finally - FreeAndNil(mcf); - end; - end; -end; -{$ELSE} -procedure TutlSettings.SaveToFile(const aFile: string); -var - fs: TFileStream; - mcf: TutlMCFFile; -begin - fs := TFileStream.Create(aFile, fmCreate); - mcf := TutlMCFFile.Create(nil); - try - SaveToConfig(mcf); - mcf.SaveToStream(fs); - finally - FreeAndNil(mcf); - FreeAndNil(fs); - end; -end; -{$ENDIF} - -constructor TutlSettings.Create; -begin - inherited Create; - fBlocks:= TBlockList.Create(true); - fRaiseChangedEventOnLoad := true; -end; - -destructor TutlSettings.Destroy; -begin - FreeAndNil(fBlocks); - inherited Destroy; -end; - -initialization - utlSettings := TutlSettings.Create; - -finalization - FreeAndNil(utlSettings); - -end. - diff --git a/uutlSystemInfo.pas b/uutlSystemInfo.pas deleted file mode 100644 index 9a0ae0a..0000000 --- a/uutlSystemInfo.pas +++ /dev/null @@ -1,523 +0,0 @@ -unit uutlSystemInfo; - -{ Package: Utils - Prefix: utl - UTiLs - Beschreibung: diese Unit enthält Klassen zum Auslesen von System Informationen (CPU, Grafikkarte, OpenGL) } - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils, uutlGenerics - {$IFDEF WINDOWS}, ActiveX, ComObj, variants {$ENDIF}; - -type -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlSystemInfo = class; - TutlSystemInfoList = specialize TutlList<TutlSystemInfo>; - TutlSystemInfo = class(TObject) - private - fName: String; - fValue: String; - fItems: TutlSystemInfoList; - function GetCount: Integer; - function GetItems(const aIndex: Integer): TutlSystemInfo; - public - property Name: String read fName; - property Value: String read fValue; - property Count: Integer read GetCount; - property Items[const aIndex: Integer]: TutlSystemInfo read GetItems; default; - - procedure Update; virtual; - function ToString: String; override; - - constructor Create; virtual; - destructor Destroy; override; - end; - TutlSystemInfoClass = class of TutlSystemInfo; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlOpenGLInfo = class(TutlSystemInfo) - public - procedure Update; override; - end; - - {$IFDEF WINDOWS} -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlWmiSystemInfo = class(TutlSystemInfo) - protected type - TStringArr = array of String; - protected - function GetComputer: String; virtual; - function GetNamespace: String; virtual; - function GetUsername: String; virtual; - function GetPassword: String; virtual; - function GetQuery: String; virtual; - function GetProperties: TStringArr; virtual; - function GetSubItemName(const aIndex: Integer): String; virtual; - public - procedure Update; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlProcessorInfo = class(TutlWmiSystemInfo) - private const - PROCESSOR_PROPERTIES: array[0..11] of String = ('AddressWidth', 'Caption', - 'CurrentClockSpeed', 'Description', 'ExtClock', 'Family', 'Manufacturer', - 'MaxClockSpeed', 'Name', 'NumberOfCores', 'NumberOfLogicalProcessors', - 'Version'); - protected - function GetQuery: String; override; - function GetProperties: TStringArr; override; - function GetSubItemName(const aIndex: Integer): String; override; - public - procedure Update; override; - end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TutlVideoControllerInfo = class(TutlWmiSystemInfo) - private const - VIDEO_CONTROLLER_PROPERTIES: array[0..11] of String = ('AdapterRAM', 'Caption', - 'CurrentBitsPerPixel', 'CurrentHorizontalResolution', 'CurrentRefreshRate', - 'CurrentScanMode', 'CurrentVerticalResolution', 'Description', 'DriverDate', - 'DriverVersion', 'Name', 'VideoProcessor'); - protected - function GetQuery: String; override; - function GetProperties: TStringArr; override; - function GetSubItemName(const aIndex: Integer): String; override; - public - procedure Update; override; - end; - {$ENDIF} - -procedure LogSystemInfo(const aClass: TutlSystemInfoClass); - -const - SYTEM_INFO_CLASSES_COUNT = {$IFDEF WINDOWS}2+{$ENDIF}1; - SYTEM_INFO_CLASSES: array[0..SYTEM_INFO_CLASSES_COUNT-1] of TutlSystemInfoClass = ( - {$IFDEF WINDOWS}TutlProcessorInfo, - TutlVideoControllerInfo,{$ENDIF} - TutlOpenGLInfo); - -implementation - -uses - uutlExceptions, math, dglOpenGL, uutlLogger; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function CreateItem(const aName, aValue: String): TutlSystemInfo; -begin - result := TutlSystemInfo.Create; - result.fName := aName; - result.fValue := aValue; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function VariantToStr(const aVariant: Variant): String; -begin - result := ''; - if (TVarData(aVariant).vtype <> varempty) and - (TVarData(aVariant).vtype <> varnull) and - (TVarData(aVariant).vtype <> varerror) then begin - result := aVariant; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure LogSystemInfo(const aClass: TutlSystemInfoClass); -var - info: TutlSystemInfo; - sList: TStringList; - i: Integer; -begin - info := aClass.Create; - sList := TStringList.Create; - try try - info.Update; - sList.Text := info.ToString; - for i := 0 to sList.Count-1 do - utlLogger.Log('SystemInfo', sList[i], []); - except on e: Exception do - utlLogger.Error('SystemInfo', 'Error while logging system info: %s', [e.Message]); - end; - finally - FreeAndNil(info); - FreeAndNil(sList); - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlSystemInfo//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlSystemInfo.GetCount: Integer; -begin - result := fItems.Count; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlSystemInfo.GetItems(const aIndex: Integer): TutlSystemInfo; -begin - if (aIndex >= 0) and (aIndex < fItems.Count) then - result := fItems[aIndex] - else - raise EOutOfRange.Create(aIndex, 0, fItems.Count-1); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlSystemInfo.Update; -begin -//DUMMY -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlSystemInfo.ToString: String; -var - str: String; - - procedure FillStr(var aStr: String; const aLen: Integer; const aChar: Char = ' '); - begin - while (Length(aStr) < aLen) do - aStr := aStr + aChar; - end; - - procedure WriteItem(const aPrefix: String; const aItem: TutlSystemInfo; const aItemLen: Integer); - var - len, i: Integer; - line: String; - begin - line := aItem.Name + ':'; - if (aItem.Count = 0) then begin - line := line; - FillStr(line, aItemLen + 2); - line := line + aItem.Value; - end; - str := str + aPrefix + line + sLineBreak; - if (aItem.Count > 0) then begin - len := 0; - for i := 0 to aItem.Count-1 do - len := max(len, Length(aItem[i].Name)); - for i := 0 to aItem.Count-1 do - WriteItem(aPrefix+' ', aItem[i], len); - end; - end; - -begin - str := ''; - WriteItem('', self, 0); - result := str; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -constructor TutlSystemInfo.Create; -begin - inherited Create; - fName := ''; - fValue := ''; - fItems := TutlSystemInfoList.Create(true); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -destructor TutlSystemInfo.Destroy; -begin - FreeAndNil(fItems); - inherited Destroy; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlOpenGLInfo//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlOpenGLInfo.Update; - - function AddItem(const aParent: TutlSystemInfo; const aName, aValue: String): TutlSystemInfo; - begin - result := CreateItem(aName, aValue); - aParent.fItems.Add(result); - end; - - function GetInteger(const aName: GLenum): String; - var - i: GLint; - begin - i := 0; - glGetIntegerv(aName, @i); - result := IntToStr(i); - end; - -var - item: TutlSystemInfo; -begin - inherited Update; - fName := 'OpenGL Information'; - fValue := ''; - item := AddItem(self, 'ImplementationBasics', ''); - AddItem(item, 'GL_VENDOR', glGetString(GL_VENDOR)); - AddItem(item, 'GL_RENDER', glGetString(GL_RENDER)); - AddItem(item, 'GL_VERSION', glGetString(GL_VERSION)); - AddItem(item, 'GL_SHADING_LANGUAGE_VERSION', glGetString(GL_SHADING_LANGUAGE_VERSION)); - - item := AddItem(self, 'Basics', ''); - AddItem(item, 'GL_MAX_VIEWPORT_DIMS', GetInteger(GL_MAX_VIEWPORT_DIMS)); - AddItem(item, 'GL_MAX_LIGHTS', GetInteger(GL_MAX_LIGHTS)); - AddItem(item, 'GL_MAX_CLIP_PLANES', GetInteger(GL_MAX_CLIP_PLANES)); - AddItem(item, 'GL_MAX_MODELVIEW_STACK_DEPTH', GetInteger(GL_MAX_MODELVIEW_STACK_DEPTH)); - AddItem(item, 'GL_MAX_PROJECTION_STACK_DEPTH', GetInteger(GL_MAX_PROJECTION_STACK_DEPTH)); - AddItem(item, 'GL_MAX_TEXTURE_STACK_DEPTH', GetInteger(GL_MAX_TEXTURE_STACK_DEPTH)); - AddItem(item, 'GL_MAX_ATTRIB_STACK_DEPTH', GetInteger(GL_MAX_ATTRIB_STACK_DEPTH)); - AddItem(item, 'GL_MAX_COLOR_MATRIX_STACK_DEPTH', GetInteger(GL_MAX_COLOR_MATRIX_STACK_DEPTH)); - AddItem(item, 'GL_MAX_LIST_NESTING', GetInteger(GL_MAX_LIST_NESTING)); - AddItem(item, 'GL_SUBPIXEL_BITS', GetInteger(GL_SUBPIXEL_BITS)); - AddItem(item, 'GL_MAX_ELEMENTS_INDICES', GetInteger(GL_MAX_ELEMENTS_INDICES)); - AddItem(item, 'GL_MAX_ELEMENTS_VERTICES', GetInteger(GL_MAX_ELEMENTS_VERTICES)); - AddItem(item, 'GL_MAX_TEXTURE_UNITS', GetInteger(GL_MAX_TEXTURE_UNITS)); - AddItem(item, 'GL_MAX_TEXTURE_COORDS', GetInteger(GL_MAX_TEXTURE_COORDS)); - AddItem(item, 'GL_MAX_SAMPLE_MASK_WORDS', GetInteger(GL_MAX_SAMPLE_MASK_WORDS)); - AddItem(item, 'GL_MAX_COLOR_TEXTURE_SAMPLES', GetInteger(GL_MAX_COLOR_TEXTURE_SAMPLES)); - AddItem(item, 'GL_MAX_DEPTH_TEXTURE_SAMPLES', GetInteger(GL_MAX_DEPTH_TEXTURE_SAMPLES)); - AddItem(item, 'GL_MAX_INTEGER_SAMPLES', GetInteger(GL_MAX_INTEGER_SAMPLES)); - - item := AddItem(self, 'Textures', ''); - AddItem(item, 'GL_MAX_TEXTURE_SIZE', GetInteger(GL_MAX_TEXTURE_SIZE)); - AddItem(item, 'GL_MAX_3D_TEXTURE_SIZE', GetInteger(GL_MAX_3D_TEXTURE_SIZE)); - AddItem(item, 'GL_MAX_CUBE_MAP_TEXTURE_SIZE', GetInteger(GL_MAX_CUBE_MAP_TEXTURE_SIZE)); - AddItem(item, 'GL_MAX_TEXTURE_LOD_BIAS', GetInteger(GL_MAX_TEXTURE_LOD_BIAS)); - AddItem(item, 'GL_MAX_ARRAY_TEXTURE_LAYERS', GetInteger(GL_MAX_ARRAY_TEXTURE_LAYERS)); - AddItem(item, 'GL_MAX_TEXTURE_BUFFER_SIZE', GetInteger(GL_MAX_TEXTURE_BUFFER_SIZE)); - AddItem(item, 'GL_MAX_RECTANGLE_TEXTURE_SIZE', GetInteger(GL_MAX_RECTANGLE_TEXTURE_SIZE)); - AddItem(item, 'GL_MAX_RENDERBUFFER_SIZE', GetInteger(GL_MAX_RENDERBUFFER_SIZE)); - - item := AddItem(self, 'FrameBuffers', ''); - AddItem(item, 'GL_MAX_DRAW_BUFFERS', GetInteger(GL_MAX_DRAW_BUFFERS)); - AddItem(item, 'GL_MAX_COLOR_ATTACHMENTS', GetInteger(GL_MAX_COLOR_ATTACHMENTS)); - AddItem(item, 'GL_MAX_SAMPLES', GetInteger(GL_MAX_SAMPLES)); - - item := AddItem(self, 'VertexShaderLimits', ''); - AddItem(item, 'GL_MAX_VERTEX_ATTRIBS', GetInteger(GL_MAX_VERTEX_ATTRIBS)); - AddItem(item, 'GL_MAX_VERTEX_UNIFORM_COMPONENTS', GetInteger(GL_MAX_VERTEX_UNIFORM_COMPONENTS)); - AddItem(item, 'GL_MAX_VERTEX_UNIFORM_VECTORS', GetInteger(GL_MAX_VERTEX_UNIFORM_VECTORS)); - AddItem(item, 'GL_MAX_VERTEX_UNIFORM_BLOCKS', GetInteger(GL_MAX_VERTEX_UNIFORM_BLOCKS)); - AddItem(item, 'GL_MAX_VERTEX_OUTPUT_COMPONENTS', GetInteger(GL_MAX_VERTEX_OUTPUT_COMPONENTS)); - AddItem(item, 'GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS', GetInteger(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS)); - - item := AddItem(self, 'FragmentShaderLimits', ''); - AddItem(item, 'GL_MAX_FRAGMENT_UNIFORM_COMPONENTS', GetInteger(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS)); - AddItem(item, 'GL_MAX_FRAGMENT_UNIFORM_VECTORS', GetInteger(GL_MAX_FRAGMENT_UNIFORM_VECTORS)); - AddItem(item, 'GL_MAX_FRAGMENT_UNIFORM_BLOCKS', GetInteger(GL_MAX_FRAGMENT_UNIFORM_BLOCKS)); - AddItem(item, 'GL_MAX_FRAGMENT_INPUT_COMPONENTS', GetInteger(GL_MAX_FRAGMENT_INPUT_COMPONENTS)); - AddItem(item, 'GL_MAX_IMAGE_UNITS', GetInteger(GL_MAX_IMAGE_UNITS)); - AddItem(item, 'GL_MAX_FRAGMENT_IMAGE_UNIFORMS', GetInteger(GL_MAX_FRAGMENT_IMAGE_UNIFORMS)); - AddItem(item, 'GL_MIN_PROGRAM_TEXEL_OFFSET', GetInteger(GL_MIN_PROGRAM_TEXEL_OFFSET)); - AddItem(item, 'GL_MAX_PROGRAM_TEXEL_OFFSET', GetInteger(GL_MAX_PROGRAM_TEXEL_OFFSET)); - AddItem(item, 'GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET', GetInteger(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET)); - AddItem(item, 'GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET', GetInteger(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET)); - - item := AddItem(self, 'CombinedFragmentAndVertexShaderLimits', ''); - AddItem(item, 'GL_MAX_UNIFORM_BUFFER_BINDINGS', GetInteger(GL_MAX_UNIFORM_BUFFER_BINDINGS)); - AddItem(item, 'GL_MAX_UNIFORM_BLOCK_SIZE', GetInteger(GL_MAX_UNIFORM_BLOCK_SIZE)); - AddItem(item, 'GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT', GetInteger(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT)); - AddItem(item, 'GL_MAX_COMBINED_UNIFORM_BLOCKS', GetInteger(GL_MAX_COMBINED_UNIFORM_BLOCKS)); - AddItem(item, 'GL_MAX_VARYING_FLOATS', GetInteger(GL_MAX_VARYING_FLOATS)); - AddItem(item, 'GL_MAX_VARYING_COMPONENTS', GetInteger(GL_MAX_VARYING_COMPONENTS)); - AddItem(item, 'GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS', GetInteger(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)); - AddItem(item, 'GL_MAX_SUBROUTINES', GetInteger(GL_MAX_SUBROUTINES)); - AddItem(item, 'GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS', GetInteger(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS)); - AddItem(item, 'GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS', GetInteger(GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS)); - AddItem(item, 'GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS', GetInteger(GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS)); - AddItem(item, 'GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS', GetInteger(GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS)); - AddItem(item, 'GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS', GetInteger(GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS)); - AddItem(item, 'GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS', GetInteger(GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS)); - - item := AddItem(self, 'GeometryShaderLimits', ''); - AddItem(item, 'GL_MAX_GEOMETRY_UNIFORM_BLOCKS', GetInteger(GL_MAX_GEOMETRY_UNIFORM_BLOCKS)); - AddItem(item, 'GL_MAX_GEOMETRY_INPUT_COMPONENTS', GetInteger(GL_MAX_GEOMETRY_INPUT_COMPONENTS)); - AddItem(item, 'GL_MAX_GEOMETRY_OUTPUT_COMPONENTS', GetInteger(GL_MAX_GEOMETRY_OUTPUT_COMPONENTS)); - AddItem(item, 'GL_MAX_GEOMETRY_OUTPUT_VERTICES', GetInteger(GL_MAX_GEOMETRY_OUTPUT_VERTICES)); - AddItem(item, 'GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS', GetInteger(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS)); - AddItem(item, 'GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS', GetInteger(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS)); - AddItem(item, 'GL_MAX_GEOMETRY_SHADER_INVOCATIONS', GetInteger(GL_MAX_GEOMETRY_SHADER_INVOCATIONS)); - - item := AddItem(self, 'TesselationShaderLimits', ''); - AddItem(item, 'GL_MAX_TESS_GEN_LEVEL', GetInteger(GL_MAX_TESS_GEN_LEVEL)); - AddItem(item, 'GL_MAX_PATCH_VERTICES', GetInteger(GL_MAX_PATCH_VERTICES)); - AddItem(item, 'GL_MAX_TESS_PATCH_COMPONENTS', GetInteger(GL_MAX_TESS_PATCH_COMPONENTS)); - AddItem(item, 'GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS', GetInteger(GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS)); - AddItem(item, 'GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS', GetInteger(GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS)); - AddItem(item, 'GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS', GetInteger(GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS)); - AddItem(item, 'GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS', GetInteger(GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS)); - AddItem(item, 'GL_MAX_TESS_CONTROL_INPUT_COMPONENTS', GetInteger(GL_MAX_TESS_CONTROL_INPUT_COMPONENTS)); - AddItem(item, 'GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS', GetInteger(GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS)); - AddItem(item, 'GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS', GetInteger(GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS)); - AddItem(item, 'GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS', GetInteger(GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS)); - AddItem(item, 'GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS', GetInteger(GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS)); - AddItem(item, 'GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS', GetInteger(GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS)); - AddItem(item, 'GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS', GetInteger(GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS)); - - item := AddItem(self, 'TransformFeedbackShaderLimits', ''); - AddItem(item, 'GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS', GetInteger(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS)); - AddItem(item, 'GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS', GetInteger(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS)); - AddItem(item, 'GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS', GetInteger(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS)); - AddItem(item, 'GL_MAX_TRANSFORM_FEEDBACK_BUFFERS', GetInteger(GL_MAX_TRANSFORM_FEEDBACK_BUFFERS)); -end; - -{$IFDEF WINDOWS} -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlWmiSystemInfo///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlWmiSystemInfo.GetComputer: String; -begin - result := 'localhost'#0; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlWmiSystemInfo.GetNamespace: String; -begin - result := 'root\CIMV2'#0; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlWmiSystemInfo.GetUsername: String; -begin - result := #0; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlWmiSystemInfo.GetPassword: String; -begin - result := #0; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlWmiSystemInfo.GetQuery: String; -begin - result := #0; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlWmiSystemInfo.GetProperties: TStringArr; -begin - SetLength(result, 0); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlWmiSystemInfo.GetSubItemName(const aIndex: Integer): String; -begin - result := IntToStr(aIndex); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlWmiSystemInfo.Update; -var - SWbemLocator: OLEVariant; - WMIService: OLEVariant; - WbemObjectSet, WbemObject: OLEVariant; - s: Variant; - pCeltFetched: LongWord; - oEnum: IEnumvariant; - i, j: Integer; - properties: TStringArr; - item: TutlSystemInfo; - - computer: Variant; - namespace: Variant; - username: Variant; - password: Variant; - query: Variant; -const - WBEM_FLAGFORWARDONLY = $00000020; -begin - inherited Update; - fItems.Clear; - CoInitialize(nil); - SWbemLocator := CreateOleObject('WbemScripting.SWbemLocator'); - //WMIService := SWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword); - computer := GetComputer; - namespace := GetNamespace; - username := GetUsername; - password := GetPassword; - query := GetQuery; - WMIService := SWbemLocator.ConnectServer(computer, namespace, username, password); - WbemObjectSet := WMIService.ExecQuery(query, 'WQL', WBEM_FLAGFORWARDONLY); - oEnum := IUnknown(WbemObjectSet._NewEnum) as IEnumVariant; - i := 0; - properties := GetProperties; - while oEnum.Next(1, WbemObject, pCeltFetched) = 0 do begin - inc(i); - item := TutlSystemInfo.Create; - item.fName := GetSubItemName(i); - fItems.Add(item); - for j := low(properties) to high(properties) do begin - s := properties[j]; - item.fItems.Add(CreateItem( - properties[j], - VariantToStr(WbemObject.Properties_.Item(s).Value))); - end; - end; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//TutlProcessorInfo///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlProcessorInfo.GetQuery: String; -begin - result := 'SELECT * FROM Win32_Processor'#0; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlProcessorInfo.GetProperties: TStringArr; -var - i: Integer; -begin - SetLength(result, Length(PROCESSOR_PROPERTIES)); - for i := low(result) to high(result) do - result[i] := PROCESSOR_PROPERTIES[i]; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlProcessorInfo.GetSubItemName(const aIndex: Integer): String; -begin - result := format('Processor %d', [aIndex]); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlProcessorInfo.Update; -begin - fName := 'Processor Information'; - fValue := ''; - inherited Update; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlVideoControllerInfo.GetQuery: String; -begin - Result := 'SELECT * FROM Win32_VideoController'; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlVideoControllerInfo.GetProperties: TStringArr; -var - i: Integer; -begin - SetLength(result, Length(VIDEO_CONTROLLER_PROPERTIES)); - for i := low(result) to high(result) do - result[i] := VIDEO_CONTROLLER_PROPERTIES[i]; -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function TutlVideoControllerInfo.GetSubItemName(const aIndex: Integer): String; -begin - Result := format('Video Controller %d', [aIndex]); -end; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -procedure TutlVideoControllerInfo.Update; -begin - fName := 'Video Controller Information'; - fValue := ''; - inherited Update; -end; -{$ENDIF} - -end. - diff --git a/uutlTiming.pas b/uutlTiming.pas deleted file mode 100644 index 4bb9794..0000000 --- a/uutlTiming.pas +++ /dev/null @@ -1,90 +0,0 @@ -unit uutlTiming; - -{ Package: Utils - Prefix: utl - UTiLs - Beschreibung: diese Unit enthält platformunabhängige Methoden für Zeitberechnungen und -messungen - Mehr oder weniger platformunabhängige Timestampgenerierung. - GetTickCount64 ms-Timer - GetMicroTime us-Timer analog php microtime() - Achtung: windows.pp deklariert auch eine GetTickCount64, wird gegen diese gelinkt - läuft die exe nicht mehr unter XP. Unbeding uutlTiming *nach* windows einbinden. } - - -{$mode objfpc}{$H+} - -interface - -uses - Classes - {$ifdef Windows} - , Windows - {$else} - , Unix, BaseUnix - {$endif}; - -function GetTickCount64: QWord; -function GetMicroTime: QWord; - -function utlRateLimited(const Reference: QWord; const Interval: QWord): boolean; - -implementation - -{$IF defined(WINDOWS)} -var - PERF_FREQ: Int64; - -function GetTickCount64: QWord; -begin - // GetTickCount64 is better, but we need to check the Windows version to use it - Result := Windows.GetTickCount(); -end; - -function GetMicroTime: QWord; -var - pc: Int64; -begin - pc := 0; - QueryPerformanceCounter(pc); - Result:= (pc * 1000*1000) div PERF_FREQ; -end; -{$ELSEIF defined(UNIX)} -function GetTickCount64: QWord; -var - tp: TTimeVal; -begin - fpgettimeofday(@tp, nil); - Result := (Int64(tp.tv_sec) * 1000) + (tp.tv_usec div 1000); -end; - -function GetMicroTime: QWord; -var - tp: TTimeVal; -begin - fpgettimeofday(@tp, nil); - Result := (Int64(tp.tv_sec) * 1000*1000) + tp.tv_usec; -end; -{$ELSE} -function GetTickCount64: QWord; -begin - Result := Trunc(Now * 24 * 60 * 60 * 1000); -end; - -function GetMicroTime: QWord; -begin - Result := Trunc(Now * 24 * 60 * 60 * 1000*1000); -end; - -{$ENDIF} - -function utlRateLimited(const Reference: QWord; const Interval: QWord): boolean; -begin - Result:= GetMicroTime - Reference > Interval; -end; - -initialization -{$IF defined(WINDOWS)} - PERF_FREQ := 0; - QueryPerformanceFrequency(PERF_FREQ); -{$ENDIF} -end. -