#ifndef included_clib_h
#define included_clib_h
#include <vppinfra/config.h>
#define宏定义用于在头文件多次被包含时出现问题
此#include暂时不理会它 。
/* Standalone means to not assume we are running on a Unix box. */
#if ! defined (CLIB_STANDALONE) && ! defined (CLIB_LINUX_KERNEL)
#define CLIB_UNIX
#endif
支持的运行环境大类就2个,UNIX和非UNIX(standalone) .
UNIX大类中,又将linux内核环境区分开来。
#include <vppinfra/types.h>
此#include暂时不用理会
/* Global DEBUG flag. Setting this to 1 or 0 turns off
ASSERT (see vppinfra/error.h) & other debugging code. */
#ifndef CLIB_DEBUG
#define CLIB_DEBUG 0
#endif
DEBUG开关,打开或者关闭代码中的某些调试功能,学习代码过程中可以打开
#ifndef NULL
#define NULL ((void *) 0)
#endif
空指针的定义,一般空指针都这样玩
#define BITS(x) (8*sizeof(x))
#define ARRAY_LEN(x) (sizeof (x)/sizeof (x[0]))
一个字段所占用的的bit数目即 它占用的字节数目乘以8--因为每个字节占用8bit。 而一个对象占用的字节数目使用sizeof运算符在编译阶段求出来。
数组长度等于数组空间的总长度(字节数目) 除以 数组元素0(第1个元素)的长度(字节数目)。 结果即是数组元素的个数--即本数组共有几个成员。这也是一种经典的惯用法。相比于对长度硬编码要灵活得多(由编译器帮你计算,而不是自己人工去数)。-- 备注,也可以人工去计数数组元素个数,但容易出错,且麻烦。
#define _STRUCT_FIELD(t,f) (((t *) 0)->f)
#define STRUCT_OFFSET_OF(t,f) ((uword) & _STRUCT_FIELD (t, f))
#define STRUCT_BIT_OFFSET_OF(t,f) (BITS(u8) * (uword) & _STRUCT_FIELD (t, f))
#define STRUCT_SIZE_OF(t,f) (sizeof (_STRUCT_FIELD (t, f)))
#define STRUCT_BITS_OF(t,f) (BITS (_STRUCT_FIELD (t, f)))
#define STRUCT_ARRAY_LEN(t,f) ARRAY_LEN (_STRUCT_FIELD (t, f))
#define STRUCT_MARK(mark) u8 mark[0]
#define STRUCT_MARK_PTR(v, f) &(v)->f
_STRUCT_FIELD读取结构体t中的成员f
STRUCT_OFFSET_OF求结构体t中成员f的偏移量(字节单位)---编译时求出
STRUCT_BIT_OFFSET_OF 同上(位为单位)
STRUCT_SIZE_OF求成员所占空间大小(字节单位)
STRUCT_BITS_OF 同上(位为单位)
STRUCT_ARRAY_LEN成员f所含数组元素的个数--如果f不是数组,则不要使用
STRUCT_MARK一般用于结构体的末尾,在运行时申请结构体的内存,并且超过结构体本身的大小,末尾多出的部分作为数组的大小,或者也只是一个符号记录结构体的末尾,并不额外占用内存空间。
STRUCT_MARK_PTR 不细说
/* Stride in bytes between struct array elements. */
#define STRUCT_STRIDE_OF(t,f) \
( ((uword) & (((t *) 0)[1].f)) \
- ((uword) & (((t *) 0)[0].f)))
这段代码讲的这个事情。假定有一个结构体t和结构体中的成员变量f。 那么在t类型变量形成的数组中,2个相邻的数组元素的f成员变量的内存距离是多少?通过这个表达式就求出来了。
仔细一想,其实就是sizeof(t)的值。
#define STRUCT_OFFSET_OF_VAR(v,f) ((uword) (&(v)->f) - (uword) (v))
这个宏也是算成员变量相对于结构体首地址的偏移。与STRUCT_OFFSET_OF功能一致
/* Used to pack structure elements. */
#define CLIB_PACKED(x) x __attribute__ ((packed))
#define CLIB_UNUSED(x) x __attribute__ ((unused))
gcc编译器扩展属性packed表示结构体内部不填充pad,采用紧密模式。缺省情况下会填充一些pad字节。
unused属性表示,被修饰的变量有可能不会被使用,针对这种情况,不要编译报警。
/* Make a string from the macro's argument */
#define CLIB_STRING_MACRO(x) #x
即 printf(CLIB_STRING_MACRO(hello)) ; 会被替换成 printf("hello");
#define __clib_unused __attribute__ ((unused))
#define __clib_weak __attribute__ ((weak))
#define __clib_packed __attribute__ ((packed))
#define __clib_constructor __attribute__ ((constructor))
weak扩展属性表示弱引用。
若两个或两个以上全局符号(函数或变量名)名字一样,而其中之一声明为weak symbol(弱符号),则这些全局符号不会引发重定义错误。链接器会忽略弱符号,去使用普通的全局符号来解析所有对这些符号的引用,但当普通的全局符号不可用时,链接器会使用弱符号。当有函数或变量名可能被用户覆盖时,该函数或变量名可以声明为一个弱符号。弱符号也称为weak alias(弱别名)
constructor扩展属性表示被修饰的代码在main函数调用前会被执行
#define never_inline __attribute__ ((__noinline__))
被修饰的函数,请求编译器不要内联展开。
#if CLIB_DEBUG > 0
#define always_inline static inline
#define static_always_inline static inline
#else
#define always_inline static inline __attribute__ ((__always_inline__))
#define static_always_inline static inline __attribute__ ((__always_inline__))
#endif
调试开关打开的情况下,不强制内联。因为有些bug是内联导致,不方便问题定位。
普通的inline只是建议编译器进行内联。而__always_inline__则强制要求编译器进行内联展开。
/* Reserved (unused) structure element with address offset between
from and to. */
#define CLIB_PAD_FROM_TO(from,to) u8 pad_##from[(to) - (from)]
从某个字节开始连续填充若干字节。如从第13字节填充到第15字节。
CLIB_PAD_FROM_TO(13,15)
将被替换成u8 pad_13[2]
/* Hints to compiler about hot/cold code. */
#define PREDICT_FALSE(x) __builtin_expect((x),0)
#define PREDICT_TRUE(x) __builtin_expect((x),1)
告诉编译器表达式x的值大概率是0,或者是1。主要用在if, while条件判断中,编译器根据这个提示对代码流程优化。如将大概率是1的代码块放在附近,其它代码放较远的地方。---因为指令跳转会使指令缓存中已存在的指令全部无效,重新加载指令降低效率。
/* Full memory barrier (read and write). */
#define CLIB_MEMORY_BARRIER() __sync_synchronize ()
主要告诉编译器,不要把这个标记前后的代码进行对调,在执行时,也不允许CPU将前后的指令对调后执行。如果没有这样的机制,编译器和CPU可以根据当前的实际情况,按自己的判断,选择一种它认为合适的方式处理。
#if __x86_64__
#define CLIB_MEMORY_STORE_BARRIER() __builtin_ia32_sfence ()
#else
#define CLIB_MEMORY_STORE_BARRIER() __sync_synchronize ()
#endif
同上
/* Arranges for function to be called before main. */
#define INIT_FUNCTION(decl) \
decl __attribute ((constructor)); \
decl
/* Arranges for function to be called before exit. */
#define EXIT_FUNCTION(decl) \
decl __attribute ((destructor)); \
decl
主要用于main函数执行前执行的代码,以及main函数返回后执行的代码的描述。但没有搜到这2个宏使用的地方。
/* Use __builtin_clz if available. */
#if uword_bits == 64
#define count_leading_zeros(x) __builtin_clzll (x)
#define count_trailing_zeros(x) __builtin_ctzll (x)
#else
#define count_leading_zeros(x) __builtin_clzl (x)
#define count_trailing_zeros(x) __builtin_ctzl (x)
#endif
64位或者32位 CPU架构下。
编译器内置的计算高位连续0bit的个数,或者低位连续0bit的个数的方法
比如64这个值的二进制是 0000...00001000000 低位连续6个0。高位连续25个0(32位CPU)或者57个0(64位CPU)
#if defined (count_leading_zeros)
always_inline uword
min_log2 (uword x)
{
uword n;
n = count_leading_zeros (x);
return BITS (uword) - n - 1;
}
#else
基于编译器内置的计算连续0bit位数的结果。求x以2为底的对数的整数部分。
比如min_log2(15) 的 值 为 3。即2的3次幂为8. 2的4次幂为16。
8~15之间的所有数,它们的min_log2的值都为3。
else部分不会允许到,所以就不解释了。
always_inline uword
max_log2 (uword x)
{
uword l = min_log2 (x);
if (x > ((uword) 1 << l))
l++;
return l;
}
基于min_log2的结果。求x以2为底的对数的整数部分--向上取整。
比如max_log2(15) 的 值 为 4。即2的4次幂为16。
9~16之间的所有数,它们的max_log2的值都为4。
always_inline u64
min_log2_u64 (u64 x)
{
if (BITS (uword) == 64)
return min_log2 (x);
else
{
uword l, y;
y = x;
l = 0;
if (y == 0)
{
l += 32;
x >>= 32;
}
l += min_log2 (x);
return l;
}
}
这里描述了如果求64位数的以2位底的对数的整数部分。
特别是else分支表示,在32位CPU上如何计算64位数的对数。
如果最低32位为0,则取高32位继续计算,结果加上32。
如果最低32位不为0,则只需要计算最低32位即可。
always_inline uword
pow2_mask (uword x)
{
return ((uword) 1 << x) - (uword) 1;
}
最低x位连续为1,其它位为0。主要用于&位运算。具有掩码语义,名副其实。
如pow2_mask(3) == 000111 二进制表示
pow2_mask(4) == 001111 二进制表示
always_inline uword
max_pow2 (uword x)
{
word y = (word) 1 << min_log2 (x);
if (x > y)
y *= 2;
return y;
}
向上取一个整数,这个整数必须是2的幂次方
如9~16向上取整为16 (2的4次幂), 17~32向上取整为32(2的5次幂)
always_inline uword
is_pow2 (uword x)
{
return 0 == (x & (x - 1));
}
判断1个数是否是2的幂次方。
如0,1,2,4,8,16,32,...就是。其它数不是
always_inline uword
round_pow2 (uword x, uword pow2)
{
return (x + pow2 - 1) & ~(pow2 - 1);
}
这里注意,pow2是一个2的幂次方数,如2,4,8,16等。x是另外一个独立的数目。
结果是将x增加一小部分,使得x的二进制表示的尾部有连续的几个0。一般用于分配内存计算字节数时多申请点内存,并且满足内存对齐约束时。调用此函数。
always_inline u64
round_pow2_u64 (u64 x, u64 pow2)
{
return (x + pow2 - 1) & ~(pow2 - 1);
}
同上,64位版本
always_inline uword
first_set (uword x)
{
return x & -x;
}
从x最低位算起,遇到的第一个值为1的bit保留,其它位全部清0。将结果返回。
always_inline uword
log2_first_set (uword x)
{
uword result;
#ifdef count_trailing_zeros
result = count_trailing_zeros (x);
#else
result = min_log2 (first_set (x));
#endif
return result;
}
计算x最低位连续的0bit的个数
always_inline f64
flt_round_down (f64 x)
{
return (int) x;
}
对小数向下取整。
always_inline word
flt_round_nearest (f64 x)
{
return (word) (x + .5);
}
对小数四舍五入取整
always_inline f64
flt_round_to_multiple (f64 x, f64 f)
{
return f * flt_round_nearest (x / f);
}
将小数四舍五入到指定的精度
#define clib_max(x,y) \
({ \
__typeof__ (x) _x = (x); \
__typeof__ (y) _y = (y); \
_x > _y ? _x : _y; \
})
#define clib_min(x,y) \
({ \
__typeof__ (x) _x = (x); \
__typeof__ (y) _y = (y); \
_x < _y ? _x : _y; \
})
#define clib_abs(x) \
({ \
__typeof__ (x) _x = (x); \
_x < 0 ? -_x : _x; \
})
求最大值,最小值,绝对值的函数宏
__typeof__(x) 表示 x变量的当前类型
__typeof__(x) _x = (x) 表示用x的当前类型再定义一个临时变量_x,其值初始化为x现在的值。