内核 addr_bit_set之LE, BE奇招BITOP_BE32_SWIZZLE

/*
 *    test bit
 */
#if defined(__LITTLE_ENDIAN)
# define BITOP_BE32_SWIZZLE    (0x1F & ~7)
#else
# define BITOP_BE32_SWIZZLE    0
#endif

static __inline__ __be32 addr_bit_set(void *token, int fn_bit)
{
    __be32 *addr = token;
    /*
     * Here,
     *     1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
     * is optimized version of
     *    htonl(1 << ((~fn_bit)&0x1F))
     * See include/asm-generic/bitops/le.h.
     */
    return (__force __be32)(1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)) &
           addr[fn_bit >> 5];
}


注释中说明了关于掩码位的计算,下面更进一步解释一下

 

令:

M1 = 1 << ((~fn_bit ^BITOP_BE32_SWIZZLE) & 0x1f);

M2 = htonl(1<<((~fn_bit)&0x1F));

 

M1M2等价原因:

 

4字节为例,BELE比特序也是倒过来的,测试IP地址第0位,相当于测试BE格式的第31位,即有:~fn_bit &0x1f,等价于31-fn_bit。测试IP地址第31位,相当于测试BE格式的第0位,即时~fn_big&0xlf,等价于31-fn_bit1<<((~fn_bit)&0x1F)这个是LE32位掩码,所以转成BE格式掩码,即htonl(1<<((~fn_bit)&0x1F))

 

htonl就是把字节03对调,字节12对调。所以~fn_bit ^BITOP_BE32_SWIZZLE(~fn_bit)3位不变,高2位按位取反,即与BITOP_BE32_SWIZZLE=11000B即可实现。

 

注:异或运算,与1异或相当取反,与0异或保持不变。



不仅字节存在顺序,位域也存在顺序:

#include <stdio.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <memory.h>

struct st_bits {
    unsigned char s1:1;
    unsigned char s2:1;
    unsigned char s3:1;
    unsigned char s4:1;
    unsigned char s5:1;
    unsigned char s6:1;
    unsigned char s7:1;
    unsigned char s8:1;
};
    
int main(int argc,char* argv[])
{
    unsigned int a=0x12345678;
    unsigned int b;
    struct st_bits* ptr;
    int i;
    int addr;
    struct in6_addr addr6;
    int err;
    char* saddr4 = "192.168.10.1";
    char* saddr6 = "2013:4::119";
    
    
    
    b = htonl(a);
    ptr = (struct st_bits*) &b;
    
    printf("Little Endian a=0x%0x\n", a);
     
    ptr = (struct st_bits*) &a;
    for(i=0; i<4; i++) {
        printf("%1x %1x %1x %1x %1x %1x %1x %1x \n", ptr[i].s1, ptr[i].s2, ptr[i].s3, ptr[i].s4, ptr[i].s5, ptr[i].s6, ptr[i].s7, ptr[i].s8);
    }
    
    printf("Big Endian b=0x%0x\n", b);
    ptr = (struct st_bits*) &b;
    for(i=0; i<4; i++) {
        printf("%1x %1x %1x %1x %1x %1x %1x %1x \n", ptr[i].s1, ptr[i].s2, ptr[i].s3, ptr[i].s4, ptr[i].s5, ptr[i].s6, ptr[i].s7, ptr[i].s8);
    }
    
    //返回的地址已经是网络字节格式,所以你无需再调用 函数htonl
    addr = inet_addr(saddr4);
        printf("ipv4:%s, BE addr=0x%0x\n", saddr4, addr);
    ptr = (struct st_bits*) &addr;
    for(i=0; i<4; i++) {
        printf("%1x %1x %1x %1x %1x %1x %1x %1x \n", ptr[i].s1, ptr[i].s2, ptr[i].s3, ptr[i].s4, ptr[i].s5, ptr[i].s6, ptr[i].s7, ptr[i].s8);
    }
    
    memset(&addr6, 0, sizeof(addr6));
    
    err = inet_pton(AF_INET6, saddr6, &addr6);
    if (err < 0) {
        printf("call inet_pton error, err=%d, errno=%d\n", err, errno);
    }
    
        printf("ipv6:%s\n", saddr6);
        ptr = (struct st_bits*) &addr6;
        for(i=0; i<16; i++) {
        printf("%02x %1x %1x %1x %1x %1x %1x %1x %1x \n", addr6.s6_addr[i], ptr[i].s1, ptr[i].s2, ptr[i].s3, ptr[i].s4, ptr[i].s5, ptr[i].s6, ptr[i].s7, ptr[i].s8);
    }
    
    return 0;
}


#运行结果(LE平台)

Little Endian a=0x12345678
0 0 0 1 1 1 1 0 
0 1 1 0 1 0 1 0 
0 0 1 0 1 1 0 0 
0 1 0 0 1 0 0 0 
Big Endian b=0x78563412
0 1 0 0 1 0 0 0 
0 0 1 0 1 1 0 0 
0 1 1 0 1 0 1 0 
0 0 0 1 1 1 1 0 
ipv4:192.168.10.1, BE addr=0x10aa8c0
0 0 0 0 0 0 1 1 
0 0 0 1 0 1 0 1 
0 1 0 1 0 0 0 0 
1 0 0 0 0 0 0 0 
ipv6:2013:4::119
20 0 0 0 0 0 1 0 0 
13 1 1 0 0 1 0 0 0 
00 0 0 0 0 0 0 0 0 
04 0 0 1 0 0 0 0 0 
00 0 0 0 0 0 0 0 0 
00 0 0 0 0 0 0 0 0 
00 0 0 0 0 0 0 0 0 
00 0 0 0 0 0 0 0 0 
00 0 0 0 0 0 0 0 0 
00 0 0 0 0 0 0 0 0 
00 0 0 0 0 0 0 0 0 
00 0 0 0 0 0 0 0 0 
00 0 0 0 0 0 0 0 0 
00 0 0 0 0 0 0 0 0 
01 1 0 0 0 0 0 0 0 
19 1 0 0 1 1 0 0 0 




你可能感兴趣的:(set)