【问题标题】:How to deal with integer max difference between 32 bit and 64bit servers?如何处理 32 位和 64 位服务器之间的整数最大差异?
【发布时间】:2013-10-06 21:38:34
【问题描述】:

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

我使用“自制”的 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

注意:字符串被分成几部分以避免堆栈溢出,请参阅此链接。

编辑删除的文件名

有没有人可以解决这个问题?

【问题讨论】:

  • 没有必要说“编辑:删除 X”,因为它只是将注意力吸引到原始帖子 stackoverflow.com/revisions/19214266/1
  • 在某些方面你是对的,但没关系,在示例中放置两个不同的长 url,你会看到它在 64 位上产生相同的结果。
  • 您无法轻易收回您在 cc-by-sa 许可下发布的内容。编辑问题会使其不那么显眼,但原始形式的问题不再是您的专属财产。此外,删除字符串会使其他人难以重现此问题。而且由于手头很少有人安装 32 位和 64 位的 PHP,这大大减少了比较的机会。
  • @MvG:关键是该函数产生相同的 id,无论输入是什么,其中的 url 只是一个示例。您可以简单地将一些不同的 url 放入示例中,函数的行为保持不变。

标签: php math 32bit-64bit uniqueidentifier


【解决方案1】:

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

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

至少在我的 64 位系统上,这会在您的第二个示例中导致所需的 2mirj1h,尽管没有此修改,我得到了 1c6ta2qjga7 而不是 1l5kc37uicb,就像您所做的那样。

我还将返回值更改为简单地返回$hash。要么它可以正确表示无符号的 32 位数字,那么前面的掩码应该强制解释。或者您的系统无法表示这些,那么添加的计算也不会让您到达那里,您必须将数字分成位组并单独将它们字符串化。

当然,最简单的解决方案是使用一些成熟的通用哈希算法,例如使用the hash function。如果你做到这一点,添加一些秘密盐可能会让你受到攻击。如果这样的哈希码的结果太长,你可以简单地取一部分输出。您可以以任何您喜欢的方式转换基数,因此您不必使用散列常用的十六进制表示法。使用加密哈希也可以减少发生冲突的机会;例如,在您的情况下,同一路径中的文档 generbM.js 将产生相同的哈希。

【讨论】:

  • 非常感谢!所有的问题都随着这个变化而消失了。我使用它的原因是它产生的结果不像其他哈希那么大。我不将它用于安全性、简单任务,有时用作校验和,有时用作 id 或文件标识符(临时或其他东西)以及不同平台之间。再次感谢!
  • 如果这样的哈希码的结果太长,可以直接取一部分输出 这样就不再保证唯一性了……
  • @ta.speot.is:散列并不能保证唯一性,它们只是被设计成使得意外(并且在加密散列不是那么意外的情况下)冲突极不可能发生。减少大小和增加碰撞的机会,但我仍然比任何具有相同输出长度(或相同熵,更精确并考虑不同基数的自制东西)更信任截断的 MD5 )。
  • 这只是一个简单的方法和其他一些方法来产生一个标识符。对我来说,它工作正常。我可能最多使用它 40 次来为某物生成一个唯一的 ID(在孤立的情况下)。在我迁移到 64 位之前,它从来没有遇到过问题。而当生成的id没有问题的时候就没有理由切换了吧?
  • 我的错误,我阅读了您的建议,并考虑了 OP 对唯一性的要求,并将哈希与 GUID 混淆。
【解决方案2】:

如果我是你,我会编写一个单元测试来确保你在 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 的等价物。

【讨论】:

  • 与第一个答案类似,但无论如何感谢。另外,不需要负校验,因为它运行在64位上,可以产生WORD。
  • 他没有提到$hash &amp; 0xFFFFFFFF 也没有编写单元测试......实际上,他说你可以只返回 $hash,但这可能会改变它的工作方式,如果你将它用于而这意味着您在依赖于它的数据库中有数据,因此这可能不是一个好主意。
  • 好吧,我已经阅读了他的帖子并让它发挥作用。我的意思是,我理解他的帖子的意思。还是谢谢!
  • Seems 你是对的,为了保持一致性,可能需要对负值进行特殊处理。愚蠢的语言,它显然有 some 无符号整数的概念,但据我所知,它没有以明智的方式处理它的语言特性。任何按位和一个正操作数可能导致负结果的语言都应该被枪杀……
猜你喜欢
  • 2012-09-03
  • 2010-12-18
  • 2014-05-27
  • 2011-05-15
  • 1970-01-01
  • 1970-01-01
  • 2010-09-21
  • 1970-01-01
  • 2013-02-12
相关资源
最近更新 更多