Search code examples
delphidelphi-xe2

Copying packed record to TByteDynArray in delphi (10 Seattle)


I need to copy a packed record to TByteDynArray in Delphi.

PMyRec = ^TMyRec;
TMyRec = packed record
  Header: THeaderRec;
  Timestamp: TTimeStamp;   // time of event
  PLoc: TLocationRec;
  A: word;
end;

Below is the code snippet that I have for doing that.

  xxx: TByteDynArray;
  m_arr : TMyRec;

  SetLength(xxx,sizeof(TMyRec));

  offset := offset + sizeof(THeaderRec);
  System.Move(xxx[offset], m_arr.Timestamp ,sizeof(TTimeStamp));

  offset := offset + sizeof(TTimeStamp);
  System.Move(xxx[offset], m_arr.PLoc,sizeof(TLocationRec));

This somehow doesn't seem to do the copying properly. Can someone point out if I am doing something wrong?


Solution

  • If you are wanting to copy FROM the record TO the byte array, then your code is doing the opposite. Pay attention to the parameters of System.Move(). Its first parameter is the Source, its second parameter is the Destination. This is different than other APIs, like memcpy(), CopyMemory(), etc.

    Try this:

    offset := 0;
    System.Move(m_arr.Header, xxx[offset], sizeof(THeaderRec));
    
    Inc(offset, sizeof(THeaderRec));
    System.Move(m_arr.Timestamp, xxx[offset], sizeof(TTimeStamp));
    
    Inc(offset, sizeof(TTimeStamp));
    System.Move(m_arr.PLoc, xxx[offset], sizeof(TLocationRec));
    
    Inc(offset, sizeof(TLocationRec));
    System.Move(m_arr.A, xxx[offset], sizeof(Word));
    

    Or, you can copy the entire record in one go:

    System.Move(m_arr, xxx[0], sizeof(TMyRec));
    

    With that said, if you need to do per-field copies, I would suggest letting the compiler calculate the field offsets for you, eg:

    offset := Integer(@PMyRec(nil).Header);
    System.Move(m_arr.Header, xxx[offset], sizeof(THeaderRec));
    
    offset := Integer(@PMyRec(nil).Timestamp);
    System.Move(m_arr.Timestamp, xxx[offset], sizeof(TTimeStamp));
    
    offset := Integer(@PMyRec(nil).PLoc);
    System.Move(m_arr.PLoc, xxx[offset], sizeof(TLocationRec));
    
    offset := Integer(@PMyRec(nil).A);
    System.Move(m_arr.A, xxx[offset], sizeof(Word));
    

    Or better, simply do not use Move() at all. You can instead type-cast the allocated byte array and use standard field assignments, eg:

    PMyRec(xxx)^.Header := m_arr.Header;
    PMyRec(xxx)^.Timestamp := m_arr.Timestamp;
    PMyRec(xxx)^.PLoc := m_arr.PLoc;
    PMyRec(xxx)^.A := m_arr.A;
    

    Or, to copy the entire record:

    PMyRec(xxx)^ := m_arr;