dbf_pgcfile.pas 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. unit dbf_pgcfile;
  2. // paged, cached file
  3. interface
  4. {$I dbf_common.inc}
  5. {$ifdef USE_CACHE}
  6. uses
  7. Classes,
  8. SysUtils,
  9. dbf_common,
  10. dbf_avl,
  11. dbf_pgfile;
  12. type
  13. PPageInfo = ^TPageInfo;
  14. TPageInfo = record
  15. TimeStamp: Cardinal;
  16. Modified: Boolean;
  17. Data: Char;
  18. end;
  19. TCachedFile = class(TPagedFile)
  20. private
  21. FPageTree: TAvlTree;
  22. FUseTree: TAvlTree;
  23. FTimeStamp: Cardinal;
  24. FPageInfoSize: Integer;
  25. FCacheSize: Integer;
  26. FMaxPages: Cardinal;
  27. function GetTimeStamp: Cardinal;
  28. procedure UpdateTimeStamp(RecNo: Integer; Data: PPageInfo);
  29. procedure PageDeleted(Sender: TAvlTree; Data: PData);
  30. procedure UpdateMaxPages;
  31. function AddToCache(RecNo: Integer; Buffer: Pointer): PPageInfo;
  32. protected
  33. procedure SetRecordSize(NewValue: Integer); override;
  34. procedure SetCacheSize(NewSize: Integer);
  35. public
  36. constructor Create;
  37. destructor Destroy; override;
  38. procedure CloseFile; override;
  39. procedure Flush; override;
  40. function ReadRecord(RecNo: Integer; Buffer: Pointer): Integer; override;
  41. procedure WriteRecord(RecNo: Integer; Buffer: Pointer); override;
  42. property CacheSize: Integer read FCacheSize write SetCacheSize;
  43. end;
  44. {$endif}
  45. implementation
  46. {$ifdef USE_CACHE}
  47. constructor TCachedFile.Create;
  48. begin
  49. inherited;
  50. FPageTree := TAvlTree.Create;
  51. FPageTree.OnDelete := PageDeleted;
  52. FUseTree := TAvlTree.Create;
  53. FPageInfoSize := 0;
  54. FTimeStamp := 0;
  55. FCacheSize := 256 * 1024;
  56. end;
  57. destructor TCachedFile.Destroy;
  58. begin
  59. Flush;
  60. FPageTree.Free;
  61. FUseTree.Free;
  62. FPageTree := nil;
  63. FUseTree := nil;
  64. inherited;
  65. end;
  66. procedure TCachedFile.Flush;
  67. begin
  68. if FPageTree <> nil then
  69. begin
  70. FPageTree.Clear;
  71. FUseTree.Clear;
  72. end;
  73. FTimeStamp := 0;
  74. end;
  75. procedure TCachedFile.CloseFile;
  76. begin
  77. // flush modified pages to disk
  78. Flush;
  79. // now we can safely close
  80. inherited;
  81. end;
  82. procedure TCachedFile.SetRecordSize(NewValue: Integer);
  83. begin
  84. inherited;
  85. // first flush all pages, restart caching with new parameters
  86. Flush;
  87. // calculate size of extra data of pagetree
  88. FPageInfoSize := SizeOf(TPageInfo) - SizeOf(Char) + RecordSize;
  89. UpdateMaxPages;
  90. end;
  91. procedure TCachedFile.SetCacheSize(NewSize: Integer);
  92. begin
  93. if FCacheSize <> NewSize then
  94. begin
  95. FCacheSize := NewSize;
  96. UpdateMaxPages;
  97. end;
  98. end;
  99. procedure TCachedFile.UpdateMaxPages;
  100. begin
  101. if RecordSize = 0 then
  102. FMaxPages := 0
  103. else
  104. FMaxPages := FCacheSize div RecordSize;
  105. end;
  106. function TCachedFile.GetTimeStamp: Cardinal;
  107. begin
  108. Result := FTimeStamp;
  109. Inc(FTimeStamp);
  110. end;
  111. procedure TCachedFile.PageDeleted(Sender: TAvlTree; Data: PData);
  112. begin
  113. // data modified? write to disk
  114. if PPageInfo(Data^.ExtraData)^.Modified then
  115. inherited WriteRecord(Data^.ID, @PPageInfo(Data^.ExtraData)^.Data);
  116. // free cached page mem
  117. FreeMem(Data^.ExtraData);
  118. end;
  119. function TCachedFile.AddToCache(RecNo: Integer; Buffer: Pointer): PPageInfo;
  120. var
  121. oldData: PData;
  122. begin
  123. // make sure there is a free page in the cache
  124. while FPageTree.Count >= FMaxPages do
  125. begin
  126. // no free space, find oldest page
  127. oldData := FUseTree.Lowest;
  128. // remove from cache
  129. FPageTree.Delete(Integer(oldData^.ExtraData));
  130. FUseTree.Delete(oldData^.ID);
  131. end;
  132. // add to cache
  133. GetMem(Result, FPageInfoSize);
  134. Result^.TimeStamp := GetTimeStamp;
  135. Result^.Modified := false;
  136. Move(Buffer^, Result^.Data, RecordSize);
  137. FPageTree.Insert(RecNo, Result);
  138. FUseTree.Insert(Result^.TimeStamp, Pointer(RecNo));
  139. end;
  140. procedure TCachedFile.UpdateTimeStamp(RecNo: Integer; Data: PPageInfo);
  141. begin
  142. // update time used
  143. FUseTree.Delete(Data^.TimeStamp);
  144. Data^.TimeStamp := GetTimeStamp;
  145. FUseTree.Insert(Data^.TimeStamp, Pointer(RecNo));
  146. end;
  147. function TCachedFile.ReadRecord(RecNo: Integer; Buffer: Pointer): Integer;
  148. var
  149. Data: PPageInfo;
  150. begin
  151. // only cache when we do not need locking
  152. if NeedLocks then
  153. begin Result := inherited ReadRecord(RecNo, Buffer) end else begin
  154. // do we have this page in cache?
  155. Data := PPageInfo(FPageTree.Find(RecNo));
  156. if Data <> nil then
  157. begin
  158. // copy from cache
  159. Move(Data^.Data, Buffer^, RecordSize);
  160. UpdateTimeStamp(RecNo, Data);
  161. Result := RecordSize;
  162. end else begin
  163. // not yet in cache
  164. Result := inherited ReadRecord(RecNo, Buffer);
  165. // add
  166. if Result > 0 then
  167. AddToCache(RecNo, Buffer);
  168. end;
  169. end;
  170. end;
  171. procedure TCachedFile.WriteRecord(RecNo: Integer; Buffer: Pointer);
  172. var
  173. Data: PPageInfo;
  174. begin
  175. // only cache when we do not need locking
  176. if NeedLocks then
  177. begin inherited end else begin
  178. // do we have this page in cache?
  179. Data := PPageInfo(FPageTree.Find(RecNo));
  180. if Data <> nil then
  181. begin
  182. // copy to cache
  183. Move(Buffer^, Data^.Data, RecordSize);
  184. UpdateTimeStamp(RecNo, Data);
  185. end else begin
  186. // add
  187. Data := AddToCache(RecNo, Buffer);
  188. // notify we've added a page
  189. UpdateCachedSize(CalcPageOffset(RecNo+PagesPerRecord));
  190. end;
  191. Data^.Modified := true;
  192. end;
  193. end;
  194. {$endif} // USE_CACHE
  195. end.