// I, Danny Milosavljevic, hereby release this code into the public domain. unit ring_buffers; {$MODE OBJFPC} {$M+} interface type TRingBuffer = class private fBuffer : array[0..8191] of Byte; // GIF LZW has 4K decompression size per code. fBufferReadPosition : Cardinal; fBufferWritePosition : Cardinal; fBufferFillCount : Cardinal; protected function GetBufferSize() : Cardinal; inline; published property FillCount : Cardinal read fBufferFillCount; property Size : Cardinal read GetBufferSize; procedure Write(const aBuffer; aSize : Cardinal); inline; function Read(var aBuffer; aSize : Cardinal) : Cardinal; inline; end; implementation { TRingBuffer } function TRingBuffer.GetBufferSize() : Cardinal; inline; begin Result := Length(fBuffer); end; procedure TRingBuffer.Write(const aBuffer; aSize : Cardinal); inline; var vBuffer : PByte; begin vBuffer := @aBuffer; // TODO optimize. while aSize > 0 do begin assert(fBufferFillCount < Length(fBuffer)); fBuffer[fBufferWritePosition] := vBuffer^; Inc(vBuffer); Inc(fBufferFillCount); Inc(fBufferWritePosition); if fBufferWritePosition >= Length(fBuffer) then fBufferWritePosition := 0; assert(fBufferWritePosition <> fBufferReadPosition); Dec(aSize); end; end; function TRingBuffer.Read(var aBuffer; aSize : Cardinal) : Cardinal; inline; var vBuffer : PByte; vBufferCount : Cardinal; vBufferCount1 : Cardinal; vBufferCount2 : Cardinal; begin vBuffer := @aBuffer; Result := 0; if fBufferFillCount > 0 then begin vBufferCount := aSize; // try to take as much as requested by the client... if vBufferCount > fBufferFillCount then // ... if possible. vBufferCount := fBufferFillCount; if fBufferReadPosition < fBufferWritePosition then begin { ------RXXXXXXW-------- } vBufferCount1 := fBufferWritePosition - fBufferReadPosition; // max count for the first Move. assert(vBufferCount <= vBufferCount1); Move(fBuffer[fBufferReadPosition], vBuffer^, vBufferCount); Inc(vBuffer, vBufferCount); end else begin { XXXW-----RXXXXXXXXXXXX } vBufferCount1 := Length(fBuffer) - fBufferReadPosition; // count for the first Move. if vBufferCount < vBufferCount1 then vBufferCount1 := vBufferCount; if vBufferCount1 > 0 then begin Move(fBuffer[fBufferReadPosition], vBuffer^, vBufferCount1); Inc(vBuffer, vBufferCount1); end; vBufferCount2 := vBufferCount - vBufferCount1; // remaining count for the second Move. if vBufferCount2 > 0 then begin Move(fBuffer[0], vBuffer^, vBufferCount2); Inc(vBuffer, vBufferCount2); end; end; Inc(fBufferReadPosition, vBufferCount); while fBufferReadPosition >= Length(fBuffer) do Dec(fBufferReadPosition, Length(fBuffer)); assert(fBufferFillCount >= vBufferCount); Dec(fBufferFillCount, vBufferCount); Inc(Result, vBufferCount); end; end; end.