Jump to content
pyscripter

Interesting way to copy dynamic arrays.

Recommended Posts

I am sure this is well known but I recently discovered a new way to copy dynamic arrays:

var
  A, B : TArray<Integer>;
...
  B = A;
  SetLength(B, Length(B));
...

Test procedure:

procedure Test();
var
  A, B : TArray<Integer>;
begin
  A := [1 , 2, 3];
  B := A;
  SetLength(B, Length(B));
  B[1] := 12;

  Assert(A[1] = 2);
  Assert(B[1] = 12);
end;

Of course Copy is simpler, but in my use case I did not want to create a copy unless it was necessary e.g.

In one part of the code

//  create A

SetLength(A, L);

...

 

In another part of the code

B = A

// do stuff with B or store it as an object field

 

If A is not recreated you save the copying operation.  Not a big deal...

 

  • Like 1

Share this post


Link to post
5 minutes ago, corneliusdavid said:

Wouldn't you want to set B's length to Length(A)?

B := A makes them the same array. SetLength just makes B unique (and contains an implicit copy).

 

I don't get what advantage assignment followed by SetLength gives you over a simple Copy. With Copy the intent is explicit.

  • Like 1

Share this post


Link to post
1 minute ago, Anders Melander said:

I don't get what advantage assignment followed by SetLength gives you over a simple Copy.

In the use case I described you avoid copying until (if) it is needed.

 

2 minutes ago, Anders Melander said:

With Copy the intent is explicit.

Agree.

Share this post


Link to post
Just now, David Heffernan said:

Why would that be different?

That's just as good a question. So either the assignment statement simply copies all elements it knows about in array A without setting the length of B (which seems like an incomplete copy) or setting the length is not needed.

Share this post


Link to post
Just now, pyscripter said:

In the use case I described you avoid copying until (if) it is needed.

I still don't get it.

Just start with the assignment and use B:= Copy(B) when you need it to be unique....  Ah, I think I get it now. You don't want to keep track of the arrays reference count.

So it's like UniqueString for dynamic arrays? Does nothing if the reference count is already 1, otherwise makes a unique copy. Right?

Share this post


Link to post
5 minutes ago, corneliusdavid said:

So either the assignment statement simply copies all elements it knows about in array A without setting the length of B (which seems like an incomplete copy) or setting the length is not needed.

Read what I wrote again.

Share this post


Link to post

I just tested this in Delphi 11:

program ArrayCopy;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils;

procedure ShowArray(AnArray: TArray<Integer>);
begin
  Writeln('Length: ' + Length(AnArray).ToString);
  for var i := 0 to Length(AnArray) - 1 do
    Writeln('[' + i.ToString + '] = ' + AnArray[i].ToString);
end;

begin
  var a, b: TArray<Integer>;

  a := [1, 2, 3];
  ShowArray(a);

  b := a;
  b[1] := 12;
  ShowArray(b);

  Readln;
end.

And the output shows SetLength() is not necessary:

image.png.03dacec008964e94eca9552790c8242c.png

Share this post


Link to post
9 minutes ago, Anders Melander said:

Just start with the assignment and use B:= Copy(B) when you need it to be unique....  Ah, I think I get it now. You don't want to keep track of the arrays reference count.

So it's like UniqueString for dynamic arrays? Does nothing if the reference count is already 1, otherwise makes a unique copy. Right?

B=A does not cost much

B = Copy(A, 0)  moves memory and has a cost.

 

In my use case A may be recreated (maybe not).  If it is not recreated then I save the moving of memory.  If it is recreated B becomes a unique copy after SetLength(A,...)

Edited by pyscripter
  • Like 2

Share this post


Link to post
1 minute ago, pyscripter said:

If it is recreated B becomes unique after SetLength(A,...)

Isn't B already unique? See the output of my test program--those are distinct arrays.

Share this post


Link to post
1 minute ago, corneliusdavid said:

just tested this in Delphi 11:

Try to output a again at the end of your routine.

Share this post


Link to post
4 minutes ago, pyscripter said:

B=A does not cost much

B = Copy(A, 0)  moves memory and has a cost.

 

In my use case A may be recreated (maybe not).  If it is not recreated then I save the moving of memory.  If it is recreated B becomes a unique copy after SetLength(A,...)

A simple "yes" would have been enough 🙂

  • Haha 1

Share this post


Link to post

I am just considering what could be the use cases.

 

How would it behave if you put B into a const parameter, passing it into a procedure.

Is the SetLength( AParamOfB, SizeOf( AParamOfB ); behaviour then still be maintained ?

 

This could maybe make sense, to have several different functions, while some need a unique copy, some don't.

 

Edited by Rollo62

Share this post


Link to post

There's DynArrayUnique but it's undocumented and not really fit for direct use:

 

procedure DynArrayUnique(var A: Pointer; typeInfo: Pointer);
begin
  if (A <> nil) and (PDynArrayRec(PByte(A) - SizeOf(TDynArrayRec))^.RefCnt > 1) then
    DynArrayCopy(A, A, typeInfo);
end;

 

  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×