接着讲函数
第十个。该函数的作用是为一个sds
字符串变量申请更多的剩余空间,作者注释里有讲。学习使用别人的代码一定要看源码注释!函数里头有用到两个宏SDS_TYPE_MASK
SDS_MAX_PREALLOC
,它们的作用在宏那节有讲。下面直接在代码中讲解要点。
// 作者注释
/* Enlarge the free space at the end of the sds string so that the caller
* is sure that after calling this function can overwrite up to addlen
* bytes after the end of the string, plus one more byte for nul term.
*
* Note: this does not change the *length* of the sds string as returned
* by sdslen(), but only the free buffer space we have. */
sds sdsMakeRoomFor(sds s, size_t addlen) {
void *sh, *newsh;
// 获取字符串的剩余可用空间,sdsavail()是头文件中定义的一个内联函数
// 它的作用在第四节有讲
size_t avail = sdsavail(s);
size_t len, newlen;
// 获取字符串原来的类型(flags),字符串首地址向左偏移一个字节就可得到
// 按位与运算后flags的结果不变
char type, oldtype = s[-1] & SDS_TYPE_MASK;
// 用于储存对应结构体的大小的变量
int hdrlen;
/* Return ASAP if there is enough space left. */
// 如果剩余可用空间已经比你想申请的空间大,就没必要申请了
if (avail >= addlen) return s;
// 已储存的字符个数,该函数的原理前面章节讲过
len = sdslen(s);
// 下面这行代码的意思就是用s的首地址减去对应结构体的大小
// 就是该结构体的首地址了。其实就是指针偏移。用到的函数前
// 一节有讲
sh = (char*)s-sdsHdrSize(oldtype);
// 字符串已使用的长度加上新增加的长度就是字符串的总空间大小
newlen = (len+addlen);
// 下面的if else语句在宏那节有讲。作用是为了避免我们可以
// 申请无线大的空间
if (newlen < SDS_MAX_PREALLOC)
newlen *= 2;
else
newlen += SDS_MAX_PREALLOC;
// 根据新的空间大小来确定新的字符串的类型。该函数的作用前一节有讲
type = sdsReqType(newlen);
/* Don't use type 5: the user is appending to the string and type 5 is
* not able to remember empty space, so sdsMakeRoomFor() must be called
* at every appending operation. */
if (type == SDS_TYPE_5) type = SDS_TYPE_8;
// 根据字符串类型获取对应结构体的大小
hdrlen = sdsHdrSize(type);
// 新字符串的类型与原字符串类型相同?
if (oldtype==type) {
// 则申请对应结构体大小加新的总字符串空间再加一个字节的
// 结束符空间的内存。s_realloc()其实就是realloc()
newsh = s_realloc(sh, hdrlen+newlen+1);
// 申请失败则返回NULL
if (newsh == NULL) return NULL;
// 成功则将字符串指向新申请的空间地址。newsh是新空间的首地址,
// 加上该结构体的大小就是结构体里头那个零长度数组的首地址
s = (char*)newsh+hdrlen;
} else { // 新字符串的类型与原字符串类型不同
/* Since the header size changes, need to move the string forward,
* and can't use realloc */
// 作者说不能使用s_realloc(),因为头结构体的大小改变了,继续
// 使用的话会造成数据储存错位,因为realloc()会自动复制原地址
// 里头的内容到新空间中去。s_malloc()就是malloc()。
newsh = s_malloc(hdrlen+newlen+1);
// 申请失败,返回NULL
if (newsh == NULL) return NULL;
// 成功则将原字符串的内容拷贝到零数组首地址后面的空间中去
// 大小就是字符个数加1,结尾放'\0'字符。
memcpy((char*)newsh+hdrlen, s, len+1);
// sh指向原字符串的头结构体的首地址,已经不需要了,释放它
s_free(sh);
// 将s指向新的字符串空间
s = (char*)newsh+hdrlen;
// 设置字符串的类型
s[-1] = type;
// 设置字符串的真实长度,不是总的空间大小
sdssetlen(s, len);
}
// 设置新字符串的总空间大小。该函数的原理前面章节有讲
sdssetalloc(s, newlen);
return s;
}
第十一个。该函数的作用是去除多余的可用空间,使得字符串的总的空间大小就是实际储存的字符串个数加一个字节的结尾符。它的原理就是新申请一段刚好能容纳字符串的空间,将内容拷贝过去,释放原来的空间。
// 作者注释
/* Reallocate the sds string so that it has no free space at the end. The
* contained string remains not altered, but next concatenation operations
* will require a reallocation.
*
* After the call, the passed sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call. */
sds sdsRemoveFreeSpace(sds s) {
void *sh, *newsh;
// 获取原字符串类型(原理和上面的差不多,就不详细说了)
char type, oldtype = s[-1] & SDS_TYPE_MASK;
// 获取原字符串的头结构体大小
int hdrlen, oldhdrlen = sdsHdrSize(oldtype);
// 字符串实际长度
size_t len = sdslen(s);
// sh指向原结构体的首地址
sh = (char*)s-oldhdrlen;
/* Check what would be the minimum SDS header that is just good enough to
* fit this string. */
// 检测哪种类型的结构体(大小最小)适合该字符串,上面原注释的意思。
// 根据长度获字符串取类型
type = sdsReqType(len);
// 根据字符串类型获取结构体大小
hdrlen = sdsHdrSize(type);
/* If the type is the same, or at least a large enough type is still
* required, we just realloc(), letting the allocator to do the copy
* only if really needed. Otherwise if the change is huge, we manually
* reallocate the string to use the different header type. */
// 下面这一大段原理和上面那个函数结尾的差不多,就不一一解释了。
// 简要翻译下原注释的意思:如果字符串类型没变或需要一个更大
// 的类型(type 8,type 16或type32等),我们就用realloc()
// 帮我们申请空间并拷贝内容,否则我们就自己用malloc(),申请
// 空间,拷贝内容
if (oldtype==type || type > SDS_TYPE_8) {
newsh = s_realloc(sh, oldhdrlen+len+1);
if (newsh == NULL) return NULL;
s = (char*)newsh+oldhdrlen;
} else {
newsh = s_malloc(hdrlen+len+1);
if (newsh == NULL) return NULL;
memcpy((char*)newsh+hdrlen, s, len+1);
s_free(sh);
s = (char*)newsh+hdrlen;
s[-1] = type;
sdssetlen(s, len);
}
sdssetalloc(s, len);
return s;
}
第十二个。该函数的作用是获取申请一个sds
字符串变量所申请的总空间大小,包括头结构体的大小、字符串实际长度加剩余可用空间、和结尾的一个字节。
// 作者注释
/* Return the total size of the allocation of the specifed sds string,
* including:
* 1) The sds header before the pointer.
* 2) The string.
* 3) The free buffer at the end if any.
* 4) The implicit null term.
*/
size_t sdsAllocSize(sds s) {
// 获取字符串的实际长度和剩余可用空间大小。该函数的原理在前面章节有讲
size_t alloc = sdsalloc(s);
// 返回对应头结构体的大小和字符串的总大小和一个字节的内存空间
return sdsHdrSize(s[-1])+alloc+1;
}
第十三个。该函数的作用是获取一个字符串变量的头结构体的首地址。
/* Return the pointer of the actual SDS allocation (normally SDS strings
* are referenced by the start of the string buffer). */
void *sdsAllocPtr(sds s) {
// 字符串的首地址减去对应结构体的大小就是头结构体的首地址
// 原理就是指针偏移
return (void*) (s-sdsHdrSize(s[-1]));
}
好吧,今天就记录到这里。:)