📄 idbuffer.pas
字号:
public
procedure Clear;
constructor Create; overload;
constructor Create(
AOnBytesRemoved: TIdBufferBytesRemoved
); overload;
constructor Create(
AGrowthFactor: Integer
); overload;
procedure CompactHead(
ACanShrink: Boolean = True
);
destructor Destroy; override;
// will extract number of bytes and treat as AnsiString though WideString will be returned in DotNet
function Extract(AByteCount: Integer = -1): string;
// all 3 extract routines append to existing data, if any
procedure ExtractToStream(AStream: TIdStreamVCL; AByteCount: Integer = -1);
procedure ExtractToIdBuffer(ABuffer: TIdBuffer; AByteCount: Integer = -1);
procedure ExtractToBytes(
var VBytes: TIdBytes;
AByteCount: Integer = -1;
AAppend: Boolean = True
);
function IndexOf(
const ABytes: TIdBytes;
AStartPos: Integer = 0
): Integer;
overload;
function IndexOf(
const AString: string;
AStartPos: Integer = 0
): Integer;
overload;
function PeekByte(
AIndex: Integer
): Byte;
procedure Remove(AByteCount: Integer);
procedure SaveToStream(AStream: TIdStream);
// Write
procedure Write(
const AString: string;
AEncoding: TIdEncoding = enDefault
); overload;
procedure Write(
ABytes: TIdBytes
); overload;
procedure Write(
AStream: TIdStreamVCL;
AByteCount: Integer = 0
); overload;
//
//Kudzu: I have removed the Bytes property. Do not add it back - it allowed "internal" access
// which caused comapacting or internal knoledge. Access via Extract or other such methods
// instead. Bytes could also be easily confused with FBytes intnernally and cause issues.
//
// Bytes also allowed direct acces without removing which could cause concurrency issues if
// the reference was kept.
//
property Capacity: Integer read GetCapacity write SetCapacity;
property Encoding: TIdEncoding read FEncoding write FEncoding;
property GrowthFactor: Integer read FGrowthFactor write FGrowthFactor;
property Size: Integer read FSize;
end;
implementation
uses
IdResourceStringsCore,
SysUtils;
{ TIdBuffer }
procedure TIdBuffer.CheckAdd(AByteCount : Integer);
begin
EIdTooMuchDataInBuffer.IfTrue(MaxInt - AByteCount < Size, RSTooMuchDataInBuffer);
end;
procedure TIdBuffer.CheckByteCount(var VByteCount : Integer);
begin
if VByteCount = -1 then begin
VByteCount := Size;
end else begin
EIdNotEnoughDataInBuffer.IfTrue(VByteCount > Size, RSNotEnoughDataInBuffer + ' ('
+ IntToStr(VByteCount) + '/' + IntToStr(Size) + ')');
end;
end;
procedure TIdBuffer.Clear;
begin
SetLength(FBytes, 0);
FHeadIndex := 0;
FSize := Length(FBytes);
end;
constructor TIdBuffer.Create(
AGrowthFactor: Integer
);
begin
Create;
FGrowthFactor := AGrowthFactor;
end;
constructor TIdBuffer.Create(
AOnBytesRemoved: TIdBufferBytesRemoved
);
begin
Create;
FOnBytesRemoved := AOnBytesRemoved;
end;
destructor TIdBuffer.Destroy;
begin
Clear;
inherited;
end;
function TIdBuffer.Extract(
AByteCount: Integer = -1
): string;
var
LBytes: TIdBytes;
begin
if AByteCount = -1 then begin
AByteCount := Size;
end;
if AByteCount > 0 then begin
ExtractToBytes(LBytes, AByteCount);
Result := BytesToString(LBytes);
end else begin
Result := '';
end;
end;
procedure TIdBuffer.ExtractToBytes(
var VBytes: TIdBytes;
AByteCount: Integer = -1;
AAppend: Boolean = True
);
var
LOldSize: Integer;
begin
if AByteCount = -1 then begin
AByteCount := Size;
end;
if AByteCount > 0 then begin
CheckByteCount(AByteCount);
if AAppend then begin
LOldSize := Length(VBytes);
SetLength(VBytes, LOldSize + AByteCount);
end else begin
LOldSize := 0;
if Length(VBytes) < AByteCount then begin
SetLength(VBytes, AByteCount);
end;
end;
CopyTIdBytes(FBytes, FHeadIndex, VBytes, LOldSize, AByteCount);
Remove(AByteCount);
end;
end;
procedure TIdBuffer.ExtractToIdBuffer(ABuffer: TIdBuffer; AByteCount: Integer);
var
LBytes: TIdBytes;
begin
if AByteCount = -1 then begin
AByteCount := Size;
end;
//TODO: Optimize this routine to directly copy from one to the other
ExtractToBytes(LBytes, AByteCount);
ABuffer.Write(LBytes);
end;
procedure TIdBuffer.ExtractToStream(AStream: TIdStreamVCL; AByteCount: Integer);
begin
if AByteCount = -1 then begin
AByteCount := Size;
end;
CompactHead;
CheckByteCount(AByteCount);
AStream.Write(FBytes, AByteCount);
Remove(AByteCount);
end;
procedure TIdBuffer.Remove(AByteCount: Integer);
begin
if AByteCount >= Size then begin
Clear;
end else begin
Inc(FHeadIndex, AByteCount);
Dec(FSize, AByteCount);
if FHeadIndex > GrowthFactor then begin
CompactHead;
end;
end;
if Assigned(FOnBytesRemoved) then begin
FOnBytesRemoved(Self, AByteCount);
end;
end;
procedure TIdBuffer.CompactHead(
ACanShrink: Boolean = True
);
begin
// Only try to compact if needed.
if FHeadIndex > 0 then begin
CopyTIdBytes(FBytes, FHeadIndex, FBytes, 0, Size);
FHeadIndex := 0;
if ACanShrink and (Capacity - Size - FHeadIndex > GrowthFactor) then begin
SetLength(FBytes, FHeadIndex + Size + GrowthFactor);
end;
end;
end;
procedure TIdBuffer.Write(ABytes: TIdBytes);
var
LByteLength: Integer;
begin
LByteLength := Length(ABytes);
CheckAdd(LByteLength);
if Size = 0 then begin
FBytes := ABytes;
FHeadIndex := 0;
FSize := Length(FBytes);
end else begin
CompactHead(False);
if (Capacity - Size - FHeadIndex) < LByteLength then begin
SetLength(FBytes, Size + LByteLength + GrowthFactor);
end;
CopyTIdBytes(ABytes, 0, FBytes, FHeadIndex + Size, LByteLength);
Inc(FSize, LByteLength);
end;
end;
procedure TIdBuffer.Write(
AStream: TIdStreamVCL;
AByteCount: Integer
);
var
LAdded: Integer;
LLength: Integer;
begin
if AByteCount = -1 then begin
// Copy remaining
LAdded := AStream.VCLStream.Size - AStream.VCLStream.Position;
end else if AByteCount = 0 then begin
// Copy all
AStream.VCLStream.Position := 0;
LAdded := AStream.VCLStream.Size;
end else begin
LAdded := Min(AByteCount, AStream.VCLStream.Size - AStream.VCLStream.Position);
end;
if LAdded > 0 then begin
LLength := Size;
CheckAdd(LAdded);
CompactHead;
SetLength(FBytes, LLength + LAdded);
AStream.ReadBytes(FBytes, LAdded, LLength);
Inc(FSize, LAdded);
end;
end;
function TIdBuffer.IndexOf(const AString: string; AStartPos: Integer): Integer;
begin
Result := IndexOf(ToBytes(AString), AStartPos);
end;
function TIdBuffer.IndexOf(const ABytes: TIdBytes; AStartPos: Integer): Integer;
var
i, j, LEnd, BytesLen: Integer;
LFound: Boolean;
begin
Result := -1;
// Dont search if it empty
if Size > 0 then begin
EIdException.IfTrue(Length(ABytes) = 0, RSBufferMissingTerminator);
EIdException.IfNotInRange(AStartPos, 0, Size - 1, RSBufferInvalidStartPos);
BytesLen := Length(ABytes);
LEnd := FHeadIndex + Size;
for i := FHeadIndex + AStartPos to LEnd - BytesLen do begin
LFound := True;
for j := 0 to BytesLen - 1 do begin
if i + j < LEnd then begin
if FBytes[i + j] <> ABytes[j] then begin
LFound := False;
Break;
end;
end
else
Break;
end;
if LFound then begin
Result := i - FHeadIndex;
if Result <> -1 then
Break;
end;
end;
end;
end;
procedure TIdBuffer.Write(
const AString: string;
AEncoding: TIdEncoding = enDefault
);
begin
if AEncoding = enDefault then begin
AEncoding := Encoding;
end;
Write(ToBytes(AString, AEncoding));
end;
function TIdBuffer.GetCapacity: Integer;
begin
Result := Length(FBytes);
end;
procedure TIdBuffer.SetCapacity(AValue: Integer);
begin
EIdException.IfTrue(AValue < Size, 'Capacity cannot be smaller than Size'); {do not localize}
CompactHead;
SetLength(FBytes, AValue);
end;
constructor TIdBuffer.Create;
begin
inherited;
FEncoding := enANSI;
FGrowthFactor := 2048;
Clear;
end;
function TIdBuffer.PeekByte(
AIndex: Integer
): Byte;
begin
EIdException.IfTrue(Size = 0, 'No bytes in buffer.'); {do not localize}
EIdException.IfNotInRange(AIndex, 0, Size - 1, 'Index out of bounds.'); {do not localize}
Result := FBytes[FHeadIndex + AIndex];
end;
procedure TIdBuffer.SaveToStream(AStream: TIdStream);
begin
CompactHead(False);
AStream.Write(FBytes, Size);
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -