Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi bitwise proc convert to PHP

I have a fairly simple function in Delphi which takes a string and produces a hashed integer based on that string:

function TfrmMain.HashElf(const Buf;  BufSize : LongInt) : LongInt;
 var
 Bytes : TByteArray absolute Buf;
 I, X  : LongInt;
begin
  Result := 0;
  for I := 0 to BufSize - 1 do begin
    Result := (Result shl 4) + Bytes[I]; 
    X := Result and $F0000000;
    if (X <> 0) then  Result := Result xor (X shr 24);
    Result := Result and (not X);
  end;
end;

I'm converting it to PHP but the results are not the same. This is what i've got in PHP:

function HashElf($Buf, $BufSize){
  $Bytes = str_split($Buf);

  for ($i= 0; $i<$BufSize;$i++){
    $Result = ($Result << 4) + Ord($Bytes[$i]);

    $X = $Result & (0xF0000000);
    if ($X<>0){$Result = $Result ^ ($X>>24);}

    $Result = ($Result & (~ $X));
  }
  return $Result;
}

if you pass in the string teststring to the Delphi function you get 195831015 however PHP returns 72559895. I noticed the difference only becomes apparent after 7 characters. If the test string is just test the results are identical.

PHP seems to have some difficulty with shifting a negative integer to the right for example the folowing line:

 if ($X<>0){$Result = $Result ^ ($X>>24);}

changed to shift left $X<<24 produces the same values as Delphi for the variable X, but the results are still different.

Am i missing something really obvious here?

EDIT: The output of the two functions are:

Delphi

  Char: t   Result: 116        X: 0
  Char: e   Result: 1957       X: 0
  Char: s   Result: 31427      X: 0
  Char: t   Result: 502948     X: 0
  Char: s   Result: 8047283    X: 0
  Char: t   Result: 128756644  X: 0
  Char: r   Result: 181058242  X: 1879048192
  Char: i   Result: 212577321  X: -1610612736
  Char: n   Result: 180011582  X: -1073741824
  Char: g   Result: 195831015  X: -1610612736

PHP

  Char: t   $Result: 116         $X: 0
  Char: e   $Result: 1957        $X: 0
  Char: s   $Result: 31427       $X: 0
  Char: t   $Result: 502948     $X: 0
  Char: s   $Result: 8047283    $X: 0
  Char: t   $Result: 128756644  $X: 0
  Char: r   $Result: 181058242  $X: 1879048192
  Char: i   $Result: 212577417  $X: -1610612736
  Char: n   $Result: 180013310  $X: -1073741824
  Char: g   $Result: 195858503  $X: -1610612736

So its not until character "i" that php starts to get off track with the calculations

EDIT2:

Added PHP function to do a logical right shift instead of arithmetic shift:

function lshiftright($var,$amt)
{
  $mask = 0x40000000;
  if($var < 0)
  {
    $var &= 0x7FFFFFFF;
    $mask = $mask >> ($amt-1);
    return ($var >> $amt) | $mask;
  }else{
    return ($var >> $amt);
  }
}

This now works! Also thanks Ignacio for the mask idea :)

like image 778
Rucia Avatar asked Oct 16 '11 06:10

Rucia


2 Answers

Are you sure Delphi is right and PHP is wrong?

Delphi's shl and shr apparently can behave unpredictably with signed integers. See: http://www.merlyn.demon.co.uk/del-bits.htm#SAR. Dr. Stockton seems to imply there are two types of shift operations: arithmetic shift (keeping the sign) and logical shifts.

The docs ( http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/expressions_xml.html ) aren't very clear on the effect of shl/shr on signed integers. They do however mention that shr/shl by one is only comparable to divions/multiplication by 2 for unsigned integers.

I couldn't find what Dr. Stockton (from the first link) calls the logical shift operations, but it would seem logical :-) to try changing the delphi implementation to use an unsigned 8-byte type (DWORD comes to mind) and see what effect that has.

like image 96
Marjan Venema Avatar answered Nov 03 '22 05:11

Marjan Venema


Mask the bits you want.

if ($X<>0){$Result = ($Result ^ ($X>>24)) & 0xFF;}
like image 1
Ignacio Vazquez-Abrams Avatar answered Nov 03 '22 05:11

Ignacio Vazquez-Abrams