C语言char*字符串的缺点
-
以\0为结尾, 就无法存储任意的二进制数据
-
拿字符串长度时需要遍历
-
拼接字符串的时候要遍历两次
- 首先遍历原串末尾,然后再判断新的串加进来空间够不够
- 够的话再遍历目的串,依次添加进来
SDS做了哪些优化
-
数据结构上 → 空间换时间
- len → 当前字符串的长度
- alloc → 当前分配了多长的空间, 这样就可以减少了内存重复分配的次数,而且还是惰性删除的
- flag → 标记sds hdr的类型
- char* sds → buf[] → 存储真正的字符串
-
根据不同的字符串长度分配不同的元数据的长度 sdshdr有4种类型(sdshdr5被废弃了)
-
紧凑型字符串结构:在sdshdr上用了编译器优化,__attribute((packed))__来告诉编译器在内存中顺序存放,不用字节对齐
SDS空间预分配 sdsMakeRoomFor函数
sds sdsMakeRoomFor(sds s, size_t addlen)
{
void *sh, *newsh;
// s最初在sdsavail函数中会通过s[-1]直接拿到hdr的类型
size_t avail = sdsavail(s);
size_t len, newlen;
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);
sh = (char *)s - sdsHdrSize(oldtype);
// 新长度暂时为当前字符串的长度 + 想要增加的长度
newlen = (len + addlen);
if (newlen < SDS_MAX_PREALLOC)
// 如果小于1MB,那么分配 2倍的newLen这么长
newlen *= 2;
else
// 否则直接分配 newLen + 1MB
newlen += SDS_MAX_PREALLOC;
type = sdsReqType(newlen);
// 准备变更整个hdr的长度
/* 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)
{
// 和以前一样的类型的话,就分配hdrlen + newlen + 1字节
newsh = s_realloc(sh, hdrlen + newlen + 1);
if (newsh == NULL)
return NULL;
s = (char *)newsh + hdrlen;
}
else
{
/* Since the header size changes, need to move the string forward,
* and can't use realloc */
newsh = s_malloc(hdrlen + newlen + 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, newlen);
return s;
}
-
预分配包含三个步骤:
-
先判断原来的字符串是不是空间足够的,是的话直接返回
-
空间不够的话就先算出newLen= addLen + 现有长度len
- 如果小于1MB, 分配 2 * newLen
- 否则分配 newLen + 1MB
-
调整hdr的长度
- 分配hdrlen + newLen + 1字节
-