home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 October / Chip_2001-10_cd1.bin / zkuste / delphi / kolekce / d456 / DCSLIB25.ZIP / DCRecordStream.pas < prev    next >
Pascal/Delphi Source File  |  2001-01-20  |  13KB  |  504 lines

  1. unit DCRecordStream;
  2.  
  3. interface
  4. uses
  5.   Classes, SysUtils, Windows;
  6.  
  7. const
  8.   RECORD_ROOT_NAME  = 'RSROOT Version 1.0';
  9.   RECORD_FLAG_EMPTY = $01;
  10.  
  11. type
  12.   TRecordType = packed record
  13.     Flags   : WORD;
  14.     DataSize: WORD;           // ╨ατ∞σ≡ Σαφφ√⌡ Γ ßδεΩσ
  15.     NextData: integer;        // ┼±δΦ ≡ατ∞σ≡ Σαφφ√⌡ ßεδⁿ°σ, ≈σ∞ ≡ατ∞σ≡ ßδεΩα
  16.                               // ≤Ωατα≥σδⁿ φα ±δσΣ≤■∙ΦΘ ßδεΩ ± Σαφφ√∞Φ
  17.     EmptyPtr: integer;        // ╙Ωατα≥σδⁿ φα ∩≤±≥εΘ ßδεΩ (0 Ωεφσ÷ ⌠αΘδα)
  18.                               // ┬ ±δ≤≈ασ:
  19.                               //  1. Ωε≡φσΓεΘ ßδεΩ(ROOT)  -
  20.                               //       ±εΣσ≡µΦ≥ ≤Ωατα≥σδⁿ φα ∩σ≡Γ√Θ ∩≤±≥εΘ ßδεΩ;
  21.                               //  2. ∩≤±≥εΘ ßδεΩ          -
  22.                               //       ≤Ωατα≥σδⁿ φα ±δσΣ≤∙ΦΘ ∩≤±≥εΘ ßδεΩ
  23.   end;
  24.  
  25.   TRecordStream = class(TFileStream)
  26.   private
  27.     FBlockSize: integer;
  28.     FRootRecord: TRecordType;
  29.     FRootData: Pointer;
  30.     function GetRecordCount: integer;
  31.     function GetRecNo: integer;
  32.     procedure SetRecNo(const Value: integer);
  33.     procedure CreateRootData;
  34.     function ClearRecord(var ARecord: TRecordType): TRecordType;
  35.     procedure SetEmpty(var ARecord: TRecordType; const AEmpty: boolean);
  36.     function GetRecordSize: integer;
  37.     procedure WriteRecord(ARecord: TRecordType; AData: Pointer;
  38.       ADataSize: integer);
  39.     procedure ReadRecord(var ARecord: TRecordType; var AData: Pointer;
  40.       var ADataSize: integer);
  41.   protected
  42.     procedure GetRootData(AData: Pointer); virtual;
  43.     procedure LoadRoot;
  44.     procedure SaveRoot;
  45.     function ReadBlock(var Buffer; Count: Longint): Longint;
  46.     function WriteBlock(const Buffer; Count: Longint): Longint;
  47.     procedure SeekRecord(const AOffset: integer; AOrigin: Word);
  48.     procedure First;
  49.     procedure Last;
  50.     function Append(AData: Pointer; ADataSize: integer): integer;
  51.     procedure Delete(ARecNo: integer);
  52.     procedure WriteData(AData: Pointer; ADataSize: integer);
  53.     procedure ReadData(var AData: Pointer; var ADataSize: integer);
  54.     procedure Next;
  55.     procedure Prior;
  56.     function LockRecord(ARecNo: integer): boolean;
  57.     function UnlockRecord(ARecNo: integer): boolean;
  58.     property RootData: Pointer read FRootData write FRootData;
  59.     property RecordCount: integer read GetRecordCount;
  60.     property RecNo: longint read GetRecNo write SetRecNo;
  61.     property BlockSize: integer read FBlockSize;
  62.     property RootRecord: TRecordType read FRootRecord;
  63.   public
  64.     constructor Create(const AFileName: string; ABlockSize: integer);
  65.     destructor Destroy; override;
  66.   end;
  67.  
  68. function _intMin(A, B: integer): integer;
  69.  
  70. implementation
  71.  
  72. function _intMin(A, B: integer): integer;
  73. {
  74.    -> eax   A
  75.    -> edx   B
  76.    <- eax   A if A < B
  77.             A if A = B
  78.             B if A > B
  79. }
  80. asm
  81.   cmp eax, edx  // ±≡αΓφΦΓασ∞ └ Φ ┬
  82.   jg  @@1       // σ±δΦ eax > edx ΦΣσ∞ ΦΣσ∞ φα @@1
  83.   jmp @@2       // Φφα≈σ Γ√⌡εΣΦ∞
  84. @@1:
  85.   mov eax, edx  // τα∩Φ±√Γασ∞ Γ ┬ Γ Result
  86. @@2:
  87. end;
  88.  
  89. function _intCompare(A, B: integer): integer;
  90. {
  91.    -> eax   A
  92.    -> edx   B
  93.    <- eax   -1 if A < B
  94.              0 if A = B
  95.              1 if A > B
  96. }
  97. asm
  98.   cmp eax, edx  // ±≡αΓφΦΓασ∞ └ Φ ┬
  99.   jge @@1       // σ±δΦ eax >= edx ΦΣσ∞ ΦΣσ∞ φα @@1
  100.   mov eax, -1   // eax < edx
  101.   jmp @@3
  102. @@1:
  103.   cmp eax, edx  // ±≡αΓφΦΓασ∞ └ Φ ┬
  104.   jg @@2        // σ±δΦ eax > edx ΦΣσ∞ ΦΣσ∞ φα @@2
  105.   mov eax, 0    // eax = edx
  106.   jmp @@3
  107. @@2:
  108.   mov eax, 1    // eax > edx
  109. @@3:
  110. end;
  111.  
  112. { THeaderStream }
  113.  
  114. function TRecordStream.Append(AData: Pointer; ADataSize: integer): integer;
  115.  var
  116.   ARecord: TRecordType;
  117. begin
  118.   if FRootRecord.EmptyPtr = 0 then
  119.   begin
  120.     Seek(0, 2);
  121.     Result := RecNo;
  122.     WriteRecord(ClearRecord(ARecord), AData, ADataSize);
  123.   end
  124.   else begin
  125.     RecNo  := FRootRecord.EmptyPtr;
  126.     Result := RecNo;
  127.     WriteData(AData, ADataSize);
  128.   end;
  129. end;
  130.  
  131. constructor TRecordStream.Create(const AFileName: string; ABlockSize: integer);
  132. begin
  133.   FBlockSize := ABlockSize;
  134.   CreateRootData;
  135.   if not FileExists(AFileName) then
  136.   begin
  137.     inherited Create(AFileName, fmCreate or fmShareDenyNone);
  138.     SaveRoot;
  139.     GetRootData(FRootData);
  140.     ClearRecord(FRootRecord);
  141.     FRootRecord.DataSize := FBlockSize;
  142.   end
  143.   else begin
  144.     inherited Create(AFileName, fmOpenReadWrite or fmShareDenyNone);
  145.     LoadRoot;
  146.   end;
  147. end;
  148.  
  149. destructor TRecordStream.Destroy;
  150. begin
  151.   SaveRoot;
  152.   FreeMem(RootData, BlockSize);
  153.   inherited;
  154. end;
  155.  
  156. procedure TRecordStream.First;
  157. begin
  158.   Seek(0, 0);
  159. end;
  160.  
  161. function TRecordStream.GetRecNo: integer;
  162. begin
  163.   Result := Position div GetRecordSize;
  164. end;
  165.  
  166. function TRecordStream.GetRecordCount: integer;
  167. begin
  168.   Result := Size div GetRecordSize;
  169. end;
  170.  
  171. procedure TRecordStream.Last;
  172. begin
  173.   Seek(-GetRecordSize, 2);
  174. end;
  175.  
  176. procedure TRecordStream.Next;
  177. begin
  178.   if RecNo < (RecordCount-1) then SeekRecord(1, 1);
  179. end;
  180.  
  181. procedure TRecordStream.Prior;
  182. begin
  183.   if RecNo > 0 then SeekRecord(-1, 1);
  184. end;
  185.  
  186. procedure TRecordStream.ReadData(var AData: Pointer; var ADataSize: integer);
  187.  var
  188.   ARecord: TRecordType;
  189. begin
  190.   ReadRecord(ARecord, AData, ADataSize);
  191. end;
  192.  
  193. procedure TRecordStream.SeekRecord(const AOffset: integer; AOrigin: Word);
  194. begin
  195.   Seek(AOffset*GetRecordSize, AOrigin);
  196. end;
  197.  
  198. procedure TRecordStream.SetRecNo(const Value: integer);
  199. begin
  200.   if Value > 0 then
  201.     Position := Value * GetRecordSize;
  202. end;
  203.  
  204. procedure TRecordStream.SaveRoot;
  205.  var
  206.   pBlock: Pointer;
  207. begin
  208.   First;
  209.   GetMem(pBlock, GetRecordSize);
  210.   LockFile(Handle, 0, 0, 1, 0);
  211.   try
  212.     FillChar(pBlock^, GetRecordSize, 0);
  213.     Move(FRootRecord, pBlock^, SizeOf(TRecordType));
  214.     Move(RootData^, (PChar(pBlock)+SizeOf(TRecordType))^, BlockSize);
  215.     Write(pBlock^, GetRecordSize);
  216.   finally
  217.     FreeMem(pBlock, GetRecordSize);
  218.     UnLockFile(Handle, 0, 0, 1, 0);
  219.   end;
  220. end;
  221.  
  222. procedure TRecordStream.WriteData(AData: Pointer; ADataSize: integer);
  223.  var
  224.   ARecord: TRecordType;
  225.   pBlock: Pointer;
  226. begin
  227.   GetMem(pBlock, GetRecordSize);
  228.   try
  229.     ReadBlock(pBlock^, GetRecordSize);
  230.     Move(pBlock^, ARecord, SizeOf(TRecordType));
  231.     WriteRecord(ARecord, AData, ADataSize);
  232.   finally
  233.     FreeMem(pBlock, GetRecordSize);
  234.   end;
  235. end;
  236.  
  237. procedure TRecordStream.LoadRoot;
  238.  var
  239.   ADataSize: integer;
  240. begin
  241.   First;
  242.   ReadRecord(FRootRecord, FRootData, ADataSize);
  243. end;
  244.  
  245. procedure TRecordStream.Delete(ARecNo: Integer);
  246.  var
  247.   AEmptyRecNo, ANextData: integer;
  248.   ARecord: TRecordType;
  249.   pBlock: Pointer;
  250. begin
  251.   AEmptyRecNo := FRootRecord.EmptyPtr;
  252.  
  253.   GetMem(pBlock, GetRecordSize);
  254.   try
  255.     RecNo     := ARecNo;
  256.     ANextData := ARecNo;
  257.  
  258.     while (ANextData <> 0) and (Position < Size) do
  259.     begin
  260.  
  261.       ReadBlock(pBlock^, GetRecordSize);
  262.       Move(pBlock^, ARecord, SizeOf(TRecordType));
  263.  
  264.       SetEmpty(ARecord, True);
  265.       with ARecord do
  266.       begin
  267.         EmptyPtr    := AEmptyRecNo;
  268.         AEmptyRecNo := ANextData;
  269.         ANextData   := ARecord.NextData;
  270.         NextData    := 0; 
  271.       end;
  272.  
  273.       FillChar((PChar(pBlock)+SizeOf(TRecordType))^, FBlockSize, 0);
  274.       Move(ARecord, pBlock^, SizeOf(TRecordType));
  275.  
  276.       if ANextData <> 0 then
  277.       begin
  278.         Write(pBlock^, GetRecordSize);
  279.         RecNo := ANextData;
  280.       end
  281.       else
  282.         WriteBlock(pBlock^, GetRecordSize);
  283.   
  284.     end;
  285.  
  286.     FRootRecord.EmptyPtr := RecNo;
  287.  
  288.   finally
  289.     FreeMem(pBlock, GetRecordSize);
  290.   end;
  291. end;
  292.  
  293. procedure TRecordStream.CreateRootData;
  294. begin
  295.   GetMem(FRootData, BlockSize);
  296.   FillChar(RootData^, BlockSize, 0);
  297. end;
  298.  
  299. procedure TRecordStream.GetRootData(AData: Pointer);
  300. begin
  301.   Move(RECORD_ROOT_NAME, AData^, Length(RECORD_ROOT_NAME));
  302. end;
  303.  
  304. procedure TRecordStream.SetEmpty(var ARecord: TRecordType;
  305.   const AEmpty: boolean);
  306. begin
  307.   if AEmpty then
  308.     ARecord.Flags := ARecord.Flags or RECORD_FLAG_EMPTY
  309.   else
  310.     ARecord.Flags := ARecord.Flags and (RECORD_FLAG_EMPTY xor $FF)
  311. end;
  312.  
  313. function TRecordStream.GetRecordSize: integer;
  314. begin
  315.   Result := SizeOf(TRecordType) + FBlockSize;
  316. end;
  317.  
  318. procedure TRecordStream.ReadRecord(var ARecord: TRecordType;
  319.   var AData: Pointer; var ADataSize: Integer);
  320.  var
  321.   pBlock: Pointer;
  322.   ABlockOffset: integer;
  323. begin
  324.   ABlockOffset := 0;
  325.   GetMem(pBlock, GetRecordSize);
  326.   try
  327.     ReadBlock(pBlock^, GetRecordSize);
  328.     Move(pBlock^, ARecord, SizeOf(TRecordType));
  329.  
  330.     ADataSize := ARecord.DataSize;
  331.     ReallocMem(AData, ADataSize);
  332.  
  333.     Move((PChar(pBlock)+SizeOf(TRecordType))^, (PChar(AData)+ABlockOffset)^,
  334.           _intMin(ADataSize, BlockSize));
  335.  
  336.     while ARecord.NextData <> 0 do
  337.     begin
  338.        Inc(ABlockOffset, BlockSize);
  339.  
  340.        RecNo := ARecord.NextData;
  341.        ReadBlock(pBlock^, GetRecordSize);
  342.  
  343.        Move(pBlock^, ARecord, SizeOf(TRecordType));
  344.        Move((PChar(pBlock)+SizeOf(TRecordType))^, (PChar(AData)+ABlockOffset)^,
  345.              _intMin(ARecord.DataSize, BlockSize));
  346.  
  347.     end;
  348.  
  349.   finally
  350.     FreeMem(pBlock, GetRecordSize);
  351.   end;
  352. end;
  353.  
  354. procedure TRecordStream.WriteRecord(ARecord: TRecordType; AData: Pointer;
  355.   ADataSize: Integer);
  356.  var
  357.   pBlock: Pointer;
  358.   ABlockOffset: integer;
  359.   AEmptyPtr: integer;
  360.   ACompare: integer;
  361.   ANextData: integer;
  362.   lFirstBlock: boolean;
  363. begin
  364.   if ADataSize < 0 then Exit;
  365.  
  366.   GetMem(pBlock, GetRecordSize);
  367.  
  368.   try
  369.     ANextData    := 0;
  370.     ABlockOffset := 0;
  371.     lFirstBlock  := True;
  372.  
  373.     while ABlockOffset < ADataSize do
  374.     begin
  375.  
  376.       if Position < Size then
  377.       begin
  378.         if not lFirstBlock then
  379.         begin
  380.           ReadBlock(pBlock^, GetRecordSize);
  381.           Move(pBlock^, ARecord, SizeOf(TRecordType));
  382.         end
  383.         else
  384.           lFirstBlock := False;
  385.         ANextData := ARecord.NextData;
  386.         AEmptyPtr := ARecord.NextData;
  387.       end
  388.       else begin
  389.         FillChar(ARecord, SizeOf(ARecord), 0);
  390.         ANextData := 0;
  391.         AEmptyPtr := 0;
  392.       end;
  393.  
  394.       if AEmptyPtr = 0 then
  395.       begin
  396.         AEmptyPtr := ARecord.EmptyPtr;
  397.         if AEmptyPtr <> 0 then
  398.           FRootRecord.EmptyPtr := AEmptyPtr
  399.         else
  400.         begin
  401.           if FRootRecord.EmptyPtr <> RecNo then
  402.             AEmptyPtr := FRootRecord.EmptyPtr
  403.           else
  404.             FRootRecord.EmptyPtr := 0;
  405.         end
  406.       end;
  407.  
  408.       with ARecord do
  409.       begin
  410.         EmptyPtr  := 0;
  411.         SetEmpty(ARecord, False);
  412.         ACompare := _intCompare(BlockSize, ADataSize-ABlockOffset);
  413.         case ACompare of
  414.           -1:
  415.             begin
  416.               if AEmptyPtr <> 0 then
  417.                 NextData := AEmptyPtr
  418.               else
  419.                 NextData := RecordCount+1;
  420.               DataSize := ADataSize;
  421.             end;
  422.            0:
  423.              begin
  424.                NextData := 0;
  425.                DataSize := ADataSize;
  426.              end;
  427.            1:
  428.              begin
  429.                NextData := 0;
  430.                DataSize := ADataSize-ABlockOffset;
  431.              end;
  432.         end;
  433.       end;
  434.  
  435.       FillChar(pBlock^, GetRecordSize, 0);
  436.       Move((PChar(AData)+ABlockOffset)^, (PChar(pBlock)+SizeOf(TRecordType))^,
  437.            _intMin(BlockSize, ADataSize-ABlockOffset));
  438.  
  439.       Move(ARecord, pBlock^, SizeOf(TRecordType));
  440.       Inc(ABlockOffset, BlockSize);
  441.  
  442.       if ARecord.NextData <> 0 then
  443.       begin
  444.         Write(pBlock^, GetRecordSize);
  445.         if ARecord.NextData > RecordCount then
  446.         begin
  447.           RecNo := RecordCount;
  448.           while ARecord.NextData > RecordCount do
  449.           begin
  450.             FillChar(pBlock^, GetRecordSize, 0);
  451.             WriteBlock(pBlock^, GetRecordSize);
  452.           end;
  453.         end;
  454.         RecNo := ARecord.NextData;
  455.       end
  456.       else begin
  457.         WriteBlock(pBlock^, GetRecordSize);
  458.       end;
  459.  
  460.     end;
  461.  
  462.     if (ANextData <> 0) and (ANextData <> RecNo) then Delete(ANextData);
  463.  
  464.   finally
  465.     FreeMem(pBlock, GetRecordSize);
  466.   end;
  467.  
  468. end;
  469.  
  470. function TRecordStream.ClearRecord(var ARecord: TRecordType): TRecordType;
  471. begin
  472.   FillChar(ARecord, SizeOf(ARecord), 0);
  473.   SetEmpty(ARecord, True);
  474.   Result := ARecord;
  475. end;
  476.  
  477. function TRecordStream.ReadBlock(var Buffer; Count: Integer): Longint;
  478. begin
  479.   Result := Read(Buffer,Count);
  480.   Prior;
  481. end;
  482.  
  483. function TRecordStream.WriteBlock(const Buffer; Count: Integer): Longint;
  484. begin
  485.   Result := Write(Buffer,Count);
  486.   Prior;
  487. end;
  488.  
  489. function TRecordStream.LockRecord(ARecNo: integer): boolean;
  490. begin
  491.   while not LockFile(Handle, ARecNo*GetRecordSize, 0, GetRecordSize, 0) do
  492.   begin
  493.     Sleep(50);
  494.   end;
  495.   Result := True;
  496. end;
  497.  
  498. function TRecordStream.UnlockRecord(ARecNo: integer): boolean;
  499. begin
  500.   Result := UnlockFile(Handle, ARecNo*GetRecordSize, 0, GetRecordSize, 0);
  501. end;
  502.  
  503. end.
  504.