[症状]
版面u查询网友,输入archmagi,按空格,列出匹配的使用者列表,
没有以该字符串开头的用户。
重新查询网友。输入全称archmagi,回车。这个用户是存在的。
也就是说,匹配用户这项功能有问题。
[原因]
查看libBBS/ucache.c里的u_namecomplete函数。有这样一段:
if (len >= ksz) {
tagv[ksz] = 0;
hash = ucache_hash(tagv) - 1;
for (n = 0; n < UCACHE_HASHBSIZE; n++) {
num = reg_ushm->hashhead[(hash + n % UCACHE_HASHBSIZE)
% UCACHE_HASHSIZE + 1];
while (num) {
if (!strncasecmp(uidshm->passwd[num - 1].userid,
tag, len)) {
strcpy(buf[(*pnum)++], uidshm->passwd[num - 1].userid);
}
num = reg_ushm->next[num - 1];
}
}
}
关键的部分是上面这段里的第三行:ucache_hash(tagv) - 1;
调试表明,对于archmagi,上面的tagv="arc",ucache_hash("arc")=171667。
而 ucache_hash("archmagi")=172041,UCACHE_HASHBSIZE=374。
也就是说,(hash + n % UCACHE_HASHBSIZE) % UCACHE_HASHSIZE + 1
最多只能达到 (171667-1+373)%330000+1=172040,因此archmagi的hash值
不会被探测到,也就找不出这个用户。
[解决方法]
观察ucache_hash函数。根据我的理解,给定userid以后,这个函数先在
hashtable里找前几个字母的bucket number(这是否就是UCACHE_HASHBSIZE
中B的含义?),找到以后赋值给 n1:
n1 = (n1 * UCACHE_HASHBSIZE) % UCACHE_HASHSIZE + 1;
我的理解是,把这个userid放到n1这个bucket里,不知道对不对。
最后加一是为了避免 n1=0 从而与无效userid混淆。
然后计算剩余userid的hash值,我的理解是这是该userid在该bucket中的一个
偏移量(n2)。最后赋值给 n1:
n1 = (n1 + n2 % UCACHE_HASHBSIZE) % UCACHE_HASHSIZE + 1;
最后加一也是为了避免返回值为0。
那么这里就出现了一个情况:相同前缀的字符串不一定在一个bucket里,因为
上面两处加了两次1。对于例中archmagi的情况,后面五个字符的hash值正好
是373,于是 H(archmagi)==H(arc)+374, 因此它们不在同一个bucket
中了。这就导致了u_namecomplete没法在arc的bucket里找到archmagi。
我想了一个解决方法是,把
n1 = (n1 + n2 % UCACHE_HASHBSIZE) % UCACHE_HASHSIZE + 1;
改成
n1 = ((n1 - 1) + n2 % UCACHE_HASHBSIZE) % UCACHE_HASHSIZE + 1;
这样就避免了加两次1的毛病。
这个解决方法能够解决archmagi的问题。但是其副作用我尚不清楚。还待前辈指教。
--
FROM 59.66.199.*