242 lines
6.9 KiB
ObjectPascal
242 lines
6.9 KiB
ObjectPascal
(* ***** 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 Abbrevia
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* TurboPower Software
|
|
*
|
|
* Portions created by the Initial Developer are Copyright (C) 1997-2002
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* ***** END LICENSE BLOCK ***** *)
|
|
|
|
{*********************************************************}
|
|
{* ABBREVIA: AbBitBkt.pas *}
|
|
{*********************************************************}
|
|
{* ABBREVIA: Bit bucket memory stream class *}
|
|
{*********************************************************}
|
|
|
|
unit AbBitBkt;
|
|
|
|
{$I AbDefine.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes,
|
|
AbUtils;
|
|
|
|
type
|
|
TAbBitBucketStream = class(TStream)
|
|
private
|
|
FBuffer : {$IFDEF UNICODE}PByte{$ELSE}PAnsiChar{$ENDIF};
|
|
FBufSize : longint;
|
|
FBufPosn : longint;
|
|
FPosn : Int64;
|
|
FSize : Int64;
|
|
FTail : longint;
|
|
protected
|
|
public
|
|
constructor Create(aBufSize : cardinal);
|
|
destructor Destroy; override;
|
|
function Read(var Buffer; Count : Longint) : Longint; override;
|
|
function Write(const Buffer; Count : Longint) : Longint; override;
|
|
function Seek(const Offset : Int64; Origin : TSeekOrigin) : Int64; override;
|
|
|
|
procedure ForceSize(aSize : Int64);
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Math, SysUtils, AbExcept;
|
|
|
|
{Notes: The buffer is a circular queue without a head pointer; FTail
|
|
is where data is next going to be written and it wraps
|
|
indescriminately. The buffer can never be empty--it is always
|
|
full (initially it is full of binary zeros.
|
|
The class is designed to act as a bit bucket for the test
|
|
feature of Abbrevia's zip code; it is not intended as a
|
|
complete class with many possible applications. It is designed
|
|
to be written to in a steady progression with some reading
|
|
back in the recently written stream (the buffer size details
|
|
how far back the Seek method will work). Seeking outside this
|
|
buffer will result in exceptions being generated.
|
|
For testing deflated files, the buffer size should be 32KB,
|
|
for imploded files, either 8KB or 4KB. The Create constructor
|
|
limits the buffer size to these values.}
|
|
|
|
{===TAbBitBucketStream===============================================}
|
|
constructor TAbBitBucketStream.Create(aBufSize : cardinal);
|
|
begin
|
|
inherited Create;
|
|
if (aBufSize <> 4096) and
|
|
(aBufSize <> 8192) and
|
|
(aBufSize <> 32768) then
|
|
FBufSize := 32768
|
|
else
|
|
FBufSize := aBufSize;
|
|
{add a 1KB leeway}
|
|
inc(FBufSize, 1024);
|
|
GetMem(FBuffer, FBufSize);
|
|
end;
|
|
{--------}
|
|
destructor TAbBitBucketStream.Destroy;
|
|
begin
|
|
if (FBuffer <> nil) then
|
|
FreeMem(FBuffer, FBufSize);
|
|
inherited Destroy;
|
|
end;
|
|
{--------}
|
|
procedure TAbBitBucketStream.ForceSize(aSize : Int64);
|
|
begin
|
|
FSize := aSize;
|
|
end;
|
|
{--------}
|
|
function TAbBitBucketStream.Read(var Buffer; Count : Longint) : Longint;
|
|
var
|
|
Chunk2Size : longint;
|
|
Chunk1Size : longint;
|
|
OutBuffer : PByte;
|
|
begin
|
|
OutBuffer := @Buffer;
|
|
{we cannot read more bytes than there is buffer}
|
|
if (Count > FBufSize) then
|
|
raise EAbBBSReadTooManyBytes.Create(Count);
|
|
{calculate the size of the chunks}
|
|
if (FBufPosn <= FTail) then begin
|
|
Chunk1Size := FTail - FBufPosn;
|
|
if (Chunk1Size > Count) then
|
|
Chunk1Size := Count;
|
|
Chunk2Size := 0;
|
|
end
|
|
else begin
|
|
Chunk1Size := FBufSize - FBufPosn;
|
|
if (Chunk1Size > Count) then begin
|
|
Chunk1Size := Count;
|
|
Chunk2Size := 0;
|
|
end
|
|
else begin
|
|
Chunk2Size := FTail;
|
|
if (Chunk2Size > (Count - Chunk1Size)) then
|
|
Chunk2Size := Count - Chunk1Size;
|
|
end
|
|
end;
|
|
{we cannot read more bytes than there are available}
|
|
if (Count > (Chunk1Size + Chunk2Size)) then
|
|
raise EAbBBSReadTooManyBytes.Create(Count);
|
|
{perform the read}
|
|
if (Chunk1Size > 0) then begin
|
|
Move(FBuffer[FBufPosn], OutBuffer^, Chunk1Size);
|
|
inc(FBufPosn, Chunk1Size);
|
|
inc(FPosn, Chunk1Size);
|
|
end;
|
|
if (Chunk2Size > 0) then begin
|
|
{we've wrapped}
|
|
Move(FBuffer[0], PByte(PtrInt(OutBuffer) + PtrInt(Chunk1Size))^, Chunk2Size);
|
|
FBufPosn := Chunk2Size;
|
|
inc(FPosn, Chunk2Size);
|
|
end;
|
|
Result := Count;
|
|
end;
|
|
{--------}
|
|
function TAbBitBucketStream.Write(const Buffer; Count : Longint) : Longint;
|
|
var
|
|
Chunk2Size : longint;
|
|
Chunk1Size : longint;
|
|
InBuffer : PByte;
|
|
Overage : longint;
|
|
begin
|
|
Result := Count;
|
|
InBuffer := @Buffer;
|
|
{we cannot write more bytes than there is buffer}
|
|
while Count > FBufSize do begin
|
|
Overage := Min(FBufSize, Count - FBufSize);
|
|
Write(InBuffer^, Overage);
|
|
Inc(PtrInt(InBuffer), Overage);
|
|
Dec(Count, Overage);
|
|
end;
|
|
{calculate the size of the chunks}
|
|
Chunk1Size := FBufSize - FTail;
|
|
if (Chunk1Size > Count) then begin
|
|
Chunk1Size := Count;
|
|
Chunk2Size := 0;
|
|
end
|
|
else begin
|
|
Chunk2Size := Count - Chunk1Size;
|
|
end;
|
|
{write the first chunk}
|
|
if (Chunk1Size > 0) then begin
|
|
Move(InBuffer^, FBuffer[FTail], Chunk1Size);
|
|
inc(FTail, Chunk1Size);
|
|
end;
|
|
{if the second chunk size is not zero, write the second chunk; note
|
|
that we have wrapped}
|
|
if (Chunk2Size > 0) then begin
|
|
Move(PByte(PtrInt(InBuffer) + PtrInt(Chunk1Size))^, FBuffer[0], Chunk2Size);
|
|
FTail := Chunk2Size;
|
|
end;
|
|
{the stream size and position have changed}
|
|
inc(FSize, Count);
|
|
FPosn := FSize;
|
|
FBufPosn := FTail;
|
|
end;
|
|
{--------}
|
|
function TAbBitBucketStream.Seek(const Offset : Int64; Origin : TSeekOrigin): Int64;
|
|
var
|
|
Posn : Int64;
|
|
BytesBack : longint;
|
|
begin
|
|
{calculate the new position}
|
|
case Origin of
|
|
soBeginning :
|
|
Posn := Offset;
|
|
soCurrent :
|
|
Posn := FPosn + Offset;
|
|
soEnd :
|
|
if (Offset = 0) then begin
|
|
{special case: position at end of stream}
|
|
FBufPosn := FTail;
|
|
FPosn := FSize;
|
|
Result := FSize;
|
|
Exit;
|
|
end
|
|
else begin
|
|
Posn := FSize + Offset;
|
|
end;
|
|
else
|
|
raise EAbBBSInvalidOrigin.Create;
|
|
end;
|
|
{calculate whether the new position is within the buffer; if not,
|
|
raise exception}
|
|
if (Posn > FSize) or
|
|
(Posn <= (FSize - FBufSize)) then
|
|
raise EAbBBSSeekOutsideBuffer.Create;
|
|
{set the internal fields for the new position}
|
|
FPosn := Posn;
|
|
BytesBack := FSize - Posn;
|
|
if (BytesBack <= FTail) then
|
|
FBufPosn := FTail - BytesBack
|
|
else
|
|
FBufPosn := longint(FTail) + FBufSize - BytesBack;
|
|
{return the new position}
|
|
Result := Posn;
|
|
end;
|
|
{====================================================================}
|
|
|
|
|
|
end.
|