123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912 |
- unit dbf_pgfile;
- interface
- {$I Dbf_Common.inc}
- uses
- Classes,
- SysUtils,
- Dbf_Common;
- //const
- // MaxHeaders = 256;
- type
- EPagedFile = Exception;
- TPagedFileMode = (pfNone, pfMemoryCreate, pfMemoryOpen, pfExclusiveCreate,
- pfExclusiveOpen, pfReadWriteCreate, pfReadWriteOpen, pfReadOnly);
- // access levels:
- //
- // - memory create
- // - exclusive create/open
- // - read/write create/open
- // - readonly open
- //
- // - memory -*-share: N/A -*-locks: disabled -*-indexes: read/write
- // - exclusive_create -*-share: deny write -*-locks: disabled -*-indexes: read/write
- // - exclusive_open -*-share: deny write -*-locks: disabled -*-indexes: read/write
- // - readwrite_create -*-share: deny none -*-locks: enabled -*-indexes: read/write
- // - readwrite_open -*-share: deny none -*-locks: enabled -*-indexes: read/write
- // - readonly -*-share: deny none -*-locks: disabled -*-indexes: readonly
- TPagedFile = class(TObject)
- protected
- FStream: TStream;
- FHeaderOffset: Integer;
- FHeaderSize: Integer;
- FRecordSize: Integer;
- FPageSize: Integer; { need for MDX, where recordsize <> pagesize }
- FRecordCount: Integer; { actually FPageCount, but we want to keep existing code }
- FPagesPerRecord: Integer;
- FCachedSize: Integer;
- FCachedRecordCount: Integer;
- FHeader: PChar;
- FActive: Boolean;
- FNeedRecalc: Boolean;
- FHeaderModified: Boolean;
- FPageOffsetByHeader: Boolean; { do pages start after header or just at BOF? }
- FMode: TPagedFileMode;
- FTempMode: TPagedFileMode;
- FUserMode: TPagedFileMode;
- FAutoCreate: Boolean;
- FNeedLocks: Boolean;
- FVirtualLocks: Boolean;
- FFileLocked: Boolean;
- FFileName: string;
- FBufferPtr: Pointer;
- FBufferAhead: Boolean;
- FBufferPage: Integer;
- FBufferOffset: Integer;
- FBufferSize: Integer;
- FBufferReadSize: Integer;
- FBufferMaxSize: Integer;
- FBufferModified: Boolean;
- FWriteError: Boolean;
- protected
- procedure SetHeaderOffset(NewValue: Integer); virtual;
- procedure SetRecordSize(NewValue: Integer); virtual;
- procedure SetHeaderSize(NewValue: Integer); virtual;
- procedure SetPageSize(NewValue: Integer);
- procedure SetPageOffsetByHeader(NewValue: Boolean); virtual;
- procedure SetRecordCount(NewValue: Integer);
- procedure SetBufferAhead(NewValue: Boolean);
- procedure SetFileName(NewName: string);
- procedure SetStream(NewStream: TStream);
- function LockSection(const Offset, Length: Cardinal; const Wait: Boolean): Boolean; virtual;
- function UnlockSection(const Offset, Length: Cardinal): Boolean; virtual;
- procedure UpdateBufferSize;
- procedure RecalcPagesPerRecord;
- procedure ReadHeader;
- procedure FlushHeader;
- procedure FlushBuffer;
- function ReadChar: Byte;
- procedure WriteChar(c: Byte);
- procedure CheckCachedSize(const APosition: Integer);
- procedure SynchronizeBuffer(IntRecNum: Integer);
- function Read(Buffer: Pointer; ASize: Integer): Integer;
- function ReadBlock(const BlockPtr: Pointer; const ASize, APosition: Integer): Integer;
- function SingleReadRecord(IntRecNum: Integer; Buffer: Pointer): Integer;
- procedure WriteBlock(const BlockPtr: Pointer; const ASize, APosition: Integer);
- procedure SingleWriteRecord(IntRecNum: Integer; Buffer: Pointer);
- function GetRecordCount: Integer;
- procedure UpdateCachedSize(CurrPos: Integer);
- property VirtualLocks: Boolean read FVirtualLocks write FVirtualLocks;
- public
- constructor Create;
- destructor Destroy; override;
- procedure CloseFile; virtual;
- procedure OpenFile; virtual;
- procedure DeleteFile;
- procedure TryExclusive; virtual;
- procedure EndExclusive; virtual;
- procedure CheckExclusiveAccess;
- procedure DisableForceCreate;
- function CalcPageOffset(const PageNo: Integer): Integer;
- function IsRecordPresent(IntRecNum: Integer): boolean;
- function ReadRecord(IntRecNum: Integer; Buffer: Pointer): Integer; virtual;
- procedure WriteRecord(IntRecNum: Integer; Buffer: Pointer); virtual;
- procedure WriteHeader; virtual;
- function FileCreated: Boolean;
- function IsSharedAccess: Boolean;
- procedure ResetError;
- function LockPage(const PageNo: Integer; const Wait: Boolean): Boolean;
- function LockAllPages(const Wait: Boolean): Boolean;
- procedure UnlockPage(const PageNo: Integer);
- procedure UnlockAllPages;
- procedure Flush; virtual;
- property Active: Boolean read FActive;
- property AutoCreate: Boolean read FAutoCreate write FAutoCreate; // only write when closed!
- property Mode: TPagedFileMode read FMode write FMode; // only write when closed!
- property TempMode: TPagedFileMode read FTempMode;
- property NeedLocks: Boolean read FNeedLocks;
- property HeaderOffset: Integer read FHeaderOffset write SetHeaderOffset;
- property HeaderSize: Integer read FHeaderSize write SetHeaderSize;
- property RecordSize: Integer read FRecordSize write SetRecordSize;
- property PageSize: Integer read FPageSize write SetPageSize;
- property PagesPerRecord: Integer read FPagesPerRecord;
- property RecordCount: Integer read GetRecordCount write SetRecordCount;
- property CachedRecordCount: Integer read FCachedRecordCount;
- property PageOffsetByHeader: Boolean read FPageOffsetbyHeader write SetPageOffsetByHeader;
- property FileLocked: Boolean read FFileLocked;
- property Header: PChar read FHeader;
- property FileName: string read FFileName write SetFileName;
- property Stream: TStream read FStream write SetStream;
- property BufferAhead: Boolean read FBufferAhead write SetBufferAhead;
- property WriteError: Boolean read FWriteError;
- end;
- implementation
- uses
- {$ifdef WIN32}
- Windows,
- {$else}
- {$ifdef KYLIX}
- Libc,
- {$endif}
- Types, Dbf_Wtil,
- {$endif}
- Dbf_Str;
- //====================================================================
- // TPagedFile
- //====================================================================
- constructor TPagedFile.Create;
- begin
- FFileName := EmptyStr;
- FHeaderOffset := 0;
- FHeaderSize := 0;
- FRecordSize := 0;
- FRecordCount := 0;
- FPageSize := 0;
- FPagesPerRecord := 0;
- FActive := false;
- FHeaderModified := false;
- FPageOffsetByHeader := true;
- FNeedLocks := false;
- FMode := pfReadOnly;
- FTempMode := pfNone;
- FAutoCreate := false;
- FVirtualLocks := true;
- FFileLocked := false;
- FHeader := nil;
- FBufferPtr := nil;
- FBufferAhead := false;
- FBufferModified := false;
- FBufferSize := 0;
- FBufferMaxSize := 0;
- FBufferOffset := 0;
- FWriteError := false;
- inherited;
- end;
- destructor TPagedFile.Destroy;
- begin
- // close physical file
- if FFileLocked then UnlockAllPages;
- CloseFile;
- FFileLocked := false;
- // free mem
- if FHeader <> nil then
- FreeMem(FHeader);
- inherited;
- end;
- procedure TPagedFile.OpenFile;
- var
- fileOpenMode: Word;
- begin
- if FActive then exit;
- // store user specified mode
- FUserMode := FMode;
- if not (FMode in [pfMemoryCreate, pfMemoryOpen]) then
- begin
- // test if file exists
- if not FileExists(FFileName) then
- begin
- // if auto-creating, adjust mode
- if FAutoCreate then case FMode of
- pfExclusiveOpen: FMode := pfExclusiveCreate;
- pfReadWriteOpen, pfReadOnly: FMode := pfReadWriteCreate;
- end;
- // it seems the VCL cannot share a file that is created?
- // create file first, then open it in requested mode
- // filecreated means 'to be created' in this context ;-)
- if FileCreated then
- FileClose(FileCreate(FFileName))
- else
- raise EPagedFile.CreateFmt(STRING_FILE_NOT_FOUND,[FFileName]);
- end;
- // specify open mode
- case FMode of
- pfExclusiveCreate: fileOpenMode := fmOpenReadWrite or fmShareDenyWrite;
- pfExclusiveOpen: fileOpenMode := fmOpenReadWrite or fmShareDenyWrite;
- pfReadWriteCreate: fileOpenMode := fmOpenReadWrite or fmShareDenyNone;
- pfReadWriteOpen: fileOpenMode := fmOpenReadWrite or fmShareDenyNone;
- else // => readonly
- fileOpenMode := fmOpenRead or fmShareDenyNone;
- end;
- // open file
- FStream := TFileStream.Create(FFileName, fileOpenMode);
- // if creating, then empty file
- if FileCreated then
- FStream.Size := 0;
- end else begin
- if FStream = nil then
- begin
- FMode := pfMemoryCreate;
- FStream := TMemoryStream.Create;
- end;
- end;
- // init size var
- FCachedSize := Stream.Size;
- // update whether we need locking
- {$ifdef _DEBUG}
- FNeedLocks := true;
- {$else}
- FNeedLocks := IsSharedAccess;
- {$endif}
- FActive := true;
- end;
- procedure TPagedFile.CloseFile;
- begin
- if FActive then
- begin
- FlushHeader;
- // don't free the user's stream
- if not (FMode in [pfMemoryOpen, pfMemoryCreate]) then
- FreeAndNil(FStream);
- // mode possibly overridden in case of auto-created file
- FMode := FUserMode;
- FActive := false;
- FCachedRecordCount := 0;
- end;
- end;
- procedure TPagedFile.DeleteFile;
- begin
- // opened -> we can not delete
- if not FActive then
- SysUtils.DeleteFile(FileName);
- end;
- function TPagedFile.FileCreated: Boolean;
- const
- CreationModes: array [pfNone..pfReadOnly] of Boolean =
- (false, true, false, true, false, true, false, false);
- // node, memcr, memop, excr, exopn, rwcr, rwopn, rdonly
- begin
- Result := CreationModes[FMode];
- end;
- function TPagedFile.IsSharedAccess: Boolean;
- const
- SharedAccessModes: array [pfNone..pfReadOnly] of Boolean =
- (false, false, false, false, false, true, true, true);
- // node, memcr, memop, excr, exopn, rwcr, rwopn, rdonly
- begin
- Result := SharedAccessModes[FMode];
- end;
- procedure TPagedFile.CheckExclusiveAccess;
- begin
- // in-memory => exclusive access!
- if IsSharedAccess then
- raise EDbfError.Create(STRING_NEED_EXCLUSIVE_ACCESS);
- end;
- function TPagedFile.CalcPageOffset(const PageNo: Integer): Integer;
- begin
- if not FPageOffsetByHeader then
- Result := FPageSize * PageNo
- else if PageNo = 0 then
- Result := 0
- else
- Result := FHeaderOffset + FHeaderSize + (FPageSize * (PageNo - 1))
- end;
- procedure TPagedFile.CheckCachedSize(const APosition: Integer);
- begin
- // file expanded?
- if APosition > FCachedSize then
- begin
- FCachedSize := APosition;
- FNeedRecalc := true;
- end;
- end;
- function TPagedFile.Read(Buffer: Pointer; ASize: Integer): Integer;
- begin
- // if we cannot read due to a lock, then wait a bit
- repeat
- Result := FStream.Read(Buffer^, ASize);
- if Result = 0 then
- begin
- // translation to linux???
- if GetLastError = ERROR_LOCK_VIOLATION then
- begin
- // wait a bit until block becomes available
- Sleep(1);
- end else begin
- // return empty block
- exit;
- end;
- end else
- exit;
- until false;
- end;
- procedure TPagedFile.UpdateCachedSize(CurrPos: Integer);
- begin
- // have we added a record?
- if CurrPos > FCachedSize then
- begin
- // update cached size, always at end
- repeat
- Inc(FCachedSize, FRecordSize);
- Inc(FRecordCount, PagesPerRecord);
- until FCachedSize >= CurrPos;
- end;
- end;
- procedure TPagedFile.FlushBuffer;
- begin
- if FBufferAhead and FBufferModified then
- begin
- WriteBlock(FBufferPtr, FBufferSize, FBufferOffset);
- FBufferModified := false;
- end;
- end;
- function TPagedFile.SingleReadRecord(IntRecNum: Integer; Buffer: Pointer): Integer;
- begin
- Result := ReadBlock(Buffer, RecordSize, CalcPageOffset(IntRecNum));
- end;
- procedure TPagedFile.SingleWriteRecord(IntRecNum: Integer; Buffer: Pointer);
- begin
- WriteBlock(Buffer, RecordSize, CalcPageOffset(IntRecNum));
- end;
- procedure TPagedFile.SynchronizeBuffer(IntRecNum: Integer);
- begin
- // record outside buffer, flush previous buffer
- FlushBuffer;
- // read new set of records
- FBufferPage := IntRecNum;
- FBufferOffset := CalcPageOffset(IntRecNum);
- if FBufferOffset + FBufferMaxSize > FCachedSize then
- FBufferReadSize := FCachedSize - FBufferOffset
- else
- FBufferReadSize := FBufferMaxSize;
- FBufferSize := FBufferReadSize;
- FBufferReadSize := ReadBlock(FBufferPtr, FBufferReadSize, FBufferOffset);
- end;
- function TPagedFile.IsRecordPresent(IntRecNum: Integer): boolean;
- begin
- // if in shared mode, recordcount can only increase, check if recordno
- // in range for cached recordcount
- if not IsSharedAccess or (IntRecNum > FCachedRecordCount) then
- FCachedRecordCount := RecordCount;
- Result := (0 <= IntRecNum) and (IntRecNum <= FCachedRecordCount);
- end;
- function TPagedFile.ReadRecord(IntRecNum: Integer; Buffer: Pointer): Integer;
- var
- Offset: Integer;
- begin
- if FBufferAhead then
- begin
- Offset := (IntRecNum - FBufferPage) * PageSize;
- if (FBufferPage <> -1) and (FBufferPage <= IntRecNum) and
- (Offset+RecordSize <= FBufferReadSize) then
- begin
- // have record in buffer, nothing to do here
- end else begin
- // need to update buffer
- SynchronizeBuffer(IntRecNum);
- // check if enough bytes read
- if RecordSize > FBufferReadSize then
- begin
- Result := 0;
- exit;
- end;
- // reset offset into buffer
- Offset := 0;
- end;
- // now we have this record in buffer
- Move(PChar(FBufferPtr)[Offset], Buffer^, RecordSize);
- // successful
- Result := RecordSize;
- end else begin
- // no buffering
- Result := SingleReadRecord(IntRecNum, Buffer);
- end;
- end;
- procedure TPagedFile.WriteRecord(IntRecNum: Integer; Buffer: Pointer);
- var
- RecEnd: Integer;
- begin
- if FBufferAhead then
- begin
- RecEnd := (IntRecNum - FBufferPage + PagesPerRecord) * PageSize;
- if (FBufferPage <> -1) and (FBufferPage <= IntRecNum) and
- (RecEnd <= FBufferMaxSize) then
- begin
- // extend buffer?
- if RecEnd > FBufferSize then
- FBufferSize := RecEnd;
- 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 WIN32}
- 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.
|