如何处理32位和64位服务器之间的整数最大差异?

时间:2013-10-06 21:38:34

标签: php math 32bit-64bit uniqueidentifier

在迁移到更好的托管订阅时,需要很长时间才能弄清楚导致我网站出现故障的原因。

我使用'自制'uniqueId生成器来生成必须唯一的所有内容,但这种唯一性不是随机的。我使用它来在多个服务之间进行通信,为文件,文章等生成可重现的唯一“数字”。

这是我所做的功能,从来没有遇到过问题(我认为它以前从不在64位系统上运行?)来生成一个唯一的id。我知道这种独特性是有限的(64.000),但直到现在才导致问题。

function suGetHashCode($s)
{
 $hash=0;
 $c=(is_string($s))?strlen($s):0;
 $i=0;
 while($i<$c) 
 {
   $hash = (($hash << 5)-$hash)+ord($s{$i++});
   //hash = hash & hash; // Convert to 32bit integer
 }
 return ( $hash < 0 )?(($hash*-1)+0xFFFFFFFF):$hash; // convert to unsigned int
} 

function suUniqueId( $s, $bAddLen = false )
{ 
  $i = base_convert( suGetHashCode( $s ), 10, 32 );
  if( $bAddLen && is_string($s) )
   { $i.=('-'.suGetLz( dechex( strlen($s)*4 ), 3 )); } 

  return $i; 
}

function suGetLz( $i, $iMaxLen ) // Leading zero
{
  if( !is_numeric( $i ) || $i < 0 || $iMaxLen <= 0 )
   { return $i; }
  $c = strlen( $i );
  while( $c < $iMaxLen )
   { $c++; $i='0'.$i; } 
  return $i;
}   

整数的最大int值在新系统上:

PHP_INT_MAX = 9223372036854775807

在其他系统上:

PHP_INT_MAX = 2147483647

好吧,我不是一个数学家,我认为这是因为0xFFFFFFFF增量为负时引起的问题(我认为这个新系统永远不会消极)。

但是如何更改它在其他系统上产生相同唯一ID的功能呢?

例如: 它为新托管服务器上的不同字符串生成相同的id:

 $sThisUrl = '<censored>';
 var_dump( suUniqueId($sThisUrl) ); // Produce: 1l5kc37uicb  
 $sThisUrl = '<censored>';
 var_dump( suUniqueId($sThisUrl) ); // Produce the same id as above: 1l5kc37uicb

但是,这必须像旧系统一样:

 $sThisUrl = '<censored>';
 var_dump( suUniqueId($sThisUrl) ); // Produce: a46q6nd  
 $sThisUrl = '<censored>';
 var_dump( suUniqueId($sThisUrl) ); // Produce: 2mirj1h

注意:字符串是分开的,以避免stackoverflow看到这个链接。

编辑删除了文件名

有人如何处理这个问题?

2 个答案:

答案 0 :(得分:1)

我建议您在处理完每个字符后截断:

$hash = (($hash << 5)-$hash)+ord($s{$i++});
$hash = $hash & 0xFFFFFFFF; // Convert to 32bit integer

至少在我的64位系统上,这会导致您的第二个示例中出现所需的2mirj1h,但如果没有进行此修改,我会得到1c6ta2qjga7而不是1l5kc37uicb

我还会将返回值更改为只返回$hash。要么它可以正确表示无符号32位数,那么前面的掩码应该强制解释。或者你的系统不能代表这些,那么增加的计算也不会让你到那里,你必须将数字分成比特组并逐个字符串化。

当然,最简单的解决方案是使用一些完善的通用散列算法,例如:使用the hash function。添加一些秘密盐,如果你的壮举,这可能会打开你的攻击。如果这样的哈希码的结果太长,您可以简单地获取部分输出。您可以按照自己喜欢的方式转换base,因此您不必使用哈希常用的十六进制表示法。使用加密哈希也可以减少冲突的可能性;例如,在您的情况下,同一路径中的文档generbM.js将产生相同的哈希值。

答案 1 :(得分:1)

如果我是你,我会编写一个单元测试,以确保在32位和64位机器上获得相同的结果。

循环应该改变如下:

while($i<$c) 
{
  $hash = (($hash << 5)-$hash)+ord($s{$i++});
  hash = hash & 0xFFFFFFFF; // Convert to 32bit integer
}
$hash = ( $hash < 0 )?(($hash*-1)+0xFFFFFFFF):$hash; // convert to unsigned int
return $hash & 0xFFFFFFFF; // Convert to 32bit integer

您的单元测试可以在32位版本上运行原始测试并保存输出。然后在64位上运行它并与那些32位结果进行比较。如果任何一个是不同的,你知道你仍然没有1比1的等价物。