由于在其缓存哈希中创建密钥的算法,a bug in Firefox(即使在新的beta版和雷区版本中)也会阻止某些文件的缓存。 Here is a link to the source code of the function。
我想确保我的所有网站文件都可以缓存。但是,我不明白为什么他们的散列函数无法为不同的URL创建唯一键。我希望有人能在psuedo-code或java中描述这个 mal 函数。
最好为开发人员创建一个实用程序,以确保在修复此错误之前使用唯一的URL。
编辑:有一些非常有用的答案,但是,我需要更多的逐步帮助来创建一个实用程序来检查这些缓存混淆。获得一些可以重现firefox创建的密钥的java代码会很棒。因此,在这个问题上开启赏金。
编辑2:这是一个部分工作的java端口(使用processing编写)。注意底部的测试;前三个按预期工作,但其他人没有。我怀疑有关签名/未签名的内容。建议
//
// the bad collision function
// http://mxr.mozilla.org/mozilla/source/netwerk/cache/src/nsDiskCacheDevice.cpp#240
//
//248 PLDHashNumber
//249 nsDiskCache::Hash(const char * key)
//250 {
//251 PLDHashNumber h = 0;
//252 for (const PRUint8* s = (PRUint8*) key; *s != '\0'; ++s)
//253 h = PR_ROTATE_LEFT32(h, 4) ^ *s;
//254 return (h == 0 ? ULONG_MAX : h);
//255 }
//
// a java port...
//
String getHash( String url )
{
//get the char array for the url string
char[] cs = getCharArray( url );
int h = 0;
//for (const PRUint8* s = (PRUint8*) key; *s != '\0'; ++s)
for ( int i=0; i < cs.length; i++ )
{ h = PR_ROTATE_LEFT32(h, 4) ^ cs[i];
}
//looks like the examples above return something in hex.
//if we get matching ints, that is ok by me.
//but for fun, lets try to hex the return vals?
String hexVal = hex( h );
return hexVal;
}
char[] getCharArray( String s )
{
char[] cs = new char[s.length()];
for (int i=0; i<s.length(); i++)
{
char c = s.charAt(i);
cs[i] = c;
}
return cs;
}
//
// how to PR_ROTATE_LEFT32
//
//110 /*
//111 ** Macros for rotate left and right. The argument 'a' must be an unsigned
//112 ** 32-bit integer type such as PRUint32.
//113 **
//114 ** There is no rotate operation in the C Language, so the construct
//115 ** (a << 4) | (a >> 28) is frequently used instead. Most compilers convert
//116 ** this to a rotate instruction, but MSVC doesn't without a little help.
//117 ** To get MSVC to generate a rotate instruction, we have to use the _rotl
//118 ** or _rotr intrinsic and use a pragma to make it inline.
//119 **
//120 ** Note: MSVC in VS2005 will do an inline rotate instruction on the above
//121 ** construct.
//122 */
//...
//128 #define PR_ROTATE_LEFT32(a, bits) _rotl(a, bits)
//return an int (32 bit). what do we do with the 'bits' parameter? ignore?
int PR_ROTATE_LEFT32( int a, int bits )
{ return (a << 4) | (a >> (32-bits));
}
//
// examples of some colliding hashes
// https://bugzilla.mozilla.org/show_bug.cgi?id=290032#c5
//
//$ ./hashit "ABA/xxx.aba"
//8ffac222
//$ ./hashit "XyZ/xxx.xYz"
//8ffac222
//$ ./hashit "CSS/xxx.css"
//8ffac222
//$ ./hashit "JPG/xxx.jpg"
//8ffac222
//$ ./hashit modules_newsfeeds/MenuBar/MenuBar.css
//15c23729
//$ ./hashit modules_newsfeeds/ListBar/ListBar.css
//15c23729
//$ ./hashit modules_newsfeeds/MenuBar/MenuBar.js
//a15c23e5
//$ ./hashit modules_newsfeeds/ListBar/ListBar.js
//a15c23e5
//
// our attempt at porting this algorithm to java...
//
void setup( )
{
String a = "ABA/xxx.aba";
String b = "CSS/xxx.css";
String c = "CSS/xxx.css";
String d = "JPG/xxx.jpg";
println( getHash(a) ); //yes 8ffac222
println( getHash(b) ); //yes 8ffac222
println( getHash(c) ); //yes 8ffac222
println( getHash(d) ); //no [??] FFFFFF98, not 8ffac222
println( "-----" );
String e = "modules_newsfeeds/MenuBar/MenuBar.css";
String f = "modules_newsfeeds/ListBar/ListBar.css";
println( getHash(e) ); //no [??] FFFFFF8C, not 15c23729
println( getHash(f) ); //no [??] FFFFFF8C, not 15c23729
println( "-----" );
String g = "modules_newsfeeds/MenuBar/MenuBar.js";
String h = "modules_newsfeeds/ListBar/ListBar.js";
println( getHash(g) ); //yes [??] FFFFFF8C, not a15c23e5
println( getHash(h) ); //yes [??] FFFFFF8C, not a15c23e5
}
答案 0 :(得分:6)
据我所知,只需阅读bugzilla条目,当出现两个不同的问题时,错误就会出现:
所以基本上,如果你有一个页面有两个非常相似的网址,这可能会发生在某些版本的Firefox上。我通常不会在不同的页面上发生这种情况,因为那时FF会有时间将条目刷新到磁盘,避免了时间问题。
因此,如果您有多个资源(脚本,图像等)都是从同一页面加载的,请确保它们有9个完全不同的字符。一种可以确保这种方法的方法是使用随机数据附加一个查询字符串(您忽略),例如:
答案 1 :(得分:5)
以下是算法的工作原理:
initialize hash to 0
for each byte
shift hash 4 bits to left (with rotate)
hash = hash XOR character
视觉上(16位版本):
00110000 = '0'
00110001 = '1'
00110010 = '2'
00110011 = '3'
0100 0011 = '4'
00110101 = '5'
====================
01000110001000010000 (and then this will be 'rotated'
so that it lines up with the end)
giving:
00100001000001000110
这意味着如果你有相同长度的字符串并且大多数相同,那么在至少一种情况下,char的低4位和下一个char xor的高4位必须是唯一的。但是,将32位数字粘贴到表中的方法可能会越来越弱,这意味着它需要字符串中特定位置的低4 xor upper4(mod 8 chars)是唯一的。
答案 2 :(得分:2)
此错误是我网站的主要问题:http://worldofsolitaire.com
我很久以前通过在.htaccess文件中使用条件规则来解决这个问题,该文件会禁用Firefox用户对网站上图像的所有缓存。这是一个非常可怕的事情,但是当时我无法追踪Firefox中的错误并且让网站稍慢一点比显示重复/损坏的图像更好。
当我读到已在最新的Firefox版本中修复的链接错误时,我在2009年4月19日(昨天)更改了条件,仅禁用Firefox 2用户的缓存。
几个小时后,我收到了来自Firefox 3用户的10多封电子邮件(已确认)他们看到了重复的图片。所以这个问题在Firefox 3中仍然是一个问题。
我决定创建一个简单的Linux测试程序,允许我检查URL以查看它们是否生成相同的缓存哈希键。
要在任何Linux系统中编译:g ++ -o ffgenhash ffgenhash.cpp
这是代码(保存到文件ffgenhash.cpp)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define ULONG_MAX 0xFFFFFFFF
#define PR_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits))))
unsigned long ffgenhash(const char * key)
{
unsigned long h=0;
for(const unsigned char * s = (unsigned char *) key; *s != '\0'; ++s)
{
h = PR_ROTATE_LEFT32(h, 4) ^ *s;
}
return (h==0 ? ULONG_MAX : h);
}
int main(int argc, char ** argv)
{
printf("%d\n", ffgenhash(argv[1]));
return 0;
}
如您所见,这里有两个真实生活URL,它们生成相同的缓存哈希键:
./ffgenhash "http://worldofsolitaire.com/decks/paris/5/12c.png"
1087949033
./ffgenhash "http://worldofsolitaire.com/decks/paris/5/13s.png"
1087949033
由于我在Javascript循环中预加载这些图像,因此尝试使用某种空的&lt; script&gt;此处无法使用标记解决方法。
事实上,我认为我唯一真正的解决方案是以某种方式修改Firefox用户的URL以生成唯一的缓存哈希密钥。这就是我将要使用的方法。
顺便说一句,我很想创建一个Firebug添加项,它将检查站点加载的所有资源,并且如果站点上的两个资源共享一个公共哈希键,那么开发人员就会知道,这会产生很大的错误。通过这种方式运行谷歌地图这样的网站会很棒,因为我在过去的几年中看到了这些图像的奇怪之处:)
答案 3 :(得分:1)
首先,你不能将所有字符串唯一地散列为整数(显然,字符串多于(固定大小)整数,因此必须存在冲突)。您可以拥有一个可以容纳所有数据集的哈希表(例如,所有文件),但要获得它,您需要更改哈希表的代码,而不是哈希函数。
其次,我发现您发布的散列函数存在问题,在此部分中:
PR_ROTATE_LEFT32(h, 4)
如果它确实旋转了h
(我没有检查过这个),旋转4意味着交换了两个8字节(我假设32位散列)部分的字符串(例如。{{ 1}}与xxxxxxxxyyyyyyyy
}将具有相同的哈希值。如果将其更改为相对于散列大小的素数(例如,5),则只会对长度为32的交换部分进行此操作。
答案 4 :(得分:1)
这是Sembiance哈希生成器的修改版本,即使在64位平台上编译也能正常工作:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define ULONG_MAX 0xFFFFFFFF
#define PR_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits))))
unsigned int ffgenhash(const char * key) {
unsigned int h=0;
for(const unsigned char * s = (unsigned char *) key; *s != '\0'; ++s) {
h = PR_ROTATE_LEFT32(h, 4) ^ *s;
}
return (h==0 ? ULONG_MAX : h);
}
int main(int argc, char ** argv) {
printf("%u\n", ffgenhash(argv[1]));
return 0;
}
答案 5 :(得分:0)
你显然错了真正的错误。当然,由于哈希算法的选择性增加,存在哈希冲突。但即使hash(x)= 1也不会导致所描述的问题。它只会通过第一个桶将O(1)查找转换为O(N)链表搜索。
真正的问题是Firefox无法处理哈希冲突。因此,它需要所有URL的完美哈希。不幸的是,“所有网址”都是您无法控制的。