| @@ -11,7 +11,7 @@ unit uutlStreamHelper; | |||||
| interface | interface | ||||
| uses | uses | ||||
| Classes, Contnrs, syncobjs; | |||||
| SysUtils, Classes, Contnrs, syncobjs; | |||||
| type | type | ||||
| TutlFourCC = string[4]; | TutlFourCC = string[4]; | ||||
| @@ -72,21 +72,23 @@ type | |||||
| procedure WriteLine(A: AnsiString); | procedure WriteLine(A: AnsiString); | ||||
| end; | end; | ||||
| { TutlReadBufferStream } | |||||
| { TutlPagedBufferStream } | |||||
| TutlReadBufferStream = class(TStream) | |||||
| TutlPagedBufferStream = class(TOwnerStream) | |||||
| public const | |||||
| DEFAULT_BUFLEN = 4096*16; | |||||
| private | 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 | protected | ||||
| function GetSize: Int64; override; | function GetSize: Int64; override; | ||||
| procedure SetSize(const NewSize: Int64); override; | procedure SetSize(const NewSize: Int64); override; | ||||
| procedure ReMapBuffer; | |||||
| procedure FlushBuffer; | |||||
| public | 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; | destructor Destroy; override; | ||||
| function Read(var Buffer; Count: Integer): Integer; override; | function Read(var Buffer; Count: Integer): Integer; override; | ||||
| function Write(const Buffer; Count: Integer): Integer; override; | function Write(const Buffer; Count: Integer): Integer; override; | ||||
| @@ -138,7 +140,7 @@ type | |||||
| implementation | implementation | ||||
| uses SysUtils,RtlConsts, uutlExceptions; | |||||
| uses RtlConsts, uutlExceptions, Math; | |||||
| type | type | ||||
| TPositionData = class | TPositionData = class | ||||
| @@ -378,96 +380,129 @@ begin | |||||
| WriteAnsiBytes(A + sLineBreak); | WriteAnsiBytes(A + sLineBreak); | ||||
| end; | end; | ||||
| { TutlReadBufferStream } | |||||
| { TutlPagedBufferStream } | |||||
| constructor TutlReadBufferStream.Create(const BaseStream: TStream; const BufferSize: Cardinal; | |||||
| constructor TutlPagedBufferStream.Create(const BaseStream: TStream; const BufferSize: Cardinal; | |||||
| const aOwnsStream: Boolean); | const aOwnsStream: Boolean); | ||||
| begin | 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; | end; | ||||
| destructor TutlReadBufferStream.Destroy; | |||||
| destructor TutlPagedBufferStream.Destroy; | |||||
| begin | begin | ||||
| FBufferValid:= false; | |||||
| if (FOwnsStream) then | |||||
| FBaseStream.Free; | |||||
| FreeMemory(FBuffer); | |||||
| FlushBuffer; | |||||
| SetLength(FBuffer, 0); | |||||
| inherited; | inherited; | ||||
| end; | end; | ||||
| function TutlReadBufferStream.Seek(Offset: Integer; Origin: Word): Integer; | |||||
| function TutlPagedBufferStream.Seek(Offset: Integer; Origin: Word): Integer; | |||||
| begin | begin | ||||
| case Origin of | 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; | end; | ||||
| function TutlReadBufferStream.GetSize: Int64; | |||||
| function TutlPagedBufferStream.GetSize: Int64; | |||||
| begin | begin | ||||
| Result:= FBaseStream.Size; | |||||
| Result:= FVirtualSize; | |||||
| end; | end; | ||||
| procedure TutlReadBufferStream.SetSize(const NewSize: Int64); | |||||
| procedure TutlPagedBufferStream.SetSize(const NewSize: Int64); | |||||
| begin | begin | ||||
| FBaseStream.Size:= NewSize; | |||||
| FVirtualSize:= NewSize; | |||||
| Source.Size:= NewSize; | |||||
| if Position > FVirtualSize then | |||||
| Position:= FVirtualSize; | |||||
| end; | end; | ||||
| function TutlReadBufferStream.Write(const Buffer; Count: Integer): Integer; | |||||
| function TutlPagedBufferStream.Write(const Buffer; Count: Integer): Integer; | |||||
| var | |||||
| bw, c: Int64; | |||||
| bp: Pointer; | |||||
| begin | 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; | end; | ||||
| function TutlReadBufferStream.Read(var Buffer; Count: Integer): Integer; | |||||
| function TutlPagedBufferStream.Read(var Buffer; Count: Integer): Integer; | |||||
| var | var | ||||
| rp, br, c: Int64; | |||||
| br, c: Int64; | |||||
| bp: Pointer; | bp: Pointer; | ||||
| begin | begin | ||||
| br:= 0; | br:= 0; | ||||
| bp:= @Buffer; | 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? | // 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 | // 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); | 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(br, c); | ||||
| Inc(FPosition, c); | |||||
| Inc(FVirtualPosition, c); | |||||
| end; | end; | ||||
| Result:= br; | Result:= br; | ||||
| end; | 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 } | { TutlFIFOStream } | ||||
| constructor TutlFIFOStream.Create(const aLockFree: boolean); | constructor TutlFIFOStream.Create(const aLockFree: boolean); | ||||