📄 dbf_pgfile.pas
字号:
end else begin
// record outside buffer, need to synchronize first
SynchronizeBuffer(IntRecNum);
RecEnd := PagesPerRecord * PageSize;
end;
// we can write this record to buffer
Move(Buffer^, PChar(FBufferPtr)[RecEnd-RecordSize], RecordSize);
FBufferModified := true;
// update cached size
UpdateCachedSize(FBufferOffset+RecEnd);
end else begin
// no buffering
SingleWriteRecord(IntRecNum, Buffer);
// update cached size
UpdateCachedSize(FStream.Position);
end;
end;
procedure TPagedFile.SetBufferAhead(NewValue: Boolean);
begin
if FBufferAhead <> NewValue then
begin
FlushBuffer;
FBufferAhead := NewValue;
UpdateBufferSize;
end;
end;
procedure TPagedFile.SetStream(NewStream: TStream);
begin
if not FActive then
FStream := NewStream;
end;
procedure TPagedFile.SetFileName(NewName: string);
begin
if not FActive then
FFileName := NewName;
end;
procedure TPagedFile.UpdateBufferSize;
begin
if FBufferAhead then
begin
FBufferMaxSize := 65536;
if RecordSize <> 0 then
Dec(FBufferMaxSize, FBufferMaxSize mod PageSize);
end else begin
FBufferMaxSize := 0;
end;
if FBufferPtr <> nil then
FreeMem(FBufferPtr);
if FBufferAhead and (FBufferMaxSize <> 0) then
GetMem(FBufferPtr, FBufferMaxSize)
else
FBufferPtr := nil;
FBufferPage := -1;
FBufferOffset := -1;
FBufferModified := false;
end;
procedure TPagedFile.WriteHeader;
begin
FHeaderModified := true;
if FNeedLocks then
FlushHeader;
end;
procedure TPagedFile.FlushHeader;
begin
if FHeaderModified then
begin
FStream.Position := FHeaderOffset;
FWriteError := (FStream.Write(FHeader^, FHeaderSize) = 0) or FWriteError;
// test if written new header
if FStream.Position > FCachedSize then
begin
// new header -> record count unknown
FCachedSize := FStream.Position;
FNeedRecalc := true;
end;
FHeaderModified := false;
end;
end;
procedure TPagedFile.ReadHeader;
{ assumes header is large enough }
var
size: Integer;
begin
// save changes before reading new header
FlushHeader;
// check if header length zero
if FHeaderSize <> 0 then
begin
// get size left in file for header
size := FStream.Size - FHeaderOffset;
// header start before EOF?
if size >= 0 then
begin
// go to header start
FStream.Position := FHeaderOffset;
// whole header in file?
if size >= FHeaderSize then
begin
// read header, nothing to be cleared
Read(FHeader, FHeaderSize);
size := FHeaderSize;
end else begin
// read what we can, clear rest
Read(FHeader, size);
end;
end else begin
// header start before EOF, clear header
size := 0;
end;
FillChar(FHeader[size], FHeaderSize-size, 0);
end;
end;
procedure TPagedFile.TryExclusive;
const NewTempMode: array[pfReadWriteCreate..pfReadOnly] of TPagedFileMode =
(pfReadWriteOpen, pfReadWriteOpen, pfReadOnly);
begin
// already in temporary exclusive mode?
if (FTempMode = pfNone) and IsSharedAccess then
begin
// save temporary mode, if now creating, then reopen non-create
FTempMode := NewTempMode[FMode];
// try exclusive mode
CloseFile;
FMode := pfExclusiveOpen;
try
OpenFile;
except
on EFOpenError do
begin
// we failed, reopen normally
EndExclusive;
end;
end;
end;
end;
procedure TPagedFile.EndExclusive;
begin
// are we in temporary file mode?
if FTempMode <> pfNone then
begin
CloseFile;
FMode := FTempMode;
FTempMode := pfNone;
OpenFile;
end;
end;
procedure TPagedFile.DisableForceCreate;
begin
case FMode of
pfExclusiveCreate: FMode := pfExclusiveOpen;
pfReadWriteCreate: FMode := pfReadWriteOpen;
end;
end;
procedure TPagedFile.SetHeaderOffset(NewValue: Integer);
//
// *) assumes is called right before SetHeaderSize
//
begin
if FHeaderOffset <> NewValue then
begin
FlushHeader;
FHeaderOffset := NewValue;
end;
end;
procedure TPagedFile.SetHeaderSize(NewValue: Integer);
begin
if FHeaderSize <> NewValue then
begin
FlushHeader;
if (FHeader <> nil) and (NewValue <> 0) then
FreeMem(FHeader);
FHeaderSize := NewValue;
if FHeaderSize <> 0 then
GetMem(FHeader, FHeaderSize);
FNeedRecalc := true;
ReadHeader;
end;
end;
procedure TPagedFile.SetRecordSize(NewValue: Integer);
begin
if FRecordSize <> NewValue then
begin
FRecordSize := NewValue;
FPageSize := NewValue;
FNeedRecalc := true;
RecalcPagesPerRecord;
end;
end;
procedure TPagedFile.SetPageSize(NewValue: Integer);
begin
if FPageSize <> NewValue then
begin
FPageSize := NewValue;
FNeedRecalc := true;
RecalcPagesPerRecord;
UpdateBufferSize;
end;
end;
procedure TPagedFile.RecalcPagesPerRecord;
begin
if FPageSize = 0 then
FPagesPerRecord := 0
else
FPagesPerRecord := FRecordSize div FPageSize;
end;
function TPagedFile.GetRecordCount: Integer;
var
currSize: Integer;
begin
// file size changed?
if FNeedLocks then
begin
currSize := FStream.Size;
if currSize <> FCachedSize then
begin
FCachedSize := currSize;
FNeedRecalc := true;
end;
end;
// try to optimize speed
if FNeedRecalc then
begin
// no file? test flags
if (FPageSize = 0) or not FActive then
FRecordCount := 0
else
if FPageOffsetByHeader then
FRecordCount := (FCachedSize - FHeaderSize - FHeaderOffset) div FPageSize
else
FRecordCount := FCachedSize div FPageSize;
if FRecordCount < 0 then
FRecordCount := 0;
// count updated
FNeedRecalc := false;
end;
Result := FRecordCount;
end;
procedure TPagedFile.SetRecordCount(NewValue: Integer);
begin
if RecordCount <> NewValue then
begin
if FPageOffsetByHeader then
FCachedSize := FHeaderSize + FHeaderOffset + FPageSize * NewValue
else
FCachedSize := FPageSize * NewValue;
// FCachedSize := CalcPageOffset(NewValue);
FRecordCount := NewValue;
FStream.Size := FCachedSize;
end;
end;
procedure TPagedFile.SetPageOffsetByHeader(NewValue: Boolean);
begin
if FPageOffsetByHeader <> NewValue then
begin
FPageOffsetByHeader := NewValue;
FNeedRecalc := true;
end;
end;
procedure TPagedFile.WriteChar(c: Byte);
begin
FWriteError := (FStream.Write(c, 1) = 0) or FWriteError;
end;
function TPagedFile.ReadChar: Byte;
begin
Read(@Result, 1);
end;
procedure TPagedFile.Flush;
begin
end;
function TPagedFile.ReadBlock(const BlockPtr: Pointer; const ASize, APosition: Integer): Integer;
begin
FStream.Position := APosition;
CheckCachedSize(APosition);
Result := Read(BlockPtr, ASize);
end;
procedure TPagedFile.WriteBlock(const BlockPtr: Pointer; const ASize, APosition: Integer);
// assumes a lock is held if necessary prior to calling this function
begin
FStream.Position := APosition;
CheckCachedSize(APosition);
FWriteError := (FStream.Write(BlockPtr^, ASize) = 0) or FWriteError;
end;
procedure TPagedFile.ResetError;
begin
FWriteError := false;
end;
// BDE compatible lock offset found!
const
{$ifdef WINDOWS}
LockOffset = $EFFFFFFE; // BDE compatible
FileLockSize = 2;
{$else}
LockOffset = $7FFFFFFF;
FileLockSize = 1;
{$endif}
// dBase supports maximum of a billion records
LockStart = LockOffset - 1000000000;
function TPagedFile.LockSection(const Offset, Length: Cardinal; const Wait: Boolean): Boolean;
// assumes FNeedLock = true
var
Failed: Boolean;
begin
// FNeedLocks => FStream is of type TFileStream
Failed := false;
repeat
Result := LockFile(TFileStream(FStream).Handle, Offset, 0, Length, 0);
// test if lock violation, then wait a bit and try again
if not Result and Wait then
begin
if (GetLastError = ERROR_LOCK_VIOLATION) then
Sleep(10)
else
Failed := true;
end;
until Result or not Wait or Failed;
end;
function TPagedFile.UnlockSection(const Offset, Length: Cardinal): Boolean;
begin
Result := UnlockFile(TFileStream(FStream).Handle, Offset, 0, Length, 0);
end;
function TPagedFile.LockAllPages(const Wait: Boolean): Boolean;
var
Offset: Cardinal;
Length: Cardinal;
begin
// do we need locking?
if FNeedLocks and not FFileLocked then
begin
if FVirtualLocks then
begin
{$ifdef SUPPORT_UINT32_CARDINAL}
Offset := LockStart;
Length := LockOffset - LockStart + FileLockSize;
{$else}
// delphi 3 has strange types:
// cardinal 0..2 GIG ?? does it produce correct code?
Offset := Cardinal(LockStart);
Length := Cardinal(LockOffset) - Cardinal(LockStart) + FileLockSize;
{$endif}
end else begin
Offset := 0;
Length := $7FFFFFFF;
end;
// lock requested section
Result := LockSection(Offset, Length, Wait);
FFileLocked := Result;
end else
Result := true;
end;
procedure TPagedFile.UnlockAllPages;
var
Offset: Cardinal;
Length: Cardinal;
begin
// do we need locking?
if FNeedLocks and FFileLocked then
begin
if FVirtualLocks then
begin
{$ifdef SUPPORT_UINT32_CARDINAL}
Offset := LockStart;
Length := LockOffset - LockStart + FileLockSize;
{$else}
// delphi 3 has strange types:
// cardinal 0..2 GIG ?? does it produce correct code?
Offset := Cardinal(LockStart);
Length := Cardinal(LockOffset) - Cardinal(LockStart) + FileLockSize;
{$endif}
end else begin
Offset := 0;
Length := $7FFFFFFF;
end;
// unlock requested section
// FNeedLocks => FStream is of type TFileStream
FFileLocked := not UnlockSection(Offset, Length);
end;
end;
function TPagedFile.LockPage(const PageNo: Integer; const Wait: Boolean): Boolean;
var
Offset: Cardinal;
Length: Cardinal;
begin
// do we need locking?
if FNeedLocks and not FFileLocked then
begin
if FVirtualLocks then
begin
Offset := LockOffset - Cardinal(PageNo);
Length := 1;
end else begin
Offset := CalcPageOffset(PageNo);
Length := RecordSize;
end;
// lock requested section
Result := LockSection(Offset, Length, Wait);
end else
Result := true;
end;
procedure TPagedFile.UnlockPage(const PageNo: Integer);
var
Offset: Cardinal;
Length: Cardinal;
begin
// do we need locking?
if FNeedLocks and not FFileLocked then
begin
// calc offset + length
if FVirtualLocks then
begin
Offset := LockOffset - Cardinal(PageNo);
Length := 1;
end else begin
Offset := CalcPageOffset(PageNo);
Length := RecordSize;
end;
// unlock requested section
// FNeedLocks => FStream is of type TFileStream
UnlockSection(Offset, Length);
end;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -