123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- unit dbf_pgcfile;
- // paged, cached file
- interface
- {$I dbf_common.inc}
- {$ifdef USE_CACHE}
- uses
- Classes,
- SysUtils,
- dbf_common,
- dbf_avl,
- dbf_pgfile;
- type
- PPageInfo = ^TPageInfo;
- TPageInfo = record
- TimeStamp: Cardinal;
- Modified: Boolean;
- Data: Char;
- end;
- TCachedFile = class(TPagedFile)
- private
- FPageTree: TAvlTree;
- FUseTree: TAvlTree;
- FTimeStamp: Cardinal;
- FPageInfoSize: Integer;
- FCacheSize: Integer;
- FMaxPages: Cardinal;
- function GetTimeStamp: Cardinal;
- procedure UpdateTimeStamp(RecNo: Integer; Data: PPageInfo);
- procedure PageDeleted(Sender: TAvlTree; Data: PData);
- procedure UpdateMaxPages;
- function AddToCache(RecNo: Integer; Buffer: Pointer): PPageInfo;
- protected
- procedure SetRecordSize(NewValue: Integer); override;
- procedure SetCacheSize(NewSize: Integer);
- public
- constructor Create;
- destructor Destroy; override;
- procedure CloseFile; override;
- procedure Flush; override;
- function ReadRecord(RecNo: Integer; Buffer: Pointer): Integer; override;
- procedure WriteRecord(RecNo: Integer; Buffer: Pointer); override;
- property CacheSize: Integer read FCacheSize write SetCacheSize;
- end;
- {$endif}
- implementation
- {$ifdef USE_CACHE}
- constructor TCachedFile.Create;
- begin
- inherited;
- FPageTree := TAvlTree.Create;
- FPageTree.OnDelete := PageDeleted;
- FUseTree := TAvlTree.Create;
- FPageInfoSize := 0;
- FTimeStamp := 0;
- FCacheSize := 256 * 1024;
- end;
- destructor TCachedFile.Destroy;
- begin
- Flush;
- FPageTree.Free;
- FUseTree.Free;
- FPageTree := nil;
- FUseTree := nil;
- inherited;
- end;
- procedure TCachedFile.Flush;
- begin
- if FPageTree <> nil then
- begin
- FPageTree.Clear;
- FUseTree.Clear;
- end;
- FTimeStamp := 0;
- end;
- procedure TCachedFile.CloseFile;
- begin
- // flush modified pages to disk
- Flush;
- // now we can safely close
- inherited;
- end;
- procedure TCachedFile.SetRecordSize(NewValue: Integer);
- begin
- inherited;
- // first flush all pages, restart caching with new parameters
- Flush;
- // calculate size of extra data of pagetree
- FPageInfoSize := SizeOf(TPageInfo) - SizeOf(Char) + RecordSize;
- UpdateMaxPages;
- end;
- procedure TCachedFile.SetCacheSize(NewSize: Integer);
- begin
- if FCacheSize <> NewSize then
- begin
- FCacheSize := NewSize;
- UpdateMaxPages;
- end;
- end;
- procedure TCachedFile.UpdateMaxPages;
- begin
- if RecordSize = 0 then
- FMaxPages := 0
- else
- FMaxPages := FCacheSize div RecordSize;
- end;
- function TCachedFile.GetTimeStamp: Cardinal;
- begin
- Result := FTimeStamp;
- Inc(FTimeStamp);
- end;
- procedure TCachedFile.PageDeleted(Sender: TAvlTree; Data: PData);
- begin
- // data modified? write to disk
- if PPageInfo(Data^.ExtraData)^.Modified then
- inherited WriteRecord(Data^.ID, @PPageInfo(Data^.ExtraData)^.Data);
- // free cached page mem
- FreeMem(Data^.ExtraData);
- end;
- function TCachedFile.AddToCache(RecNo: Integer; Buffer: Pointer): PPageInfo;
- var
- oldData: PData;
- begin
- // make sure there is a free page in the cache
- while FPageTree.Count >= FMaxPages do
- begin
- // no free space, find oldest page
- oldData := FUseTree.Lowest;
- // remove from cache
- FPageTree.Delete(Integer(oldData^.ExtraData));
- FUseTree.Delete(oldData^.ID);
- end;
- // add to cache
- GetMem(Result, FPageInfoSize);
- Result^.TimeStamp := GetTimeStamp;
- Result^.Modified := false;
- Move(Buffer^, Result^.Data, RecordSize);
- FPageTree.Insert(RecNo, Result);
- FUseTree.Insert(Result^.TimeStamp, Pointer(RecNo));
- end;
- procedure TCachedFile.UpdateTimeStamp(RecNo: Integer; Data: PPageInfo);
- begin
- // update time used
- FUseTree.Delete(Data^.TimeStamp);
- Data^.TimeStamp := GetTimeStamp;
- FUseTree.Insert(Data^.TimeStamp, Pointer(RecNo));
- end;
- function TCachedFile.ReadRecord(RecNo: Integer; Buffer: Pointer): Integer;
- var
- Data: PPageInfo;
- begin
- // only cache when we do not need locking
- if NeedLocks then
- begin Result := inherited ReadRecord(RecNo, Buffer) end else begin
- // do we have this page in cache?
- Data := PPageInfo(FPageTree.Find(RecNo));
- if Data <> nil then
- begin
- // copy from cache
- Move(Data^.Data, Buffer^, RecordSize);
- UpdateTimeStamp(RecNo, Data);
- Result := RecordSize;
- end else begin
- // not yet in cache
- Result := inherited ReadRecord(RecNo, Buffer);
- // add
- if Result > 0 then
- AddToCache(RecNo, Buffer);
- end;
- end;
- end;
- procedure TCachedFile.WriteRecord(RecNo: Integer; Buffer: Pointer);
- var
- Data: PPageInfo;
- begin
- // only cache when we do not need locking
- if NeedLocks then
- begin inherited end else begin
- // do we have this page in cache?
- Data := PPageInfo(FPageTree.Find(RecNo));
- if Data <> nil then
- begin
- // copy to cache
- Move(Buffer^, Data^.Data, RecordSize);
- UpdateTimeStamp(RecNo, Data);
- end else begin
- // add
- Data := AddToCache(RecNo, Buffer);
- // notify we've added a page
- UpdateCachedSize(CalcPageOffset(RecNo+PagesPerRecord));
- end;
- Data^.Modified := true;
- end;
- end;
- {$endif} // USE_CACHE
- end.
|