{**************************************************************************************************} { } { Project JEDI Code Library (JCL) } { } { 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 JclStreams.pas. } { } { The Initial Developer of the Original Code is Robert Marquardt. Portions created by } { Robert Marquardt are Copyright (C) Robert Marquardt (robert_marquardt att gmx dott de) } { All rights reserved. } { } { Contributors: } { Florent Ouchet (outchy) } { Heinz Zastrau } { Andreas Schmidt } { } {**************************************************************************************************} { } { Stream-related functions and classes } { } {**************************************************************************************************} { } { Last modified: $Date:: 2009-01-18 19:59:35 +0100 (dim., 18 janv. 2009) $ } { Revision: $Rev:: 2600 $ } { Author: $Author:: outchy $ } { } {**************************************************************************************************} unit JclStreams; {$I jcl.inc} interface uses {$IFDEF UNITVERSIONING} JclUnitVersioning, {$ENDIF UNITVERSIONING} {$IFDEF MSWINDOWS} Windows, {$ENDIF MSWINDOWS} {$IFDEF LINUX} Libc, {$ENDIF LINUX} SysUtils, Classes, {$IFDEF HAS_UNIT_CONTNRS} Contnrs, {$ENDIF HAS_UNIT_CONTNRS} JclBase, JclStringConversions; type {$IFDEF COMPILER5} TSeekOrigin = (soBeginning, soCurrent, soEnd); {$ENDIF COMPILER5} EJclStreamError = class(EJclError); // abstraction layer to support Delphi 5 and C++Builder 5 streams // 64 bit version of overloaded functions are introduced TJclStream = class(TStream) protected {$IFNDEF CLR} procedure SetSize(NewSize: Longint); overload; override; {$ENDIF ~CLR} procedure SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); {$IFDEF COMPILER5} reintroduce; overload; virtual; {$ELSE} overload; override; {$ENDIF} public {$IFNDEF CLR} function Seek(Offset: Longint; Origin: Word): Longint; overload; override; {$ENDIF ~CLR} function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; {$IFDEF COMPILER5} reintroduce; overload; virtual; {$ELSE} overload; override; {$ENDIF} end; //=== VCL stream replacements === {$IFNDEF CLR} TJclHandleStream = class(TJclStream) private FHandle: THandle; protected procedure SetSize(const NewSize: Int64); override; public constructor Create(AHandle: THandle); 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; property Handle: THandle read FHandle; end; TJclFileStream = class(TJclHandleStream) public constructor Create(const FileName: TFileName; Mode: Word; Rights: Cardinal = 0); destructor Destroy; override; end; {$ENDIF ~CLR} { TJclCustomMemoryStream = class(TJclStream) end; TJclMemoryStream = class(TJclCustomMemoryStream) end; TJclStringStream = class(TJclStream) end; TJclResourceStream = class(TJclCustomMemoryStream) end; } //=== new stream ideas === TJclEmptyStream = class(TJclStream) protected procedure SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); override; public {$IFDEF CLR} function Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; override; function Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; override; {$ELSE ~CLR} function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; {$ENDIF ~CLR} function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; end; TJclNullStream = class(TJclStream) private FPosition: Int64; FSize: Int64; protected procedure SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); override; public {$IFDEF CLR} function Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; override; function Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; override; {$ELSE ~CLR} function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; {$ENDIF ~CLR} function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; end; TJclRandomStream = class(TJclNullStream) {$IFDEF CLR} private FRandomGenerator: System.Random; {$ENDIF CLR} protected function GetRandSeed: Longint; virtual; procedure SetRandSeed(Seed: Longint); virtual; public {$IFDEF CLR} constructor Create; destructor Destroy; override; {$ENDIF CLR} function RandomData: Byte; virtual; procedure Randomize; dynamic; {$IFDEF CLR} function Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; override; {$ELSE ~CLR} function Read(var Buffer; Count: Longint): Longint; override; {$ENDIF ~CLR} property RandSeed: Longint read GetRandSeed write SetRandSeed; end; TJclMultiplexStream = class(TJclStream) private FStreams: TList; FReadStreamIndex: Integer; function GetStream(Index: Integer): TStream; function GetCount: Integer; procedure SetStream(Index: Integer; const Value: TStream); function GetReadStream: TStream; procedure SetReadStream(const Value: TStream); procedure SetReadStreamIndex(const Value: Integer); protected procedure SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); override; public constructor Create; destructor Destroy; override; {$IFDEF CLR} function Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; override; function Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; override; {$ELSE ~CLR} function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; {$ENDIF ~CLR} function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; function Add(NewStream: TStream): Integer; procedure Clear; function Remove(AStream: TStream): Integer; procedure Delete(const Index: Integer); property Streams[Index: Integer]: TStream read GetStream write SetStream; property ReadStreamIndex: Integer read FReadStreamIndex write SetReadStreamIndex; property ReadStream: TStream read GetReadStream write SetReadStream; property Count: Integer read GetCount; end; TJclStreamDecorator = class(TJclStream) private FAfterStreamChange: TNotifyEvent; FBeforeStreamChange: TNotifyEvent; FOwnsStream: Boolean; FStream: TStream; procedure SetStream(Value: TStream); protected procedure DoAfterStreamChange; virtual; procedure DoBeforeStreamChange; virtual; procedure SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); override; public constructor Create(AStream: TStream; AOwnsStream: Boolean = False); destructor Destroy; override; {$IFDEF CLR} function Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; override; function Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; override; {$ELSE ~CLR} function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; {$ENDIF ~CLR} function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; property AfterStreamChange: TNotifyEvent read FAfterStreamChange write FAfterStreamChange; property BeforeStreamChange: TNotifyEvent read FBeforeStreamChange write FBeforeStreamChange; property OwnsStream: Boolean read FOwnsStream write FOwnsStream; property Stream: TStream read FStream write SetStream; end; TJclBufferedStream = class(TJclStreamDecorator) protected FBuffer: array of Byte; FBufferCurrentSize: Longint; FBufferMaxModifiedPos: Longint; FBufferSize: Longint; FBufferStart: Int64; // position of the first byte of the buffer in stream FPosition: Int64; // current position in stream function BufferHit: Boolean; function GetCalcedSize: Int64; virtual; function LoadBuffer: Boolean; virtual; {$IFDEF CLR} function ReadFromBuffer(var Buffer: array of Byte; Count, Start: Longint): Longint; function WriteToBuffer(const Buffer: array of Byte; Count, Start: Longint): Longint; {$ELSE ~CLR} function ReadFromBuffer(var Buffer; Count, Start: Longint): Longint; function WriteToBuffer(const Buffer; Count, Start: Longint): Longint; {$ENDIF ~CLR} protected procedure DoAfterStreamChange; override; procedure DoBeforeStreamChange; override; procedure SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); override; public constructor Create(AStream: TStream; AOwnsStream: Boolean = False); destructor Destroy; override; procedure Flush; virtual; {$IFDEF CLR} function Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; override; function Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; override; {$ELSE ~CLR} function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; {$ENDIF ~CLR} function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; property BufferSize: Longint read FBufferSize write FBufferSize; end; TStreamNotifyEvent = procedure(Sender: TObject; Position: Int64; Size: Int64) of object; TJclEventStream = class(TJclStreamDecorator) private FNotification: TStreamNotifyEvent; procedure DoNotification; protected procedure DoBeforeStreamChange; override; procedure DoAfterStreamChange; override; procedure SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); override; public constructor Create(AStream: TStream; ANotification: TStreamNotifyEvent = nil; AOwnsStream: Boolean = False); {$IFDEF CLR} function Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; override; function Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; override; {$ELSE ~CLR} function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; {$ENDIF ~CLR} function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; property OnNotification: TStreamNotifyEvent read FNotification write FNotification; end; TJclEasyStream = class(TJclStreamDecorator) public function IsEqual(Stream: TStream): Boolean; function ReadBoolean: Boolean; function ReadChar: Char; function ReadAnsiChar: AnsiChar; function ReadWideChar: WideChar; function ReadByte: Byte; {$IFNDEF CLR} function ReadCurrency: Currency; function ReadDateTime: TDateTime; function ReadExtended: Extended; {$ENDIF ~CLR} function ReadDouble: Double; function ReadInt64: Int64; function ReadInteger: Integer; function ReadCString: string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} function ReadCAnsiString: AnsiString; function ReadCWideString: WideString; function ReadShortString: string; function ReadSingle: Single; function ReadSizedString: string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} function ReadSizedAnsiString: AnsiString; function ReadSizedWideString: WideString; procedure WriteBoolean(Value: Boolean); procedure WriteChar(Value: Char); procedure WriteAnsiChar(Value: AnsiChar); procedure WriteWideChar(Value: WideChar); procedure WriteByte(Value: Byte); {$IFNDEF CLR} procedure WriteCurrency(const Value: Currency); procedure WriteDateTime(const Value: TDateTime); procedure WriteExtended(const Value: Extended); {$ENDIF ~CLR} procedure WriteDouble(const Value: Double); procedure WriteInt64(Value: Int64); overload; procedure WriteInteger(Value: Integer); overload; procedure WriteCString(const Value: string); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} procedure WriteCAnsiString(const Value: AnsiString); procedure WriteCWideString(const Value: WideString); {$IFDEF KEEP_DEPRECATED} procedure WriteStringDelimitedByNull(const Value: string); {$ENDIF KEEP_DEPRECATED} procedure WriteShortString(const Value: ShortString); procedure WriteSingle(const Value: Single); procedure WriteSizedString(const Value: string); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} procedure WriteSizedAnsiString(const Value: AnsiString); procedure WriteSizedWideString(const Value: WideString); end; TJclScopedStream = class(TJclStream) private FParentStream: TStream; FStartPos: Int64; FCurrentPos: Int64; FMaxSize: Int64; protected procedure SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); override; public // scopedstream starting at the current position of the ParentStream // if MaxSize is positive or null, read and write operations cannot overrun this size or the ParentStream limitation // if MaxSize is negative, read and write operations are unlimited (up to the ParentStream limitation) constructor Create(AParentStream: TStream; AMaxSize: Int64 = -1); {$IFDEF CLR} function Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; override; function Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; override; {$ELSE ~CLR} function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; {$ENDIF ~CLR} function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; property ParentStream: TStream read FParentStream; property StartPos: Int64 read FStartPos; property MaxSize: Int64 read FMaxSize write FMaxSize; end; TJclStreamSeekEvent = function(Sender: TObject; const Offset: Int64; Origin: TSeekOrigin): Int64 of object; TJclStreamReadEvent = function(Sender: TObject; var Buffer; {$IFDEF CLR}Offset,{$ENDIF CLR} Count: Longint): Longint of object; TJclStreamWriteEvent = function(Sender: TObject; const Buffer; {$IFDEF CLR}Offset,{$ENDIF CLR}Count: Longint): Longint of object; TJclStreamSizeEvent = procedure(Sender: TObject; {$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64) of object; TJclDelegatedStream = class(TJclStream) private FOnSeek: TJclStreamSeekEvent; FOnRead: TJclStreamReadEvent; FOnWrite: TJclStreamWriteEvent; FOnSize: TJclStreamSizeEvent; protected procedure SetSize({$IFNDEF CLR}const{$ENDIF CLR} NewSize: Int64); override; public function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; {$IFDEF CLR} function Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; override; function Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; override; {$ELSE ~CLR} function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; {$ENDIF ~CLR} property OnSeek: TJclStreamSeekEvent read FOnSeek write FOnSeek; property OnRead: TJclStreamReadEvent read FOnRead write FOnRead; property OnWrite: TJclStreamWriteEvent read FOnWrite write FOnWrite; property OnSize: TJclStreamSizeEvent read FOnSize write FOnSize; end; // ancestor classes for streams with checksums and encrypted streams // data are stored in sectors: each BufferSize-d buffer is followed by FBlockOverHeader bytes // containing the checksum. In case of an encrypted stream, there is no byte // but sector is encrypted // reusing some code from TJclBufferedStream TJclSectoredStream = class(TJclBufferedStream) protected FSectorOverHead: Longint; function FlatToSectored(const Position: Int64): Int64; function SectoredToFlat(const Position: Int64): Int64; function GetCalcedSize: Int64; override; function LoadBuffer: Boolean; override; procedure DoAfterStreamChange; override; procedure AfterBlockRead; virtual; // override to check protection procedure BeforeBlockWrite; virtual; // override to compute protection procedure SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); override; public constructor Create(AStorageStream: TStream; AOwnsStream: Boolean = False; ASectorOverHead: Longint = 0); procedure Flush; override; end; TJclCRC16Stream = class(TJclSectoredStream) protected procedure AfterBlockRead; override; procedure BeforeBlockWrite; override; public constructor Create(AStorageStream: TStream; AOwnsStream: Boolean = False); end; TJclCRC32Stream = class(TJclSectoredStream) protected procedure AfterBlockRead; override; procedure BeforeBlockWrite; override; public constructor Create(AStorageStream: TStream; AOwnsStream: Boolean = False); end; {$IFDEF CLR} {$IFDEF BDS5_UP} {$DEFINE SIZE64} {$ENDIF ~BDS5_UP} {$ELSE ~CLR} {$IFDEF COMPILER7_UP} {$DEFINE SIZE64} {$ENDIF ~COMPILER7_UP} {$ENDIF ~CLR} TJclSplitStream = class(TJclStream) private FVolume: TStream; FVolumeIndex: Integer; FVolumeMaxSize: Int64; FPosition: Int64; FVolumePosition: Int64; protected function GetVolume(Index: Integer): TStream; virtual; abstract; function GetVolumeMaxSize(Index: Integer): Int64; virtual; abstract; function GetSize: Int64; {$IFDEF SIZE64}override;{$ENDIF SIZE64} procedure SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); override; procedure InternalLoadVolume(Index: Integer); public constructor Create; function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; {$IFDEF CLR} function Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; override; function Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; override; {$ELSE ~CLR} function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; {$ENDIF ~CLR} end; TJclVolumeEvent = function(Index: Integer): TStream of object; TJclVolumeMaxSizeEvent = function(Index: Integer): Int64 of object; TJclDynamicSplitStream = class(TJclSplitStream) private FOnVolume: TJclVolumeEvent; FOnVolumeMaxSize: TJclVolumeMaxSizeEvent; protected function GetVolume(Index: Integer): TStream; override; function GetVolumeMaxSize(Index: Integer): Int64; override; public property OnVolume: TJclVolumeEvent read FOnVolume write FOnVolume; property OnVolumeMaxSize: TJclVolumeMaxSizeEvent read FOnVolumeMaxSize write FOnVolumeMaxSize; end; TJclSplitVolume = class public MaxSize: Int64; Stream: TStream; OwnStream: Boolean; end; TJclStaticSplitStream = class(TJclSplitStream) private FVolumes: TObjectList; function GetVolumeCount: Integer; protected function GetVolume(Index: Integer): TStream; override; function GetVolumeMaxSize(Index: Integer): Int64; override; public constructor Create; destructor Destroy; override; function AddVolume(AStream: TStream; AMaxSize: Int64 = 0; AOwnStream: Boolean = False): Integer; property VolumeCount: Integer read GetVolumeCount; property Volumes[Index: Integer]: TStream read GetVolume; property VolumeMaxSizes[Index: Integer]: Int64 read GetVolumeMaxSize; end; TJclStringStream = class(TJclBufferedStream) protected FBOM: array of Byte; FCharacterReader: TJclStreamGetNextCharFunc; FCharacterWriter: TJclStreamSetNextCharFunc; FPeekPosition: Int64; function GetCalcedSize: Int64; override; public constructor Create(AStream: TStream; AOwnsStream: Boolean = False); virtual; function ReadString(var Buffer: string; Start, Count: Longint): Longint; function ReadAnsiString(var Buffer: AnsiString; Start, Count: Longint): Longint; function ReadWideString(var Buffer: WideString; Start, Count: Longint): Longint; function WriteString(const Buffer: string; Start, Count: Longint): Longint; function WriteAnsiString(const Buffer: AnsiString; Start, Count: Longint): Longint; function WriteWideString(const Buffer: WideString; Start, Count: Longint): Longint; function PeekChar(var Buffer: Char): Boolean; function PeekAnsiChar(var Buffer: AnsiChar): Boolean; function PeekWideChar(var Buffer: WideChar): Boolean; function ReadChar(var Buffer: Char): Boolean; function ReadAnsiChar(var Buffer: AnsiChar): Boolean; function ReadWideChar(var Buffer: WideChar): Boolean; function WriteChar(Value: Char): Boolean; function WriteAnsiChar(Value: AnsiChar): Boolean; function WriteWideChar(Value: WideChar): Boolean; function SkipBOM: LongInt; virtual; function WriteBOM: Longint; virtual; end; TJclStringStreamClass = class of TJclStringStream; TJclAnsiStream = class(TJclStringStream) public constructor Create(AStream: TStream; AOwnsStream: Boolean = False); override; end; TJclUTF8Stream = class(TJclStringStream) public constructor Create(AStream: TStream; AOwnsStream: Boolean = False); override; end; TJclUTF16Stream = class(TJclStringStream) public constructor Create(AStream: TStream; AOwnsStream: Boolean = False); override; end; TJclStringEncoding = (seAnsi, seUTF8, seUTF16, seAuto); TJclAutoStream = class(TJclStringStream) private FEncoding: TJclStringEncoding; public constructor Create(AStream: TStream; AOwnsStream: Boolean = False); override; function SkipBOM: LongInt; override; property Encoding: TJclStringEncoding read FEncoding; end; // call TStream.Seek(Int64,TSeekOrigin) if present (TJclStream or COMPILER6_UP) // otherwize call TStream.Seek(LongInt,Word) with range checking function StreamSeek(Stream: TStream; const Offset: Int64; const Origin: TSeekOrigin): Int64; // buffered copy of all available bytes from Source to Dest // returns the number of bytes that were copied function StreamCopy(Source: TStream; Dest: TStream; BufferSize: Longint = 4096): Int64; // buffered copy of all available characters from Source to Dest // retuns the number of characters (in specified encoding) that were copied function StringStreamCopy(Source, Dest: TJclStringStream; BufferLength: Longint = 4096): Int64; function AnsiStringStreamCopy(Source, Dest: TJclStringStream; BufferLength: Longint = 4096): Int64; function WideStringStreamCopy(Source, Dest: TJclStringStream; BufferLength: Longint = 4096): Int64; // compares 2 streams for differencies function CompareStreams(A, B : TStream; BufferSize: Longint = 4096): Boolean; // compares 2 files for differencies (calling CompareStreams) function CompareFiles(const FileA, FileB: TFileName; BufferSize: Longint = 4096): Boolean; {$IFDEF UNITVERSIONING} const UnitVersioning: TUnitVersionInfo = ( RCSfile: '$URL: https://jcl.svn.sourceforge.net/svnroot/jcl/tags/JCL-1.104-Build3248/jcl/source/common/JclStreams.pas $'; Revision: '$Revision: 2600 $'; Date: '$Date: 2009-01-18 19:59:35 +0100 (dim., 18 janv. 2009) $'; LogPath: 'JCL\source\common' ); {$ENDIF UNITVERSIONING} implementation uses {$IFDEF CLR} System.Text, {$ENDIF CLR} JclResources, JclMath; {$IFDEF KYLIX} function __open(PathName: PChar; Flags: Integer; Mode: Integer): Integer; cdecl; external 'libc.so.6' name 'open'; {$ENDIF KYLIX} function StreamSeek(Stream: TStream; const Offset: Int64; const Origin: TSeekOrigin): Int64; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF SUPPORTS_INLINE} begin if Assigned(Stream) then begin {$IFDEF COMPILER5} if Stream is TJclStream then Result := TJclStream(Stream).Seek(Offset, Origin) else if (Offset <= MaxLongint) or (Offset > -MaxLongint) then Result := Stream.Seek(Longint(Offset), Ord(Origin)) else Result := -1; {$ELSE} Result := Stream.Seek(Offset, Origin); {$ENDIF COMPILER5} end else Result := -1; end; function StreamCopy(Source: TStream; Dest: TStream; BufferSize: Longint): Int64; var Buffer: array of Byte; ByteCount: Longint; begin Result := 0; SetLength(Buffer, BufferSize); repeat {$IFDEF CLR} ByteCount := Source.Read(Buffer, 0, BufferSize); {$ELSE ~CLR} ByteCount := Source.Read(Buffer[0], BufferSize); {$ENDIF ~CLR} Result := Result + ByteCount; {$IFDEF CLR} Dest.WriteBuffer(Buffer, ByteCount); {$ELSE ~CLR} Dest.WriteBuffer(Buffer[0], ByteCount); {$ENDIF ~CLR} until ByteCount < BufferSize; end; function StringStreamCopy(Source, Dest: TJclStringStream; BufferLength: Longint = 4096): Int64; var Buffer: string; CharCount: Longint; begin Result := 0; SetLength(Buffer, BufferLength); repeat CharCount := Source.ReadString(Buffer, 1, BufferLength); Result := Result + CharCount; CharCount := Dest.WriteString(Buffer, 1, CharCount); until CharCount = 0; end; function AnsiStringStreamCopy(Source, Dest: TJclStringStream; BufferLength: Longint = 4096): Int64; var Buffer: AnsiString; CharCount: Longint; begin Result := 0; SetLength(Buffer, BufferLength); repeat CharCount := Source.ReadAnsiString(Buffer, 1, BufferLength); Result := Result + CharCount; CharCount := Dest.WriteAnsiString(Buffer, 1, CharCount); until CharCount = 0; end; function WideStringStreamCopy(Source, Dest: TJclStringStream; BufferLength: Longint = 4096): Int64; var Buffer: WideString; CharCount: Longint; begin Result := 0; SetLength(Buffer, BufferLength); repeat CharCount := Source.ReadWideString(Buffer, 1, BufferLength); Result := Result + CharCount; CharCount := Dest.WriteWideString(Buffer, 1, CharCount); until CharCount = 0; end; function CompareStreams(A, B : TStream; BufferSize: Longint = 4096): Boolean; var BufferA, BufferB: array of Byte; ByteCountA, ByteCountB: Longint; {$IFDEF CLR} Index: Longint; {$ENDIF CLR} begin SetLength(BufferA, BufferSize); try SetLength(BufferB, BufferSize); try repeat {$IFDEF CLR} ByteCountA := A.Read(BufferA, 0, BufferSize); ByteCountB := A.Read(BufferB, 0, BufferSize); {$ELSE ~CLR} ByteCountA := A.Read(BufferA[0], BufferSize); ByteCountB := B.Read(BufferB[0], BufferSize); {$ENDIF ~CLR} Result := (ByteCountA = ByteCountB); {$IFDEF CLR} if Result then for Index := 0 to ByteCountA - 1 do if BufferA[Index] <> BufferB[Index] then begin Result := False; Break; end; {$ELSE CLR} Result := Result and CompareMem(BufferA, BufferB, ByteCountA); {$ENDIF ~CLR} until (ByteCountA <> BufferSize) or (ByteCountB <> BufferSize) or not Result; finally SetLength(BufferB, 0); end; finally SetLength(BufferA, 0); end; end; function CompareFiles(const FileA, FileB: TFileName; BufferSize: Longint = 4096): Boolean; var A, B: TStream; begin A := TFileStream.Create(FileA, fmOpenRead or fmShareDenyWrite); try B := TFileStream.Create(FileB, fmOpenRead or fmShareDenyWrite); try Result := CompareStreams(A, B, BufferSize); finally B.Free; end; finally A.Free; end; end; //=== { TJclStream } ========================================================= {$IFNDEF CLR} function TJclStream.Seek(Offset: Longint; Origin: Word): Longint; var Result64: Int64; begin case Origin of soFromBeginning: Result64 := Seek(Int64(Offset), soBeginning); soFromCurrent: Result64 := Seek(Int64(Offset), soCurrent); soFromEnd: Result64 := Seek(Int64(Offset), soEnd); else Result64 := -1; end; if (Result64 < 0) or (Result64 > High(Longint)) then Result64 := -1; Result := Result64; end; {$ENDIF ~CLR} function TJclStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; begin // override to customize Result := -1; end; {$IFNDEF CLR} procedure TJclStream.SetSize(NewSize: Longint); begin SetSize(Int64(NewSize)); end; {$ENDIF ~CLR} procedure TJclStream.SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); begin // override to customize end; {$IFNDEF CLR} //=== { TJclHandleStream } =================================================== constructor TJclHandleStream.Create(AHandle: THandle); begin inherited Create; FHandle := AHandle; end; function TJclHandleStream.Read(var Buffer; Count: Longint): Longint; begin {$IFDEF MSWINDOWS} if (Count <= 0) or not ReadFile(Handle, Buffer, DWORD(Count), DWORD(Result), nil) then Result := 0; {$ENDIF MSWINDOWS} {$IFDEF LINUX} Result := __read(Handle, Buffer, Count); {$ENDIF LINUX} end; function TJclHandleStream.Write(const Buffer; Count: Longint): Longint; begin {$IFDEF MSWINDOWS} if (Count <= 0) or not WriteFile(Handle, Buffer, DWORD(Count), DWORD(Result), nil) then Result := 0; {$ENDIF MSWINDOWS} {$IFDEF LINUX} Result := __write(Handle, Buffer, Count); {$ENDIF LINUX} end; {$IFDEF MSWINDOWS} function TJclHandleStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; const INVALID_SET_FILE_POINTER = -1; type TLarge = record case Boolean of False: (OffsetLo: Longint; OffsetHi: Longint); True: (Offset64: Int64); end; var Offs: TLarge; begin Offs.Offset64 := Offset; Offs.OffsetLo := SetFilePointer(Handle, Offs.OffsetLo, @Offs.OffsetHi, Ord(Origin)); if (Offs.OffsetLo = INVALID_SET_FILE_POINTER) and (GetLastError <> NO_ERROR) then Result := -1 else Result := Offs.Offset64; end; {$ENDIF MSWINDOWS} {$IFDEF LINUX} function TJclHandleStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; const SeekOrigins: array [TSeekOrigin] of Cardinal = ( SEEK_SET {soBeginning}, SEEK_CUR {soCurrent}, SEEK_END {soEnd} ); begin {$IFDEF KYLIX} Result := __lseek(Handle, Offset, SeekOrigins[Origin]); {$ELSE} Result := lseek(Handle, Offset, SeekOrigins[Origin]); {$ENDIF} end; {$ENDIF LINUX} procedure TJclHandleStream.SetSize(const NewSize: Int64); begin Seek(NewSize, soBeginning); {$IFDEF MSWINDOWS} if not SetEndOfFile(Handle) then RaiseLastOSError; {$ENDIF MSWINDOWS} {$IFDEF LINUX} if ftruncate(Handle, Position) = -1 then raise EJclStreamError.CreateRes(@RsStreamsSetSizeError); {$ENDIF LINUX} end; //=== { TJclFileStream } ===================================================== constructor TJclFileStream.Create(const FileName: TFileName; Mode: Word; Rights: Cardinal); var H: THandle; {$IFDEF LINUX} const INVALID_HANDLE_VALUE = -1; {$ENDIF LINUX} begin if Mode = fmCreate then begin {$IFDEF LINUX} {$IFDEF KYLIX} H := __open(PChar(FileName), O_CREAT or O_RDWR, FileAccessRights); {$ELSE ~KYLIX} H := open(PChar(FileName), O_CREAT or O_RDWR, $666); {$ENDIF} {$ELSE ~LINUX} H := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); {$ENDIF ~LINUX} inherited Create(H); if Handle = INVALID_HANDLE_VALUE then {$IFDEF CLR} raise EJclStreamError.CreateFmt(RsStreamsCreateError, [FileName]); {$ELSE} raise EJclStreamError.CreateResFmt(@RsStreamsCreateError, [FileName]); {$ENDIF CLR} end else begin H := THandle(FileOpen(FileName, Mode)); inherited Create(H); if Handle = INVALID_HANDLE_VALUE then {$IFDEF CLR} raise EJclStreamError.CreateFmt(RsStreamsOpenError, [FileName]); {$ELSE} raise EJclStreamError.CreateResFmt(@RsStreamsOpenError, [FileName]); {$ENDIF CLR} end; end; destructor TJclFileStream.Destroy; begin {$IFDEF MSWINDOWS} if Handle <> INVALID_HANDLE_VALUE then CloseHandle(Handle); {$ENDIF MSWINDOWS} {$IFDEF LINUX} __close(Handle); {$ENDIF LINUX} inherited Destroy; end; {$ENDIF ~CLR} //=== { TJclEmptyStream } ==================================================== // a stream which stays empty no matter what you do // so it is a Unix /dev/null equivalent procedure TJclEmptyStream.SetSize({$IFNDEF CLR}const{$ENDIF CLR} NewSize: Int64); begin // nothing end; {$IFDEF CLR} function TJclEmptyStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclEmptyStream.Read(var Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin // you cannot read anything Result := 0; end; {$IFDEF CLR} function TJclEmptyStream.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclEmptyStream.Write(const Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin // you cannot write anything Result := 0; end; function TJclEmptyStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; begin if Offset <> 0 then // seeking to anywhere except the position 0 is an error Result := -1 else Result := 0; end; //=== { TJclNullStream } ===================================================== // a stream which only keeps position and size, but no data // so it is a Unix /dev/zero equivalent (?) procedure TJclNullStream.SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); begin if NewSize > 0 then FSize := NewSize else FSize := 0; if FPosition > FSize then FPosition := FSize; end; {$IFDEF CLR} function TJclNullStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; var Index: Longint; {$ELSE ~CLR} function TJclNullStream.Read(var Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin if Count < 0 then Count := 0; // FPosition > FSize is possible! if FSize - FPosition < Count then Count := FSize - FPosition; // does not read if beyond EOF if Count > 0 then begin {$IFDEF CLR} for Index := Offset to Offset + Count - 1 do Buffer[Index] := 0; {$ELSE ~CLR} FillChar(Buffer, Count, 0); {$ENDIF ~CLR} FPosition := FPosition + Count; end; Result := Count; end; {$IFDEF CLR} function TJclNullStream.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclNullStream.Write(const Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin if Count < 0 then Count := 0; FPosition := FPosition + Count; // writing when FPosition > FSize is possible! if FPosition > FSize then FSize := FPosition; Result := Count; end; function TJclNullStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; var Rel: Int64; begin case Origin of soBeginning: Rel := 0; soCurrent: Rel := FPosition; soEnd: Rel := FSize; else // force Rel + Offset = -1 (code is never reached) Rel := Offset - 1; end; if Rel + Offset >= 0 then begin // all non-negative destination positions including beyond EOF are valid FPosition := Rel + Offset; Result := FPosition; end else Result := -1; end; //=== { TJclRandomStream } =================================================== // A TJclNullStream decendant which returns random data when read // so it is a Unix /dev/random equivalent {$IFDEF CLR} constructor TJclRandomStream.Create; begin inherited Create; FRandomGenerator := System.Random.Create; end; destructor TJclRandomStream.Destroy; begin FRandomGenerator.Free; inherited Destroy; end; {$ENDIF CLR} function TJclRandomStream.GetRandSeed: Longint; begin {$IFDEF CLR} Result := 0; {$ELSE ~CLR} Result := System.RandSeed; {$ENDIF ~CLR} end; procedure TJclRandomStream.SetRandSeed(Seed: Longint); begin {$IFDEF CLR} FRandomGenerator.Free; FRandomGenerator := System.Random.Create(Seed); {$ELSE ~CLR} System.RandSeed := Seed; {$ENDIF ~CLR} end; function TJclRandomStream.RandomData: Byte; begin {$IFDEF CLR} Result := FRandomGenerator.Next(256); {$ELSE ~CLR} Result := System.Random(256); {$ENDIF ~CLR} end; procedure TJclRandomStream.Randomize; begin {$IFNDEF CLR} System.Randomize; {$ENDIF ~CLR} end; {$IFDEF CLR} function TJclRandomStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; var I: Longint; begin // this handles all necessary checks Count := inherited Read(Buffer, Offset, Count); for I := Offset to Offset + Count - 1 do Buffer[I] := RandomData; Result := Count; end; {$ELSE ~CLR} function TJclRandomStream.Read(var Buffer; Count: Longint): Longint; var I: Longint; BufferPtr: PByte; begin // this handles all necessary checks Count := inherited Read(Buffer, Count); BufferPtr := @Buffer; for I := 0 to Count - 1 do begin BufferPtr^ := RandomData; Inc(BufferPtr); end; Result := Count; end; {$ENDIF ~CLR} //=== { TJclMultiplexStream } ================================================ constructor TJclMultiplexStream.Create; begin inherited Create; FStreams := TList.Create; FReadStreamIndex := -1; end; destructor TJclMultiplexStream.Destroy; begin FStreams.Free; inherited Destroy; end; function TJclMultiplexStream.Add(NewStream: TStream): Integer; begin {$IFDEF CLR} Result := FStreams.Add(NewStream); {$ELSE ~CLR} Result := FStreams.Add(Pointer(NewStream)); {$ENDIF ~CLR} end; procedure TJclMultiplexStream.Clear; begin FStreams.Clear; FReadStreamIndex := -1; end; procedure TJclMultiplexStream.Delete(const Index: Integer); begin FStreams.Delete(Index); if ReadStreamIndex = Index then FReadStreamIndex := -1 else if ReadStreamIndex > Index then Dec(FReadStreamIndex); end; function TJclMultiplexStream.GetReadStream: TStream; begin if FReadStreamIndex >= 0 then Result := TStream(FStreams.Items[FReadStreamIndex]) else Result := nil; end; function TJclMultiplexStream.GetStream(Index: Integer): TStream; begin Result := TStream(FStreams.Items[Index]); end; function TJclMultiplexStream.GetCount: Integer; begin Result := FStreams.Count; end; {$IFDEF CLR} function TJclMultiplexStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclMultiplexStream.Read(var Buffer; Count: Longint): Longint; {$ENDIF ~CLR} var Stream: TStream; begin Stream := ReadStream; if Assigned(Stream) then Result := Stream.Read(Buffer, {$IFDEF CLR}Offset,{$ENDIF CLR} Count) else Result := 0; end; function TJclMultiplexStream.Remove(AStream: TStream): Integer; begin {$IFDEF CLR} Result := FStreams.Remove(AStream); {$ELSE ~CLR} Result := FStreams.Remove(Pointer(AStream)); {$ENDIF ~CLR} if FReadStreamIndex = Result then FReadStreamIndex := -1 else if FReadStreamIndex > Result then Dec(FReadStreamIndex); end; function TJclMultiplexStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; begin // what should this function do? Result := -1; end; procedure TJclMultiplexStream.SetReadStream(const Value: TStream); begin {$IFDEF CLR} FReadStreamIndex := FStreams.IndexOf(Value); {$ELSE ~CLR} FReadStreamIndex := FStreams.IndexOf(Pointer(Value)); {$ENDIF ~CLR} end; procedure TJclMultiplexStream.SetReadStreamIndex(const Value: Integer); begin FReadStreamIndex := Value; end; procedure TJclMultiplexStream.SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); begin // what should this function do? end; procedure TJclMultiplexStream.SetStream(Index: Integer; const Value: TStream); begin FStreams.Items[Index] := {$IFDEF CLR}Value;{$ELSE ~CLR}Pointer(Value){$ENDIF ~CLR}; end; {$IFDEF CLR} function TJclMultiplexStream.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclMultiplexStream.Write(const Buffer; Count: Longint): Longint; {$ENDIF ~CLR} var Index: Integer; ByteWritten, MinByteWritten: Longint; begin MinByteWritten := Count; for Index := 0 to Self.Count - 1 do begin ByteWritten := TStream(FStreams.Items[Index]).Write(Buffer, {$IFDEF CLR}Offset,{$ENDIF CLR} Count); if ByteWritten < MinByteWritten then MinByteWritten := ByteWritten; end; Result := MinByteWritten; end; //=== { TJclStreamDecorator } ================================================ constructor TJclStreamDecorator.Create(AStream: TStream; AOwnsStream: Boolean = False); begin inherited Create; FStream := AStream; FOwnsStream := AOwnsStream; end; destructor TJclStreamDecorator.Destroy; begin if OwnsStream then FStream.Free; inherited Destroy; end; procedure TJclStreamDecorator.DoAfterStreamChange; begin if Assigned(FAfterStreamChange) then FAfterStreamChange(Self); end; procedure TJclStreamDecorator.DoBeforeStreamChange; begin if Assigned(FBeforeStreamChange) then FBeforeStreamChange(Self); end; {$IFDEF CLR} function TJclStreamDecorator.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclStreamDecorator.Read(var Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin if Assigned(FStream) then Result := Stream.Read(Buffer, {$IFDEF CLR}Offset,{$ENDIF CLR} Count) else Result := 0; end; function TJclStreamDecorator.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; begin Result := StreamSeek(Stream, Offset, Origin); end; procedure TJclStreamDecorator.SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); begin if Assigned(FStream) then Stream.Size := NewSize; end; procedure TJclStreamDecorator.SetStream(Value: TStream); begin if Value <> FStream then try DoBeforeStreamChange; finally if OwnsStream then FStream.Free; FStream := Value; DoAfterStreamChange; end; end; {$IFDEF CLR} function TJclStreamDecorator.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclStreamDecorator.Write(const Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin if Assigned(FStream) then Result := Stream.Write(Buffer, {$IFDEF CLR}Offset,{$ENDIF CLR} Count) else Result := 0; end; //=== { TJclBufferedStream } ================================================= constructor TJclBufferedStream.Create(AStream: TStream; AOwnsStream: Boolean = False); begin inherited Create(AStream, AOwnsStream); if Stream <> nil then FPosition := Stream.Position; BufferSize := 4096; end; destructor TJclBufferedStream.Destroy; begin Flush; inherited Destroy; end; function TJclBufferedStream.BufferHit: Boolean; begin Result := (FBufferStart <= FPosition) and (FPosition < (FBufferStart + FBufferCurrentSize)); end; procedure TJclBufferedStream.DoAfterStreamChange; begin inherited DoAfterStreamChange; FBufferCurrentSize := 0; // invalidate buffer after stream is changed FBufferStart := 0; if Stream <> nil then FPosition := Stream.Position; end; procedure TJclBufferedStream.DoBeforeStreamChange; begin inherited DoBeforeStreamChange; Flush; end; procedure TJclBufferedStream.Flush; begin if (Stream <> nil) and (FBufferMaxModifiedPos > 0) then begin Stream.Position := FBufferStart; {$IFDEF CLR} Stream.WriteBuffer(FBuffer, FBufferMaxModifiedPos); {$ELSE ~CLR} Stream.WriteBuffer(FBuffer[0], FBufferMaxModifiedPos); {$ENDIF ~CLR} FBufferMaxModifiedPos := 0; end; end; function TJclBufferedStream.GetCalcedSize: Int64; begin if Assigned(Stream) then Result := Stream.Size else Result := 0; if Result < FBufferMaxModifiedPos + FBufferStart then Result := FBufferMaxModifiedPos + FBufferStart; end; function TJclBufferedStream.LoadBuffer: Boolean; begin Flush; if Length(FBuffer) <> FBufferSize then SetLength(FBuffer, FBufferSize); if Stream <> nil then begin Stream.Position := FPosition; {$IFDEF CLR} FBufferCurrentSize := Stream.Read(FBuffer, FBufferSize); {$ELSE ~CLR} FBufferCurrentSize := Stream.Read(FBuffer[0], FBufferSize); {$ENDIF ~CLR} end else FBufferCurrentSize := 0; FBufferStart := FPosition; Result := (FBufferCurrentSize > 0); end; {$IFDEF CLR} function TJclBufferedStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclBufferedStream.Read(var Buffer; Count: Longint): Longint; const Offset = 0; {$ENDIF ~CLR} begin Result := Count + Offset; while Count > 0 do begin if not BufferHit then if not LoadBuffer then Break; Dec(Count, ReadFromBuffer(Buffer, Count, Result - Count)); end; Result := Result - Count - Offset; end; {$IFDEF CLR} function TJclBufferedStream.ReadFromBuffer(var Buffer: array of Byte; Count, Start: Longint): Longint; {$ELSE ~CLR} function TJclBufferedStream.ReadFromBuffer(var Buffer; Count, Start: Longint): Longint; {$ENDIF ~CLR} var BufPos: Longint; {$IFDEF CLR} I: Longint; {$ELSE ~CLR} P: PAnsiChar; {$ENDIF ~CLR} begin Result := Count; BufPos := FPosition - FBufferStart; if Result > FBufferCurrentSize - BufPos then Result := FBufferCurrentSize - BufPos; {$IFDEF CLR} for I := 0 to Result - 1 do Buffer[Start + I] := FBuffer[BufPos + I]; {$ELSE ~CLR} P := @Buffer; Move(FBuffer[BufPos], P[Start], Result); {$ENDIF ~CLR} Inc(FPosition, Result); end; function TJclBufferedStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; var NewPos: Int64; begin NewPos := FPosition; case Origin of soBeginning: NewPos := Offset; soCurrent: Inc(NewPos, Offset); soEnd: NewPos := GetCalcedSize + Offset; else NewPos := -1; end; if NewPos < 0 then NewPos := -1 else FPosition := NewPos; Result := NewPos; end; procedure TJclBufferedStream.SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); begin inherited SetSize(NewSize); if NewSize < (FBufferStart + FBufferMaxModifiedPos) then begin FBufferMaxModifiedPos := NewSize - FBufferStart; if FBufferMaxModifiedPos < 0 then FBufferMaxModifiedPos := 0; end; if NewSize < (FBufferStart + FBufferCurrentSize) then begin FBufferCurrentSize := NewSize - FBufferStart; if FBufferCurrentSize < 0 then FBufferCurrentSize := 0; end; // fix from Marcelo Rocha if Stream <> nil then FPosition := Stream.Position; end; {$IFDEF CLR} function TJclBufferedStream.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclBufferedStream.Write(const Buffer; Count: Longint): Longint; const Offset = 0; {$ENDIF ~CLR} begin Result := Count + Offset; while Count > 0 do begin if not BufferHit then begin if (FBufferStart <= FPosition) and (FPosition < (FBufferStart + FBufferSize)) then begin if Length(FBuffer) <> FBufferSize then SetLength(FBuffer, FBufferSize); end else LoadBuffer; end; Dec(Count, WriteToBuffer(Buffer, Count, Result - Count)); end; Result := Result - Count - Offset; end; {$IFDEF CLR} function TJclBufferedStream.WriteToBuffer(const Buffer: array of Byte; Count, Start: Longint): Longint; {$ELSE ~CLR} function TJclBufferedStream.WriteToBuffer(const Buffer; Count, Start: Longint): Longint; {$ENDIF ~CLR} var BufPos: Longint; {$IFDEF CLR} I: Longint; {$ELSE ~CLR} P: PAnsiChar; {$ENDIF ~CLR} begin Result := Count; BufPos := FPosition - FBufferStart; if Result > Length(FBuffer) - BufPos then Result := Length(FBuffer) - BufPos; if FBufferCurrentSize < BufPos + Result then FBufferCurrentSize := BufPos + Result; {$IFDEF CLR} for I := 0 to Result - 1 do FBuffer[BufPos + I] := Buffer[Start + I]; {$ELSE ~CLR} P := @Buffer; Move(P[Start], FBuffer[BufPos], Result); {$ENDIF ~CLR} FBufferMaxModifiedPos := BufPos + Result; Inc(FPosition, Result); end; //=== { TJclEventStream } ==================================================== constructor TJclEventStream.Create(AStream: TStream; ANotification: TStreamNotifyEvent = nil; AOwnsStream: Boolean = False); begin inherited Create(AStream, AOwnsStream); FNotification := ANotification; end; procedure TJclEventStream.DoAfterStreamChange; begin inherited DoAfterStreamChange; if Stream <> nil then DoNotification; end; procedure TJclEventStream.DoBeforeStreamChange; begin inherited DoBeforeStreamChange; if Stream <> nil then DoNotification; end; procedure TJclEventStream.DoNotification; begin if Assigned(FNotification) then FNotification(Self, Stream.Position, Stream.Size); end; {$IFDEF CLR} function TJclEventStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclEventStream.Read(var Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin Result := inherited Read(Buffer, Count); DoNotification; end; function TJclEventStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; begin Result := inherited Seek(Offset, Origin); DoNotification; end; procedure TJclEventStream.SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); begin inherited SetSize(NewSize); DoNotification; end; {$IFDEF CLR} function TJclEventStream.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclEventStream.Write(const Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin Result := inherited Write(Buffer, Count); DoNotification; end; //=== { TJclEasyStream } ===================================================== function TJclEasyStream.IsEqual(Stream: TStream): Boolean; var SavePos, StreamSavePos: Int64; begin SavePos := Position; StreamSavePos := Stream.Position; try Position := 0; Stream.Position := 0; Result := CompareStreams(Self, Stream); finally Position := SavePos; Stream.Position := StreamSavePos; end; end; function TJclEasyStream.ReadBoolean: Boolean; begin {$IFDEF CLR} ReadBuffer(Result); {$ELSE ~CLR} ReadBuffer(Result, SizeOf(Result)); {$ENDIF ~CLR} end; function TJclEasyStream.ReadChar: Char; begin {$IFDEF CLR} ReadBuffer(Result); {$ELSE ~CLR} ReadBuffer(Result, SizeOf(Result)); {$ENDIF ~CLR} end; function TJclEasyStream.ReadAnsiChar: AnsiChar; begin {$IFDEF CLR} ReadBuffer(Result); {$ELSE ~CLR} ReadBuffer(Result, SizeOf(Result)); {$ENDIF ~CLR} end; function TJclEasyStream.ReadWideChar: WideChar; begin {$IFDEF CLR} ReadBuffer(Result); {$ELSE ~CLR} ReadBuffer(Result, SizeOf(Result)); {$ENDIF ~CLR} end; function TJclEasyStream.ReadByte: Byte; begin {$IFDEF CLR} ReadBuffer(Result); {$ELSE ~CLR} ReadBuffer(Result, SizeOf(Result)); {$ENDIF ~CLR} end; {$IFNDEF CLR} function TJclEasyStream.ReadCurrency: Currency; begin ReadBuffer(Result, SizeOf(Result)); end; function TJclEasyStream.ReadDateTime: TDateTime; begin ReadBuffer(Result, SizeOf(Result)); end; {$ENDIF ~CLR} function TJclEasyStream.ReadDouble: Double; begin {$IFDEF CLR} ReadBuffer(Result); {$ELSE ~CLR} ReadBuffer(Result, SizeOf(Result)); {$ENDIF ~CLR} end; {$IFNDEF CLR} function TJclEasyStream.ReadExtended: Extended; begin ReadBuffer(Result, SizeOf(Result)); end; {$ENDIF ~CLR} function TJclEasyStream.ReadInt64: Int64; begin {$IFDEF CLR} ReadBuffer(Result); {$ELSE ~CLR} ReadBuffer(Result, SizeOf(Result)); {$ENDIF ~CLR} end; function TJclEasyStream.ReadInteger: Integer; begin {$IFDEF CLR} ReadBuffer(Result); {$ELSE ~CLR} ReadBuffer(Result, SizeOf(Result)); {$ENDIF ~CLR} end; function TJclEasyStream.ReadCString: string; begin {$IFDEF SUPPORTS_UNICODE} Result := ReadCWideString; {$ELSE ~SUPPORTS_UNICODE} Result := ReadCAnsiString; {$ENDIF ~SUPPORTS_UNICODE} end; function TJclEasyStream.ReadCAnsiString: AnsiString; var {$IFDEF CLR} SB: System.Text.StringBuilder; Ch: AnsiChar; {$ELSE ~CLR} CurrPos: Longint; StrSize: Integer; {$ENDIF ~CLR} begin {$IFDEF CLR} SB := System.Text.StringBuilder.Create; repeat Ch := ReadAnsiChar; if Ch <> #0 then SB.Append(Ch); until Ch = #0; Result := SB.ToString; {$ELSE ~CLR} CurrPos := Position; repeat until ReadAnsiChar = #0; StrSize := Position - CurrPos - 1; SetLength(Result, StrSize); Position := CurrPos; ReadBuffer(Result[1], StrSize * SizeOf(Result[1])); Position := Position + 1; {$ENDIF ~CLR} end; function TJclEasyStream.ReadCWideString: WideString; var {$IFDEF CLR} SB: System.Text.StringBuilder; Ch: WideChar; {$ELSE ~CLR} CurrPos: Integer; StrSize: Integer; {$ENDIF ~CLR} begin {$IFDEF CLR} SB := System.Text.StringBuilder.Create; repeat Ch := ReadWideChar; if Ch <> #0 then SB.Append(Ch); until Ch = #0; Result := SB.ToString; {$ELSE ~CLR} CurrPos := Position; repeat until ReadWideChar = #0; StrSize := Position - CurrPos - 1; SetLength(Result, StrSize); Position := CurrPos; ReadBuffer(Result[1], StrSize * SizeOf(Result[1])); Position := Position + 1; {$ENDIF ~CLR} end; function TJclEasyStream.ReadShortString: string; var {$IFDEF CLR} SB: System.Text.StringBuilder; {$ENDIF CLR} StrSize: Integer; begin StrSize := Ord(ReadChar); {$IFDEF CLR} SB := System.Text.StringBuilder.Create(StrSize); while StrSize > 0 do begin SB.Append(ReadChar); Dec(StrSize); end; Result := SB.ToString; {$ELSE ~CLR} SetString(Result, PChar(nil), StrSize); ReadBuffer(Pointer(Result)^, StrSize); {$ENDIF ~CLR} end; function TJclEasyStream.ReadSingle: Single; begin {$IFDEF CLR} ReadBuffer(Result); {$ELSE ~CLR} ReadBuffer(Result, SizeOf(Result)); {$ENDIF ~CLR} end; function TJclEasyStream.ReadSizedString: string; begin {$IFDEF SUPPORTS_UNICODE} Result := ReadSizedWideString; {$ELSE ~SUPPORTS_UNICODE} Result := ReadSizedAnsiString; {$ENDIF ~SUPPORTS_UNICODE} end; function TJclEasyStream.ReadSizedAnsiString: AnsiString; var {$IFDEF CLR} SB: System.Text.StringBuilder; {$ENDIF CLR} StrSize: Integer; begin StrSize := ReadInteger; {$IFDEF CLR} SB := System.Text.StringBuilder.Create(StrSize); while StrSize > 0 do begin SB.Append(ReadAnsiChar); Dec(StrSize); end; Result := SB.ToString; {$ELSE ~CLR} SetLength(Result, StrSize); ReadBuffer(Result[1], StrSize * SizeOf(Result[1])); {$ENDIF ~CLR} end; function TJclEasyStream.ReadSizedWideString: WideString; var {$IFDEF CLR} SB: System.Text.StringBuilder; {$ENDIF CLR} StrSize: Integer; begin StrSize := ReadInteger; {$IFDEF CLR} SB := System.Text.StringBuilder.Create(StrSize); while StrSize > 0 do begin SB.Append(ReadWideChar); Dec(StrSize); end; Result := SB.ToString; {$ELSE ~CLR} SetLength(Result, StrSize); ReadBuffer(Result[1], StrSize * SizeOf(Result[1])); {$ENDIF ~CLR} end; procedure TJclEasyStream.WriteBoolean(Value: Boolean); begin {$IFDEF CLR} WriteBuffer(Value); {$ELSE ~CLR} WriteBuffer(Value, SizeOf(Value)); {$ENDIF ~CLR} end; procedure TJclEasyStream.WriteChar(Value: Char); begin {$IFDEF CLR} WriteBuffer(Value); {$ELSE ~CLR} WriteBuffer(Value, SizeOf(Value)); {$ENDIF ~CLR} end; procedure TJclEasyStream.WriteAnsiChar(Value: AnsiChar); begin {$IFDEF CLR} WriteBuffer(Value); {$ELSE ~CLR} WriteBuffer(Value, SizeOf(Value)); {$ENDIF ~CLR} end; procedure TJclEasyStream.WriteWideChar(Value: WideChar); begin {$IFDEF CLR} WriteBuffer(Value); {$ELSE ~CLR} WriteBuffer(Value, SizeOf(Value)); {$ENDIF ~CLR} end; procedure TJclEasyStream.WriteByte(Value: Byte); begin {$IFDEF CLR} WriteBuffer(Value); {$ELSE ~CLR} WriteBuffer(Value, SizeOf(Value)); {$ENDIF ~CLR} end; {$IFNDEF CLR} procedure TJclEasyStream.WriteCurrency(const Value: Currency); begin WriteBuffer(Value, SizeOf(Value)); end; procedure TJclEasyStream.WriteDateTime(const Value: TDateTime); begin WriteBuffer(Value, SizeOf(Value)); end; {$ENDIF ~CLR} procedure TJclEasyStream.WriteDouble(const Value: Double); begin {$IFDEF CLR} WriteBuffer(Value); {$ELSE ~CLR} WriteBuffer(Value, SizeOf(Value)); {$ENDIF ~CLR} end; {$IFNDEF CLR} procedure TJclEasyStream.WriteExtended(const Value: Extended); begin WriteBuffer(Value, SizeOf(Value)); end; {$ENDIF ~CLR} procedure TJclEasyStream.WriteInt64(Value: Int64); begin {$IFDEF CLR} WriteBuffer(Value); {$ELSE ~CLR} WriteBuffer(Value, SizeOf(Value)); {$ENDIF ~CLR} end; procedure TJclEasyStream.WriteInteger(Value: Integer); begin {$IFDEF CLR} WriteBuffer(Value); {$ELSE ~CLR} WriteBuffer(Value, SizeOf(Value)); {$ENDIF ~CLR} end; procedure TJclEasyStream.WriteCString(const Value: string); begin {$IFDEF SUPPORTS_UNICODE} WriteCWideString(Value); {$ELSE ~SUPPORTS_UNICODE} WriteCAnsiString(Value); {$ENDIF ~SUPPORTS_UNICODE} end; procedure TJclEasyStream.WriteCAnsiString(const Value: AnsiString); var StrSize: Integer; {$IFDEF CLR} I: Longint; {$ENDIF CLR} begin StrSize := Length(Value); {$IFDEF CLR} for I := 1 to StrSize do WriteAnsiChar(Value[I]); WriteAnsiChar(#0); {$ELSE ~CLR} WriteBuffer(Value[1], (StrSize + 1) * SizeOf(Value[1])); {$ENDIF ~CLR} end; procedure TJclEasyStream.WriteCWideString(const Value: WideString); var StrSize: Integer; {$IFDEF CLR} I: Integer; {$ENDIF CLR} begin StrSize := Length(Value); {$IFDEF CLR} for I := 1 to StrSize do WriteWideChar(Value[I]); WriteWideChar(#0); {$ELSE ~CLR} WriteBuffer(Value[1], (StrSize + 1) * SizeOf(Value[1])); {$ENDIF ~CLR} end; {$IFDEF KEEP_DEPRECATED} procedure TJclEasyStream.WriteStringDelimitedByNull(const Value: string); begin WriteCString(Value); end; {$ENDIF KEEP_DEPRECATED} procedure TJclEasyStream.WriteShortString(const Value: ShortString); {$IFDEF CLR} var I: Longint; {$ENDIF CLR} begin {$IFDEF CLR} for I := 0 to Length(Value) do inherited WriteBuffer(Value[I]); {$ELSE ~CLR} WriteBuffer(Value[0], Length(Value) + 1); {$ENDIF ~CLR} end; procedure TJclEasyStream.WriteSingle(const Value: Single); begin {$IFDEF CLR} WriteBuffer(Value); {$ELSE ~CLR} WriteBuffer(Value, SizeOf(Value)); {$ENDIF ~~ CLR} end; procedure TJclEasyStream.WriteSizedString(const Value: string); begin {$IFDEF SUPPORTS_UNICODE} WriteSizedWideString(Value); {$ELSE ~SUPPORTS_UNICODE} WriteSizedAnsiString(Value); {$ENDIF ~SUPPORTS_UNICODE} end; procedure TJclEasyStream.WriteSizedAnsiString(const Value: AnsiString); var StrSize: Integer; {$IFDEF CLR} I: Longint; {$ENDIF CLR} begin StrSize := Length(Value); WriteInteger(StrSize); {$IFDEF CLR} for I := 1 to StrSize do WriteAnsiChar(Value[I]); {$ELSE ~CLR} WriteBuffer(Value[1], StrSize * SizeOf(Value[1])); {$ENDIF ~CLR} end; procedure TJclEasyStream.WriteSizedWideString(const Value: WideString); var StrSize: Integer; {$IFDEF CLR} I: Integer; {$ENDIF CLR} begin StrSize := Length(Value); WriteInteger(StrSize); {$IFDEF CLR} for I := 1 to StrSize do WriteWideChar(Value[I]); {$ELSE ~CLR} WriteBuffer(Value[1], StrSize * SizeOf(Value[1])); {$ENDIF ~CLR} end; //=== { TJclScopedStream } =================================================== constructor TJclScopedStream.Create(AParentStream: TStream; AMaxSize: Int64); begin inherited Create; FParentStream := AParentStream; FStartPos := ParentStream.Position; FCurrentPos := 0; FMaxSize := AMaxSize; end; {$IFDEF CLR} function TJclScopedStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclScopedStream.Read(var Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin if (MaxSize >= 0) and ((FCurrentPos + Count) > MaxSize) then Count := MaxSize - FCurrentPos; if (Count > 0) and Assigned(ParentStream) then begin Result := ParentStream.Read(Buffer, {$IFDEF CLR}Offset,{$ENDIF CLR} Count); Inc(FCurrentPos, Result); end else Result := 0; end; function TJclScopedStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; begin case Origin of soBeginning: begin if (Offset < 0) or ((MaxSize >= 0) and (Offset > MaxSize)) then Result := -1 // low and high bound check else Result := StreamSeek(ParentStream, StartPos + Offset, soBeginning) - StartPos; end; soCurrent: begin if Offset = 0 then Result := FCurrentPos // speeding the Position property up else if ((FCurrentPos + Offset) < 0) or ((MaxSize >= 0) and ((FCurrentPos + Offset) > MaxSize)) then Result := -1 // low and high bound check else Result := StreamSeek(ParentStream, Offset, soCurrent) - StartPos; end; soEnd: begin if (MaxSize >= 0) then begin if (Offset > 0) or (MaxSize < -Offset) then // low and high bound check Result := -1 else Result := StreamSeek(ParentStream, StartPos + MaxSize + Offset, soBeginning) - StartPos; end else begin Result := StreamSeek(ParentStream, Offset, soEnd); if (Result <> -1) and (Result < StartPos) then // low bound check begin Result := -1; StreamSeek(ParentStream, StartPos + FCurrentPos, soBeginning); end; end; end; else Result := -1; end; if Result <> -1 then FCurrentPos := Result; end; procedure TJclScopedStream.SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); var ScopedNewSize: Int64; begin if (FMaxSize >= 0) and (NewSize >= (FStartPos + FMaxSize)) then ScopedNewSize := FMaxSize + FStartPos else ScopedNewSize := NewSize; inherited SetSize(ScopedNewSize); end; {$IFDEF CLR} function TJclScopedStream.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclScopedStream.Write(const Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin if (MaxSize >= 0) and ((FCurrentPos + Count) > MaxSize) then Count := MaxSize - FCurrentPos; if (Count > 0) and Assigned(ParentStream) then begin Result := ParentStream.Write(Buffer, Count); Inc(FCurrentPos, Result); end else Result := 0; end; //=== { TJclDelegateStream } ================================================= procedure TJclDelegatedStream.SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); begin if Assigned(FOnSize) then FOnSize(Self, NewSize); end; function TJclDelegatedStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; begin if Assigned(FOnSeek) then Result := FOnSeek(Self, Offset, Origin) else Result := -1; end; {$IFDEF CLR} function TJclDelegatedStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclDelegatedStream.Read(var Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin if Assigned(FOnRead) then Result := FOnRead(Self, Buffer, {$IFDEF CLR}Offset,{$ENDIF CLR} Count) else Result := -1; end; {$IFDEF CLR} function TJclDelegatedStream.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclDelegatedStream.Write(const Buffer; Count: Longint): Longint; {$ENDIF ~CLR} begin if Assigned(FOnWrite) then Result := FOnWrite(Self, Buffer, {$IFDEF CLR}Offset,{$ENDIF CLR} Count) else Result := -1; end; //=== { TJclSectoredStream } ================================================= procedure TJclSectoredStream.AfterBlockRead; begin // override to customize (checks of protection) end; procedure TJclSectoredStream.BeforeBlockWrite; begin // override to customize (computation of protection) end; constructor TJclSectoredStream.Create(AStorageStream: TStream; AOwnsStream: Boolean; ASectorOverHead: Integer); begin inherited Create(AStorageStream, AOwnsStream); FSectorOverHead := ASectorOverHead; if Stream <> nil then FPosition := SectoredToFlat(Stream.Position); end; procedure TJclSectoredStream.DoAfterStreamChange; begin inherited DoAfterStreamChange; if Stream <> nil then FPosition := SectoredToFlat(Stream.Position); end; function TJclSectoredStream.FlatToSectored(const Position: Int64): Int64; begin Result := (Position div BufferSize) * (BufferSize + FSectorOverHead) // add overheads of previous buffers + Position mod BufferSize; // offset in sector end; procedure TJclSectoredStream.Flush; begin if (Stream <> nil) and (FBufferMaxModifiedPos > 0) then begin BeforeBlockWrite; Stream.Position := FlatToSectored(FBufferStart); {$IFDEF CLR} Stream.WriteBuffer(FBuffer, FBufferCurrentSize + FSectorOverHead); {$ELSE ~CLR} Stream.WriteBuffer(FBuffer[0], FBufferCurrentSize + FSectorOverHead); {$ENDIF ~CLR} FBufferMaxModifiedPos := 0; end; end; function TJclSectoredStream.GetCalcedSize: Int64; var VirtualSize: Int64; begin if Assigned(Stream) then Result := SectoredToFlat(Stream.Size) else Result := 0; VirtualSize := FBufferMaxModifiedPos + FBufferStart; if Result < VirtualSize then Result := VirtualSize; end; function TJclSectoredStream.LoadBuffer: Boolean; var TotalSectorSize: Longint; begin Flush; TotalSectorSize := FBufferSize + FSectorOverHead; if Length(FBuffer) <> TotalSectorSize then SetLength(FBuffer, TotalSectorSize); FBufferStart := (FPosition div BufferSize) * BufferSize; if Stream <> nil then begin Stream.Position := FlatToSectored(FBufferStart); {$IFDEF CLR} FBufferCurrentSize := Stream.Read(FBuffer, TotalSectorSize); {$ELSE ~CLR} FBufferCurrentSize := Stream.Read(FBuffer[0], TotalSectorSize); {$ENDIF ~CLR} if FBufferCurrentSize > 0 then begin Dec(FBufferCurrentSize, FSectorOverHead); AfterBlockRead; end; end else FBufferCurrentSize := 0; Result := (FBufferCurrentSize > 0); end; function TJclSectoredStream.SectoredToFlat(const Position: Int64): Int64; var TotalSectorSize: Int64; begin TotalSectorSize := BufferSize + FSectorOverHead; Result := (Position div TotalSectorSize) * BufferSize // remove previous overheads + Position mod TotalSectorSize; // offset in sector end; procedure TJclSectoredStream.SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); begin inherited SetSize(FlatToSectored(NewSize)); end; //=== { TJclCRC16Stream } ==================================================== procedure TJclCRC16Stream.AfterBlockRead; var CRC: Word; begin CRC := FBuffer[FBufferCurrentSize] + (FBuffer[FBufferCurrentSize + 1] shl 8); if {$IFDEF COMPILER5}CheckCrc16D5{$ELSE}CheckCrc16{$ENDIF COMPILER5}(FBuffer, FBufferCurrentSize, CRC) < 0 then {$IFDEF CLR} raise EJclStreamError.Create(RsStreamsCRCError); {$ELSE ~CLR} raise EJclStreamError.CreateRes(@RsStreamsCRCError); {$ENDIF ~CLR} end; procedure TJclCRC16Stream.BeforeBlockWrite; var CRC: Word; begin CRC := Crc16(FBuffer, FBufferCurrentSize); FBuffer[FBufferCurrentSize] := CRC and $FF; FBuffer[FBufferCurrentSize + 1] := CRC shr 8; end; constructor TJclCRC16Stream.Create(AStorageStream: TStream; AOwnsStream: Boolean); begin inherited Create(AStorageStream, AOwnsStream, 2); end; //=== { TJclCRC32Stream } ==================================================== procedure TJclCRC32Stream.AfterBlockRead; var CRC: Cardinal; begin CRC := FBuffer[FBufferCurrentSize] + (FBuffer[FBufferCurrentSize + 1] shl 8) + (FBuffer[FBufferCurrentSize + 2] shl 16) + (FBuffer[FBufferCurrentSize + 3] shl 24); if {$IFDEF COMPILER5}CheckCrc32D5{$ELSE}CheckCrc32{$ENDIF COMPILER5}(FBuffer, FBufferCurrentSize, CRC) < 0 then {$IFDEF CLR} raise EJclStreamError.Create(RsStreamsCRCError); {$ELSE ~CLR} raise EJclStreamError.CreateRes(@RsStreamsCRCError); {$ENDIF ~CLR} end; procedure TJclCRC32Stream.BeforeBlockWrite; var CRC: Cardinal; begin CRC := Crc32(FBuffer, FBufferCurrentSize); FBuffer[FBufferCurrentSize] := CRC and $FF; FBuffer[FBufferCurrentSize + 1] := (CRC shr 8) and $FF; FBuffer[FBufferCurrentSize + 2] := (CRC shr 16) and $FF; FBuffer[FBufferCurrentSize + 3] := (CRC shr 24) and $FF; end; constructor TJclCRC32Stream.Create(AStorageStream: TStream; AOwnsStream: Boolean); begin inherited Create(AStorageStream, AOwnsStream, 4); end; //=== { TJclSplitStream } ==================================================== constructor TJclSplitStream.Create; begin inherited Create; FVolume := nil; FVolumeIndex := -1; FVolumeMaxSize := 0; FPosition := 0; FVolumePosition := 0; end; function TJclSplitStream.GetSize: Int64; var OldVolumeIndex: Integer; OldVolumePosition, OldPosition: Int64; begin OldVolumeIndex := FVolumeIndex; OldVolumePosition := FVolumePosition; OldPosition := FPosition; Result := 0; try FVolumeIndex := -1; repeat InternalLoadVolume(FVolumeIndex + 1); if not Assigned(FVolume) then Break; Result := Result + FVolume.Size; until FVolume.Size = 0; finally InternalLoadVolume(OldVolumeIndex); FPosition := OldPosition; if Assigned(FVolume) then FVolumePosition := StreamSeek(FVolume, OldVolumePosition, soBeginning); end; end; procedure TJclSplitStream.InternalLoadVolume(Index: Integer); begin if Index = -1 then Index := 0; if Index <> FVolumeIndex then begin FVolumeIndex := Index; FVolumePosition := 0; FVolume := GetVolume(Index); FVolumeMaxSize := GetVolumeMaxSize(Index); if Assigned(FVolume) then StreamSeek(FVolume, 0, soBeginning); end; end; {$IFDEF CLR} function TJclSplitStream.Read(var Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclSplitStream.Read(var Buffer; Count: Longint): Longint; {$ENDIF ~CLR} var {$IFNDEF CLR} Data: PByte; {$ENDIF ~CLR} Total, LoopRead: Longint; begin Result := 0; InternalLoadVolume(FVolumeIndex); if not Assigned(FVolume) then Exit; {$IFNDEF CLR} Data := PByte(@Buffer); {$ENDIF ~CLR} Total := Count; repeat // try to read (Count) bytes from current stream {$IFDEF CLR} LoopRead := FVolume.Read(Buffer, Offset, Count); {$ELSE ~CLR} LoopRead := FVolume.Read(Data^, Count); {$ENDIF ~CLR} FVolumePosition := FVolumePosition + LoopRead; FPosition := FPosition + LoopRead; Inc(Result, LoopRead); if Result = Total then Break; // with next volume Dec(Count, Result); {$IFDEF CLR} Inc(Offset, Result); {$ELSE ~CLR} Inc(Data, Result); {$ENDIF ~CLR} InternalLoadVolume(FVolumeIndex + 1); if not Assigned(FVolume) then Break; until False; end; function TJclSplitStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; var ExpectedPosition, RemainingOffset: Int64; begin case TSeekOrigin(Origin) of soBeginning: ExpectedPosition := Offset; soCurrent: ExpectedPosition := FPosition + Offset; soEnd: ExpectedPosition := Size - Offset; else {$IFDEF CLR} raise EJclStreamError.Create(RsStreamsSeekError); {$ELSE ~CLR} raise EJclStreamError.CreateRes(@RsStreamsSeekError); {$ENDIF ~CLR} end; RemainingOffset := ExpectedPosition - FPosition; Result := FPosition; repeat InternalLoadVolume(FVolumeIndex); if not Assigned(FVolume) then Break; if RemainingOffset < 0 then begin // FPosition > ExpectedPosition, seek backward if FVolumePosition >= -RemainingOffset then begin // seek in current volume FVolumePosition := StreamSeek(FVolume, FVolumePosition + RemainingOffset, soBeginning); Result := Result + RemainingOffset; FPosition := Result; RemainingOffset := 0; end else begin // seek to previous volume if FVolumeIndex = 0 then Exit; // seek to the beginning of current volume RemainingOffset := RemainingOffset + FVolumePosition; Result := Result - FVolumePosition; FPosition := Result; FVolumePosition := StreamSeek(FVolume, 0, soBeginning); // load previous volume InternalLoadVolume(FVolumeIndex - 1); if not Assigned(FVolume) then Break; Result := Result - FVolume.Size; FPosition := Result; RemainingOffset := RemainingOffset + FVolume.Size; end; end else if RemainingOffset > 0 then begin // FPosition < ExpectedPosition, seek forward if (FVolumeMaxSize = 0) or ((FVolumePosition + RemainingOffset) < FVolumeMaxSize) then begin // can seek in current volume FVolumePosition := StreamSeek(FVolume, FVolumePosition + RemainingOffset, soBeginning); Result := Result + RemainingOffset; FPosition := Result; RemainingOffset := 0; end else begin // seek to next volume RemainingOffset := RemainingOffset - FVolumeMaxSize + FVolumePosition; Result := Result + FVolumeMaxSize - FVolumePosition; FPosition := Result; InternalLoadVolume(FVolumeIndex + 1); if not Assigned(FVolume) then Break; end; end; until RemainingOffset = 0; end; procedure TJclSplitStream.SetSize({$IFNDEF CLR}const{$ENDIF ~CLR} NewSize: Int64); var OldVolumeIndex: Integer; OldVolumePosition, OldPosition, RemainingSize, VolumeSize: Int64; begin OldVolumeIndex := FVolumeIndex; OldVolumePosition := FVolumePosition; OldPosition := FPosition; RemainingSize := NewSize; try FVolumeIndex := 0; repeat InternalLoadVolume(FVolumeIndex); if not Assigned(FVolume) then Break; if (FVolumeMaxSize > 0) and (RemainingSize > FVolumeMaxSize) then VolumeSize := FVolumeMaxSize else VolumeSize := RemainingSize; FVolume.Size := VolumeSize; RemainingSize := RemainingSize - VolumeSize; Inc(FVolumeIndex); until RemainingSize = 0; finally InternalLoadVolume(OldVolumeIndex); FPosition := OldPosition; if Assigned(FVolume) then FVolumePosition := StreamSeek(FVolume, OldVolumePosition, soBeginning); end; end; {$IFDEF CLR} function TJclSplitStream.Write(const Buffer: array of Byte; Offset, Count: Longint): Longint; {$ELSE ~CLR} function TJclSplitStream.Write(const Buffer; Count: Longint): Longint; {$ENDIF ~CLR} var {$IFNDEF CLR} Data: PByte; {$ENDIF ~CLR} Total, LoopWritten: Longint; begin Result := 0; InternalLoadVolume(FVolumeIndex); if not Assigned(FVolume) then Exit; {$IFNDEF CLR} Data := PByte(@Buffer); {$ENDIF ~CLR} Total := Count; repeat // do not write more than (VolumeMaxSize) bytes in current stream if (FVolumeMaxSize > 0) and ((Count + FVolumePosition) > FVolumeMaxSize) then LoopWritten := FVolumeMaxSize - FVolumePosition else LoopWritten := Count; // try to write (Count) bytes from current stream {$IFDEF CLR} LoopWritten := FVolume.Write(Buffer, Offset, LoopWritten); {$ELSE ~CLR} LoopWritten := FVolume.Write(Data^, LoopWritten); {$ENDIF ~CLR} FVolumePosition := FVolumePosition + LoopWritten; FPosition := FPosition + LoopWritten; Inc(Result, LoopWritten); if Result = Total then Break; // with next volume Dec(Count, LoopWritten); {$IFDEF CLR} Inc(Offset, LoopWritten); {$ELSE ~CLR} Inc(Data, LoopWritten); {$ENDIF ~CLR} InternalLoadVolume(FVolumeIndex + 1); if not Assigned(FVolume) then Break; until False; end; //=== { TJclDynamicSplitStream } ============================================= function TJclDynamicSplitStream.GetVolume(Index: Integer): TStream; begin if Assigned(FOnVolume) then Result := FOnVolume(Index) else Result := nil; end; function TJclDynamicSplitStream.GetVolumeMaxSize(Index: Integer): Int64; begin if Assigned(FOnVolumeMaxSize) then Result := FOnVolumeMaxSize(Index) else Result := 0; end; //=== { TJclStaticSplitStream } =========================================== constructor TJclStaticSplitStream.Create; begin inherited Create; FVolumes := TObjectList.Create(True); end; destructor TJclStaticSplitStream.Destroy; var Index: Integer; AVolumeRec: TJclSplitVolume; begin if Assigned(FVolumes) then begin for Index := 0 to FVolumes.Count - 1 do begin AVolumeRec := TJclSplitVolume(FVolumes.Items[Index]); if AVolumeRec.OwnStream then AVolumeRec.Stream.Free; end; FVolumes.Free; end; inherited Destroy; end; function TJclStaticSplitStream.AddVolume(AStream: TStream; AMaxSize: Int64; AOwnStream: Boolean): Integer; var AVolumeRec: TJclSplitVolume; begin AVolumeRec := TJclSplitVolume.Create; AVolumeRec.MaxSize := AMaxSize; AVolumeRec.Stream := AStream; AVolumeRec.OwnStream := AOwnStream; Result := FVolumes.Add(AVolumeRec); end; function TJclStaticSplitStream.GetVolume(Index: Integer): TStream; begin Result := TJclSplitVolume(FVolumes.Items[Index]).Stream; end; function TJclStaticSplitStream.GetVolumeCount: Integer; begin Result := FVolumes.Count; end; function TJclStaticSplitStream.GetVolumeMaxSize(Index: Integer): Int64; begin Result := TJclSplitVolume(FVolumes.Items[Index]).MaxSize; end; //=== { TJclStringStream } ==================================================== constructor TJclStringStream.Create(AStream: TStream; AOwnsStream: Boolean); begin inherited Create(AStream, AOwnsStream); end; function TJclStringStream.GetCalcedSize: Int64; begin {$IFDEF CLR} raise EJclStreamError.Create(RsStreamsSeekError); {$ELSE ~CLR} raise EJclStreamError.CreateRes(@RsStreamsSeekError); {$ENDIF ~CLR} end; function TJclStringStream.PeekAnsiChar(var Buffer: AnsiChar): Boolean; var Pos: Int64; Ch: UCS4; begin Pos := FPosition; FPosition := FPeekPosition; Result := FCharacterReader(Self, Ch); if Result then Buffer := UCS4ToAnsiChar(Ch); FPosition := Pos; FPeekPosition := FPeekPosition + 1; end; function TJclStringStream.PeekChar(var Buffer: Char): Boolean; var Pos: Int64; Ch: UCS4; begin Pos := FPosition; FPosition := FPeekPosition; Result := FCharacterReader(Self, Ch); if Result then Buffer := UCS4ToChar(Ch); FPosition := Pos; FPeekPosition := FPeekPosition + 1; end; function TJclStringStream.PeekWideChar(var Buffer: WideChar): Boolean; var Pos: Int64; Ch: UCS4; begin Pos := FPosition; FPosition := FPeekPosition; Result := FCharacterReader(Self, Ch); if Result then Buffer := UCS4ToWideChar(Ch); FPosition := Pos; FPeekPosition := FPeekPosition + 1; end; function TJclStringStream.ReadString(var Buffer: string; Start, Count: Longint): Longint; var Index, StrPos: Integer; Ch: UCS4; begin Index := Start; while Index < Start + Count - 1 do // avoid overflow on surrogate pairs for WideString begin if FCharacterReader(Self, Ch) then begin StrPos := Index; if StringSetNextChar(Buffer, StrPos, Ch) and (StrPos > 0) then Index := StrPos else Break; // end of string (write) end else Break; // end of stream (read) end; Result := Index - Start; FPeekPosition := FPosition; end; function TJclStringStream.ReadAnsiChar(var Buffer: AnsiChar): Boolean; var Ch: UCS4; begin Result := FCharacterReader(Self, Ch); if Result then Buffer := UCS4ToAnsiChar(Ch); FPeekPosition := FPosition; end; function TJclStringStream.ReadAnsiString(var Buffer: AnsiString; Start, Count: Longint): Longint; var Index, StrPos: Integer; Ch: UCS4; begin Index := Start; while Index < Start + Count do begin if FCharacterReader(Self, Ch) then begin StrPos := Index; if AnsiSetNextChar(Buffer, StrPos, Ch) and (StrPos > 0) then Index := StrPos else Break; // end of string (write) end else Break; // end of stream (read) end; Result := Index - Start; FPeekPosition := FPosition; end; function TJclStringStream.ReadChar(var Buffer: Char): Boolean; var Ch: UCS4; begin Result := FCharacterReader(Self, Ch); if Result then Buffer := UCS4ToChar(Ch); FPeekPosition := FPosition; end; function TJclStringStream.ReadWideChar(var Buffer: WideChar): Boolean; var Ch: UCS4; begin Result := FCharacterReader(Self, Ch); if Result then Buffer := UCS4ToWideChar(Ch); FPeekPosition := FPosition; end; function TJclStringStream.ReadWideString(var Buffer: WideString; Start, Count: Longint): Longint; var Index, StrPos: Integer; Ch: UCS4; begin Index := Start; while Index < Start + Count - 1 do // avoid overflow on surrogate pairs begin if FCharacterReader(Self, Ch) then begin StrPos := Index; if UTF16SetNextChar(Buffer, StrPos, Ch) and (StrPos > 0) then Index := StrPos else Break; // end of string (write) end else Break; // end of stream (read) end; Result := Index - Start; FPeekPosition := FPosition; end; function TJclStringStream.SkipBOM: Longint; var Pos: Int64; I: Integer; BOM: array of Byte; begin if Length(FBOM) > 0 then begin SetLength(BOM, Length(FBOM)); Pos := Position; {$IFDEF CLR} Result := Read(BOM, Low(BOM), Length(BOM)); {$ELSE ~CLR} Result := Read(BOM[0], Length(BOM) * SizeOf(BOM[0])); {$ENDIF ~CLR} if Result = Length(FBOM) * SizeOf(FBOM[0]) then for I := Low(FBOM) to High(FBOM) do if BOM[I - Low(FBOM)] <> FBOM[I] then Result := 0; if Result <> Length(FBOM) * SizeOf(FBOM[0]) then Position := Pos; end else Result := 0; FPeekPosition := FPosition; end; function TJclStringStream.WriteBOM: Longint; begin if Length(FBOM) > 0 then begin {$IFDEF CLR} Result := Write(FBOM, Low(FBOM), Length(FBOM)); {$ELSE ~CLR} Result := Write(FBOM[0], Length(FBOM) * SizeOf(FBOM[0])); {$ENDIF ~CLR} end else Result := 0; FPeekPosition := FPosition; end; function TJclStringStream.WriteChar(Value: Char): Boolean; begin Result := FCharacterWriter(Self, CharToUCS4(Value)); end; function TJclStringStream.WriteString(const Buffer: string; Start, Count: Longint): Longint; var Index, StrPos: Integer; Ch: UCS4; begin Index := Start; while Index < Start + Count do begin StrPos := Index; Ch := StringGetNextChar(Buffer, StrPos); if (StrPos > 0) and FCharacterWriter(Self, Ch) then Index := StrPos else Break; // end of string (read) or end of stream (write) end; Result := Index - Start; FPeekPosition := FPosition; end; function TJclStringStream.WriteAnsiChar(Value: AnsiChar): Boolean; begin Result := FCharacterWriter(Self, AnsiCharToUCS4(Value)); FPeekPosition := FPosition; end; function TJclStringStream.WriteAnsiString(const Buffer: AnsiString; Start, Count: Longint): Longint; var Index, StrPos: Integer; Ch: UCS4; begin Index := Start; while Index < Start + Count do begin StrPos := Index; Ch := AnsiGetNextChar(Buffer, StrPos); if (StrPos > 0) and FCharacterWriter(Self, Ch) then Index := StrPos else Break; // end of string (read) or end of stream (write) end; Result := Index - Start; FPeekPosition := FPosition; end; function TJclStringStream.WriteWideChar(Value: WideChar): Boolean; begin Result := FCharacterWriter(Self, WideCharToUCS4(Value)); FPeekPosition := FPosition; end; function TJclStringStream.WriteWideString(const Buffer: WideString; Start, Count: Longint): Longint; var Index, StrPos: Integer; Ch: UCS4; begin Index := Start; while Index < Start + Count do begin StrPos := Index; Ch := UTF16GetNextChar(Buffer, StrPos); if (StrPos > 0) and FCharacterWriter(Self, Ch) then Index := StrPos else Break; // end of string (read) or end of stream (write) end; Result := Index - Start; FPeekPosition := FPosition; end; //=== { TJclAnsiStream } ====================================================== constructor TJclAnsiStream.Create(AStream: TStream; AOwnsStream: Boolean); begin inherited Create(AStream, AOwnsStream); // not adding the @ character causes an internal error in Delphi 5 and C++Builder 5 FCharacterReader := {$IFNDEF CLR}@{$ENDIF ~CLR}AnsiGetNextCharFromStream; FCharacterWriter := {$IFNDEF CLR}@{$ENDIF ~CLR}AnsiSetNextCharToStream; SetLength(FBOM, 0); end; //=== { TJclUTF8Stream } ====================================================== constructor TJclUTF8Stream.Create(AStream: TStream; AOwnsStream: Boolean); var I: Integer; begin inherited Create(AStream, AOwnsStream); FCharacterReader := {$IFNDEF CLR}@{$ENDIF ~CLR}UTF8GetNextCharFromStream; FCharacterWriter := {$IFNDEF CLR}@{$ENDIF ~CLR}UTF8SetNextCharToStream; SetLength(FBOM, Length(BOM_UTF8)); for I := Low(BOM_UTF8) to High(BOM_UTF8) do FBOM[I - Low(BOM_UTF8)] := BOM_UTF8[I]; end; //=== { TJclUTF16Stream } ===================================================== constructor TJclUTF16Stream.Create(AStream: TStream; AOwnsStream: Boolean); var I: Integer; begin inherited Create(AStream, AOwnsStream); FCharacterReader := {$IFNDEF CLR}@{$ENDIF ~CLR}UTF16GetNextCharFromStream; FCharacterWriter := {$IFNDEF CLR}@{$ENDIF ~CLR}UTF16SetNextCharToStream; SetLength(FBOM, Length(BOM_UTF16_LSB)); for I := Low(BOM_UTF16_LSB) to High(BOM_UTF16_LSB) do FBOM[I - Low(BOM_UTF16_LSB)] := BOM_UTF16_LSB[I]; end; //=== { TJclAutoStream } ====================================================== constructor TJclAutoStream.Create(AStream: TStream; AOwnsStream: Boolean); var Pos: Int64; I, MaxLength, ReadLength: Integer; BOM: array of Byte; begin inherited Create(AStream, AOwnsStream); MaxLength := Length(BOM_UTF8); if MaxLength < Length(BOM_UTF16_LSB) then MaxLength := Length(BOM_UTF16_LSB); Pos := FPosition; SetLength(BOM, MaxLength); {$IFDEF CLR} ReadLength := Read(BOM, Low(BOM), Length(BOM)) div SizeOf(BOM[0]); {$ELSE ~CLR} ReadLength := Read(BOM[0], Length(BOM) * SizeOf(BOM[0])) div SizeOf(BOM[0]); {$ENDIF ~CLR} FEncoding := seAuto; // try UTF8 BOM if (FEncoding = seAuto) and (ReadLength >= Length(BOM_UTF8) * SizeOf(BOM_UTF8[0])) then begin FEncoding := seUTF8; for I := Low(BOM_UTF8) to High(BOM_UTF8) do if BOM[I - Low(BOM_UTF8)] <> BOM_UTF8[I] then begin FEncoding := seAuto; Break; end; end; // try UTF16 BOM if (FEncoding = seAuto) and (ReadLength >= Length(BOM_UTF16_LSB) * SizeOf(BOM_UTF16_LSB[0])) then begin FEncoding := seUTF16; for I := Low(BOM_UTF16_LSB) to High(BOM_UTF16_LSB) do if BOM[I - Low(BOM_UTF8)] <> BOM_UTF16_LSB[I] then begin FEncoding := seAuto; Break; end; end; case FEncoding of seUTF8: begin SetLength(FBOM, Length(BOM_UTF8)); for I := Low(BOM_UTF8) to High(BOM_UTF8) do FBOM[I - Low(BOM_UTF8)] := BOM_UTF8[I]; FCharacterReader := {$IFNDEF CLR}@{$ENDIF ~CLR}UTF8GetNextCharFromStream; FCharacterWriter := {$IFNDEF CLR}@{$ENDIF ~CLR}UTF8SetNextCharToStream; FPosition := Pos + Length(BOM_UTF8) * SizeOf(BOM_UTF8[0]); end; seUTF16: begin SetLength(FBOM, Length(BOM_UTF16_LSB)); for I := Low(BOM_UTF16_LSB) to High(BOM_UTF16_LSB) do FBOM[I - Low(BOM_UTF16_LSB)] := BOM_UTF16_LSB[I]; FCharacterReader := {$IFNDEF CLR}@{$ENDIF ~CLR}UTF16GetNextCharFromStream; FCharacterWriter := {$IFNDEF CLR}@{$ENDIF ~CLR}UTF16SetNextCharToStream; FPosition := Pos + Length(BOM_UTF16_LSB) * SizeOf(BOM_UTF16_LSB[0]); end; seAuto, seAnsi: begin // defaults to Ansi FEncoding := seAnsi; SetLength(FBOM, 0); FCharacterReader := {$IFNDEF CLR}@{$ENDIF ~CLR}AnsiGetNextCharFromStream; FCharacterWriter := {$IFNDEF CLR}@{$ENDIF ~CLR}AnsiSetNextCharToStream; FPosition := Pos; end; end; FPeekPosition := FPosition; end; function TJclAutoStream.SkipBOM: LongInt; begin // already skipped to determine encoding Result := 0; end; {$IFDEF UNITVERSIONING} initialization RegisterUnitVersion(HInstance, UnitVersioning); finalization UnregisterUnitVersion(HInstance); {$ENDIF UNITVERSIONING} end.