(* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * http://www.opensource.org/licenses/cddl1.php. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * http://www.opensource.org/licenses/cddl1.php. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2007 Andreas Schneider *) {@abstract(This unit contains procedures and classes to help with stream handling. It can be used to ease copying of streams and to assist in writing and reading specialized types to/from streams.) @bold(Warning!!!)@br Due to a problem with generics in FPC 2.2.0 I introduced @link(TStreamType) as workaround to reference the actual type of stream used inside the @link(TStreamWrapper). @author(Andreas Schneider ) @created(2007-07-08) @lastmod(2007-11-14)} unit UStreamHelper; {$mode objfpc}{$H+} interface uses Classes, RtlConsts, SysUtils; type {@name is the stub for the method which will handle the OnProgress callbacks. @param(ATotal Specifies the complete size of the operation.) @param(ACurrent Specifies the current position during the operation.)} TOnProgressEvent = procedure(ATotal, ACurrent: Cardinal) of object; { TFifoStream } {@abstract(The @name contains special handling for queuing and dequeing. It is meant to be used as a network queue.)} TFifoStream = class(TStream) destructor Destroy; override; protected FMemory: Pointer; FSize, FRealSize, FPosition, FLockOffset: Longint; FCapacity: Longint; procedure SetCapacity(ANewCapacity: Longint); procedure SetPointer(APtr: Pointer; ASize: Longint); function GetOptimalCapacity(ANewCapacity: Longint): Longint; function Realloc(var NewCapacity: Longint): Pointer; virtual; property Capacity: Longint read FCapacity write SetCapacity; public function GetSize: Int64; override; function Read(var Buffer; ACount: Longint): Longint; override; function Write(const Buffer; ACount: Longint): Longint; override; function Seek(AOffset: Longint; AOrigin: Word): Longint; override; procedure LoadFromStream(AStream: TStream); procedure SaveToStream(AStream: TStream); procedure LoadFromFile(const FileName: string); procedure SaveToFile(const FileName: string); procedure SetSize(ANewSize: Longint); override; procedure Clear; procedure Dequeue(ACount: Longint); // = class(TObject{, IStream}) {@abstract(@name implements @link(IStream) and offers a bunch of functions to ease reading and writing special types (like @link(Integer)s or @link(String)s.))} TStreamWrapper = class(TObject) constructor Create(AStream: TStreamType; AOwnsStream: Boolean = True); // 0 do begin if (ACount > SizeOf(buffer)) then i := SizeOf(Buffer) else i := ACount; i := ASource.Read(buffer, i); i := ATarget.Write(buffer, i); if i = 0 then break; Dec(ACount, i); Inc(Result, i); if Assigned(AOnProgress) then AOnProgress(targetSize, Result); end; end; { TFifoStream } const TMSGrow = 4096; { Use 4k blocks. } destructor TFifoStream.Destroy; begin Clear; inherited Destroy; end; procedure TFifoStream.SetCapacity(ANewCapacity: Longint); begin SetPointer(Realloc(ANewCapacity), FSize); FCapacity := ANewCapacity; end; procedure TFifoStream.SetPointer(APtr: Pointer; ASize: Longint); begin FMemory := APtr; FSize := ASize; end; function TFifoStream.GetOptimalCapacity(ANewCapacity: Longint): Longint; begin Result := ANewCapacity; if Result <= 0 then Result := 0 else begin // if growing, grow at least a quarter if (Result > FCapacity) and (Result < (5 * FCapacity) div 4) then Result := (5 * FCapacity) div 4; // round off to block size. Result := (Result + (TMSGrow-1)) and not (TMSGROW-1); end; end; function TFifoStream.Realloc(var NewCapacity: Longint): Pointer; begin NewCapacity := GetOptimalCapacity(NewCapacity); // Only now check ! if NewCapacity = FCapacity then Result := FMemory else begin Result := ReAllocMem(FMemory, NewCapacity); if (Result = nil) and (NewCapacity > 0) then raise EStreamError.Create(SMemoryStreamError); end; end; function TFifoStream.GetSize: Int64; begin Result := FSize; end; function TFifoStream.Read(var Buffer; ACount: Longint): Longint; begin Result := 0; If (FSize > 0) and (FPosition - FLockOffset < FSize) then begin Result := FSize - (FPosition - FLockOffset); if Result > ACount then Result := ACount; Move((FMemory + FPosition)^, Buffer, Result); FPosition := FPosition + Result; end; end; function TFifoStream.Write(const Buffer; ACount: Longint): Longint; var NewPos: Longint; begin Unlock; if ACount = 0 then Exit(0); NewPos := FPosition + ACount; if NewPos > FSize then begin if NewPos > FCapacity then SetCapacity(NewPos); FSize := NewPos; end; System.Move(Buffer, (FMemory + FPosition)^, ACount); FPosition := NewPos; Result := ACount; end; function TFifoStream.Seek(AOffset: Longint; AOrigin: Word): Longint; begin case AOrigin of soFromBeginning : FPosition := AOffset + FLockOffset; soFromEnd : FPosition := FSize + AOffset + FLockOffset; soFromCurrent : FPosition := FPosition + AOffset; end; Result := FPosition - FLockOffset; end; procedure TFifoStream.LoadFromStream(AStream: TStream); begin Unlock; AStream.Position := 0; SetSize(AStream.Size); If FSize > 0 then AStream.ReadBuffer(FMemory^,FSize); end; procedure TFifoStream.SaveToStream(AStream: TStream); begin if FSize > 0 then AStream.WriteBuffer((FMemory + FLockOffset)^, FSize); end; procedure TFifoStream.LoadFromFile(const FileName: string); var S: TFileStream; begin S := TFileStream.Create(FileName, fmOpenRead); try LoadFromStream(S); finally S.free; end; end; procedure TFifoStream.SaveToFile(const FileName: string); var S: TFileStream; begin S := TFileStream.Create(FileName, fmCreate); try SaveToStream(S); finally S.free; end; end; procedure TFifoStream.SetSize(ANewSize: Longint); begin Unlock; SetCapacity(ANewSize); FSize := ANewSize; if FPosition > FSize then FPosition := FSize; end; procedure TFifoStream.Clear; begin FSize := 0; FRealSize := 0; FPosition := 0; FLockOffset := 0; SetCapacity(0); end; procedure TFifoStream.Dequeue(ACount: Longint); var newCapacity, newSize: Longint; queue, newMemory: Pointer; begin Unlock; if ACount >= FSize then begin Size := 0; Exit; end; queue := FMemory + ACount; newSize := FSize - ACount; newCapacity := GetOptimalCapacity(newSize); if newCapacity <> FCapacity then begin newMemory := GetMem(newCapacity); System.Move(queue^, newMemory^, newSize); if (newMemory = nil) and (newCapacity > 0) then raise EStreamError.Create(SMemoryStreamError); FreeMem(FMemory); FMemory := newMemory; FCapacity := newCapacity; end else System.Move(queue^, FMemory^, newSize); FSize := newSize; if FPosition > ACount then Dec(FPosition, ACount) else FPosition := 0; end; procedure TFifoStream.Enqueue(const Buffer; ACount: Longint); var oldPos: Int64; begin Unlock; oldPos := FPosition; FPosition := FSize; Write(Buffer, ACount); FPosition := oldPos; end; procedure TFifoStream.Lock(AOffset, ASize: Longint); begin if (FLockOffset <> 0) or (FRealSize <> 0) then Exit; FLockOffset := AOffset; FRealSize := FSize; FSize := ASize; end; procedure TFifoStream.Unlock; begin if (FLockOffset = 0) and (FRealSize = 0) then Exit; FLockOffset := 0; FSize := FRealSize; FRealSize := 0; end; { TStreamWrapper } constructor TStreamWrapper.Create(AStream: TStreamType; AOwnsStream: Boolean); begin inherited Create; FStream := TStream(AStream); FOwnsStream := AOwnsStream; end; destructor TStreamWrapper.Destroy; begin if FOwnsStream and Assigned(FStream) then FreeAndNil(FStream); inherited Destroy; end; function TStreamWrapper.GetStream: TStreamType; begin Result := TStreamType(FStream); end; procedure TStreamWrapper.SetStream(AStream: TStreamType); begin FStream := TStream(AStream); end; function TStreamWrapper.ReadBoolean: Boolean; begin if not Assigned(FStream) then Exit(False); FStream.Read(Result, SizeOf(Boolean)); end; function TStreamWrapper.ReadByte: Byte; begin if not Assigned(FStream) then Exit(0); FStream.Read(Result, SizeOf(Byte)); end; function TStreamWrapper.ReadCardinal: Cardinal; begin if not Assigned(FStream) then Exit(0); FStream.Read(Result, SizeOf(Cardinal)); end; function TStreamWrapper.ReadInteger: Integer; begin if not Assigned(FStream) then Exit(0); FStream.Read(Result, SizeOf(Integer)); end; function TStreamWrapper.ReadInt64: Int64; begin if not Assigned(FStream) then Exit(0); FStream.Read(Result, SizeOf(Int64)); end; function TStreamWrapper.ReadSmallInt: SmallInt; begin if not Assigned(FStream) then Exit(0); FStream.Read(Result, SizeOf(SmallInt)); end; function TStreamWrapper.ReadWord: Word; begin if not Assigned(FStream) then Exit(0); FStream.Read(Result, SizeOf(Word)); end; function TStreamWrapper.ReadString: string; begin if not Assigned(FStream) then Exit(''); Result := ReadStringFixed(ReadInteger); end; function TStreamWrapper.ReadStringFixed(ALength: Integer): string; begin if not Assigned(FStream) then Exit(''); SetLength(Result, ALength); FStream.Read(PChar(Result)^, ALength); end; procedure TStreamWrapper.WriteBoolean(AValue: Boolean); begin if not Assigned(FStream) then Exit; FStream.Write(AValue, SizeOf(Boolean)); end; procedure TStreamWrapper.WriteByte(AValue: Byte); begin if not Assigned(FStream) then Exit; FStream.Write(AValue, SizeOf(Byte)); end; procedure TStreamWrapper.WriteCardinal(AValue: Cardinal); begin if not Assigned(FStream) then Exit; FStream.Write(AValue, SizeOf(Cardinal)); end; procedure TStreamWrapper.WriteInteger(AValue: Integer); begin if not Assigned(FStream) then Exit; FStream.Write(AValue, SizeOf(Integer)); end; procedure TStreamWrapper.WriteInt64(AValue: Int64); begin if not Assigned(FStream) then Exit; FStream.Write(AValue, SizeOf(Int64)); end; procedure TStreamWrapper.WriteSmallInt(AValue: SmallInt); begin if not Assigned(FStream) then Exit; FStream.Write(AValue, SizeOf(SmallInt)); end; procedure TStreamWrapper.WriteWord(AValue: Word); begin if not Assigned(FStream) then Exit; FStream.Write(AValue, SizeOf(Word)); end; procedure TStreamWrapper.WriteString(AValue: string); var stringLength: Integer; begin if not Assigned(FStream) then Exit; stringLength := Length(AValue); WriteInteger(stringLength); WriteStringFixed(AValue, stringLength); end; procedure TStreamWrapper.WriteStringFixed(AValue: string; ALength: Integer); begin if not Assigned(FStream) then Exit; FStream.Write(PChar(AValue)^, ALength); end; function TStreamWrapper.Read(ABuffer: PByte; ACount: Cardinal): Cardinal; begin Result := FStream.Read(ABuffer^, ACount); end; function TStreamWrapper.Write(ABuffer: PByte; ACount: Cardinal): Cardinal; begin Result := FStream.Write(ABuffer^, ACount); end; procedure TStreamWrapper.Skip(ACount: Cardinal); begin FStream.Seek(ACount, soFromCurrent); end; end.