// I, Danny Milosavljevic, hereby release this code into the public domain. unit custom_decoders; {$M+} {$MODE OBJFPC} interface uses sysutils, classes, ring_buffers; type { does not actually READ from Source. Usage: override ReadFromSourceAndDecode. in it, call Buffer.Write() } TCustomDecoder = class(TOwnerStream) private fBEOF : Boolean; fBSourceEOF : Boolean; fBuffer : TRingBuffer; // decoded data. fEncodedBuffer : array[0..((1024 * 5 + 3) div 4) - 1] of Byte; // 1280. // could also be put on the stack, doesn't need to persist between calls. protected // sigh... fPosition : Int64; published constructor Create(aSource : TStream); procedure Finish(); virtual; function FinishedP() : Boolean; virtual; protected function GetPosition() : Int64; override; property Buffer : TRingBuffer read fBuffer; property BSourceEOF : Boolean read fBSourceEOF write fBSourceEOF; // set this is you reached some end marker. procedure ReadFromSourceAndDecode(aDecodedBufferAvailableByteCount : Cardinal); virtual; abstract; // override this. public destructor Destroy(); override; function Seek(aOffset : longint; aOrigin : word) : longint; override; function Seek(const aOffset: Int64; aOrigin: TSeekOrigin): Int64; override; overload; function Read(var aBuffer; aCount : longint) : longint; override; end; { reads directly and automagically from Source, calls DecodeBlock. useful for byte-oriented data sources, mostly. Usage: override DecodeBlock. in it, call Buffer.Write. } TCustomDirectDecoder = class(TCustomDecoder) protected function GetPermittedReadCount(aBufferAvailableSpace : Cardinal) : Cardinal; virtual; procedure ReadFromSourceAndDecode(aDecodedBufferAvailableByteCount : Cardinal); override; procedure DecodeBlock(aEncodedBuffer : PByte; aEncodedBufferCount : Cardinal); virtual; abstract; end; // TODO encoder... implementation { TCustomDecoder } constructor TCustomDecoder.Create(aSource : TStream); begin inherited Create(aSource); fBuffer := TRingBuffer.Create(); end; destructor TCustomDecoder.Destroy(); begin Self.Finish(); FreeAndNil(fBuffer); inherited Destroy; end; function TCustomDecoder.FinishedP() : Boolean; begin Result := True; end; procedure TCustomDecoder.Finish(); begin end; function TCustomDecoder.Seek(const aOffset: Int64; aOrigin: TSeekOrigin): Int64; begin if (aOrigin = soCurrent) and (aOffset = 0) then begin // get position. Result := fPosition; Exit; end; raise EReadError.Create('could not seek...'); //assert(fState in [ascInitial, ascNoEncodedChar]); //Result := inherited Seek(aOffset, aOrigin); // bad idea. end; function TCustomDecoder.Seek(aOffset : longint; aOrigin : word) : longint; begin Result := Self.Seek(Int64(aOffset), TSeekOrigin(aOrigin)); end; function TCustomDecoder.GetPosition() : Int64; begin Result := fPosition; end; function TCustomDecoder.Read(var aBuffer; aCount : longint) : longint; var vAvailableCount : Cardinal; vBuffer : PByte; vBufferCount : Cardinal; begin vBuffer := @aBuffer; Result := 0; if fBEOF then begin Exit; end; repeat // first use up the buffer contents as far as possible. if aCount <= 0 then Break; vBufferCount := fBuffer.Read(vBuffer^, aCount); assert(vBufferCount <= aCount); Inc(vBuffer, vBufferCount); Dec(aCount, vBufferCount); Inc(Result, vBufferCount); if fBSourceEOF and (vBufferCount = 0) then begin fBEOF := True; Break; end; if aCount <= 0 then Break; // here, aCount contains the REMAINING request and the buffer is either empty or there wasn't that much needed anyway (in the latter case the Exit above finished the function). // if then, there's still something needed, fill the buffer only as far as we need to. assert(fBuffer.FillCount = 0); vAvailableCount := fBuffer.Size - fBuffer.FillCount; // already done: if fBSourceEOF Self.ReadFromSourceAndDecode(vAvailableCount); until (aCount <= 0) or (fBEOF); // or (vPermittedReadCount = 0); Inc(fPosition, Result); end; { TCustomDirectDecoder } function TCustomDirectDecoder.GetPermittedReadCount(aBufferAvailableSpace : Cardinal) : Cardinal; begin Result := aBufferAvailableSpace shr 2; end; procedure TCustomDirectDecoder.ReadFromSourceAndDecode(aDecodedBufferAvailableByteCount : Cardinal); var vEncodedBufferCount : Cardinal; vPermittedReadCount : Cardinal; begin vPermittedReadCount := GetPermittedReadCount(aDecodedBufferAvailableByteCount); {if aCount < vAvailableCount then begin vAvailableCount := aCount;} if vPermittedReadCount = 0 then // ??? raise EReadError.Create('was not allowed to read anything from the source'); vEncodedBufferCount := 0; if not fBSourceEOF then vEncodedBufferCount := Source.Read(fEncodedBuffer[0], vPermittedReadCount); if (vEncodedBufferCount = 0) then begin // EOF fBSourceEOF := True; if not FinishedP() then Finish() // make sure we catch the "virtual characters". This could fill the buffer a little bit. {else fBEOF := True}; Exit; //Continue; end; Self.DecodeBlock(@fEncodedBuffer[0], vEncodedBufferCount); end; initialization assert(Sizeof(Cardinal) >= 4); end.