什么是word_at_a_time.h中的has_zero和find_zero用于

时间:2013-06-08 08:15:00

标签: c hash linux-kernel

在linux内核中,inlucde / linux / word_at_a_time.h,有两个功能:

static inline long find_zero(unsigned long mask)
{
        long byte = 0;
#ifdef CONFIG_64BIT
        if (mask >> 32)
                mask >>= 32;
        else
                byte = 4;
#endif
        if (mask >> 16)
                mask >>= 16;    
        else
                byte += 2;
        return (mask >> 8) ? byte : byte + 1;
}

static inline bool has_zero(unsigned long val, 
                            unsigned long *data, 
                            const struct word_at_a_time *c)
{
        unsigned long rhs = val | c->low_bits;
        *data = rhs;
        return (val + c->high_bits) & ~rhs;
}

它在哈希函数中使用,在git log中它说:

 - has_zero(): take a word, and determine if it has a zero byte in it.
   It gets the word, the pointer to the constant pool, and a pointer to
   an intermediate "data" field it can set.

但我仍然没有得到

(1)这个功能做什么?,和 (2)他们是如何做到的?

3 个答案:

答案 0 :(得分:5)

假设位从最低有效位(LSB)(编号0)到最高有效位(MSB)编号。

它做什么?

函数find_zero()使用类似于分而治之的技术从左侧搜索第一个 N(< = 7)字节,其值为零。

它是怎么做的?

假设一个64位系统定义了CONFIG_64BIT,然后执行以下代码:

#ifdef CONFIG_64BIT
        if (mask >> 32)  //Any bit=1 in left 32 bits 
                mask >>= 32;
        else
                byte = 4;  //<--No, fist 4 bytes are zero

