You've already forked lazarus-ccr
321 lines
11 KiB
ObjectPascal
321 lines
11 KiB
ObjectPascal
![]() |
{*********************************************************}
|
||
|
{* FlashFiler: Table access to streams *}
|
||
|
{*********************************************************}
|
||
|
|
||
|
(* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is TurboPower FlashFiler
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* TurboPower Software
|
||
|
*
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1996-2002
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** *)
|
||
|
|
||
|
{$I ffdefine.inc}
|
||
|
|
||
|
unit fftbstrm;
|
||
|
|
||
|
interface
|
||
|
|
||
|
uses
|
||
|
Windows,
|
||
|
SysUtils,
|
||
|
Classes,
|
||
|
ffconst,
|
||
|
ffllbase,
|
||
|
ffsrmgr,
|
||
|
ffllexcp,
|
||
|
ffsrbase,
|
||
|
ffsrlock,
|
||
|
fffile,
|
||
|
fftbbase;
|
||
|
|
||
|
procedure FFTblDeleteStream(aFI : PffFileInfo;
|
||
|
aTI : PffTransInfo;
|
||
|
aStreamNr : TffWord32);
|
||
|
{-Deletes a stream from the file}
|
||
|
|
||
|
procedure FFTblReadStream(aFI : PffFileInfo;
|
||
|
aTI : PffTransInfo;
|
||
|
aStreamNr : TffWord32;
|
||
|
aStream : TStream);
|
||
|
{-Reads a stream from the file}
|
||
|
{ NOTE: the data is read to the current position of the stream}
|
||
|
|
||
|
procedure FFTblWriteStream(aFI : PffFileInfo;
|
||
|
aTI : PffTransInfo;
|
||
|
var aStreamNr : TffWord32;
|
||
|
aStream : TStream;
|
||
|
aCreateNew: boolean;
|
||
|
aStreamID : Longint);
|
||
|
{-Writes a stream to the file; optionally creates it as a new one}
|
||
|
|
||
|
implementation
|
||
|
|
||
|
{===Helper routines==================================================}
|
||
|
function AddNewStreamBlock(FI : PffFileInfo;
|
||
|
TI : PffTransInfo;
|
||
|
FileHeader : PffBlockHeaderFile;
|
||
|
PrevStreamBlock: PffBlock;
|
||
|
var aReleaseMethod : TffReleaseMethod) : PffBlock;
|
||
|
var
|
||
|
StreamBlock : PffBlock;
|
||
|
StrmBlockHdr : PffBlockHeaderStream absolute StreamBlock;
|
||
|
PrevStrmBlockHdr : PffBlockHeaderStream absolute PrevStreamBlock;
|
||
|
begin
|
||
|
{Note: assumes that PrevStreamBlock has been marked dirty}
|
||
|
with FileHeader^ do begin
|
||
|
{get a new block}
|
||
|
StreamBlock := FFTblHlpGetNewBlock(FI, TI, aReleaseMethod);
|
||
|
FillChar(StreamBlock^[ffc_BlockHeaderSizeStream],
|
||
|
(bhfBlockSize - ffc_BlockHeaderSizeStream), 0);
|
||
|
{set up the stream block header information}
|
||
|
with StrmBlockHdr^ do begin
|
||
|
bhsSignature := ffc_SigStreamBlock;
|
||
|
bhsNextBlock := $FFFFFFFF;
|
||
|
bhsLSN := 0;
|
||
|
bhsNextStrmBlock := $FFFFFFFF;
|
||
|
bhsStreamType := 0;
|
||
|
bhsStreamLength := 0;
|
||
|
bhsOwningStream := 0;
|
||
|
end;
|
||
|
{chain this block to the previous stream block}
|
||
|
if Assigned(PrevStreamBlock) then begin
|
||
|
PrevStrmBlockHdr^.bhsNextStrmBlock := StrmBlockHdr^.bhsThisBlock;
|
||
|
end;
|
||
|
end;
|
||
|
Result := StreamBlock;
|
||
|
end;
|
||
|
{--------}
|
||
|
function ReadVfyStreamBlock(FI : PffFileInfo;
|
||
|
TI : PffTransInfo;
|
||
|
const aBlockNumber : TffWord32;
|
||
|
const aMarkDirty : boolean;
|
||
|
var aReleaseMethod : TffReleaseMethod) : PffBlock;
|
||
|
var
|
||
|
StreamBlock : PffBlock;
|
||
|
StrmBlockHdr: PffBlockHeaderStream absolute StreamBlock;
|
||
|
begin
|
||
|
with FI^ do begin
|
||
|
{verify the block number}
|
||
|
if (aBlockNumber <= 0) or (aBlockNumber >= fiUsedBlocks) then
|
||
|
FFRaiseException(EffServerException, ffStrResServer, fferrBadBlockNr,
|
||
|
[FI^.fiName^, aBlockNumber]);
|
||
|
{now get the stream block}
|
||
|
StreamBlock := FFBMGetBlock(FI, TI, aBlockNumber, aMarkDirty,
|
||
|
aReleaseMethod);
|
||
|
{verify that it's a stream block}
|
||
|
if (StrmBlockHdr^.bhsSignature <> ffc_SigStreamBlock) or
|
||
|
(StrmBlockHdr^.bhsThisBlock <> aBlockNumber) then
|
||
|
FFRaiseException(EffServerException, ffStrResServer, fferrBadStreamBlock,
|
||
|
[FI^.fiName^, aBlockNumber]);
|
||
|
end;
|
||
|
Result := StreamBlock;
|
||
|
end;
|
||
|
{--------}
|
||
|
procedure DeleteStreamPrim(FI : PffFileInfo;
|
||
|
TI : PffTransInfo;
|
||
|
FileHeader : PffBlockHeaderFile;
|
||
|
aStreamNr : Longint;
|
||
|
aKeep1stBlock: boolean);
|
||
|
var
|
||
|
StreamBlock : PffBlock;
|
||
|
StrmBlockHdr: PffBlockHeaderStream absolute StreamBlock;
|
||
|
NextBlock : TffWord32;
|
||
|
aReleaseMethod : TffReleaseMethod;
|
||
|
begin
|
||
|
|
||
|
{ Assumption: File header block is exclusively locked. }
|
||
|
|
||
|
{ Read & verify the 1st block for the stream. }
|
||
|
StreamBlock := ReadVfyStreamBlock(FI, TI, aStreamNr, true, aReleaseMethod);
|
||
|
|
||
|
try
|
||
|
{ Get the next block. }
|
||
|
NextBlock := StrmBlockHdr^.bhsNextStrmBlock;
|
||
|
|
||
|
{ If required, delete this block. }
|
||
|
if not aKeep1stBlock then
|
||
|
FFTblHlpDeleteBlock(FI, FileHeader, StreamBlock);
|
||
|
finally
|
||
|
aReleaseMethod(StreamBlock);
|
||
|
end;
|
||
|
|
||
|
{ Delete the succeeding blocks. }
|
||
|
while NextBlock <> ffc_W32NoValue do begin
|
||
|
{ Read & verify the next stream block. }
|
||
|
StreamBlock := ReadVfyStreamBlock(FI, TI, NextBlock, true, aReleaseMethod);
|
||
|
try
|
||
|
{ Get the next stream block. }
|
||
|
NextBlock := StrmBlockHdr^.bhsNextStrmBlock;
|
||
|
|
||
|
{ Add this block to the free blocks list. }
|
||
|
FFTblHlpDeleteBlock(FI, FileHeader, StreamBlock);
|
||
|
finally
|
||
|
aReleaseMethod(StreamBlock);
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
{====================================================================}
|
||
|
|
||
|
|
||
|
|
||
|
{===Stream routines==================================================}
|
||
|
procedure FFTblDeleteStream(aFI : PffFileInfo;
|
||
|
aTI : PffTransInfo;
|
||
|
aStreamNr : TffWord32);
|
||
|
var
|
||
|
FileHeader : PffBlockHeaderFile;
|
||
|
aReleaseMethod : TffReleaseMethod;
|
||
|
begin
|
||
|
{first get the file header, block 0}
|
||
|
FileHeader := PffBlockHeaderFile(FFBMGetBlock(aFI, aTI, 0, true,
|
||
|
aReleaseMethod));
|
||
|
try
|
||
|
{now delete the entire chain of blocks in the stream}
|
||
|
DeleteStreamPrim(aFi, aTI, FileHeader, aStreamNr, false);
|
||
|
finally
|
||
|
aReleaseMethod(PffBlock(FileHeader));
|
||
|
end;
|
||
|
end;
|
||
|
{--------}
|
||
|
procedure FFTblReadStream(aFI : PffFileInfo;
|
||
|
aTI : PffTransInfo;
|
||
|
aStreamNr : TffWord32;
|
||
|
aStream : TStream);
|
||
|
var
|
||
|
StreamBlock : PffBlock;
|
||
|
StrmBlockHdr : PffBlockHeaderStream absolute StreamBlock;
|
||
|
NextBlock : TffWord32;
|
||
|
BytesToGo : Longint;
|
||
|
BytesToCopy : Longint;
|
||
|
MaxDataInBlock: integer;
|
||
|
ThisBlock : TffWord32;
|
||
|
aStrmRelMethod : TffReleaseMethod;
|
||
|
begin
|
||
|
{ Calculate the maximum size of each stream block. }
|
||
|
MaxDataInBlock := aFI^.fiBlockSize - ffc_BlockHeaderSizeStream;
|
||
|
|
||
|
{ Read & verify the first block for the stream. }
|
||
|
ThisBlock := aStreamNr;
|
||
|
StreamBlock := ReadVfyStreamBlock(aFI, aTI, ThisBlock, false,
|
||
|
aStrmRelMethod);
|
||
|
try
|
||
|
BytesToGo := StrmBlockHdr^.bhsStreamLength;
|
||
|
while (BytesToGo > 0) do begin
|
||
|
{ Copy the data from the block to the stream. }
|
||
|
BytesToCopy := FFMinL(MaxDataInBlock, BytesToGo);
|
||
|
aStream.Write(StreamBlock^[ffc_BlockHeaderSizeStream], BytesToCopy);
|
||
|
dec(BytesToGo, BytesToCopy);
|
||
|
|
||
|
{ Calc the next stream block. }
|
||
|
NextBlock := StrmBlockHdr^.bhsNextStrmBlock;
|
||
|
|
||
|
if (BytesToGo <> 0) then begin
|
||
|
ThisBlock := NextBlock;
|
||
|
aStrmRelMethod(StreamBlock);
|
||
|
{ Read & verify the next block for the stream. }
|
||
|
StreamBlock := ReadVfyStreamBlock(aFI, aTI, ThisBlock, false,
|
||
|
aStrmRelMethod);
|
||
|
end;
|
||
|
end;
|
||
|
finally
|
||
|
aStrmRelMethod(StreamBlock);
|
||
|
end;
|
||
|
end;
|
||
|
{--------}
|
||
|
procedure FFTblWriteStream(aFI : PffFileInfo;
|
||
|
aTI : PffTransInfo;
|
||
|
var aStreamNr : TffWord32;
|
||
|
aStream : TStream;
|
||
|
aCreateNew: boolean;
|
||
|
aStreamID : Longint);
|
||
|
var
|
||
|
FileHeader : PffBlockHeaderFile;
|
||
|
StreamBlock : PffBlock;
|
||
|
StrmBlockHdr : PffBlockHeaderStream absolute StreamBlock;
|
||
|
BytesToGo : Longint;
|
||
|
BytesToCopy : Longint;
|
||
|
MaxDataInBlock : Integer;
|
||
|
aFHRelMethod,
|
||
|
aStrmRelMethod : TffReleaseMethod;
|
||
|
PrevStreamBlock : PffBlock; {!!.01}
|
||
|
PrevStrmBlockHdr : PffBlockHeaderStream; {!!.01}
|
||
|
PrevStrmRelMethod : TffReleaseMethod; {!!.01}
|
||
|
begin
|
||
|
|
||
|
{ Get the file header block. }
|
||
|
FileHeader := PffBlockHeaderFile(FFBMGetBlock(aFI, aTI, 0, true,
|
||
|
aFHRelMethod));
|
||
|
try
|
||
|
{ If we are rewriting the stream, delete all but the first block,
|
||
|
then read that first block}
|
||
|
if not aCreateNew then begin
|
||
|
DeleteStreamPrim(aFI, aTI, FileHeader, aStreamNr, true);
|
||
|
StreamBlock := ReadVfyStreamBlock(aFI, aTI, aStreamNr, true,
|
||
|
aStrmRelMethod);
|
||
|
end
|
||
|
{ Otherwise we are creating a new stream, so get a new block. }
|
||
|
else begin
|
||
|
StreamBlock := AddNewStreamBlock(aFI, aTI, FileHeader, nil,
|
||
|
aStrmRelMethod);
|
||
|
with StrmBlockHdr^ do begin
|
||
|
aStreamNr := bhsThisBlock;
|
||
|
bhsOwningStream := aStreamNr;
|
||
|
end;
|
||
|
end;
|
||
|
StrmBlockHdr^.bhsStreamType := aStreamID;
|
||
|
{ Set the stream length and therefore the number of bytes to copy. }
|
||
|
BytesToGo := aStream.Size;
|
||
|
StrmBlockHdr^.bhsStreamLength := BytesToGo;
|
||
|
MaxDataInBlock := FileHeader^.bhfBlockSize - ffc_BlockHeaderSizeStream;
|
||
|
|
||
|
{ Prepare the stream (position at start). }
|
||
|
aStream.Seek(0, soFromBeginning);
|
||
|
|
||
|
{ Copy the stream data to this block and other blocks, as required. }
|
||
|
BytesToCopy := FFMinL(MaxDataInBlock, BytesToGo);
|
||
|
aStream.Read(StreamBlock^[ffc_BlockHeaderSizeStream], BytesToCopy);
|
||
|
dec(BytesToGo, BytesToCopy);
|
||
|
while (BytesToGo > 0) do begin
|
||
|
PrevStreamBlock := StreamBlock; {!!.01}
|
||
|
PrevStrmBlockHdr := StrmBlockHdr; {!!.01}
|
||
|
PrevStrmRelMethod := aStrmRelMethod; {!!.01}
|
||
|
{aStrmRelMethod(StreamBlock);} {!!.01 deleted}
|
||
|
StreamBlock := AddNewStreamBlock(aFI, aTI, FileHeader, StreamBlock,
|
||
|
aStrmRelMethod);
|
||
|
PrevStrmBlockHdr^.bhsNextBlock := StrmBlockHdr^.bhsThisBlock; {!!.01}
|
||
|
PrevStrmRelMethod(PrevStreamBlock); {!!.01}
|
||
|
with StrmBlockHdr^ do begin
|
||
|
bhsOwningStream := aStreamNr;
|
||
|
bhsStreamType := aStreamID;
|
||
|
end;
|
||
|
BytesToCopy := FFMinL(MaxDataInBlock, BytesToGo);
|
||
|
aStream.Read(StreamBlock^[ffc_BlockHeaderSizeStream], BytesToCopy);
|
||
|
dec(BytesToGo, BytesToCopy);
|
||
|
end;
|
||
|
finally
|
||
|
aStrmRelMethod(StreamBlock);
|
||
|
aFHRelMethod(PffBlock(FileHeader));
|
||
|
end;
|
||
|
end;
|
||
|
{====================================================================}
|
||
|
|
||
|
end.
|