| @@ -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<Size) do begin | |||
| // Welches Buffer-Segment wird gesucht? | |||
| rp:= (FPosition div FBufferLen) * FBufferLen; | |||
| // ist das das aktuelle? | |||
| if not FBufferValid or (FBufferStart <> rp) then begin | |||
| // Segment holen | |||
| FBaseStream.Position:= rp; | |||
| FBufferAvail:= FBaseStream.Read(FBuffer^, FBufferLen); | |||
| FBufferStart:= rp; | |||
| FBufferValid:= true; | |||
| end; | |||
| while (br < Count) and (FVirtualPosition<FVirtualSize) do begin | |||
| ReMapBuffer; | |||
| // Wie viel Daten daraus brauchen wir bzw. können wir kriegen? | |||
| c:= Count - br; | |||
| if c > 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); | |||