diff --git a/uutlStreamHelper.pas b/uutlStreamHelper.pas index 15c6254..f98c1c2 100644 --- a/uutlStreamHelper.pas +++ b/uutlStreamHelper.pas @@ -11,7 +11,7 @@ unit uutlStreamHelper; interface uses - Classes, Contnrs, syncobjs; + SysUtils, Classes, Contnrs, syncobjs; type TutlFourCC = string[4]; @@ -72,21 +72,23 @@ type procedure WriteLine(A: AnsiString); end; - { TutlReadBufferStream } + { TutlPagedBufferStream } - TutlReadBufferStream = class(TStream) + TutlPagedBufferStream = class(TOwnerStream) + public const + DEFAULT_BUFLEN = 4096*16; private - FBaseStream: TStream; - FBuffer: Pointer; - FBufferValid: boolean; - FBufferStart, FBufferLen, FBufferAvail: Int64; - FPosition: int64; - FOwnsStream: Boolean; + FVirtualSize, FVirtualPosition: Int64; + FBuffer: TBytes; + FBufferStart: Int64; + FBufferModified: boolean; protected function GetSize: Int64; override; procedure SetSize(const NewSize: Int64); override; + procedure ReMapBuffer; + procedure FlushBuffer; public - constructor Create(const BaseStream: TStream; const BufferSize: Cardinal; const aOwnsStream: Boolean = false); + constructor Create(const BaseStream: TStream; const BufferSize: Cardinal = DEFAULT_BUFLEN; const aOwnsStream: Boolean = false); destructor Destroy; override; function Read(var Buffer; Count: Integer): Integer; override; function Write(const Buffer; Count: Integer): Integer; override; @@ -138,7 +140,7 @@ type implementation -uses SysUtils,RtlConsts, uutlExceptions; +uses RtlConsts, uutlExceptions, Math; type TPositionData = class @@ -378,96 +380,129 @@ begin WriteAnsiBytes(A + sLineBreak); end; -{ TutlReadBufferStream } +{ TutlPagedBufferStream } -constructor TutlReadBufferStream.Create(const BaseStream: TStream; const BufferSize: Cardinal; +constructor TutlPagedBufferStream.Create(const BaseStream: TStream; const BufferSize: Cardinal; const aOwnsStream: Boolean); begin - inherited Create; - FBaseStream:= BaseStream; - FBufferLen:= BufferSize; - FBuffer:= GetMemory(FBufferLen); - FOwnsStream := aOwnsStream; - FPosition:= 0; + inherited Create(BaseStream); + SourceOwner:= aOwnsStream; + SetLength(FBuffer, BufferSize); + FVirtualPosition:= 0; + FVirtualSize:= Source.Size; + FBufferStart:= -1; + ReMapBuffer; end; -destructor TutlReadBufferStream.Destroy; +destructor TutlPagedBufferStream.Destroy; begin - FBufferValid:= false; - if (FOwnsStream) then - FBaseStream.Free; - FreeMemory(FBuffer); + FlushBuffer; + SetLength(FBuffer, 0); inherited; end; -function TutlReadBufferStream.Seek(Offset: Integer; Origin: Word): Integer; +function TutlPagedBufferStream.Seek(Offset: Integer; Origin: Word): Integer; begin case Origin of - soFromBeginning: FPosition := Offset; - soFromCurrent: Inc(FPosition, Offset); - soFromEnd: FPosition := Size + Offset; - end; - Result := FPosition; + soFromBeginning: FVirtualPosition := Offset; + soFromCurrent: Inc(FVirtualPosition, Offset); + soFromEnd: FVirtualPosition := Size + Offset; + end; + ReMapBuffer; + Result := FVirtualPosition; end; -function TutlReadBufferStream.GetSize: Int64; +function TutlPagedBufferStream.GetSize: Int64; begin - Result:= FBaseStream.Size; + Result:= FVirtualSize; end; -procedure TutlReadBufferStream.SetSize(const NewSize: Int64); +procedure TutlPagedBufferStream.SetSize(const NewSize: Int64); begin - FBaseStream.Size:= NewSize; + FVirtualSize:= NewSize; + Source.Size:= NewSize; + if Position > FVirtualSize then + Position:= FVirtualSize; end; -function TutlReadBufferStream.Write(const Buffer; Count: Integer): Integer; +function TutlPagedBufferStream.Write(const Buffer; Count: Integer): Integer; +var + bw, c: Int64; + bp: Pointer; begin - FBufferValid:= false; - FBaseStream.Position:= FPosition; - Result:= FBaseStream.Write(Buffer, Count); - FPosition:= FBaseStream.Position; + bw:= 0; + bp:= @Buffer; + while (bw < Count) do begin + ReMapBuffer; + // Wie viel Daten können wir schreiben? + c:= Min(Count - bw, Length(FBuffer) - (FVirtualPosition-FBufferStart)); + + // das schreiben und buffer weiterschieben + Move(bp^, FBuffer[FVirtualPosition-FBufferStart], c); + Inc(Bp, c); + Inc(bw, c); + Inc(FVirtualPosition, c); + if FVirtualPosition > FVirtualSize then + FVirtualSize:= FVirtualPosition; + end; + if bw > 0 then + FBufferModified:= true; + Result:= bw; end; -function TutlReadBufferStream.Read(var Buffer; Count: Integer): Integer; +function TutlPagedBufferStream.Read(var Buffer; Count: Integer): Integer; var - rp, br, c: Int64; + br, c: Int64; bp: Pointer; begin br:= 0; bp:= @Buffer; - while (br < Count) and (FPosition rp) then begin - // Segment holen - FBaseStream.Position:= rp; - FBufferAvail:= FBaseStream.Read(FBuffer^, FBufferLen); - FBufferStart:= rp; - FBufferValid:= true; - end; - + while (br < Count) and (FVirtualPosition FBufferAvail - (FPosition-FBufferStart) then - c:= FBufferAvail - (FPosition-FBufferStart); + c:= Min(Min(Count - br, Length(FBuffer) - (FVirtualPosition-FBufferStart)), FVirtualSize - FVirtualPosition); // das rausholen und buffer weiterschieben - {$IFDEF FPC} - // FPC: kein Cast, direkt mit Pointer in richtiger Größe rechnen - Move(Pointer(FBuffer + (FPosition-FBufferStart))^, bp^, c); + Move(FBuffer[FVirtualPosition-FBufferStart], bp^, c); Inc(Bp, c); - {$ELSE} - // Delphi ist eh nur i386, also fix 32bit - Move(Pointer(Cardinal(FBuffer) + (FPosition-FBufferStart))^, bp^, c); - Inc(Cardinal(Bp), c); - {$ENDIF} Inc(br, c); - Inc(FPosition, c); + Inc(FVirtualPosition, c); end; Result:= br; end; +procedure TutlPagedBufferStream.ReMapBuffer; +var + newbs: Int64; + inbuf: Int64; +begin + // Welches Buffer-Segment wird gesucht? + newbs:= (FVirtualPosition div Length(FBuffer)) * Length(FBuffer); + // ist das das aktuelle? + if FBufferStart <> newbs then begin + FlushBuffer; + // Segment holen + Source.Position:= newbs; + inbuf:= Min(Length(FBuffer), FVirtualSize - newbs); + Source.ReadBuffer(FBuffer[0], inbuf); + FBufferStart:= newbs; + FBufferModified:= false; + end; +end; + +procedure TutlPagedBufferStream.FlushBuffer; +var + towrite: Int64; +begin + if not FBufferModified then + // Nothing to do + Exit; + + Source.Position:= FBufferStart; + towrite:= Min(Length(FBuffer), FVirtualSize - FBufferStart); + Source.WriteBuffer(FBuffer[0], towrite); +end; + { TutlFIFOStream } constructor TutlFIFOStream.Create(const aLockFree: boolean);