继续讲解源码中的函数
这次的函数全在.c文件中。先看前两个。
static inline int sdsHdrSize(char type) {
switch(type&SDS_TYPE_MASK) {
case SDS_TYPE_5:
return sizeof(struct sdshdr5);
case SDS_TYPE_8:
return sizeof(struct sdshdr8);
case SDS_TYPE_16:
return sizeof(struct sdshdr16);
case SDS_TYPE_32:
return sizeof(struct sdshdr32);
case SDS_TYPE_64:
return sizeof(struct sdshdr64);
}
return 0;
}
static inline char sdsReqType(size_t string_size) {
if (string_size < 1<<5)
return SDS_TYPE_5;
if (string_size < 1<<8)
return SDS_TYPE_8;
if (string_size < 1<<16)
return SDS_TYPE_16;
#if (LONG_MAX == LLONG_MAX)
if (string_size < 1ll<<32)
return SDS_TYPE_32;
#endif
return SDS_TYPE_64;
}
前两个都是inline
函数,为第三个函数服务的。第一个函数static inline int sdsHdrSize(char type);
,我也不知道Hdr的全称,不过函数的作用就是根据传入参数type
来返回对应类型的结构体的大小。具体传什么参数呢?
这个又得根据第二个函数static inline char sdsReqType(size_t string_size);
来决定了,它的作用就是根据传入的参数string_size
来决定返回哪种类型的字符串,是type 5还是type 8等。如何决定?其实string_size
就是我们要创建一个sds
字符串变量时的空间大小。1<<5 == 32
1<<8 == 256
就不一一列举了,左移32的计算器算不出来…为什么是小于这些数呢?宏那节有张图片,说char*
在不同位数系统中所指向的缓冲区的最大值是有限制的,而这些数正好是最大值的2倍(好像是2倍),如1<<16 == 65536
2^15 == 32768
。为什么是小于2倍而不是小于最大值,这个问题我也还不清楚。总之,这个函数就是根据我们要创建的字符串空间大小来决定字符串是什么类型的。
第三个函数sds sdsnewlen(const void *init, size_t initlen);
,就是创建一个sds
字符串变量,需要一个初始化长度initlen
。代码有点长,要点直接在代码中解释了-
// 作者的注释
/* Create a new sds string with the content specified by the 'init' pointer
* and 'initlen'.
* If NULL is used for 'init' the string is initialized with zero bytes.
*
* The string is always null-termined (all the sds strings are, always) so
* even if you create an sds string with:
*
* mystring = sdsnewlen("abc",3);
*
* You can print the string with printf() as there is an implicit \0 at the
* end of the string. However the string is binary safe and can contain
* \0 characters in the middle, as the length is stored in the sds header. */
sds sdsnewlen(const void *init, size_t initlen) {
void *sh;
sds s;
// 通过上面讲的第二个函数决定字符串的类型
char type = sdsReqType(initlen);
/* Empty strings are usually created in order to append. Use type 8
* since type 5 is not good at this. */
if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
// 通过第一个函数获得对应结构体的大小
int hdrlen = sdsHdrSize(type);
// 指向字符串的flags地址,需要通过它来设置字符串的类型
unsigned char *fp; /* flags pointer. */
// 申请内存空间,大小为头结构体的大小加上字符串的初始化
// 大小,再加一个字节用来存null结束符,为什么是这么多字
// 节?之前那章数据结构有讲
sh = s_malloc(hdrlen+initlen+1);
// 如果init地址不为空
if (!init)
// 就将刚申请的空间初始化为0
memset(sh, 0, hdrlen+initlen+1);
// 如果申请空间失败,就返回NULL
if (sh == NULL) return NULL;
// 典型的指针偏移,结构体的首地址偏移一个结构体的大小就
// 是结构体里头那个零长度数组的首地址,然后用一个sds变
// 量指向它
s = (char*)sh+hdrlen;
// sds变量指向的地址向左偏移一个字节就是结构体里头flags的地址
fp = ((unsigned char*)s)-1;
// 根据字符串类型来初始化对应的头结构体里的内容
switch(type) {
// type 5的不会用到,不讲
case SDS_TYPE_5: {
*fp = type | (initlen << SDS_TYPE_BITS);
break;
}
// 如果是8位系统
case SDS_TYPE_8: {
// 通过宏定义一个对应的结构体sh,该宏的用法在宏那节有讲
SDS_HDR_VAR(8,s);
// 结构体里头储存字符串长度的变量
sh->len = initlen;
// 结构体里头储存申请的空间大小的变量,刚创建字符串变量
// 的时候,就是初始化大小
sh->alloc = initlen;
// 结构体里头储存字符串类型的变量
*fp = type;
break;
}
// 解释同上
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
// 解释同上
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
// 解释同上
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
}
// 如果初始化长度不为0且init地址不为空
if (initlen && init)
// 则将init地址里头的内容拷贝给sds字符串变量
memcpy(s, init, initlen);
// 字符串结尾字符
s[initlen] = '\0';
// 返回创建的sds字符串变量
return s;
}
第四个
// 作者注释
/* Create an empty (zero length) sds string. Even in this case the string
* always has an implicit null term. */
sds sdsempty(void) {
// 直接调用第三个函数,创建一个什么内容都没有,且初始化长度为0的
// sds字符串变量。暂不知道有什么用
return sdsnewlen("",0);
}
第五个
// 作者注释
/* Create a new sds string starting from a null terminated C string. */
sds sdsnew(const char *init) {
// 计算init指向的字符串的个数
size_t initlen = (init == NULL) ? 0 : strlen(init);
// 然后调用第三个函数创建一个储存init指向的内容,初始化长度
// 为initlen的sds变量
return sdsnewlen(init, initlen);
}
第六个
// 作者注释
/* Duplicate an sds string. */
sds sdsdup(const sds s) {
// 调用第三个函数创建一个sds变量并将传入的sds变量内容复制过去
// sdslen()函数在上一节函数介绍里有讲
return sdsnewlen(s, sdslen(s));
}
第七个
// 作者注释
/* Free an sds string. No operation is performed if 's' is NULL. */
void sdsfree(sds s) {
if (s == NULL) return;
// sds变量是在堆中申请的空间,需要free。
// 我们在创建一个sds变量的时候是一次性申请了一段空间,
// 所以传入头结构体的首地址就行了。
// s变量指向的地址减去对应结构体的大小就是该结构体的
// 首地址。s[-1]就是字符串类型,sdsHdrSize()函数
// 上面有讲
s_free((char*)s-sdsHdrSize(s[-1]));
}
第八个
// 作者注释(看下还是很有用的)
/* Set the sds string length to the length as obtained with strlen(), so
* considering as content only up to the first null term character.
*
* This function is useful when the sds string is hacked manually in some
* way, like in the following example:
*
* s = sdsnew("foobar");
* s[2] = '\0';
* sdsupdatelen(s);
* printf("%d\n", sdslen(s));
*
* The output will be "2", but if we comment out the call to sdsupdatelen()
* the output will be "6" as the string was modified but the logical length
* remains 6 bytes. */
// 更新一个sds变量的长度
void sdsupdatelen(sds s) {
// 计算字符串的长度
int reallen = strlen(s);
// 设置字符串的长度,该函数在前一节有讲
sdssetlen(s, reallen);
}
第九个
// 作者注释
/* Modify an sds string in-place to make it empty (zero length).
* However all the existing buffer is not discarded but set as free space
* so that next append operations will not require allocations up to the
* number of bytes previously available. */
// 清空一个sds变量
void sdsclear(sds s) {
// 设置变量的长度为0
sdssetlen(s, 0);
// 将第一个字符置为0
s[0] = '\0';
}
好了,这节先讲这几个,后面还有好几个。:)