Search code examples
c#delphipointersxorunsafe

Can't get the same result as the Delphi code, after I exclusive-OR an address of a char array in C#


Parameters: InLong = 0, Posit = 5, and from an ASCII file TmPChar{.,STX,NUL,NUL}

Delphi code

Procedure TForm1.GetLongFromBuf(Var InLong : Longint; Posit : Integer; ZRepB : ZrepBuf);
Var
  TmpPChar     : Array[0..3] Of Char;
  PLong        : ^Longint;
  I            : Byte;
Begin
For I:= 0 To 3 Do
   TmpPChar[I] := ZRepB[Posit+I];
PLong := @TmpPChar;
InLong := PLong^;
End;

Outputs: TmPChar {'.', #2, #0, #0}, PLong = 13F54C, InLong = 558

C# code

unsafe static long GetLongFromBuf(long InLong, int Posit, char[] ZRepB){
 long* Plong;
 char[] TmpPChar = new char[4];
 for (byte i = 0; i < TmpPChar.Length; i++){
    TmpPChar[i] = ZRepB[(Posit-1) + (i)];
 }
 fixed(char* ch = TmpPChar){
  PLong = (long*)&ch;
  InLong ^= (long)PLong;
 }
 return InLong;
}

Outputs: TmPChar{'.','\u0002','\0','0'}, PLong = 0x0000000000b3cc18, InLong = 11783192


Solution

  • It appears that you are using this Delphi code without really understanding what it is doing. From your results, we can conclude you are using a pre-unicode version of Delphi (ie: D2007 or earlier). We can also guess that ZrepBuf is defining an array of bytes or [Ansi]Char. The method, then, works as follows :

    For I:= 0 To 3 Do
      TmpPChar[I] := ZRepB[Posit+I];  /* Copy four sequential bytes to TmpPChar array */
    PLong := @TmpPChar;               /* Take a pointer to the head of the array */ 
    InLong := PLong^;                 /* Dereference the pointer, interpreting as a 32-bit int */
    

    This is code to convert four bytes to a 32-bit integer. In Delphi the LongInt type is an alias for the 32-bit integer type, equivalent to the int type in C#, not long. There is no use of the XOR operator in the Delphi code. In PLong^, the ^ operator is a dereference operation.

    In C# you can avoid unsafe code entirely and simply perform this conversion using the BitConverter class:

     byte[] b = new byte[4] { 0x2E, 0x02, 0x00, 0x00 }; 
     int result = BitConverter.ToInt32(b, 0);  // result == 558
    

    Here I've defined the input array as a byte[] since a char in C# (and in Delphi 2009 or newer) is a 16-bit type (two bytes) for storing Unicode characters. The data you are reading is ANSI encoded - I'm presuming you understand how to read your text file into a byte array.

    Incidentally, in more modern Delphi you could also re-write the pointer code above to use the TEncoding class to perform this function as described here in a similar way to the BitConverter class in C#.