上节讲了源码中的数据结构,这节开始讲源码中的函数。由于函数比较多,打算分开几节
先讲下头文件中的6个inline
函数。对于inline
的定义,看下百科inline关键字
第一个
static inline size_t sdslen(const sds s) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
return SDS_TYPE_5_LEN(flags);
case SDS_TYPE_8:
return SDS_HDR(8,s)->len;
case SDS_TYPE_16:
return SDS_HDR(16,s)->len;
case SDS_TYPE_32:
return SDS_HDR(32,s)->len;
case SDS_TYPE_64:
return SDS_HDR(64,s)->len;
}
return 0;
}
该函数的作用是获取一个已存在的sds
字符串变量的长度(注意是储存的字符个数,不是整个变量的空间大小),和strlen()
用法一样,不过效率高一点,因为sdslen()
是直接找到相应地址上存好的长度信息(每个sds
变量所指向的地址前都有一段结构体地址用来存相关信息。数据结构那节有讲)然后返回给我们,而strlen()
每次都要通过指针偏移来计算字符串长度,所以效率会低一些。
第一行里头的flags
变量是指sds
变量的类型,是32位系统下的还是64位系统下的,它的可取值就是0~4,也就是前几篇文章中提到的5个宏。这行代码就是要获取传入的字符串变量的类型。怎么获取的呢?看上去有点奇葩,数组的下标是-1??一般都是从0开始的。实际是可以这么用的。其实就是指针偏移吧(我不确定这样理解对不对…),数组通过下标来确定我们所获取的元素的地址距离首地址多远,从而找到该元素的地址,-1就是向后偏移一位(习惯把0的右边当做向前),看头文件中结构体的声明可知,那个零长度数组的前面就是一个unsigned char
类型的变量flags
,它占一个字节,而我们的一个sds
变量就是指向那个零长度数组的首地址,所以向后偏移一位就可以得到flags
(2017/12/10更新-之所以偏移一个字节,是因为那个0长度数组是char
类型的,它在32位或是64位系统都是占1个字节,而unsigned char
也是一个字节,因此[-1]这个下标的地址就是flags的地址.注意:如果我们有一个int
类型的指针p,那么p + 1
或p[1]
就偏移了sizeof(int) * 1
个字节,而不是1个字节!)。事实上,当一个指针指向某个合法的地址时,我们可以通过指针偏移来访问其它地址的内容的,如果地址存在的话,如果不存在,程序就会崩溃。
然后就是switch case
语句了。确定flags
后,就和SDS_TYPE_MASK
按位与运算一下,其实运算后还是原来flags
的值,不明白为啥要多一步运算。如果是SDS_TYPE_*
5个中的一种,就用宏SDS_HDR(T, s)
直接找到对应结构体的地址,然后就可以返回结构体中的len
变量内容,也就是字符串已使用的长度了。那个宏的用法在宏介绍那节有讲。type 5那个就不讲了。
第一个函数就讲到这。
第二个
static inline size_t sdsavail(const sds s) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5: {
return 0;
}
case SDS_TYPE_8: {
SDS_HDR_VAR(8,s);
return sh->alloc - sh->len;
}
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
return sh->alloc - sh->len;
}
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
return sh->alloc - sh->len;
}
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
return sh->alloc - sh->len;
}
}
return 0;
}
该函数的作用是获取一个字符串变量的剩余可用空间,avail
就是available
可用的意思。同样的,第一行是获取flags
,判断是type 32还是type 64等(type 5的不讲)。然后用宏SDS_HDR_VAR(T, s)
定义一个对应的结构体并指向该字符串变量的头结构体的首地址,就可以用该结构体里头最大申请的空间减去已用空间就是剩余可用空间了。该宏的用法在宏介绍那节也讲过。
第三个
static inline void sdssetlen(sds s, size_t newlen) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
{
unsigned char *fp = ((unsigned char*)s)-1;
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
}
break;
case SDS_TYPE_8:
SDS_HDR(8,s)->len = newlen;
break;
case SDS_TYPE_16:
SDS_HDR(16,s)->len = newlen;
break;
case SDS_TYPE_32:
SDS_HDR(32,s)->len = newlen;
break;
case SDS_TYPE_64:
SDS_HDR(64,s)->len = newlen;
break;
}
}
这个函数的作用是设置一个字符串变量已用的长度,逻辑和前两个都差不多,就是先获取flags
,再switch case
最后是宏的用法。前两个函数都理解了那这个也很容易理解。
剩下3个不细讲了。第4个static inline void sdsinclen(sds s, size_t inc);
是设置字符串新增的长度;第5个static inline size_t sdsalloc(const sds s);
是获取字符串变量的最大申请空间;最后一个static inline void sdssetalloc(sds s, size_t newlen);
是设置字符串变量的最大申请空间。
好,这节就讲到这,下节继续。:)