一、sds格式
sds header定义:
1 struct sdshdr { 2 unsigned int len; 3 unsigned int free; 4 char buf[]; 5 };
sizeof(struct sdshdr)= 2*sizeof(unsigned int), char buf[]等价于char buf[0], 仅对编译器有效,并不实际占用存储。
其中len是使用的长度,free是剩余的长度,再加上一个C语言中的‘\0‘结束符
sizeof(buf) = len + free + 1, 格式如下:
二、sds基本操作
1、创建sds对象
1 sds sdsnewlen(const void *init, size_t initlen) { 2 struct sdshdr *sh; 3 4 if (init) { 5 sh = zmalloc(sizeof(struct sdshdr)+initlen+1); 6 } else { 7 sh = zcalloc(sizeof(struct sdshdr)+initlen+1); 8 } 9 if (sh == NULL) return NULL; 10 sh->len = initlen; 11 sh->free = 0; 12 if (initlen && init) 13 memcpy(sh->buf, init, initlen); 14 sh->buf[initlen] = ‘\0‘; 15 return (char*)sh->buf; 16 } 17 18 /* Create a new sds string starting from a null terminated C string. */ 19 sds sdsnew(const char *init) { 20 size_t initlen = (init == NULL) ? 0 : strlen(init); 21 return sdsnewlen(init, initlen); 22 }
sdsnew根据输入C字符串init,创建新的sds对象,typedef char *sds; sds即char *类型。
sds s = sdsnew("abc");
当执行上面这行代码,会在堆内存上面开辟一段连续的空间,具体的空间大小是11个字节的空间。
最前面4个字节存的是len ,也就是sds的长度
其次4个字节存的是free,剩余的空间。
最后才是 3个才是存的是我们实际的char的信息
也就是每创建一个sds,都会执行上面的操作,都会申请额外的8个字节才存len和free信息。
既然是一段连续的空间,那么只要已知一个指针,那么就能拿出struct结构里面的任何数据。
还是用上面那个例子
sds s = sdsnew("abc");
这个时候s实际上存的是buf首个char数据的地址。也就是说向前 移8个字节就能到struct sdshdr的len的地址(准确的说是int len的首地址)。8个字节刚好就是struct sdshdr占的空间字节的大小。
所以下面这个是成立的
static inline size_t sdslen(const sds s) { struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));//把buf的首地址向前移动8个,也是到了len return sh->len; }
s - (sizeof(struct sdshdr)) 表示将指针向前移动到 struct sdshdr 的起点,从而得出一个指向 sdshdr 结构的指针:
sds中函数比较简单,这里不做一一介绍,感兴趣可以直接阅读sds.c中的源码。
三、sds特点
1、可以通过O(1)获取字符串长度,直接返回sds->len即可,C字符串用strlen需要O(N)
2、杜绝缓冲区溢出。同样因为 sds已经记录长度信息
3、空间预分配和惰性空间释放
sds中空间预分配相关函数:
1 sds sdsMakeRoomFor(sds s, size_t addlen) { 2 struct sdshdr *sh, *newsh; 3 size_t free = sdsavail(s); 4 size_t len, newlen; 5 6 if (free >= addlen) return s; // 需要增加的长度小于free,直接返回,避免了频繁内存申请 7 len = sdslen(s); 8 sh = (void*) (s-(sizeof(struct sdshdr))); 9 newlen = (len+addlen); 10 if (newlen < SDS_MAX_PREALLOC) // 总长度(len+addlen)小于SDS_MAX_PREALLOC(1MB), 新申请长度扩大为2*newlen 11 newlen *= 2; 12 else 13 newlen += SDS_MAX_PREALLOC; // 否则,只比需求量增加1MB 14 newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1); 15 if (newsh == NULL) return NULL; 16 17 newsh->free = newlen - len; 18 return newsh->buf; 19 }
惰性空间释放用于优化字符串的缩短操作,不用每次缩短都释放内存,仅修改free和len的大小即可。
例如使用sdstrim操作删除sds中的的特定字符,最终只要修改free/len大小,不涉及free操作。
真正需要释放sds中的free空间,可以调用:
1 sds sdsRemoveFreeSpace(sds s) { 2 struct sdshdr *sh; 3 4 sh = (void*) (s-(sizeof(struct sdshdr))); 5 sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1); 6 sh->free = 0; 7 return sh->buf; 8 }
通过realloc来重新分配一块不包含free的空间,原空间realloc会自动释放。
4、sds主要api信息