Algorithmus von String.GetHashCode()

Muggefronc

Grünschnabel
Hallo alle...

ich habe folgendes Problem: Mit einer .NET-Anwendung wurde eine Datenbank erstellt. Bei der Erstellung wurden Strings mit GetHashCode umgewandelt und in die DB gespeichert. Jetzt muss ich mich mit PHP auf diese DB zugreifen und die Hashes mit Strings vergleichen (also ähnlich einem Passwortvergleich). Dummerweise konnte ich nirgens herausfinden mit welchem Algorithmus GetHashCode arbeitet, den brauch ich ja für den Vergleich. Zudem arbeitet GetHashCode ja scheinbar von Framework-Version zu FW-Version verschieden. In meinem Fall handelt es sich um FW 3. Kann mir da jemand weiterhelfen?

Nachtrag: Also ich bin jetzt soweit, dass ich den Code mit Reflector rausbekommen habe. Den habe ich dann in PHP übersetzt, aber leider liefert die PHP-Version ein anderes Ergebnis als GetHashCode. Hier die Quellcodes:

C++
Code:
[ReliabilityContract(Consistency::WillNotCorruptState, Cer::MayFail)]
public: override Int32 __gc* GetHashCode()
{
    fixed (Char __gc** str = *static_cast<__box Char __gc***>(this))
    {
        Char __gc** chPtr = str;
        Int32 __gc* num = 0x15051505;
        Int32 __gc* num2 = num;
        Int32 __gc** numPtr = *static_cast<__box Int32 __gc***>(chPtr);
        for (Int32 __gc* i = this->Length; (i > 0); i -= 4)
        {
            num = ((((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0]);
            if (i <= 2)
            {
                break;
            }
            num2 = ((((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1]);
            numPtr += 2;
        }
        return (num + (num2 * 0x5d588b65));
    }
}




PHP
Code:
function GetHashCode($name)
{
      $num = 0x15051505;
      $num2 = $num;
      $numPtr=0;
      $len=strlen($name);

      for ($i = 0; $i < $len; $i += 4)
      {
      	  $c1=ord($name{$i});
      	  $c2=ord($name{$i+1});
          $num = ((($num << 5) + $num) + ($num >> 0x1b)) ^ (($c1<<16)|$c2);
          if ($i >= $len-4)
          {
              break;
          }
          $c1=ord($name{$i+2});
      	  $c2=ord($name{$i+3});
          $num2 = ((($num2 << 5) + $num2) + ($num2 >> 0x1b)) ^ (($c1<<16)|$c2);
      }
      return (integer)($num + ($num2 * 0x5d588b65));
}

Sieht jemand was da schiefläuft?
 
Zuletzt bearbeitet:
Falls du kurz Zeit hast, dann erzähl doch dem Rest der Menschheit was deine Lösung war, damit sie nicht dumm stirbt. Der eine oder andere hat vielleicht genau das selbe Problem wie du.
 
Gerne will ich zur Weltverbesserung beitragen. :) Das Problem ist, dass sich die GetHashCode-Funktion Overflow von 32-Bit Integern zunutze macht. Overflows werden in PHP jedoch anders behandelt als in C++. PHP sagt praktisch bis zudiesem Wert und nicht weiter, C++ ist da hardwareorientiert und beim Overflow springt der Wert. D.h. man muss in PHP das Overflowverhalten von C++ immitieren. Weiteres Problem ist die Handhabung von Strings. Die .NET Framework-String-Klasse behandelt String standardmäßig im Unicodeformat, PHP nicht. Aber ich glaub ein Code sagt mehr als 1000 Worte. :)

Code:
function ordUTF8($c, $index = 0, &$bytes = null)
{
  $len = strlen($c);
  $bytes = 1;

  if ($index >= $len)
    return false;

  $h = ord($c{$index});

  if ($h <= 0x7F) {
    $bytes = 1;
    return $h;
  }
  else if ($h < 0xC2)
    return false;
  else if ($h <= 0xDF && $index < $len - 1) {
    $bytes = 2;
    return ($h & 0x1F) <<  6 | (ord($c{$index + 1}) & 0x3F);
  }
  else if ($h <= 0xEF && $index < $len - 2) {
    $bytes = 3;
    return ($h & 0x0F) << 12 | (ord($c{$index + 1}) & 0x3F) << 6
                             | (ord($c{$index + 2}) & 0x3F);
  }           
  else if ($h <= 0xF4 && $index < $len - 3) {
    $bytes = 4;
    return ($h & 0x0F) << 18 | (ord($c{$index + 1}) & 0x3F) << 12
                             | (ord($c{$index + 2}) & 0x3F) << 6
                             | (ord($c{$index + 3}) & 0x3F);
  }
  else
    return false;
}

function Int32BufferOverflow($value)
{
	$maxValue=bcpow("2","31"); //32 bit signed
	if(bccomp($value,"0")==1)
	{
		$result=bcmod($value,$maxValue);
		if(intval(bcdiv($value,$maxValue))%2)
		{
			$result-=$maxValue;
		}
	}
	elseif(bccomp($value,"0")==-1)
	{
		$result=bcmod($value,$maxValue);
		if(intval(bcdiv($value,$maxValue))%2)
		{
			$result+=$maxValue;
		}
	}
	return $result;
}

function GetHashCode($name)
{
      settype($num,integer);
      $num = 0x15051505;
      settype($num2,integer);
      $num2 = $num;
      $len=strlen($name);
      $iPtr=0;
      for ($i = $len; $i>0 ; $i-=4)
      {    		
      	  settype($value,integer);
      	  $v1=ordUTF8($name,$iPtr,$bytes);
      	  $iPtr+=$bytes;
      	  $i-=($bytes-1);
      	  $v2=ordUTF8($name,$iPtr,$bytes);
      	  $iPtr+=$bytes;
      	  $i-=($bytes-1);
      	  $value=$v1 | ($v2<<16);		
          $num = ((($num << 5) + $num) + ($num >> 0x1b)) ^ $value;
          if ($i <= 2)
          {
              break;
          }
          $v1=ordUTF8($name,$iPtr,$bytes);
      	  $iPtr+=$bytes;
      	  $i-=($bytes-1);
      	  $v2=ordUTF8($name,$iPtr,$bytes);
      	  $iPtr+=$bytes;
      	  $i-=($bytes-1);
      	  $value=$v1 | ($v2<<16);
          $num2 = ((($num2 << 5) + $num2) + ($num2 >> 0x1b)) ^ $value;
      }
      $num2=Int32BufferOverflow(bcmul(strval($num2),"1566083941"));
      $num=Int32BufferOverflow(bcadd($num,$num2));
      return $num;
}
Wer Fragen hat, kann sich gern melden...
 
Zurück