Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2017-08

Dangers of using Move command to insert data into a TBytes array?

3 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bo Berglund

I just want to check with you which way is safer when using Move() commands in a procedure to combine two TBytes arrays by inserting one into the other at a specified point.

What I need to do is the following:
- Increase the size of the Dest array by the length of the Src array
- Move the data following the insertion point in the Dest array towards the end starting at Index
- Copy the data from the Src array into Dest at Index where the hole has been created

Like this:

procedure  InsertBytes(var Dest: TBytes; Src: TBytes; Index: integer);
var
  LenD, LenS: integer;
begin
  if Length(Src) = 0 then exit;
  LenD := Length(Dest);
  LenS := Length(Src);
  if Index > LenD then Index := LenD;  //Make an append instead
  SetLength(Dest, LenD + LenS);
  Move(Dest[Index], Dest[Index + LenS], LenD - Index);  //Create space
  Move(Src[0], Dest[Index], LenS);  //Insert data
end;

If the calculation of the number of bytes to move (LenD - Index) for some reason is wrong and results in too many bytes being moved, what will happen then? Will Move() overwrite whatever is following the Dest array in memory?

Would it be safer to use a local intermediate array to combine Dest and Src and then in the end Move the data back into Dest?

I have tested the above procedure and it looks OK, but I am worried that in some cases the same kind of construct would be wrong and maybe cause unknown side effects.
I must use Move() for handling other data of various kinds like packed records and such too...

Bo Berglund Developer in Sweden

Reply by email

Michael Van Canneyt
to Bo Berglund
Bo Berglund wrote 31 lines
I just want to check with you which way is safer when using Move() commands in a procedure to combine two TBytes arrays by inserting one into the other at a specified point. What I need to do is the following: - Increase the size of the Dest array by the length of the Src array - Move the data following the insertion point in the Dest array towards the end starting at Index - Copy the data from the Src array into Dest at Index where the hole has been created Like this: procedure InsertBytes(var Dest: TBytes; Src: TBytes; Index: integer); var LenD, LenS: integer; begin if Length(Src) = 0 then exit; LenD := Length(Dest); LenS := Length(Src); if Index > LenD then Index := LenD; //Make an append instead SetLength(Dest, LenD + LenS); Move(Dest[Index], Dest[Index + LenS], LenD - Index); //Create space Move(Src[0], Dest[Index], LenS); //Insert data end; If the calculation of the number of bytes to move (LenD - Index) for some reason is wrong and results in too many bytes being moved, what will happen then? Will Move() overwrite whatever is following the Dest array in memory?

Yes.

Would it be safer to use a local intermediate array to combine Dest and Src and then in the end Move the data back into Dest?

Not really.
If you calculate lengths wrong, the end result is unpredictable in any case.

Show quoted text 6 lines
I have tested the above procedure and it looks OK, but I am worried that in some cases the same kind of construct would be wrong and maybe cause unknown side effects. I must use Move() for handling other data of various kinds like packed records and such too...

If the packed records contain managed data (ansistrings and the like): Don't use move.

Michael.

Reply by email

Bo Berglund
to Michael Van Canneyt
Michael Van Canneyt wrote 5 lines
> I must use Move() for handling other data of various kinds like packed > records and such too... If the packed records contain managed data (ansistrings and the like): Don't use move.

Thanks for the advice!
I now have looked over the code and found that the records used are fixed length, but there exist in some places a few ID members that are supposed to hold an array of AnsiChar, like in this example:

  TSSCmdDirRec = packed record
		CmdProgNum:	shortint;
		InUse:			shortint;
		ProgID: array[0..16] of AnsiChar; // <== here!
		meastype:		byte;
		numSteps:		word;
		MUX:				shortint;
		numElecs:		word;
		progLoc: 		cardinal;
		ArrayID:		shortint;
	end;

These records are equivalents of the C structs used on an embedded data collection system, which sends data organized in this way in straight binary form. In the case of ProgID it is defined there as:

char     progID[17];

What I do is that I receive the binary data into a buffer, then use Move() to copy the data into a local varable of the record type and finally I use a procedure to switch endianness on members which are multi-byte.
This has worked fine until Unicode was introduced in Delphi/FPC when I tried to fix it using AnsiChar and AnsiString as appropriate. But no string type in these records, just arrays of AnsiChar...

Anyway, I'm glad I checked with you. I initially had calculated the number of bytes in my Move operation erroneously. Now fixed. I did not see any side effects of it though, probably just pure luck.

Bo Berglund Developer in Sweden

Reply by email