2011-10-16 23 views
7

que tienen una función bastante simple en Delphi, que toma una cadena y produce un número entero hash basado en esa cadena:Delphi bit a bit proc convertido a PHP

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; 

estoy convertirlo en PHP, pero los resultados no son lo mismo. Esto es lo que tengo en 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; 
} 

si pasa en el TestString cadena a la función de Delphi que presentamos lo mejor 195831015 embargo PHP devuelve 72559895. Me di cuenta de la diferencia sólo se hace evidente después de 7 caracteres. Si la cadena de prueba es solo prueba, los resultados son idénticos.

PHP parece tener cierta dificultad con el cambio de un entero negativo a la derecha, por ejemplo, la línea de folowing:

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

cambió a desplazamiento a la izquierda $ X < < 24 produce los mismos valores que Delphi para la variable X , pero los resultados aún son diferentes.

¿Me falta algo muy obvio aquí?

EDIT: La salida de las dos funciones son:

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 

Así que no es hasta el carácter "i" que PHP comienza a salirse de pista con los cálculos

EDIT2:

Añadida función PHP para hacer un desplazamiento lógico a la derecha en lugar de desplazamiento aritmético:

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

Esto funciona ahora! También gracias a Ignacio por la idea de máscara :)

Respuesta

1

¿Estás seguro de que Delphi tiene razón y PHP está equivocado?

Shl y shr de Delphi aparentemente pueden comportarse imprevisiblemente con enteros enteros. Ver: http://www.merlyn.demon.co.uk/del-bits.htm#SAR. El Dr. Stockton parece implicar que hay dos tipos de operaciones de cambio: cambio aritmético (mantenimiento del signo) y cambios lógicos.

Los documentos (http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/expressions_xml.html) no son muy claros sobre el efecto de shl/shr en enteros con signo. Sin embargo, mencionan que shr/shl por uno solo es comparable a divions/multiplication por 2 para enteros sin signo.

No pude encontrar lo que el Dr. Stockton (desde el primer enlace) llama las operaciones de cambio lógico, pero parece lógico :-) intentar cambiar la implementación delphi para usar un tipo de 8 bytes sin signo (viene DWORD a la mente) y ver qué efecto tiene.

+0

Gracias por la sugerencia: revisar el manual de PHP de nuevo revela que el cambio de bit es solamente aritmético. Lo tuve trabajando en Delphi con una máscara de bits adicional, pero luego pensé que sería mejor forzar a PHP a hacer lo que quisiera - ver edición para código adicional – Rucia

1

Enmascare los pedacitos que desee.

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

gracias, aunque la cantidad de bits que necesito es 0x7FFFFFFF.Esto significa que los resultados aún son inconsistentes entre Delphi y PHP. Además, las dos rutinas se ejecutarán conjuntas entre sí, por lo que deben producir los mismos resultados todas las veces. – Rucia

+0

Si está desplazando a la derecha 24, entonces no, solo le quedan 8 bits. –