In another question, David Heffernan posted a comment about his “all time least favourite Delphi construct”:
SetLength(FItems, Length(FItems)+1);
The construct he dislikes was heavily used in the Pebongo project, such as in this excerpt:
procedure TBSONDocument.ReadStream( F: TStream );
var
len : integer;
elmtype : byte;
elmname : string;
begin
Clear;
f.Read( len, sizeof( len ) );
f.Read( elmtype, sizeof( byte ) );
while elmtype <> BSON_EOF do // Loop
begin
elmname := _ReadString( f );
SetLength( FItems, length( FItems ) + 1 ); // This, spotted by TOndrej
case elmtype of
BSON_ARRAY: FItems[high( FItems )] := TBSONArrayItem.Create;
BSON_BINARY: FItems[high( FItems )] := TBSONBinaryItem.Create;
...
end;
f.Read( elmtype, sizeof( byte ) );
end;
end;
What are the alternatives?
What I was talking about was growing dynamic arrays 1 element at a time:
In a loop, and with large arrays, this can lead to memory address fragmentation. When this happens you can find yourself unable to allocate a large contiguous block of memory even though the total available address space is large. If you are constrained by 32 bit address space this can be a very real problem. In addition, performance can also be an issue.
There are a variety of ways to avoid the problem:
TBSONDocument.Clonein your question does.TListorTList<T>container. Although these use dynamic arrays as their underlying storage they implement a capacity based approach to allocation. When they are full, they allocate a large number of extra items in anticipation for future additions. This again can be perfectly efficient.