TStringBuilder.

TStringBuilder.Append calls SetLength on each and every call. And SetLength contains a try-except block that is always activated.

*Faulty code*
procedure TStringBuilder.SetLength(Value: Integer);
var
LOldLength: Integer;
begin
if Value < 0 then
raise ERangeError.CreateResFmt(@SParamIsNegative, ['Value']); // DO NOT LOCALIZE
if Value > MaxCapacity then
raise ERangeError.CreateResFmt(@SListCapacityError, [Value]);

LOldLength := FLength;
try
FLength := Value;
if FLength > Capacity then
ExpandCapacity;
except
on E: EOutOfMemory do
begin
FLength := LOldLength;
raise;
end;
end;
end;

*Improved code*
property Capacity: NativeUInt read FCapacity write SetCapacity;

procedure TStringBuilder.SetLength(Value: NativeUInt);
begin
if Value > Capacity then ExpandCapacity(Value);
FLength:= Value;
end;

procedure TStringBuilder.SetCapacity(Value: NativeUInt);
begin
if Value < Length then
raise ERangeError.CreateResFmt(@SListCapacityError, [Value]);
if Value > FMaxCapacity then
raise ERangeError.CreateResFmt(@SListCapacityError, [Value]);

System.SetLength(FData, Value);
FCapacity:= Value;
end;



Personally I'd remove the try entirely, I don't believe it's possible to recover from OoM, but with this simple change the TStringBuilder is much much faster.

Please vote for issue: https://quality.embarcadero.com/browse/RSP-19178
https://quality.embarcadero.com/browse/RSP-19178

Comments