首先注释maskunsigned,因此>>是无符号右筛选。 (like Java's >>>

首先检查mask值是否大于2 32 (或者我们可以说unsigned long mask位是否在编号为32的位之间63是一个。)

mask >> 32将生成一个纯值,即mask右移,0扩展为32位,并导致32高位包含零。

例如,如果maks位如下:

63     56 55     48 47     40 39     32 31     24 23     16 15        7       0
▼       ▼ ▼       ▼ ▼       ▼ ▼       ▼ ▼       ▼ ▼       ▼ ▼         ▼       ▼
0000 1000 0000 0000 0000 0000 0000 0100 0000 0000 0000 0000 0000 0000 0001 0101
▲                                                                             ▲
MSB                                                                         LSM

在此示例中,位号3459为1,因此(mask >> 32) == true(或称为非零!0)。因此,对于此示例if (mask >> 32) == if(!0) 在一般情况下,如果位号3263中的任何位为1,则mask将更新为mask >>= 32; == mask = mask >> 32;(如果为真,则为声明执行)。这导致高阶32位由于右移而变为低阶32位(并且位32到63变为0)。

在上面的示例中,mask变为:

           shifted OLD bit number ----> 63                 45                32
63                  47               32 31                 15                 0
▼                   ▼                 ▼ ▼                   ▼                 ▼
0000 0000 0000 0000 0000 0000 0000 0000 0000 1000 0000 0000 0000 0000 0000 0100
▲                                                                             ▲
MSB                                                                         LSM
|-------------------------------------|
| All are zero                        |

注意:从32到63的位是0,从031的位从上面从位32复制到63

到此为止:

<强>案例-1:
如果3263中的任何位在原始mask一个,则if执行true并屏蔽更新。 (正如我上面解释的那样)。变量long byte仍为0。然后在下一个if(mask >> 16)中,函数find_zero()将继续在位4863内搜索值为零的字节(在if(mask >> 16)中,位编号为{如果任何位为1,将检查{1}}到48

<强>案例-2:
如果原始633263的所有位均为零,那么mask条件将变为false(即if == if(mask >> 32))并且if(0)没有更新,变量mask变为long byte,这表明4中的高四字节为0。在mask之后,函数if(mask >> 16)将搜索位范围为find_zero()16的零字节。

同样,在第二个31中:

if()

它将检查编号为if (mask >> 16) mask >>= 16; else byte += 2; 的位之间是否有任何位。如果所有位都为零,那么16 to 31将在其他部分递增byte,在if部分掩码将通过无符号右移16位更新。 (注意:如果2更新mask,那么实际上mask检查if(mask >> 16)之间的任何位是否为1,否则为48 to 63 }原始掩码中的16

随后,最后一个条件运算符以类似的方式工作,以检查位为318之间的位是否为1。

15

因此,函数 long byte = 0; 64 bit `mask` | | ▼ if(any bit from 32 to 62 is one)---+ | | |False: if(0) |True: if(!0) all bits between 32 to 64 |A byte=Zero NOT found are zero Some bits are one from bit 32-63 | | | | byte = 4 byte= 0, and 64 bit `mask` <--Orignal `mask` updated as `mask >>= 32;` | | | | ▼ ▼ if(Any bit from 16 to 31 is one) if(Any bit from 48 to 63 is one) |Because high order bits are Zero |Note due to >> all high order |It check for bits numbered 16-31 |bits becomes zero. | |2nd if checks for bit from 16-31 | |that were originally bit | | numbered 48 to 63 | | ▼ ▼ Case False: if(0) Case False: if(0) All bits Zero from 16-31 All bits Zero from 49-63 mask will NOT updated and mask will NOT updated and byte = 6 byte = 2 Case True: if(!0) Case False: if(!0) Some bits are one from 16-31 Some bits are one from 49-63 mask updated mask Updated byte = 4 byte = 0 | | | | ▼ ▼ more four cases more four cases Total 8 different answer outputs from `0` to `7` 从左侧搜索值为零的第一个 find_zero()个字节。输出字节值可以从N0,也不考虑最右边的字节(7)。
(*注意:long为64位长= 8 * 8位= 8字节。*)

"8"

byte = byte NUMBER ("): "1" "2" "3" "4" "5" "6" "7" "8" 63 56 55 48 47 40 39 32 31 24 23 16 15 7 0 ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ 0000 1000 0000 0000 0000 0000 0000 0100 0000 0000 0000 0000 0000 0000 0001 0101 ▲ ▲ MSB LSM - &gt;意味着第一个字节不为零
byte = 0 - &gt;高位字节为零,位编号为156
byte = 63 - &gt;两个高位字节为零,位编号为248
byte = 63 - &gt;三个高位字节为零,位编号为340
   :
   :
同样,byte = 63 - &gt;左起七个字节是0,(或全0)。

流的图

  

flow-diagram

代码

下面我写了一个小代码,用不同的值调用7函数,帮助你理解函数:

find_zero()

请观察输出以了解功能:

int main(){ 
 printf("\nmask=0x0, all bytes are zero, result =%ld", find_zero(0x0L)); // not prints 8
 printf("\nmask=0xff, left seven bytes are zero, result =%ld", find_zero(0xffL));
 printf("\nmask=0xffff, left six bytes are zero, result =%ld", find_zero(0xffffL));
 printf("\nmask=0xffffff, left five bytes are zero, result =%ld", find_zero(0xffffffL));
 printf("\nmask=0xffffffff, left four bytes are zero, result =%ld", find_zero(0xffffffffL));
 printf("\nmask=0xffffffffff, left three bytes are zero, result =%ld", find_zero(0xffffffffffL));
 printf("\nmask=0xffffffffffff, left two bytes are zero, result =%ld", find_zero(0xffffffffffffL));
 printf("\nmask=0xffffffffffffff, left most one byte is zero, result =%ld", find_zero(0xffffffffffffffL));
 printf("\nmask=0xffffffffffffffff, No byte is zero, result =%ld", find_zero(0xffffffffffffffffL));
 printf("\nmask=0x8000000000000000L, first byte is NOT zero (so no search), result =%ld", find_zero(0x8000000000000000LL));  
    printf("\n");
    return 1;
}

注意:如果所有字节都为零,则打印mask=0x0, all bytes are zero, result =7 mask=0xff, left seven bytes are zero, result =7 mask=0xffff, left six bytes are zero, result =6 mask=0xffffff, left five bytes are zero, result =5 mask=0xffffffff, left four bytes are zero, result =4 mask=0xffffffffff, left three bytes are zero, result =3 mask=0xffffffffffff, left two bytes are zero, result =2 mask=0xffffffffffffff, left most one byte is zero, result =1 mask=0xffffffffffffffff, No byte is zero, result =0 mask=0x8000000000000000L, first byte is NOT zero (so no search), result =0 而不是8.

答案 1 :(得分:0)

第一个函数查找掩码中非空的第一个字节,并从左侧开始返回索引。

64位示例,xx表示“任意字节”,“nn”表示“非零字节”:

.nn.xx.xx.xx.xx.xx.xx.xx -> find_zero() -> 0
.00.nn.xx.xx.xx.xx.xx.xx -> find_zero() -> 1
.00.00.nn.xx.xx.xx.xx.xx -> find_zero() -> 2
...
.00.00.00.00.00.00.00.nn -> find_zero() -> 7
.00.00.00.00.00.00.00.00 -> find_zero() -> 7 (this one is strange)

如果不是最后一个含糊不清的情况,我会将此函数命名为find_first_significant_byte_index() ...

第二个函数根据位掩码拆分val,将其“low_bits”存储在* data中以供进一步引用,并在val上返回一些奇怪的op结果。 (无法确定最后一个)。

答案 2 :(得分:0)

请参阅&#34;一次一个字界面&#34;:https://lwn.net/Articles/501492/

如果输入中包含零字节,则

has_zero()返回非零掩码。

find_zero()找到 第一个零点使用掩码作为输入的位置(从技术上讲,它不是那个掩码,但该掩码进一步按摩了另外两个功能)。 find_zero()没有在输入掩码中找到零。

请参阅链接文章中的示例用法:

if (has_zero(value, &bits, &constants)) {
    bits = prep_zero_mask(value, bits, &constants);
    bits = create_zero_mask(bits);
    zero_byte = find_zero(bits);
    /* ... */