redis之sds(simple dynamic string)阅读笔记2--sds的复制,释放和更新长度
redis之sds(simple dynamic string)阅读笔记2--sds的复制,释放和更新长度 1复制sds字符串函数 sdsdup ********************************************* /* Duplicate an sds string. */ sds sdsdup(const sds s) { return sdsnewlen(s, sdslen(s)); } sdsnewlen 函数我们之前已经看过了,sds的类型其实就是指向字符的指针,其定义如下 typedef char *sds; 我们主要看函数sdslen,如下 ********************************************* static inline size_t sdslen(const sds s) { unsigned char flags = s[-1];// 因为是连续内存,所以s后退1的地址就是flags的地址,里面的内容就是flags switch(flags&SDS_TYPE_MASK) { //获取低3位的值 case SDS_TYPE_5: return SDS_TYPE_5_LEN(flags); //获取flags高5位的值,即字符串长度 case SDS_TYPE_8: return SDS_HDR(8,s)->len; //直接得到字符串长度 case SDS_TYPE_16: return SDS_HDR(16,s)->len; case SDS_TYPE_32: return SDS_HDR(32,s)->len; case SDS_TYPE_64: return SDS_HDR(64,s)->len; } return 0; } ********************************************************** #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) 其中T是数字(5,8,16,32,64),s是sds类型的字符串 观察如下结构体 struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; /* used */ uint8_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[]; }; s 表示字符串s所在的内存开始地址,即buf sizeof(struct sdshdr##T) 表示对应结构体的大小,即 len的长度 + alloc的长度 + flags的长度 那么 (s) - (sizeof(struct sdshdr##T) ) 就是字符串所在结构体的初始内存地址,类型为 struct sdshdr##T *sh 这里为什么要用小括号包裹起来,防止宏定义展开的时候出现问题 ********************************************************** #define SDS_TYPE_MASK 7 #define SDS_TYPE_BITS 3 #define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) ********************************************************** 2释放字符串函数sdsfree ********************************************************** /* Free an sds string. No operation is performed if 's' is NULL. */ void sdsfree(sds s) { if (s == NULL) return; s_free((char*)s-sdsHdrSize(s[-1])); //释放内存 这里的s_free和之前的s_malloc我们后续再统一讲解 } ********************************************************** 3.更新字符串的长度sdsupdatelen, 原文解释的很清楚了,如下注释 ********************************************************** /* Set the sds string length to the length as obtained with strlen(), so * considering as content only up to the first null term character. 设置SDS字符串的长度为函数strlen获得的长度,所以内容为指导第一个/0为止 * 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() 如果去掉对函数sdsupdatelen的注释,那么输出结果就是2 * the output will be "6" as the string was modified but the logical length * remains 6 bytes. 如果不去掉对函数sdsupdatelen的注释,那么这个字符串的长度就是6 */ void sdsupdatelen(sds s) { size_t reallen = strlen(s); sdssetlen(s, reallen); } ********************************************************** struct __attribute__ ((__packed__)) sdshdr5 { unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ char buf[]; }; 高5位记录长度,低三位记录类型 ********************************************************** static inline void sdssetlen(sds s, size_t newlen) { unsigned char flags = s[-1]; switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: { unsigned char *fp = ((unsigned char*)s)-1; //s后退一位为flags *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); //高5位记录字符串长度 } break; case SDS_TYPE_8: SDS_HDR(8,s)->len = newlen; //SDS_HDR宏定义获取指向结构体的指针, 设置结构体的使用长度len break; case SDS_TYPE_16: SDS_HDR(16,s)->len = newlen; break; case SDS_TYPE_32: SDS_HDR(32,s)->len = newlen; break; case SDS_TYPE_64: SDS_HDR(64,s)->len = newlen; break; } } ********************************************************** 4清空字符串函数sdsclear,原文解释的很清楚了,如下注释 /* Modify an sds string in-place to make it empty (zero length). 设置一个sds字符串为空(长度为0) * 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. */ void sdsclear(sds s) { sdssetlen(s, 0); //上面已解释 s[0] = '\0'; } **********************************************************