310 lines
7.6 KiB
ObjectPascal
310 lines
7.6 KiB
ObjectPascal
{ sdStreams.pas
|
|
|
|
- TsdFastMemStream with improved capacity setting
|
|
- TsdStringStream
|
|
- TsdBufferWriter
|
|
|
|
Author: Nils Haeck M.Sc.
|
|
copyright (c) 2002 - 2011 SimDesign BV (www.simdesign.nl)
|
|
}
|
|
unit sdStreams;
|
|
|
|
{$ifdef lcl}{$MODE Delphi}{$endif}
|
|
|
|
{$define simdesign.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, sdDebug;
|
|
|
|
type
|
|
|
|
// TsdFastMemStream deals differently with capacity compared to a normal
|
|
// TMemoryStream; it increases the capacity with the natural growing function
|
|
// (fibonacci) each time, and has an initial capacity of $1000. The initial
|
|
// capacity is configurable with the create parameter.
|
|
TsdFastMemStream = class(TStream)
|
|
private
|
|
FMemory: Pointer;
|
|
FPosition: longint;
|
|
FFib1: longint;
|
|
FCapacity: longint;
|
|
FSize: longint;
|
|
protected
|
|
procedure SetCapacity(Value: longint);
|
|
procedure SetSize(NewSize: Longint); override;
|
|
public
|
|
constructor Create(InitialCapacity: longint = $1000);
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
function Read(var Buffer; Count: Longint): Longint; override;
|
|
function Write(const Buffer; Count: Longint): Longint; override;
|
|
function Seek(Offset: Longint; Origin: Word): Longint; override;
|
|
procedure LoadFromFile(AFilename: string);
|
|
procedure LoadFromStream(Stream: TStream);
|
|
procedure SaveToFile(AFilename: string);
|
|
procedure SaveToStream(Stream: TStream);
|
|
property Memory: Pointer read FMemory;
|
|
property Size: longint read FSize write SetSize;
|
|
end;
|
|
|
|
// Delphi's implementation of TStringStream is severely flawed, it does a SetLength
|
|
// on each write, which slows down everything to a crawl. This implementation over-
|
|
// comes this issue.
|
|
TsdStringStream = class(TsdFastMemStream)
|
|
public
|
|
constructor Create(const S: Utf8String);
|
|
function DataString: Utf8String;
|
|
end;
|
|
|
|
// TsdBufferWriter is a buffered stream that takes another stream (ASource)
|
|
// and writes only buffer-wise to it, and writes to the stream are first
|
|
// done to the buffer. This stream type can only support writing.
|
|
TsdBufferWriter = class(TsdFastMemStream)
|
|
private
|
|
FSource: TStream;
|
|
FChunkSize: integer;
|
|
FRawBuffer: array of byte;
|
|
FRawPosition: Integer;
|
|
protected
|
|
procedure WriteChunk(Count: integer);
|
|
public
|
|
// Create the buffered writer stream by passing the destination stream in ASource,
|
|
// this destination stream must already be initialized.
|
|
constructor Create(ASource: TStream; AChunkSize: integer);
|
|
destructor Destroy; override;
|
|
function Read(var Buffer; Count: Longint): Longint; override;
|
|
function Write(const Buffer; Count: Longint): Longint; override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ TsdFastMemStream }
|
|
|
|
procedure TsdFastMemStream.Clear;
|
|
begin
|
|
SetCapacity(0);
|
|
FSize := 0;
|
|
FPosition := 0;
|
|
end;
|
|
|
|
constructor TsdFastMemStream.Create(InitialCapacity: Integer);
|
|
begin
|
|
inherited Create;
|
|
FFib1 := InitialCapacity div 2;
|
|
FCapacity := InitialCapacity;
|
|
if FFib1 < 4 then
|
|
FFib1 := 4;
|
|
if FCapacity < 4 then
|
|
FCapacity := 4;
|
|
ReallocMem(FMemory, FCapacity);
|
|
end;
|
|
|
|
destructor TsdFastMemStream.Destroy;
|
|
begin
|
|
ReallocMem(FMemory, 0);
|
|
inherited;
|
|
end;
|
|
|
|
procedure TsdFastMemStream.LoadFromFile(AFilename: string);
|
|
var
|
|
Stream: TStream;
|
|
begin
|
|
Stream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
LoadFromStream(Stream);
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TsdFastMemStream.LoadFromStream(Stream: TStream);
|
|
var
|
|
Count: Longint;
|
|
begin
|
|
Stream.Position := 0;
|
|
Count := Stream.Size;
|
|
SetSize(Count);
|
|
if Count <> 0 then Stream.ReadBuffer(FMemory^, Count);
|
|
end;
|
|
|
|
function TsdFastMemStream.Read(var Buffer; Count: Integer): Longint;
|
|
begin
|
|
if (FPosition >= 0) and (Count >= 0) then
|
|
begin
|
|
Result := FSize - FPosition;
|
|
if Result > 0 then
|
|
begin
|
|
if Result > Count then
|
|
Result := Count;
|
|
Move(Pointer(Longint(FMemory) + FPosition)^, Buffer, Result);
|
|
Inc(FPosition, Result);
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := 0;
|
|
end;
|
|
|
|
procedure TsdFastMemStream.SaveToFile(AFilename: string);
|
|
var
|
|
Stream: TStream;
|
|
begin
|
|
Stream := TFileStream.Create(AFileName, fmCreate);
|
|
try
|
|
SaveToStream(Stream);
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TsdFastMemStream.SaveToStream(Stream: TStream);
|
|
begin
|
|
if FSize <> 0 then Stream.WriteBuffer(FMemory^, FSize);
|
|
end;
|
|
|
|
function TsdFastMemStream.Seek(Offset: Integer; Origin: Word): Longint;
|
|
begin
|
|
case Origin of
|
|
soFromBeginning: FPosition := Offset;
|
|
soFromCurrent: Inc(FPosition, Offset);
|
|
soFromEnd: FPosition := FSize + Offset;
|
|
end;
|
|
Result := FPosition;
|
|
end;
|
|
|
|
procedure TsdFastMemStream.SetCapacity(Value: longint);
|
|
// Fibonacci 0,1,1,2,3,5,8,... FCapacity is Fib2.
|
|
// Fibonacci is a natural growing function where
|
|
// 0 + 1 = 1; 1 + 1 = 2; 1 + 2 = 3; 2 + 3 = 5; etc
|
|
var
|
|
Fib3: longint;
|
|
begin
|
|
while FCapacity < Value do
|
|
begin
|
|
Fib3 := FFib1 + FCapacity;
|
|
FFib1 := FCapacity;
|
|
FCapacity := Fib3;
|
|
end;
|
|
ReallocMem(FMemory, FCapacity);
|
|
end;
|
|
|
|
procedure TsdFastMemStream.SetSize(NewSize: longint);
|
|
var
|
|
OldPosition: Longint;
|
|
begin
|
|
OldPosition := FPosition;
|
|
SetCapacity(NewSize);
|
|
FSize := NewSize;
|
|
if OldPosition > NewSize then
|
|
Seek(0, soFromEnd);
|
|
end;
|
|
|
|
function TsdFastMemStream.Write(const Buffer; Count: Integer): Longint;
|
|
var
|
|
NewPos: Longint;
|
|
begin
|
|
if (FPosition >= 0) and (Count >= 0) then
|
|
begin
|
|
NewPos := FPosition + Count;
|
|
if NewPos > 0 then
|
|
begin
|
|
if NewPos > FSize then
|
|
begin
|
|
if NewPos > FCapacity then
|
|
SetCapacity(NewPos);
|
|
FSize := NewPos;
|
|
end;
|
|
System.Move(Buffer, Pointer(Longint(FMemory) + FPosition)^, Count);
|
|
FPosition := NewPos;
|
|
Result := Count;
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := 0;
|
|
end;
|
|
|
|
{ TsdStringStream }
|
|
|
|
constructor TsdStringStream.Create(const S: Utf8String);
|
|
begin
|
|
inherited Create;
|
|
SetSize(length(S));
|
|
if Size > 0 then
|
|
begin
|
|
Write(S[1], Size);
|
|
Position := 0;
|
|
end;
|
|
end;
|
|
|
|
function TsdStringStream.DataString: Utf8String;
|
|
begin
|
|
SetLength(Result, Size);
|
|
if Size > 0 then
|
|
begin
|
|
Position := 0;
|
|
Read(Result[1], length(Result));
|
|
end;
|
|
end;
|
|
|
|
{ TsdBufferWriter }
|
|
|
|
constructor TsdBufferWriter.Create(ASource: TStream; AChunkSize: integer);
|
|
begin
|
|
inherited Create;
|
|
FSource := ASource;
|
|
FChunkSize := AChunkSize;
|
|
SetLength(FRawBuffer, FChunkSize);
|
|
end;
|
|
|
|
destructor TsdBufferWriter.Destroy;
|
|
begin
|
|
// write the last chunk, if any
|
|
WriteChunk(FRawPosition);
|
|
// free the rawbuffer
|
|
SetLength(FRawBuffer, 0);
|
|
inherited;
|
|
end;
|
|
|
|
function TsdBufferWriter.Read(var Buffer; Count: Integer): Longint;
|
|
begin
|
|
// not implemented
|
|
raise Exception.Create('not implemented');
|
|
end;
|
|
|
|
function TsdBufferWriter.Write(const Buffer; Count: Integer): Longint;
|
|
var
|
|
Idx, Siz: integer;
|
|
begin
|
|
// index in the source buffer
|
|
Idx := 0;
|
|
// remaining size
|
|
Siz := Count;
|
|
|
|
// surplus
|
|
while FRawPosition + Siz >= FChunkSize do
|
|
begin
|
|
Move(TByteArray(Buffer)[Idx], FRawBuffer[FRawPosition], FChunkSize - FRawPosition);
|
|
WriteChunk(FChunkSize);
|
|
dec(Siz, FChunkSize - FRawPosition);
|
|
inc(Idx, FChunkSize - FRawPosition);
|
|
FRawPosition := 0;
|
|
end;
|
|
|
|
// copy the raw buffer
|
|
Move(TByteArray(Buffer)[Idx], FRawBuffer[FRawPosition], Siz);
|
|
inc(FRawPosition, Siz);
|
|
|
|
Result := Count;
|
|
end;
|
|
|
|
procedure TsdBufferWriter.WriteChunk(Count: integer);
|
|
begin
|
|
if Count > 0 then
|
|
begin
|
|
FSource.WriteBuffer(FRawBuffer[0], Count);
|
|
end;
|
|
end;
|
|
|
|
end.
|