redis源码分析(2)字符串sds.c
sds是一个char*的类型别名
可以把这个结构体和对应操作函数类比为C++中STL的string类。
因此可知,如果传入buf的地址s,该结构体的地址就是s-sizeof(struct sdshdr);
这两个函数比较好理解,当传入的sds类型而非普通字符数组时,求sds的现有长度和sds的可用长度。
有了len这个变量以后,获取字符串长度的时间复杂度为O(1),而C字符串获取字符串长度的时间复杂度为O(N)。
sdsnewlen 给定一个字符串init和字符串长度initlen,创建一个新的没有预留空间的sds。
如果init指针是有效值的话,就将init的内容复制到buf中去,如果没有的话,全部初始化为0.
可以看到,与字符数组一样,会预留1长度用来存放'\0'。
这样做的好处是在某些情况下可以重用C字符串函数库中的一些函数。比如,可以直接使用printf("%s",sds->buf);而无需重新位sdshdr编写打印函数。
注意,返回的是buf的地址。
sdsempty调用sdsnewlen创建一个空字符串。
但是我觉得return sdsnewlen(NULL,0)这种写法可能更好,从上面的图中可以看到,为“”字符串额外提供了空间,不过相差不大,可以看到相同的字符串常量地址是一样的。
sdsnew()复制一个以'\0'结尾的C风格字符串
sdsup与sdsnew类似,但是参数是一个sds类型。
释放函数。
在不释放sds的情况下重置,改变了len和free,相当于‘\0’后的数据全都无效了。
/* 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 中 buf 的长度进行扩展,确保在函数执行之后, * buf 至少会有 addlen + 1 长度的空余空间 * (额外的 1 字节是为 \0 准备的) * * 返回值 * sds :扩展成功返回扩展后的 sds * 扩展失败返回 NULL * * 复杂度 * T = O(N) */ sds sdsMakeRoomFor(sds s, size_t addlen) { struct sdshdr *sh, *newsh; // 获取 s 目前的空余空间长度 size_t free = sdsavail(s); size_t len, newlen; // s 目前的空余空间已经足够,无须再进行扩展,直接返回 if (free >= addlen) return s; // 获取 s 目前已占用空间的长度 len = sdslen(s); sh = (void*) (s-(sizeof(struct sdshdr))); // s 最少需要的长度 newlen = (len+addlen); // 根据新长度,为 s 分配新空间所需的大小 if (newlen < SDS_MAX_PREALLOC) // 如果新长度小于 SDS_MAX_PREALLOC // 那么为它分配两倍于所需长度的空间 newlen *= 2; else // 否则,分配长度为目前长度加上 SDS_MAX_PREALLOC newlen += SDS_MAX_PREALLOC; // T = O(N) newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1); // 内存不足,分配失败,返回 if (newsh == NULL) return NULL; // 更新 sds 的空余长度 newsh->free = newlen - len; // 返回 sds return newsh->buf; }