integrated TFuture

This commit is contained in:
Michael Schimmel
2025-06-04 01:55:24 +02:00
parent 06ec0db105
commit f571965762
8 changed files with 1223 additions and 227 deletions
+1 -3
View File
@@ -41,9 +41,7 @@ object ChartForm: TChartForm
Height = 23
Anchors = [akLeft, akTop, akRight]
TabOrder = 0
Text =
'C:\Users\Brummel\Documents\cAlgo\TickData\Pepperstone\EURUSD_201' +
'7.tab'
Text = '\\COFFEE\TickData\Pepperstone\EURUSD_2017_02.tab_zip'
end
object LoadButton: TButton
Left = 686
+4 -3
View File
@@ -543,12 +543,13 @@ var
InitialDirectory: string;
SelectedPath, SelectedSymbol: string;
SelectedYear: Integer;
SelectedMonth: Integer;
IsValidSyntax: Boolean;
begin
OpenDialog := TOpenDialog.Create( Self );
try
OpenDialog.Title := 'Open Tick Data File';
OpenDialog.Filter := 'Tick Data Files (*.tab)|*.TAB|All Files (*.*)|*.*';
OpenDialog.Filter := 'Tick Data Files (*.tab)|*.tab;*.tab_zip;*.tab_live|All Files (*.*)|*.*';
OpenDialog.DefaultExt := 'tab';
OpenDialog.Options := [ofFileMustExist, ofPathMustExist, ofHideReadOnly, ofEnableSizing];
@@ -574,11 +575,11 @@ begin
Memo.Lines.Add( FormatDateTime( 'yyyy-mm-dd hh:nn:ss', Now ) + ' - File selected via dialog: ' + OpenDialog.FileName );
IsValidSyntax := Myc.TickLoader.TryParseFileName( OpenDialog.FileName,
SelectedPath, SelectedSymbol, SelectedYear );
SelectedPath, SelectedSymbol, SelectedYear, SelectedMonth );
if not IsValidSyntax then
begin
MessageDlg( Format( 'Warning: The selected filename ''%s'' does not match the expected format (Prefix_Symbol_Year.tab).',
MessageDlg( Format( 'Warning: The selected filename ''%s'' does not match the expected format (Prefix_Symbol_Year_Month.tab).',
[System.IOUtils.TPath.GetFileName( OpenDialog.FileName )] ), mtWarning, [mbOK], 0 );
Memo.Lines.Add( 'Warning: Incorrect filename syntax chosen by user.' );
end;
+3 -1
View File
@@ -1,10 +1,12 @@
program ChartDisplayProject;
uses
FastMM5,
Vcl.Forms,
ChartDisplayMain in 'ChartDisplayMain.pas' {ChartForm},
Myc.OHLCCache in '..\common\Myc.OHLCCache.pas',
Myc.ChartDataController in '..\common\Myc.ChartDataController.pas';
Myc.ChartDataController in '..\common\Myc.ChartDataController.pas',
DataStreamer in 'DataStreamer.pas';
{$R *.res}
+8 -10
View File
@@ -71,7 +71,7 @@
<UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
<UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
<SanitizedProjectName>ChartDisplayProject</SanitizedProjectName>
<DCC_UnitSearchPath>..\common;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
<DCC_UnitSearchPath>..\common;T:\Myc\Src;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
<VerInfo_Locale>1031</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
</PropertyGroup>
@@ -110,6 +110,9 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win64)'!=''">
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
<DCC_RunTimeTypeInfo>true</DCC_RunTimeTypeInfo>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
@@ -129,10 +132,10 @@
</DelphiCompile>
<DCCReference Include="ChartDisplayMain.pas">
<Form>ChartForm</Form>
<FormType>dfm</FormType>
</DCCReference>
<DCCReference Include="..\common\Myc.OHLCCache.pas"/>
<DCCReference Include="..\common\Myc.ChartDataController.pas"/>
<DCCReference Include="DataStreamer.pas"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
@@ -153,12 +156,7 @@
<Source>
<Source Name="MainSource">ChartDisplayProject.dpr</Source>
</Source>
<Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcboffice2k290.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcbofficexp290.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k290.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp290.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
</Excluded_Packages>
<Excluded_Packages/>
</Delphi.Personality>
<Deployment Version="5">
<DeployFile LocalName="Win32\Debug\ChartDisplayProject.exe" Configuration="Debug" Class="ProjectOutput">
@@ -173,9 +171,9 @@
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="Win64\Debug\ChartDisplayProject.rsm" Configuration="Debug" Class="DebugSymbols">
<DeployFile LocalName="Win64\Release\ChartDisplayProject.exe" Configuration="Release" Class="ProjectOutput">
<Platform Name="Win64">
<RemoteName>ChartDisplayProject.rsm</RemoteName>
<RemoteName>ChartDisplayProject.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
+671
View File
@@ -0,0 +1,671 @@
unit DataStreamer;
interface
uses
System.SysUtils, System.Classes, System.Generics.Collections, System.IOUtils, System.Threading,
System.Zip;
type
IDataPage = interface
['{B3E3C5B7-7C1A-4A8B-8B7E-15C9D9E0F4A6}']
function GetStream: TStream;
function GetSourceIdentifier: string;
function GetLastModificationTime: TDateTime;
end;
IRecordStreamReader<T: record> = interface
['{C1B7E8F0-5A0A-4F3B-BD28-18D5E5A3F7A0}']
function GetCurrentRecord: T;
function MoveToNextRecord: Boolean;
procedure Reset;
function GetCurrentRecordIndex: Int64;
function Seek(RecordIndex: Int64): Int64;
property CurrentRecord: T read GetCurrentRecord;
property CurrentRecordIndex: Int64 read GetCurrentRecordIndex;
end;
TLocalFilePage = class(TInterfacedObject, IDataPage)
private
FActualFilePath: string;
FMemoryStream: TMemoryStream;
FLastModifiedOnDisk: TDateTime;
function GetStream: TStream;
function GetSourceIdentifier: string;
function GetLastModificationTime: TDateTime;
public
constructor Create(const AActualFilePath: string; ADataStream: TMemoryStream; ALastModifiedTimeOnDisk: TDateTime);
destructor Destroy; override;
class function IsFileModified(const AFileName: string; KnownLastModified: TDateTime): Boolean;
end;
TStreamingFileRecordReader<T: record> = class(TInterfacedObject, IRecordStreamReader<T>)
private
FDirectoryPath: string;
FDataSetName: string;
FExtension: string;
FFilePaths: TStringList;
FCachedPages: TObjectDictionary<string, IDataPage>;
FPageCacheQueue: TQueue<string>;
FMaxCacheSize: Integer;
FPreloadPageTask: ITask;
FLockObject: TObject;
FCurrentPage: IDataPage;
FCurrentPageStream: TStream;
FCurrentPageIndexInFileList: Integer;
FRecordSize: Integer;
FCurrentRecord: T;
FRecordsInCurrentPage: Int64;
FRecordsReadFromCurrentPage: Int64;
FGlobalRecordIndex: Int64;
procedure ScanDirectoryAndMatchFiles;
function LoadPage(ActualFilePath: string): IDataPage;
procedure EnsurePageInCache(ActualFilePath: string; IsPreload: Boolean = False);
procedure EvictOldestPage;
procedure DoPreloadWork(const APathToPreload: string);
procedure StartPreloadNextPage;
procedure InvalidatePageIfModified(PageToCheck: IDataPage; IsCurrentPage: Boolean);
function ActivatePage(PageIndexInFileList: Integer): Boolean;
function GetRecordCountForFile(ActualFilePath: string): Int64;
function GetCurrentRecord: T;
function MoveToNextRecord: Boolean;
procedure Reset;
function GetCurrentRecordIndex: Int64;
function Seek(RecordIndex: Int64): Int64;
public
constructor Create(const ADirectoryPath: string; const ADataSetName: string; const AExtension: string; AMaxCacheSize: Integer = 2);
destructor Destroy; override;
end;
implementation
uses
System.DateUtils, System.Types, System.StrUtils,
System.Math;
{ TLocalFilePage }
constructor TLocalFilePage.Create(const AActualFilePath: string; ADataStream: TMemoryStream; ALastModifiedTimeOnDisk: TDateTime);
begin
inherited Create;
FActualFilePath := AActualFilePath;
FMemoryStream := ADataStream;
FMemoryStream.Position := 0;
FLastModifiedOnDisk := ALastModifiedTimeOnDisk;
end;
destructor TLocalFilePage.Destroy;
begin
FMemoryStream.Free;
inherited Destroy;
end;
function TLocalFilePage.GetStream: TStream;
begin
FMemoryStream.Position := 0;
result := FMemoryStream;
end;
function TLocalFilePage.GetSourceIdentifier: string;
begin
result := FActualFilePath;
end;
function TLocalFilePage.GetLastModificationTime: TDateTime;
begin
result := FLastModifiedOnDisk;
end;
class function TLocalFilePage.IsFileModified(const AFileName: string; KnownLastModified: TDateTime): Boolean;
var
currentModTime: TDateTime;
begin
if TFile.Exists(AFileName) then
begin
currentModTime := TFile.GetLastWriteTime(AFileName);
result := Abs(MilliSecondsBetween(currentModTime, KnownLastModified)) > 1000;
end
else
begin
result := True;
end;
end;
{ TStreamingFileRecordReader<T> }
constructor TStreamingFileRecordReader<T>.Create(const ADirectoryPath: string; const ADataSetName: string; const AExtension: string; AMaxCacheSize: Integer);
begin
inherited Create;
FLockObject := TObject.Create;
if not TDirectory.Exists(ADirectoryPath) then
raise EDirectoryNotFoundException.Create('Directory not found: ' + ADirectoryPath);
if Trim(ADataSetName) = '' then
raise EArgumentException.Create('DataSetName cannot be empty.');
if Trim(AExtension) = '' then
raise EArgumentException.Create('Extension cannot be empty.');
FDirectoryPath := IncludeTrailingPathDelimiter(ADirectoryPath);
FDataSetName := ADataSetName;
FExtension := AExtension;
if StartsText('.', FExtension) then
FExtension := Copy(FExtension, 2, Length(FExtension) -1);
FFilePaths := TStringList.Create;
FFilePaths.Sorted := True;
FCachedPages := TObjectDictionary<string, IDataPage>.Create([doOwnsValues]);
FPageCacheQueue := TQueue<string>.Create;
FMaxCacheSize := System.Math.Max(2, AMaxCacheSize);
FRecordSize := SizeOf(T);
if FRecordSize = 0 then
raise EArgumentException.Create('Record size for type T cannot be zero.');
FCurrentPageIndexInFileList := -1;
FGlobalRecordIndex := -1;
FRecordsReadFromCurrentPage := 0;
FRecordsInCurrentPage := 0;
FCurrentPage := nil;
FCurrentPageStream := nil;
ScanDirectoryAndMatchFiles;
Reset;
end;
destructor TStreamingFileRecordReader<T>.Destroy;
var
taskToWaitFor: ITask;
begin
taskToWaitFor := nil;
TMonitor.Enter(FLockObject);
try
taskToWaitFor := FPreloadPageTask;
FPreloadPageTask := nil;
finally
TMonitor.Exit(FLockObject);
end;
if (taskToWaitFor <> nil) and (taskToWaitFor.Status in [TTaskStatus.Created, TTaskStatus.WaitingToRun, TTaskStatus.Running]) then
begin
// Consider taskToWaitFor.Cancel; taskToWaitFor.Wait;
end;
FFilePaths.Free;
FCachedPages.Free;
FPageCacheQueue.Free;
FLockObject.Free;
inherited Destroy;
end;
procedure TStreamingFileRecordReader<T>.ScanDirectoryAndMatchFiles;
var
allFiles: TStringDynArray;
fileNameOnly, filePath: string;
expectedPrefix, baseExtensionPattern, zipExtensionPattern: string;
begin
TMonitor.Enter(FLockObject);
try
FFilePaths.Clear;
allFiles := TDirectory.GetFiles(FDirectoryPath);
expectedPrefix := FDataSetName + '_';
baseExtensionPattern := '.' + FExtension;
zipExtensionPattern := baseExtensionPattern + '_zip';
for filePath in allFiles do
begin
fileNameOnly := ExtractFileName(filePath);
if StartsText(expectedPrefix, fileNameOnly) then
begin
if EndsText(zipExtensionPattern, fileNameOnly.ToLower) or EndsText(baseExtensionPattern, fileNameOnly.ToLower) then
begin
FFilePaths.Add(filePath);
end;
end;
end;
finally
TMonitor.Exit(FLockObject);
end;
end;
function TStreamingFileRecordReader<T>.LoadPage(ActualFilePath: string): IDataPage;
var
dataStream: TMemoryStream;
fileLastModified: TDateTime;
zipFile: TZipFile;
isCompressed: Boolean;
fileExtensionOnDisk: string;
decompressionStream: TStream;
localHeader: TZipHeader;
fileNameInZip: string;
begin
dataStream := TMemoryStream.Create;
try
fileLastModified := TFile.GetLastWriteTime(ActualFilePath);
fileExtensionOnDisk := ExtractFileExt(ActualFilePath);
isCompressed := EndsText('_zip', fileExtensionOnDisk.ToLower);
if isCompressed then
begin
zipFile := TZipFile.Create;
try
zipFile.Open(ActualFilePath, zmRead);
if zipFile.FileCount > 0 then
begin
fileNameInZip := zipFile.FileNames[0];
zipFile.Read(fileNameInZip, decompressionStream, localHeader);
try
dataStream.CopyFrom(decompressionStream, 0);
finally
decompressionStream.Free;
end;
end
else
raise Exception.Create('Compressed file is empty: ' + ActualFilePath);
finally
zipFile.Free;
end;
end
else
dataStream.LoadFromFile(ActualFilePath);
dataStream.Position := 0;
result := TLocalFilePage.Create(ActualFilePath, dataStream, fileLastModified);
except
dataStream.Free;
raise;
end;
end;
procedure TStreamingFileRecordReader<T>.EnsurePageInCache(ActualFilePath: string; IsPreload: Boolean);
var
page: IDataPage;
newlyLoadedPage: IDataPage;
begin
// Assumes FLockObject is held by the caller
if not FCachedPages.TryGetValue(ActualFilePath, page) then
begin
if FCachedPages.Count >= FMaxCacheSize then
EvictOldestPage;
newlyLoadedPage := LoadPage(ActualFilePath);
FCachedPages.Add(ActualFilePath, newlyLoadedPage);
FPageCacheQueue.Enqueue(ActualFilePath);
end
else
begin
if not IsPreload then
InvalidatePageIfModified(page, page = FCurrentPage);
end;
end;
procedure TStreamingFileRecordReader<T>.EvictOldestPage;
var
oldestFilePath: string;
begin
// Assumes FLockObject is held by the caller
if FPageCacheQueue.Count > 0 then
begin
oldestFilePath := FPageCacheQueue.Dequeue;
if FCachedPages.ContainsKey(oldestFilePath) then
FCachedPages.Remove(oldestFilePath);
end;
end;
procedure TStreamingFileRecordReader<T>.InvalidatePageIfModified(PageToCheck: IDataPage; IsCurrentPage: Boolean);
var
actualFilePath: string;
reloadedPage: IDataPage;
begin
// Assumes FLockObject is held by the caller
if PageToCheck = nil then exit;
actualFilePath := PageToCheck.GetSourceIdentifier;
if TLocalFilePage.IsFileModified(actualFilePath, PageToCheck.GetLastModificationTime) then
begin
FCachedPages.Remove(actualFilePath);
if IsCurrentPage then
begin
FCurrentPage := nil;
FCurrentPageStream := nil;
reloadedPage := LoadPage(actualFilePath);
FCachedPages.Add(actualFilePath, reloadedPage);
FCurrentPage := reloadedPage;
FCurrentPageStream := FCurrentPage.GetStream;
if (FCurrentPageStream <> nil) and (FCurrentPageStream.Size >= FRecordSize) then
FRecordsInCurrentPage := FCurrentPageStream.Size div FRecordSize
else
FRecordsInCurrentPage := 0;
if (FRecordsReadFromCurrentPage * FRecordSize < FCurrentPageStream.Size) and (FRecordsReadFromCurrentPage <= FRecordsInCurrentPage) then
FCurrentPageStream.Position := FRecordsReadFromCurrentPage * FRecordSize
else
begin
FCurrentPageStream.Position := 0;
FRecordsReadFromCurrentPage := 0;
end;
end;
end;
end;
procedure TStreamingFileRecordReader<T>.DoPreloadWork(const APathToPreload: string);
begin
try
TMonitor.Enter(FLockObject);
try
if not FCachedPages.ContainsKey(APathToPreload) then
EnsurePageInCache(APathToPreload, True);
finally
TMonitor.Exit(FLockObject);
end;
except
on E: Exception do
begin
// Optional: Log error E.Message
end;
end;
end;
procedure TStreamingFileRecordReader<T>.StartPreloadNextPage;
var
nextPageIndexToLoad: Integer;
nextFilePath: string;
currentTask: ITask;
begin
TMonitor.Enter(FLockObject);
try
currentTask := FPreloadPageTask;
if (currentTask <> nil) and (currentTask.Status in [TTaskStatus.Created, TTaskStatus.WaitingToRun, TTaskStatus.Running]) then
exit;
nextPageIndexToLoad := FCurrentPageIndexInFileList + 1;
if nextPageIndexToLoad < FFilePaths.Count then
begin
nextFilePath := FFilePaths[nextPageIndexToLoad];
if FCachedPages.ContainsKey(nextFilePath) then
exit;
FPreloadPageTask := TTask.Run(
procedure
begin
DoPreloadWork(nextFilePath);
end
);
end;
finally
TMonitor.Exit(FLockObject);
end;
end;
function TStreamingFileRecordReader<T>.ActivatePage(PageIndexInFileList: Integer): Boolean;
var
actualFilePath: string;
pageFromCache: IDataPage;
begin
result := False;
if (PageIndexInFileList < 0) or (PageIndexInFileList >= FFilePaths.Count) then
begin FCurrentPage := nil; FCurrentPageStream := nil; exit; end;
actualFilePath := FFilePaths[PageIndexInFileList];
TMonitor.Enter(FLockObject);
try
EnsurePageInCache(actualFilePath, False);
if not FCachedPages.TryGetValue(actualFilePath, pageFromCache) then
begin FCurrentPage := nil; FCurrentPageStream := nil; exit; end;
FCurrentPage := pageFromCache;
FCurrentPageStream := FCurrentPage.GetStream;
FCurrentPageIndexInFileList := PageIndexInFileList;
FRecordsReadFromCurrentPage := 0;
if (FCurrentPageStream <> nil) and (FCurrentPageStream.Size >= FRecordSize) then
FRecordsInCurrentPage := FCurrentPageStream.Size div FRecordSize
else
FRecordsInCurrentPage := 0;
if FRecordsInCurrentPage > 0 then
begin
result := True;
StartPreloadNextPage;
end;
finally
TMonitor.Exit(FLockObject);
end;
end;
function TStreamingFileRecordReader<T>.GetRecordCountForFile(ActualFilePath: string): Int64;
var
page: IDataPage;
stream: TStream;
begin
result := 0;
EnsurePageInCache(ActualFilePath, False); // Assumes FLockObject is held by caller
if FCachedPages.TryGetValue(ActualFilePath, page) then
begin
stream := page.GetStream;
if (stream <> nil) and (stream.Size >= FRecordSize) then
result := stream.Size div FRecordSize;
end;
end;
function TStreamingFileRecordReader<T>.GetCurrentRecord: T;
begin
if FGlobalRecordIndex < 0 then
Result := System.Default(T)
else
Result := FCurrentRecord;
end;
function TStreamingFileRecordReader<T>.GetCurrentRecordIndex: Int64;
begin
result := FGlobalRecordIndex;
end;
function TStreamingFileRecordReader<T>.MoveToNextRecord: Boolean;
var
bytesRead: NativeInt; // ReadData<T> returns NativeInt
attemptedNextPageActivation: Boolean;
begin
result := False;
attemptedNextPageActivation := False;
TMonitor.Enter(FLockObject);
try
while True do
begin
if FCurrentPageStream = nil then
begin
if attemptedNextPageActivation or (FCurrentPageIndexInFileList >= FFilePaths.Count - 1) then
begin FGlobalRecordIndex := -1; exit; end;
if not ActivatePage(FCurrentPageIndexInFileList + 1) then
begin FGlobalRecordIndex := -1; exit; end;
attemptedNextPageActivation := True;
end;
if FRecordsReadFromCurrentPage >= FRecordsInCurrentPage then
begin
if attemptedNextPageActivation or (FCurrentPageIndexInFileList >= FFilePaths.Count - 1) then
begin FGlobalRecordIndex := -1; exit; end;
FCurrentPageStream := nil;
if not ActivatePage(FCurrentPageIndexInFileList + 1) then
begin FGlobalRecordIndex := -1; exit; end;
attemptedNextPageActivation := True;
continue;
end;
FCurrentPageStream.Position := FRecordsReadFromCurrentPage * FRecordSize;
if (FCurrentPageStream.Size - FCurrentPageStream.Position) < FRecordSize then
begin
FRecordsReadFromCurrentPage := FRecordsInCurrentPage;
attemptedNextPageActivation := False;
FCurrentPageStream := nil;
continue;
end;
// Verwende die generische TStream.ReadData<T> Methode
bytesRead := FCurrentPageStream.ReadData<T>(FCurrentRecord); // User line 518 context
if bytesRead = FRecordSize then // FRecordSize is SizeOf(T)
begin
Inc(FRecordsReadFromCurrentPage);
if FGlobalRecordIndex < 0 then
FGlobalRecordIndex := 0
else
Inc(FGlobalRecordIndex); // << User line 518 (E2010), hopefully fixed by ReadData<T>
result := True; exit;
end
else
begin
FRecordsReadFromCurrentPage := FRecordsInCurrentPage;
attemptedNextPageActivation := False;
FCurrentPageStream := nil;
continue;
end;
end;
finally
TMonitor.Exit(FLockObject);
end;
end;
procedure TStreamingFileRecordReader<T>.Reset;
var
taskToCheck: ITask;
begin
TMonitor.Enter(FLockObject);
try
taskToCheck := FPreloadPageTask;
if (taskToCheck <> nil) and (taskToCheck.Status in [TTaskStatus.Created, TTaskStatus.WaitingToRun, TTaskStatus.Running]) then
begin
// Cancellation logic
end;
FPreloadPageTask := nil;
FCurrentPageIndexInFileList := -1;
FGlobalRecordIndex := -1;
FRecordsReadFromCurrentPage := 0;
FRecordsInCurrentPage := 0;
FCurrentPage := nil;
FCurrentPageStream := nil;
FCurrentRecord := System.Default(T);
if FFilePaths.Count > 0 then
if not ActivatePage(0) then;
finally
TMonitor.Exit(FLockObject);
end;
end;
function TStreamingFileRecordReader<T>.Seek(RecordIndex: Int64): Int64;
var
pageIdx: Integer;
cumulativeRecordOffset: Int64;
recordsInCurrentFile: Int64;
recordOffsetInPage: Int64;
effectiveTargetIdx: Int64;
i: Integer;
sumRecordsBeforeThisPage: Int64;
bytesRead: NativeInt; // For ReadData<T>
begin
TMonitor.Enter(FLockObject);
try
if FFilePaths.Count = 0 then
begin
FCurrentPageIndexInFileList := -1; FGlobalRecordIndex := -1; FCurrentPage := nil; FCurrentPageStream := nil;
FCurrentRecord := System.Default(T);
result := -1; exit;
end;
effectiveTargetIdx := RecordIndex;
if effectiveTargetIdx < 0 then effectiveTargetIdx := 0;
cumulativeRecordOffset := 0;
for pageIdx := 0 to FFilePaths.Count - 1 do
begin
recordsInCurrentFile := GetRecordCountForFile(FFilePaths[pageIdx]);
if effectiveTargetIdx < cumulativeRecordOffset + recordsInCurrentFile then
begin
if not ActivatePage(pageIdx) then
begin
FCurrentPageIndexInFileList := -1; FGlobalRecordIndex := -1; FCurrentPage := nil; FCurrentPageStream := nil;
FCurrentRecord := System.Default(T);
result := -1; exit;
end;
recordOffsetInPage := effectiveTargetIdx - cumulativeRecordOffset;
FCurrentPageStream.Position := recordOffsetInPage * FRecordSize;
// Verwende die generische TStream.ReadData<T> Methode
bytesRead := FCurrentPageStream.ReadData<T>(FCurrentRecord);
if bytesRead = FRecordSize then
begin
FGlobalRecordIndex := effectiveTargetIdx; FRecordsReadFromCurrentPage := recordOffsetInPage + 1;
result := FGlobalRecordIndex; exit;
end
else
begin // User line ~604 context (E2008)
FCurrentPageIndexInFileList := -1; FGlobalRecordIndex := -1; FCurrentPage := nil; FCurrentPageStream := nil;
FCurrentRecord := System.Default(T);
result := -1; exit;
end;
end;
inc(cumulativeRecordOffset, recordsInCurrentFile);
end;
if cumulativeRecordOffset = 0 then // User line ~616 context (E2029) for assignment after this block
begin
FCurrentPageIndexInFileList := -1; FGlobalRecordIndex := -1; FCurrentPage := nil; FCurrentPageStream := nil;
FCurrentRecord := System.Default(T);
result := -1; exit;
end;
for pageIdx := FFilePaths.Count - 1 downto 0 do
begin
recordsInCurrentFile := GetRecordCountForFile(FFilePaths[pageIdx]);
if recordsInCurrentFile > 0 then
begin
if not ActivatePage(pageIdx) then
begin
FCurrentPageIndexInFileList := -1; FGlobalRecordIndex := -1; FCurrentPage := nil; FCurrentPageStream := nil;
FCurrentRecord := System.Default(T);
result := -1; exit;
end;
recordOffsetInPage := recordsInCurrentFile - 1;
FCurrentPageStream.Position := recordOffsetInPage * FRecordSize;
// Verwende die generische TStream.ReadData<T> Methode
bytesRead := FCurrentPageStream.ReadData<T>(FCurrentRecord);
if bytesRead = FRecordSize then
begin
sumRecordsBeforeThisPage := 0;
for i := 0 to pageIdx - 1 do
sumRecordsBeforeThisPage := sumRecordsBeforeThisPage + GetRecordCountForFile(FFilePaths[i]);
FGlobalRecordIndex := sumRecordsBeforeThisPage + recordOffsetInPage; // User line 616 context (E2029/E2014)
FRecordsReadFromCurrentPage := recordOffsetInPage + 1;
result := FGlobalRecordIndex; exit;
end
else
begin // User line ~639 context (E2008)
FCurrentPageIndexInFileList := -1; FGlobalRecordIndex := -1; FCurrentPage := nil; FCurrentPageStream := nil;
FCurrentRecord := System.Default(T);
result := -1; exit;
end;
end;
end;
FCurrentPageIndexInFileList := -1; FGlobalRecordIndex := -1; FCurrentPage := nil; FCurrentPageStream := nil;
FCurrentRecord := System.Default(T);
result := -1; exit;
finally
TMonitor.Exit(FLockObject);
end;
end;
end.