sds.h
/** * redis 动态字符串 */#ifndef __SDS_H#define __SDS_H#define SDS_MAX_PREALLOC (1024*1024)extern const char *SDS_NOINIT;#include <sys/types.h>#include <stdarg.h>#include <stdint.h>/** * sds 类型是 char * 的别名 */typedef char *sds;/* Note: sdshdr5 is never used, we just access the flags byte directly. * However is here to document the layout of type 5 SDS strings. */// 根据存储的内容选择不同数据结构,以节省内存// 这里的 '__attribute__ ((__packed__))' 要求编译器取消内存对齐优化,按照实际的占用字节数进行对齐// string_size < 32struct __attribute__ ((__packed__)) sdshdr5 { /** * 前 5 位保存字符串长度,后 3 位保存类型值 */ unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ //实际保存字符串数据的地方以及末尾的一个 \0 char buf[];};// 32 <= string_size < 256struct __attribute__ ((__packed__)) sdshdr8 { /* 记录当前 buf 的长度(不包含 '\0') */ uint8_t len; /* used */ /* 记录当前 buf 总共分配的内存大小(不包含 '\0') */ uint8_t alloc; /* excluding the header and null terminator */ /* 记录当前 buf 的属性,用来标识到底是 sdshdr8 还是 sdshdr16 等 */ unsigned char flags; // 实际保存字符串数据的地方以及末尾的一个 \0 char buf[];};// 256 <= string_size < 65536(64K)struct __attribute__ ((__packed__)) sdshdr16 { // 记录当前 buf 的长度(不包含 '\0') uint16_t len; /* used */ // 记录当前 buf 总共分配的内存大小(不包含 '\0' uint16_t alloc; /* excluding the header and null terminator */ //记录当前 buf 的属性,用来标识到底是 sdshdr8 还是 sdshdr16 等 unsigned char flags; /* 3 lsb of type, 5 unused bits */ // 实际保存字符串数据的地方以及末尾的一个 \0 char buf[];};// 65536(64K) <= string_size < 4GBstruct __attribute__ ((__packed__)) sdshdr32 { //记录当前 buf 的长度(不包含 '\0') uint32_t len; /* used */ //记录当前 buf 总共分配的内存大小(不包含 '\0') uint32_t alloc; /* excluding the header and null terminator */ //记录当前 buf 的属性等 unsigned char flags; /* 3 lsb of type, 5 unused bits */ // 实际保存字符串数据的地方以及末尾的一个 \0 char buf[];};// 4G <= string_sizestruct __attribute__ ((__packed__)) sdshdr64 { //记录当前 buf 的长度(不包含 '\0') uint64_t len; /* used */ //记录当前 buf 总共分配的内存大小(不包含 '\0') uint64_t alloc; /* excluding the header and null terminator */ //记录当前 buf 的属性等 unsigned char flags; /* 3 lsb of type, 5 unused bits */ // 实际保存字符串数据的地方以及末尾的一个 \0 char buf[];};// flags 值的定义#define SDS_TYPE_5 0#define SDS_TYPE_8 1#define SDS_TYPE_16 2#define SDS_TYPE_32 3#define SDS_TYPE_64 4// 掩码,flags & SDS_TYPE_MASK 即可获得具体的 flags 值#define SDS_TYPE_MASK 7#define SDS_TYPE_BITS 3// 这里的 s 是一个 sds// 可以通过 sds 的头指针进行寻址,拿到整个 struct 的指针// buf[-1]的值是flags的值 存的值再内存中是连续的 (len alloc flags buf)#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)//判断字符串长度 inline内联static inline size_t sdslen(const sds s) { // 通过 buf 的 -1 下标获取 flags 的值 unsigned char flags = s[-1]; //SDS_TYPE_MASK = 7 flags&SDS_TYPE_MASK运算获取flags具体的值 switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: //flags的值 flags >> 3 return SDS_TYPE_5_LEN(flags); case SDS_TYPE_8: //SDS_HDR(8,s) == ((struct sdshdr8 *)((s)-(sizeof(struct sdshdrT8)))->len sdshdr8的len方法 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;}//sds可用内存大小static inline size_t sdsavail(const sds s) { //s[-1]的值是flags的值, 要求编译器取消内存对齐优化,按照实际的占用字节数进行对齐,buf的前面是flags unsigned char flags = s[-1]; //flags&SDS_TYPE_MASK运算获取flags具体的值 switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: { return 0; } //sh->alloc - sh->len; 分配内存大小减去字符串长度 case SDS_TYPE_8: { SDS_HDR_VAR(8,s); return sh->alloc - sh->len; } case SDS_TYPE_16: { SDS_HDR_VAR(16,s); return sh->alloc - sh->len; } case SDS_TYPE_32: { SDS_HDR_VAR(32,s); return sh->alloc - sh->len; } case SDS_TYPE_64: { SDS_HDR_VAR(64,s); return sh->alloc - sh->len; } } return 0;}//给sds设置新的长度static inline void sdssetlen(sds s, size_t newlen) { //获取flags的值 unsigned char flags = s[-1]; switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: { unsigned char *fp = ((unsigned char*)s)-1; /** * 位或| 二进制中只要有一个为1 就位1 10010 | 01100 = 11110 * 左移 << newlen << 3 = newlen * 2^3 = newlen * 8 */ //0 | newlen * 8 = newlen * 8 *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); } break; case SDS_TYPE_8: SDS_HDR(8,s)->len = newlen; 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; }}/** * 增加sds字符串长度 * @param s 字符串 * @param inc 新的长度 */static inline void sdsinclen(sds s, size_t inc) { unsigned char flags = s[-1]; switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: { unsigned char *fp = ((unsigned char*)s)-1; unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); } break; case SDS_TYPE_8: SDS_HDR(8,s)->len += inc; break; case SDS_TYPE_16: SDS_HDR(16,s)->len += inc; break; case SDS_TYPE_32: SDS_HDR(32,s)->len += inc; break; case SDS_TYPE_64: SDS_HDR(64,s)->len += inc; break; }}/** * sds 分配的大小 = sds可用大小 + sds长度 * @param s 字符串 * @return *//* sdsalloc() = sdsavail() + sdslen() */static inline size_t sdsalloc(const sds s) { unsigned char flags = s[-1]; switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: //flags >> 3 = flags / 8 return SDS_TYPE_5_LEN(flags); case SDS_TYPE_8: return SDS_HDR(8,s)->alloc; case SDS_TYPE_16: return SDS_HDR(16,s)->alloc; case SDS_TYPE_32: return SDS_HDR(32,s)->alloc; case SDS_TYPE_64: return SDS_HDR(64,s)->alloc; } return 0;}/** * 设置字符串可用内存为新分配的内存 * @param s 字符串 * @param newlen 新的长度 */static inline void sdssetalloc(sds s, size_t newlen) { unsigned char flags = s[-1]; //选择那种字符串类型 switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: /* Nothing to do, this type has no total allocation info. */ break; case SDS_TYPE_8: //设置字符串buf内存 sds->alloc = newlen SDS_HDR(8,s)->alloc = newlen; break; case SDS_TYPE_16: SDS_HDR(16,s)->alloc = newlen; break; case SDS_TYPE_32: SDS_HDR(32,s)->alloc = newlen; break; case SDS_TYPE_64: SDS_HDR(64,s)->alloc = newlen; break; }}//创建一个initlen长度的sds字符串,如果创建不成功会返回异常sds sdsnewlen(const void *init, size_t initlen);//尝试创建initlen长度的sds字符串,创建不成功返回NULLsds sdstrynewlen(const void *init, size_t initlen);//给字符串init创建一个sdhsds sdsnew(const char *init);//创建一个空字符串sds sdsempty(void);//复制字符串sds sdsdup(const sds s);//释放字符串void sdsfree(sds s);//扩展sds字符串长度sds sdsgrowzero(sds s, size_t len);//sds连接固定长度len的char字符串sds sdscatlen(sds s, const void *t, size_t len);//sds连接char字符串,模仿strcatsds sdscat(sds s, const char *t);//sds连接sds字符串,模仿strcatsds sdscatsds(sds s, const sds t);//sds拷贝固定长度len的char字符串sds sdscpylen(sds s, const char *t, size_t len);//sds拷贝char字符串sds sdscpy(sds s, const char *t);//sds连接格式化字符串,采用C实现格式功能sds sdscatvprintf(sds s, const char *fmt, va_list ap);#ifdef __GNUC__sds sdscatprintf(sds s, const char *fmt, ...) __attribute__((format(printf, 2, 3)));#elsesds sdscatprintf(sds s, const char *fmt, ...);#endif//sds连接格式化字符串,自定义格式化功能sds sdscatfmt(sds s, char const *fmt, ...);// 前后去掉字符 csetsds sdstrim(sds s, const char *cset);void sdssubstr(sds s, size_t start, size_t len);//截断字符串void sdsrange(sds s, ssize_t start, ssize_t end);//更新字符串长度void sdsupdatelen(sds s);//清空sds字符串void sdsclear(sds s);//比较两个sds字符串int sdscmp(const sds s1, const sds s2);//sds分割函数 对于strsplitsds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);//释放分割出来的sdsvoid sdsfreesplitres(sds *tokens, int count);//sds转小写void sdstolower(sds s);//sds转大写void sdstoupper(sds s);//longlong类型转化为sds字符串sds sdsfromlonglong(long long value);//sds添加转义字符字符串sds sdscatrepr(sds s, const char *p, size_t len);//sds根据转义字符分割,必须用sdsfreesplitres().sds *sdssplitargs(const char *line, int *argc);sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);//字符串连接后,转化为sdssds sdsjoin(char **argv, int argc, char *sep);//sds 连接sdssds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);/* Callback for sdstemplate. The function gets called by sdstemplate * every time a variable needs to be expanded. The variable name is * provided as variable, and the callback is expected to return a * substitution value. Returning a NULL indicates an error. */typedef sds (*sdstemplate_callback_t)(const sds variable, void *arg);//sds回调模板函数sds sdstemplate(const char *template, sdstemplate_callback_t cb_func, void *cb_arg);// sds扩展函数 sds的扩容/* Low level functions exposed to the user API */sds sdsMakeRoomFor(sds s, size_t addlen);//配合sdsMakeRoomFor使用,重置sds长度void sdsIncrLen(sds s, ssize_t incr);//sds释放空闲空间sds sdsRemoveFreeSpace(sds s);//计算sds开辟空间的大小size_t sdsAllocSize(sds s);//返回buf缓冲指针void *sdsAllocPtr(sds s);/* Export the allocator used by SDS to the program using SDS. * Sometimes the program SDS is linked to, may use a different set of * allocators, but may want to allocate or free things that SDS will * respectively free or allocate. *///开辟size大小的空间void *sds_malloc(size_t size);//如果size> ptr.length 重新开辟size大小的空间void *sds_realloc(void *ptr, size_t size);//释放指针空间void sds_free(void *ptr);#ifdef REDIS_TESTint sdsTest(int argc, char *argv[], int accurate);#endif#endif
sds.c
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <assert.h>#include <limits.h>#include "sds.h"#include "sdsalloc.h"/** * 常量 */const char *SDS_NOINIT = "SDS_NOINIT";/** * 获取sds类型结构占用的内存大小 * @param type sds的类型 * @return 内存大小 */static inline int sdsHdrSize(char type) { switch(type&SDS_TYPE_MASK) { case SDS_TYPE_5: return sizeof(struct sdshdr5); case SDS_TYPE_8: return sizeof(struct sdshdr8); case SDS_TYPE_16: return sizeof(struct sdshdr16); case SDS_TYPE_32: return sizeof(struct sdshdr32); case SDS_TYPE_64: return sizeof(struct sdshdr64); } return 0;}/** * 通过sds字符串长度获取所需要的sds类型 * @param string_size 字符串长度 * @return 字符串类型 */static inline char sdsReqType(size_t string_size) { //string_size < 2^5 = string_size < 32 if (string_size < 1<<5) return SDS_TYPE_5; // 32 <= string_size < 256 if (string_size < 1<<8) return SDS_TYPE_8; // 256 <= string_size < 65536(64K) if (string_size < 1<<16) return SDS_TYPE_16; // LONG_MAX == LLONG_MAX = 8 代表64位系统#if (LONG_MAX == LLONG_MAX)// 65536(64K) <= string_size < 4GBif (string_size < 1ll<<32) return SDS_TYPE_32;// 4G <= string_sizereturn SDS_TYPE_64;#else//32位系统return SDS_TYPE_32;#endif}/** * 获取sds字符串在该类型中的允许的最大长度 * @param type 类型 * @return 长度 */static inline size_t sdsTypeMaxSize(char type) { if (type == SDS_TYPE_5) return (1<<5) - 1; if (type == SDS_TYPE_8) return (1<<8) - 1; if (type == SDS_TYPE_16) return (1<<16) - 1;#if (LONG_MAX == LLONG_MAX) //预编译命令 代表64位系统 if (type == SDS_TYPE_32) return (1ll<<32) - 1;#endif //-1 相当于最大的 SDS_TYPE_64 或者 SDS_TYPE_32 return -1; /* this is equivalent to the max SDS_TYPE_64 or SDS_TYPE_32 */}/* Create a new sds string with the content specified by the 'init' pointer * and 'initlen'. * If NULL is used for 'init' the string is initialized with zero bytes. * If SDS_NOINIT is used, the buffer is left uninitialized; * * The string is always null-termined (all the sds strings are, always) so * even if you create an sds string with: * * mystring = sdsnewlen("abc",3); * * You can print the string with printf() as there is an implicit \0 at the * end of the string. However the string is binary safe and can contain * \0 characters in the middle, as the length is stored in the sds header. *//** * 用于创建sds字符串 * const void *init 代表任何类型的值 * @param init init指针 init 代表任何类型的值,值为常量,无法修改其值,但是可以其使指针指向其他对象 * @param initlen 字符串长度 * @param trymalloc 分配的buf大小 * @return sds字符串 */sds _sdsnewlen(const void *init, size_t initlen, int trymalloc) { void *sh; sds s; //通过sds字符串长度获取sds类型 char type = sdsReqType(initlen); /* Empty strings are usually created in order to append. Use type 8 * since type 5 is not good at this. */ // 如果类型SDS_TYPE_5 并且长度位0 则类型设置为 SDS_TYPE_8 ,空的字符串通常使用类型SDS_TYPE_8,以便于追加字符, if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; //sds 的类型占用的内存的大小 int hdrlen = sdsHdrSize(type); //定义flags标识 标识sds的buf属性 unsigned char *fp; /* flags pointer. */ size_t usable; //断言 字符串长度 + 类型占用的长度 + 1 > 字符串长度 assert(initlen + hdrlen + 1 > initlen); /* Catch size_t overflow */ // 分配内存的大小 sh如果为NULL则分配失败 否则usable为分配的可用内存大小 sh = trymalloc? s_trymalloc_usable(hdrlen+initlen+1, &usable) : s_malloc_usable(hdrlen+initlen+1, &usable); //如果分配内存失败则返回 NULL if (sh == NULL) return NULL; // 如果init==SDS_NOINIT(这里判断地址是否相同),设置init为NULL if (init==SDS_NOINIT) init = NULL; else if (!init) // 清空内存内容 memset(sh, 0, hdrlen+initlen+1); // c字符串 s = (char*)sh+hdrlen; // 标志位(sdshdr_xx中flags) fp = ((unsigned char*)s)-1; //字符串可用内存大小 = 分配的内存大小-sds结构大小-1 usable = usable-hdrlen-1; //判断可用内存大小 是否大于该类型最大可用大小 if (usable > sdsTypeMaxSize(type)) //是的话分配该类型最大可用内存大小 usable = sdsTypeMaxSize(type); // 根据得到的sds类型设置头部信息 switch(type) { case SDS_TYPE_5: { *fp = type | (initlen << SDS_TYPE_BITS); break; } case SDS_TYPE_8: { SDS_HDR_VAR(8,s); sh->len = initlen; sh->alloc = usable; *fp = type; break; } case SDS_TYPE_16: { SDS_HDR_VAR(16,s); sh->len = initlen; sh->alloc = usable; *fp = type; break; } case SDS_TYPE_32: { SDS_HDR_VAR(32,s); sh->len = initlen; sh->alloc = usable; *fp = type; break; } case SDS_TYPE_64: { SDS_HDR_VAR(64,s); sh->len = initlen; sh->alloc = usable; *fp = type; break; } } // 拷贝字符串到sds字符串中 if (initlen && init) memcpy(s, init, initlen); // 设置标准结束符 s[initlen] = '\0'; return s;}/** * 创建sds字符串 使用s_malloc_usable创建内存空间 * @param init 任意类型的值 常量 值不可更改 但指针可以更改指向的对象 * @param initlen * @return 字符串 */sds sdsnewlen(const void *init, size_t initlen) { return _sdsnewlen(init, initlen, 0);}/** * 尝试创建sds,使用s_trymalloc_usable创建内存空间 * @param init 任意类型的值 * @param initlen * @return */sds sdstrynewlen(const void *init, size_t initlen) { return _sdsnewlen(init, initlen, 1);}/** * 创建0长度的字符串 * @return sds字符串 *//* Create an empty (zero length) sds string. Even in this case the string * always has an implicit null term. */sds sdsempty(void) { return sdsnewlen("",0);}/** * 从一个以null结尾的 C字符串 创建一个新的sds字符串 * @param init * @return *//* Create a new sds string starting from a null terminated C string. */sds sdsnew(const char *init) { // 判断传入的字符串是否为NULL 如果为NULL为创建一个空的sds字符串,否则创建strlen(init)长度的sds字符串 size_t initlen = (init == NULL) ? 0 : strlen(init); return sdsnewlen(init, initlen);}/** * 复制一个sds * @param s * @return *//* Duplicate an sds string. */sds sdsdup(const sds s) { return sdsnewlen(s, sdslen(s));}/** * 释放sds字符串 * @param s sds字符串 *//* Free an sds string. No operation is performed if 's' is NULL. */void sdsfree(sds s) { //如果sds字符串为NULL为不作操作 if (s == NULL) return; //释放内存空间 s_free((char*)s-sdsHdrSize(s[-1]));}/* Set the sds string length to the length as obtained with strlen(), so * considering as content only up to the first null term character. * * 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() * the output will be "6" as the string was modified but the logical length * remains 6 bytes. *//** * 将sds的已经使用的长度修改成第一个'\0'结尾的字符串的长度,相当于从第一个\0截断 * * 当修改给sds字符串中某一个字符为'\0' , * 此时需要执行该方法去更新sds字符串长度,因为sds长度记录在sds头len中,如果不去更新,则sds长度不变, * 例子: * s = sdsnew("foobar"); * s[2] = '\0'; * dsupdatelen(s); * rintf("%d\n", sdslen(s)); * 此时输出长度为2 * 如果没有dsupdatelen(s); 则sds长度还是原来的6 * @param s */void sdsupdatelen(sds s) { //获取sds长度即 到第一个'\0'的长度 size_t reallen = strlen(s); // 给sds设置新的长度 sdssetlen(s, reallen);}/* Modify an sds string in-place to make it empty (zero length). * 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. *//** * 将sds的内存清零但不释放 * 设置sds长度为0,并将第一个字符位置设置为结束符 '\0' * @param s */void sdsclear(sds s) { sdssetlen(s, 0); s[0] = '\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扩充空间以供以后使用 * 该方法用于添加sds字符串sds的alloc的小 即可用空间大小,不会改变字符串长度 * @param s sds字符串 * @param addlen 需要新增的大小 * @return sds字符串 */sds sdsMakeRoomFor(sds s, size_t addlen) { void *sh, *newsh; //获取sds当前可用空间 size_t avail = sdsavail(s); size_t len, newlen, reqlen; //获取sds的类型 s[-1]= flags oldtype = flags & SDS_TYPE_MASK; char type, oldtype = s[-1] & SDS_TYPE_MASK; int hdrlen; size_t usable; //如果可用空间大于扩容大小 则无须扩容直接返回 /* Return ASAP if there is enough space left. */ if (avail >= addlen) return s; //判断字符串长度 len = sdslen(s); //获取sds类型结构占用的内存大小 sh = (char*)s-sdsHdrSize(oldtype); //新的内存大小等于 sds字符串长度+新增长度 reqlen = newlen = (len+addlen); //断言 新增后的长度要大于 sds字符串长度 assert(newlen > len); /* Catch size_t overflow */ //如果新的长度小于 1M,则扩容为其2倍 if (newlen < SDS_MAX_PREALLOC) newlen *= 2; //否则+1M else newlen += SDS_MAX_PREALLOC; //修改要扩容sds字符串的类型 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. */ //如果类型是SDS_TYPE_5 的话将类型设置为 SDS_TYPE_8 if (type == SDS_TYPE_5) type = SDS_TYPE_8; //获取新的类型的结构占用的空间大小 hdrlen = sdsHdrSize(type); //断言 新的类型结构占用的空间 + 扩容后的长度+1 要大于 扩容前sds字符串长度+addlen新增长度 assert(hdrlen + newlen + 1 > reqlen); /* Catch size_t overflow */ if (oldtype==type) { //如果旧的类型和新的类型一致,则重新分配内存 newsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable); 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 */ //如果新类型和旧内存不一致则重新创建一个新的内存空间 //分配内存 如果为NULL则内存分配失败 否则usable为可用内存大小 newsh = s_malloc_usable(hdrlen+newlen+1, &usable); if (newsh == NULL) return NULL; //复制字符串 memcpy((char*)newsh+hdrlen, s, len+1); //释放之前的sds字符串 s_free(sh); s = (char*)newsh+hdrlen; //设置新标识 s[-1] = type; //给sds设置新的长度 sdssetlen(s, len); } //可用内存 = 分配的可用内存-sds类型结构内存-1 usable = usable-hdrlen-1; //如果可用内存 > 类型最大的内存则重新设置sds类型 if (usable > sdsTypeMaxSize(type)) usable = sdsTypeMaxSize(type); //设置字符串可用内存为新分配的内存 sdssetalloc(s, usable); 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的分配内存 设置sds可用空间为0 * 重新分配sds字符串内存空间,传入的s字符串将不可以,指针将指向新创建的sds字符串 * @param s sds字符串 * @return */sds sdsRemoveFreeSpace(sds s) { void *sh, *newsh; //s[-1] = flags sds类型 char type, oldtype = s[-1] & SDS_TYPE_MASK; int hdrlen, oldhdrlen = sdsHdrSize(oldtype); //获取sds字符串长度 size_t len = sdslen(s); //获取s的可用内存 size_t avail = sdsavail(s); //sda的buf长度 = sds长度 - sds类型结构占用的内存空间大小 sh = (char*)s-oldhdrlen; //如果可用空间已经为0,那么无须调整 /* Return ASAP if there is no space left. */ if (avail == 0) return s; /* Check what would be the minimum SDS header that is just good enough to * fit this string. */ //获取该长度所需要的的sds类型 type = sdsReqType(len); //获取sds类型结构占用的内存大小 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. */ if (oldtype==type || type > SDS_TYPE_8) { //如果旧sds类型和 sds字符串最少需要的sds类型一致,重新分配内存并将之前sds内存释放 newsh = s_realloc(sh, oldhdrlen+len+1); //如果分配失败则返回NULL if (newsh == NULL) return NULL; //c 字符串 s = (char*)newsh+oldhdrlen; } else { //sds字符串最少需要空间的sds类型 和 原sds类型不一致 分配内存 newsh = s_malloc(hdrlen+len+1); //如果为NULL则分配失败 if (newsh == NULL) return NULL; //复制 memcpy((char*)newsh+hdrlen, s, len+1); //释放原sds串内存 s_free(sh); // c串 s = (char*)newsh+hdrlen; //设置新的类型 s[-1] = type; //设置新的sds长度 sdssetlen(s, len); } //设置sds可用内存为0的内存 sdssetalloc(s, len); return s;}/* Return the total size of the allocation of the specified 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. */// 返回一个sds的总长度,包括头,二进制字符串分配的内存长度和'\0'size_t sdsAllocSize(sds s) { //sds分配的内存大小 size_t alloc = sdsalloc(s); // sds类型结构占用的内存大小 + 可用内存空间 + 1 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). *//** * 返回一个sds的控制结构的起始地址 * @param s sds字符串 * @return 返回buf缓冲指针 */void *sdsAllocPtr(sds s) { return (void*) (s-sdsHdrSize(s[-1]));}/* Increment the sds length and decrements the left free space at the * end of the string according to 'incr'. Also set the null term * in the new end of the string. * * This function is used in order to fix the string length after the * user calls sdsMakeRoomFor(), writes something after the end of * the current string, and finally needs to set the new length. * * Note: it is possible to use a negative increment in order to * right-trim the string. * * Usage example: * * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the * following schema, to cat bytes coming from the kernel to the end of an * sds string without copying into an intermediate buffer: * * oldlen = sdslen(s); * s = sdsMakeRoomFor(s, BUFFER_SIZE); * nread = read(fd, s+oldlen, BUFFER_SIZE); * ... check for nread <= 0 and handle it ... * sdsIncrLen(s, nread); *//** * 增加一个sds的已经使用的长度,要求加完之后不能超过分配的内存长度 * @param s * @param incr */void sdsIncrLen(sds s, ssize_t incr) { unsigned char flags = s[-1]; size_t len; //判断类型 switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5: { //标志位flags unsigned char *fp = ((unsigned char*)s)-1; unsigned char oldlen = SDS_TYPE_5_LEN(flags); assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr))); *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS); len = oldlen+incr; break; } case SDS_TYPE_8: { SDS_HDR_VAR(8,s); assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); len = (sh->len += incr); break; } case SDS_TYPE_16: { SDS_HDR_VAR(16,s); assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); len = (sh->len += incr); break; } case SDS_TYPE_32: { SDS_HDR_VAR(32,s); assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); len = (sh->len += incr); break; } case SDS_TYPE_64: { SDS_HDR_VAR(64,s); assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr))); len = (sh->len += incr); break; } default: len = 0; /* Just to avoid compilation warnings. */ } s[len] = '\0';}/* Grow the sds to have the specified length. Bytes that were not part of * the original length of the sds will be set to zero. * * if the specified length is smaller than the current length, no operation * is performed. *//** * 给一个sds的已经使用长度增加到指定长度,并将增加部分内存清零 * @param s * @param len * @return */sds sdsgrowzero(sds s, size_t len) { //获取sds字符串长度 size_t curlen = sdslen(s); //指定的长度如果下雨当前sds字符串长度 则不做处理 if (len <= curlen) return s; //sds扩容 s = sdsMakeRoomFor(s,len-curlen); //如果为NULL 则内存分配失败 if (s == NULL) return NULL; // s+curlen ~ len-curlen+1 区间为0 /* Make sure added region doesn't contain garbage */ memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ //设置sds长度 sdssetlen(s, len); return s;}/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the * end of the specified sds string 's'. * * 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后面连接给定长度的二进制字符串 * @param s sds字符串 * @param t 任意类型的值 * @param len 长度 * @return sds字符串 */sds sdscatlen(sds s, const void *t, size_t len) { //获取sds字符串长度 size_t curlen = sdslen(s); //sds扩容 s = sdsMakeRoomFor(s,len); if (s == NULL) return NULL; //复制字符串 memcpy(s+curlen, t, len); //重新设置字符串 sdssetlen(s, curlen+len); //sds末尾置'\0' s[curlen+len] = '\0'; return s;}/* Append the specified null terminated C string to the sds string 's'. * * 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后面连接一个C字符串 * @param s * @param t * @return */sds sdscat(sds s, const char *t) { return sdscatlen(s, t, strlen(t));}/* Append the specified sds 't' to the existing sds 's'. * * After the call, the modified sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. *//** * 将一个sds的已经使用的内存连接到另一个sds中 * @param s * @param t * @return */sds sdscatsds(sds s, const sds t) { return sdscatlen(s, t, sdslen(t));}/* Destructively modify the sds string 's' to hold the specified binary * safe string pointed by 't' of length 'len' bytes. *//** * 将一个给定长度的二进制字符串复制到sds,并更新已经使用的长度 * @param s * @param t * @param len * @return */sds sdscpylen(sds s, const char *t, size_t len) { //如果长度len大于sds分配的内存大小 if (sdsalloc(s) < len) { //sds扩容 s = sdsMakeRoomFor(s,len-sdslen(s)); if (s == NULL) return NULL; } //复制字符串 memcpy(s, t, len); s[len] = '\0'; //设置长度 sdssetlen(s, len); return s;}/* Like sdscpylen() but 't' must be a null-termined string so that the length * of the string is obtained with strlen(). *//** * 将一个C字符串复制到sds * @param s * @param t * @return */sds sdscpy(sds s, const char *t) { return sdscpylen(s, t, strlen(t));}/* Helper for sdscatlonglong() doing the actual number -> string * conversion. 's' must point to a string with room for at least * SDS_LLSTR_SIZE bytes. * * The function returns the length of the null-terminated string * representation stored at 's'. */#define SDS_LLSTR_SIZE 21/** * 将一个long long转换成C字符串格式 * @param s * @param value * @return */int sdsll2str(char *s, long long value) { char *p, aux; unsigned long long v; size_t l; /* Generate the string representation, this method produces * a reversed string. */ v = (value < 0) ? -value : value; p = s; do { *p++ = '0'+(v%10); v /= 10; } while(v); if (value < 0) *p++ = '-'; /* Compute length and add null term. */ l = p-s; *p = '\0'; /* Reverse the string. */ p--; while(s < p) { aux = *s; *s = *p; *p = aux; s++; p--; } return l;}/** * 将一个unsigned long long 转换成C字符串格式 * @param s * @param v * @return *//* Identical sdsll2str(), but for unsigned long long type. */int sdsull2str(char *s, unsigned long long v) { char *p, aux; size_t l; /* Generate the string representation, this method produces * a reversed string. */ p = s; do { *p++ = '0'+(v%10); v /= 10; } while(v); /* Compute length and add null term. */ l = p-s; *p = '\0'; /* Reverse the string. */ p--; while(s < p) { aux = *s; *s = *p; *p = aux; s++; p--; } return l;}/* Create an sds string from a long long value. It is much faster than: * * sdscatprintf(sdsempty(),"%lld\n", value); *//** * 使用long long制造一个sds * @param value * @return */sds sdsfromlonglong(long long value) { char buf[SDS_LLSTR_SIZE]; //转换成C串 int len = sdsll2str(buf,value); //创建sds字符串 使用s_malloc_usable创建内存空 return sdsnewlen(buf,len);}/** * 将可变长格式化字符串连接到sds后面 * @param s * @param fmt * @param ap * @return *//* Like sdscatprintf() but gets va_list instead of being variadic. */sds sdscatvprintf(sds s, const char *fmt, va_list ap) { va_list cpy; char staticbuf[1024], *buf = staticbuf, *t; //获取fmt字符串长度 * 2 size_t buflen = strlen(fmt)*2; int bufstrlen; /* We try to start using a static buffer for speed. * If not possible we revert to heap allocation. */ if (buflen > sizeof(staticbuf)) { // 如果格式化字符串长度*2 大于 缓冲区长度,则分配内存 buf = s_malloc(buflen); if (buf == NULL) return NULL; } else { //如果格式化字符串长度*2 小于等于缓冲区大小 则设置为缓冲区大小 buflen = sizeof(staticbuf); } /* Alloc enough space for buffer and \0 after failing to * fit the string in the current buffer size. */ while(1) { //将ap复制到cpy va_copy(cpy,ap); //将可变参数格式化输出到一个字符数组。 bufstrlen = vsnprintf(buf, buflen, fmt, cpy); //将cpy置为NULL va_end(cpy); if (bufstrlen < 0) { //如果bufstrlen小于0 且 buf 不等于 staticbuf释放buf 并返回NULL if (buf != staticbuf) s_free(buf); return NULL; } if (((size_t)bufstrlen) >= buflen) { if (buf != staticbuf) s_free(buf); buflen = ((size_t)bufstrlen) + 1; buf = s_malloc(buflen); if (buf == NULL) return NULL; continue; } break; } /* Finally concat the obtained string to the SDS string and return it. */ //将字符串拼接到sds后面 t = sdscatlen(s, buf, bufstrlen); if (buf != staticbuf) s_free(buf); //释放buf缓冲区内存 return t;}/* Append to the sds string 's' a string obtained using printf-alike format * specifier. * * After the call, the modified sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. * * Example: * * s = sdsnew("Sum is: "); * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). * * Often you need to create a string from scratch with the printf-alike * format. When this is the need, just use sdsempty() as the target string: * * s = sdscatprintf(sdsempty(), "... your format ...", args); *//** * 是sdscatvprintf的包装, 将可变长格式化字符串连接到sds后面 * @param s * @param fmt * @param ... * @return */sds sdscatprintf(sds s, const char *fmt, ...) { va_list ap; char *t; va_start(ap, fmt); t = sdscatvprintf(s,fmt,ap); va_end(ap); return t;}/* This function is similar to sdscatprintf, but much faster as it does * not rely on sprintf() family functions implemented by the libc that * are often very slow. Moreover directly handling the sds string as * new data is concatenated provides a performance improvement. * * However this function only handles an incompatible subset of printf-alike * format specifiers: * * %s - C String * %S - SDS string * %i - signed int * %I - 64 bit signed integer (long long, int64_t) * %u - unsigned int * %U - 64 bit unsigned integer (unsigned long long, uint64_t) * %% - Verbatim "%" character. *//** * 和sdscatvprintf功能一样,区别是不使用sprintf族函数,性能高 * @param s * @param fmt * @param ... * @return */sds sdscatfmt(sds s, char const *fmt, ...) { //initlen= s长度 size_t initlen = sdslen(s); const char *f = fmt; long i; va_list ap; /* To avoid continuous reallocations, let's start with a buffer that * can hold at least two times the format string itself. It's not the * best heuristic but seems to work in practice. */ //sds扩容 s = sdsMakeRoomFor(s, strlen(fmt)*2); //宏 ap储存fmt参数 va_start(ap,fmt); f = fmt; /* Next format specifier byte to process. */ i = initlen; /* Position of the next byte to write to dest str. */ while(*f) { char next, *str; size_t l; long long num; unsigned long long unum; /* Make sure there is always space for at least 1 char. */ if (sdsavail(s)==0) { //如果可用空间为0,则扩容,内存大小+1 s = sdsMakeRoomFor(s,1); } switch(*f) { case '%': next = *(f+1); f++; switch(next) { case 's': case 'S': str = va_arg(ap,char*); //next如果是s则使用strlen获取长度,否则使用sds的sdslen根据sds的len值去获取长度 l = (next == 's') ? strlen(str) : sdslen(str); if (sdsavail(s) < l) { //如果可以长度小于l长度则扩容 s = sdsMakeRoomFor(s,l); } //复制 memcpy(s+i,str,l); //增加sds字符串长度 sdsinclen(s,l); i += l; break; case 'i': case 'I': if (next == 'i') num = va_arg(ap,int); else num = va_arg(ap,long long); { //SDS_LLSTR_SIZE = 21 char buf[21] char buf[SDS_LLSTR_SIZE]; //将long long转成C串 l = sdsll2str(buf,num); if (sdsavail(s) < l) { //可用空间小于l则扩容 s = sdsMakeRoomFor(s,l); } //复制 memcpy(s+i,buf,l); //增加sds字符串长度 sdsinclen(s,l); i += l; } break; case 'u': case 'U': if (next == 'u') unum = va_arg(ap,unsigned int); else unum = va_arg(ap,unsigned long long); { char buf[SDS_LLSTR_SIZE]; //将一个unsigned long long 转换成C字符串格式 l = sdsull2str(buf,unum); if (sdsavail(s) < l) { //扩容 s = sdsMakeRoomFor(s,l); } memcpy(s+i,buf,l); sdsinclen(s,l); i += l; } break; default: /* Handle %% and generally %<unknown>. */ //处理%%的情况 s[i++] = next; //增加sds字符串长 sdsinclen(s,1); break; } break; default: s[i++] = *f; sdsinclen(s,1); break; } f++; } //设置ap 为NULL va_end(ap); //sds串末尾置为'\0' /* Add null-term */ s[i] = '\0'; return s;}/* Remove the part of the string from left and from right composed just of * contiguous characters found in 'cset', that is a null terminted C string. * * After the call, the modified sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. * * Example: * * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); * s = sdstrim(s,"Aa. :"); * printf("%s\n", s); * * Output will be just "HelloWorld". *//** * 从sds的头和尾去掉所有和给定字符一致的内容,保留剩下的部分 * @param s * @param cset * @return sds */sds sdstrim(sds s, const char *cset) { char *start, *end, *sp, *ep; size_t len; //sp 指向头 ep指向尾 sp = start = s; ep = end = s+sdslen(s)-1; // 用于去掉sp头部有参数cset的部分 while(sp <= end && strchr(cset, *sp)) sp++; while(ep > sp && strchr(cset, *ep)) ep--; //新的长度 len = (sp > ep) ? 0 : ((ep-sp)+1); //sds拷贝 sp到s if (s != sp) memmove(s, sp, len); s[len] = '\0'; //设置sds sdssetlen(s,len); return s;}/* Changes the input string to be a subset of the original. * It does not release the free space in the string, so a call to * sdsRemoveFreeSpace may be wise after. *//** * * @param s * @param start * @param len */void sdssubstr(sds s, size_t start, size_t len) { /* Clamp out of range input */ //获取sds长度 size_t oldlen = sdslen(s); //如果起始点大于sds长度 则设置start = len = 0 if (start >= oldlen) start = len = 0; //长度如果大于 sds长度-起始点 重新设置长度为 sds长度-起始点 if (len > oldlen-start) len = oldlen-start; //将 s 的 N 个字节复制到 DEST,保证重叠字符串的正确行为。 /* Move the data */ if (len) memmove(s, s+start, len); s[len] = 0; //给sds设置新的长度 sdssetlen(s,len);}/* Turn the string into a smaller (or equal) string containing only the * substring specified by the 'start' and 'end' indexes. * * start and end can be negative, where -1 means the last character of the * string, -2 the penultimate character, and so forth. * * The interval is inclusive, so the start and end characters will be part * of the resulting string. * * The string is modified in-place. * * NOTE: this function can be misleading and can have unexpected behaviour, * specifically when you want the length of the new string to be 0. * Having start==end will result in a string with one character. * please consider using sdssubstr instead. * * Example: * * s = sdsnew("Hello World"); * sdsrange(s,1,-1); => "ello World" *//** * 将sds切片,提供起始和终止位置,可以是负数表示从结尾开始 * @param s sds字符串 * @param start 开始 * @param end 结束 */void sdsrange(sds s, ssize_t start, ssize_t end) { size_t newlen, len = sdslen(s); if (len == 0) return; if (start < 0) start = len + start; if (end < 0) end = len + end; newlen = (start > end) ? 0 : (end-start)+1; sdssubstr(s, start, newlen);}/** * 将sds的所有字母变成小写,内部是tolower * @param s sds *//* Apply tolower() to every character of the sds string 's'. */void sdstolower(sds s) { size_t len = sdslen(s), j; for (j = 0; j < len; j++) s[j] = tolower(s[j]);}/** * 将sds的所有字母变成大写,内部是toupper * @param s sds *//* Apply toupper() to every character of the sds string 's'. */void sdstoupper(sds s) { size_t len = sdslen(s), j; for (j = 0; j < len; j++) s[j] = toupper(s[j]);}/* Compare two sds strings s1 and s2 with memcmp(). * * Return value: * * positive if s1 > s2. * negative if s1 < s2. * 0 if s1 and s2 are exactly the same binary string. * * If two strings share exactly the same prefix, but one of the two has * additional characters, the longer string is considered to be greater than * the smaller one. *//** * 比较两个sds,内部是memcmp * @param s1 sds1 * @param s2 sds2 * @return */int sdscmp(const sds s1, const sds s2) { size_t l1, l2, minlen; int cmp; //获取sds字符串长度 l1 = sdslen(s1); l2 = sdslen(s2); minlen = (l1 < l2) ? l1 : l2; /** * memcmp * s1 < s2 cmp <0 * s1==s2 cmp =0 * s1>s2 cmp > 0 */ cmp = memcmp(s1,s2,minlen); //如果cmp = 0的话 就比较长度 if (cmp == 0) return l1>l2? 1: (l1<l2? -1: 0); return cmp;}/* Split 's' with separator in 'sep'. An array * of sds strings is returned. *count will be set * by reference to the number of tokens returned. * * On out of memory, zero length string, zero length * separator, NULL is returned. * * Note that 'sep' is able to split a string using * a multi-character separator. For example * sdssplit("foo_-_bar","_-_"); will return two * elements "foo" and "bar". * * This version of the function is binary-safe but * requires length arguments. sdssplit() is just the * same function but for zero-terminated strings. *//** * * 将字符串用指定的分割器分割成若干部分,存入sds指针数组,并返回这个指针数组,是简单的字符串匹配,可以使用KMP优化 * 输入长度为@len的字符串@s,以及长度为@seplen的字符串@sep。使用@sep对@s进行切分。 * 切分好的字符串通过返回值@tokens传出,切分得到的个数通过参数@count传出。 * * @param s s字符串 * @param len 长度 * @param sep 分隔字符串 * @param seplen 分隔字符串的长度 * @param count 分隔后的数组个数 * @return 返回@tokens */sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count) { int elements = 0, slots = 5; long start = 0, j; sds *tokens; //如果分隔字符串长度小于0 或者要被分割的字符串长度小于0 那么直接返回NULL if (seplen < 1 || len < 0) return NULL; //分配内存空间 tokens = s_malloc(sizeof(sds)*slots); //如果tokens为NULL 分配内存失败 if (tokens == NULL) return NULL; if (len == 0) { *count = 0; return tokens; } for (j = 0; j < (len-(seplen-1)); j++) { /* make sure there is room for the next element and the final one */ //确保预留空间 if (slots < elements+2) { sds *newtokens; slots *= 2; //分配内存 newtokens = s_realloc(tokens,sizeof(sds)*slots); if (newtokens == NULL) goto cleanup; tokens = newtokens; } /* search the separator */ /* * 两种情况: * 1、sep只有一个字符,一个字符一个字符比较,如果相等则进行分割 * 2、sep是字符串有多个字符,直接用memcmp进行比较,等于0代表相等,每次比较的长度为seplen */ if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { //分割字符串 tokens[elements] = sdsnewlen(s+start,j-start); //判断是否分配内存失败,如果失败则goto到cleanup去释放tokens空间 if (tokens[elements] == NULL) goto cleanup; elements++; start = j+seplen; //跳过分割符 j = j+seplen-1; /* skip the separator */ } } /* Add the final element. We are sure there is room in the tokens array. */ //最后添加一个元素,确保tokens长度不为0 tokens[elements] = sdsnewlen(s+start,len-start); //判断内存分配是否成功 if (tokens[elements] == NULL) goto cleanup; elements++; *count = elements; return tokens;cleanup: { //释放tokens数组中的元素 int i; for (i = 0; i < elements; i++) sdsfree(tokens[i]); s_free(tokens); *count = 0; return NULL; }}/** * 释放sdssplitlen产生的指针数组和其中的sds的内存 * @param tokens 分割后得到的数组 * @param count 数组中元素个数 *//* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */void sdsfreesplitres(sds *tokens, int count) { if (!tokens) return; while(count--) sdsfree(tokens[count]); s_free(tokens);}/* Append to the sds string "s" an escaped string representation where * all the non-printable characters (tested with isprint()) are turned into * escapes in the form "\n\r\a...." or "\x<hex-number>". * * After the call, the modified sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. *//** * 处理包含转义字符的二进制字符串 * @param s * @param p * @param len * @return */sds sdscatrepr(sds s, const char *p, size_t len) { //给s字符串后拼接 "\"" s = sdscatlen(s,"\"",1); while(len--) { switch(*p) { case '\\': case '"': //将可变长格式化字符串连接到sds后面 s = sdscatprintf(s,"\\%c",*p); break; case '\n': s = sdscatlen(s,"\\n",2); break; case '\r': s = sdscatlen(s,"\\r",2); break; case '\t': s = sdscatlen(s,"\\t",2); break; case '\a': s = sdscatlen(s,"\\a",2); break; case '\b': s = sdscatlen(s,"\\b",2); break; default: //检查所传的字符是否是可打印的 可打印为true 否则为false if (isprint(*p)) //将可变长格式化字符串连接到sds后面 s = sdscatprintf(s,"%c",*p); else s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); break; } p++; } //给s字符串后拼接 "\"" return sdscatlen(s,"\"",1);}/** * 判断char c是否是十六进制数,是根据ASCII码判断的 * @param c 字符 * @return *//* Helper function for sdssplitargs() that returns non zero if 'c' * is a valid hex digit. */int is_hex_digit(char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');}/** * 将一个十六进制字符转换成十进制 * @param c 字符 * @return *//* Helper function for sdssplitargs() that converts a hex digit into an * integer from 0 to 15 */int hex_digit_to_int(char c) { switch(c) { case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; case 'a': case 'A': return 10; case 'b': case 'B': return 11; case 'c': case 'C': return 12; case 'd': case 'D': return 13; case 'e': case 'E': return 14; case 'f': case 'F': return 15; default: return 0; }}/* Split a line into arguments, where every argument can be in the * following programming-language REPL-alike form: * * foo bar "newline are supported\n" and "\xff\x00otherstuff" * * The number of arguments is stored into *argc, and an array * of sds is returned. * * The caller should free the resulting array of sds strings with * sdsfreesplitres(). * * Note that sdscatrepr() is able to convert back a string into * a quoted string in the same format sdssplitargs() is able to parse. * * The function returns the allocated tokens on success, even when the * input string is empty, or NULL if the input contains unbalanced * quotes or closed quotes followed by non space characters * as in: "foo"bar or "foo' *//** * 字符串分割函数 * 将一行内容读取到sds指针数组,解析配置文件时使用,每个配置项保存在一个sds中,此函数返回一个sds指针数组和其元素个数 * @param line * @param argc * @return */sds *sdssplitargs(const char *line, int *argc) { const char *p = line; char *current = NULL; char **vector = NULL; *argc = 0; while(1) { /* skip blanks */ // isspace 判断字符是否为空白字符 不是空白字符 返回0 ,是空白字符 返回非0 while(*p && isspace(*p)) p++; if (*p) { /* get a token */ int inq=0; /* set to 1 if we are in "quotes" */ int insq=0; /* set to 1 if we are in 'single quotes' */ int done=0; //如果curent为NULL则创建一个空的sds字符串 if (current == NULL) current = sdsempty(); while(!done) { // *p = '"' inq = 1 //处理单引号 if (inq) { //处理16进制数字转换 // 如果满足 *p == '\\' *(p+1) == 'x' 和 *(p+2)、*(p+3)都是十六进制 if (*p == '\\' && *(p+1) == 'x' && is_hex_digit(*(p+2)) && is_hex_digit(*(p+3))) { unsigned char byte; // 16进制转10进制 byte = (hex_digit_to_int(*(p+2))*16)+ hex_digit_to_int(*(p+3)); current = sdscatlen(current,(char*)&byte,1); p += 3; } else if (*p == '\\' && *(p+1)) { //处理特殊字符 char c; p++; switch(*p) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'b': c = '\b'; break; case 'a': c = '\a'; break; default: c = *p; break; } current = sdscatlen(current,&c,1); } else if (*p == '"') { /* closing quote must be followed by a space or * nothing at all. */ if (*(p+1) && !isspace(*(p+1))) goto err; done=1; } else if (!*p) { /* unterminated quotes */ goto err; } else { current = sdscatlen(current,p,1); } } else if (insq) { //处理单引号 if (*p == '\\' && *(p+1) == '\'') { p++; current = sdscatlen(current,"'",1); } else if (*p == '\'') { /* closing quote must be followed by a space or * nothing at all. */ if (*(p+1) && !isspace(*(p+1))) goto err; done=1; } else if (!*p) { /* unterminated quotes */ goto err; } else { current = sdscatlen(current,p,1); } } else { switch(*p) { case ' ': case '\n': case '\r': case '\t': case '\0': done=1; break; case '"': inq=1; break; case '\'': insq=1; break; default: //拼接到curent后面 current = sdscatlen(current,p,1); break; } } if (*p) p++; } //分配内存空间 /* add the token to the vector */ vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); vector[*argc] = current; (*argc)++; current = NULL; } else { /* Even on empty input string return something not NULL. */ //即使输入为空也返回不为空的内容 if (vector == NULL) vector = s_malloc(sizeof(void*)); return vector; } }err: while((*argc)--) sdsfree(vector[*argc]); s_free(vector); if (current) sdsfree(current); *argc = 0; return NULL;}/* Modify the string substituting all the occurrences of the set of * characters specified in the 'from' string to the corresponding character * in the 'to' array. * * For instance: sdsmapchars(mystring, "ho", "01", 2) * will have the effect of turning the string "hello" into "0ell1". * * The function returns the sds string pointer, that is always the same * as the input pointer since no resize is needed. *//** * 将sds中出现在指定字符集中的字符用另一个字符集中的字符代替,要求两个字符集长度相等 * @param s sds字符串 * @param from 指定的字符 * @param to 替换成的字符 * @param setlen 长度 * @return sds */sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { size_t j, i, l = sdslen(s); for (j = 0; j < l; j++) { for (i = 0; i < setlen; i++) { if (s[j] == from[i]) { s[j] = to[i]; break; } } } return s;}/* Join an array of C strings using the specified separator (also a C string). * Returns the result as an sds string. *//** * 将若干C字符串用提供的分割器连接起来,产生一个sds * @param argv C字符串 * @param argc 个数 * @param sep 分割符 * @return */sds sdsjoin(char **argv, int argc, char *sep) { sds join = sdsempty(); int j; for (j = 0; j < argc; j++) { //字符串拼接到join后面 join = sdscat(join, argv[j]); if (j != argc-1) join = sdscat(join,sep); } return join;}/** * 和sdsjoin类似,连接的是若干sds * @param argv 若干个sds字符串 * @param argc 个数 * @param sep 分隔符 * @param seplen 长度 * @return *//* Like sdsjoin, but joins an array of SDS strings. */sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) { sds join = sdsempty(); int j; for (j = 0; j < argc; j++) { join = sdscatsds(join, argv[j]); if (j != argc-1) join = sdscatlen(join,sep,seplen); } return join;}/* Wrappers to the allocators used by SDS. Note that SDS will actually * just use the macros defined into sdsalloc.h in order to avoid to pay * the overhead of function calls. Here we define these wrappers only for * the programs SDS is linked to, if they want to touch the SDS internals * even if they use a different allocator. *//** * 分配内存 * @param size * @return */void *sds_malloc(size_t size) { return s_malloc(size); }/** * 重新分配内存 * @param ptr * @param size * @return */void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }/** * 释放内存 * @param ptr */void sds_free(void *ptr) { s_free(ptr); }/* Perform expansion of a template string and return the result as a newly * allocated sds. * * Template variables are specified using curly brackets, e.g. {variable}. * An opening bracket can be quoted by repeating it twice. *//** * sds回调模板函数 * @param template * @param cb_func * @param cb_arg * @return */sds sdstemplate(const char *template, sdstemplate_callback_t cb_func, void *cb_arg){ sds res = sdsempty(); const char *p = template; while (*p) { /* Find next variable, copy everything until there */ const char *sv = strchr(p, '{'); if (!sv) { /* Not found: copy till rest of template and stop */ res = sdscat(res, p); break; } else if (sv > p) { /* Found: copy anything up to the begining of the variable */ res = sdscatlen(res, p, sv - p); } /* Skip into variable name, handle premature end or quoting */ //跳过遍历名称 sv++; if (!*sv) goto error; /* Premature end of template */ if (*sv == '{') { /* Quoted '{' */ p = sv + 1; res = sdscat(res, "{"); continue; } /* Find end of variable name, handle premature end of template */ const char *ev = strchr(sv, '}'); if (!ev) goto error; /* Pass variable name to callback and obtain value. If callback failed, * abort. */ // 通过变量名称去回调 sds varname = sdsnewlen(sv, ev - sv); sds value = cb_func(varname, cb_arg); //释放内存空间 sdsfree(varname); if (!value) goto error; //将值添加到结果 /* Append value to result and continue */ res = sdscat(res, value); //释放值 sdsfree(value); p = ev + 1; } return res;error: //释放res sdsfree(res); return NULL;}#ifdef REDIS_TEST#include <stdio.h>#include <limits.h>#include "testhelp.h"#define UNUSED(x) (void)(x)static sds sdsTestTemplateCallback(sds varname, void *arg) { UNUSED(arg); static const char *_var1 = "variable1"; static const char *_var2 = "variable2"; if (!strcmp(varname, _var1)) return sdsnew("value1"); else if (!strcmp(varname, _var2)) return sdsnew("value2"); else return NULL;}int sdsTest(int argc, char **argv, int accurate) { UNUSED(argc); UNUSED(argv); UNUSED(accurate); { sds x = sdsnew("foo"), y; test_cond("Create a string and obtain the length", sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0); sdsfree(x); x = sdsnewlen("foo",2); test_cond("Create a string with specified length", sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0); x = sdscat(x,"bar"); test_cond("Strings concatenation", sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); x = sdscpy(x,"a"); test_cond("sdscpy() against an originally longer string", sdslen(x) == 1 && memcmp(x,"a\0",2) == 0); x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); test_cond("sdscpy() against an originally shorter string", sdslen(x) == 33 && memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0); sdsfree(x); x = sdscatprintf(sdsempty(),"%d",123); test_cond("sdscatprintf() seems working in the base case", sdslen(x) == 3 && memcmp(x,"123\0",4) == 0); sdsfree(x); x = sdscatprintf(sdsempty(),"a%cb",0); test_cond("sdscatprintf() seems working with \\0 inside of result", sdslen(x) == 3 && memcmp(x,"a\0""b\0",4) == 0); { sdsfree(x); char etalon[1024*1024]; for (size_t i = 0; i < sizeof(etalon); i++) { etalon[i] = '0'; } x = sdscatprintf(sdsempty(),"%0*d",(int)sizeof(etalon),0); test_cond("sdscatprintf() can print 1MB", sdslen(x) == sizeof(etalon) && memcmp(x,etalon,sizeof(etalon)) == 0); } sdsfree(x); x = sdsnew("--"); x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX); test_cond("sdscatfmt() seems working in the base case", sdslen(x) == 60 && memcmp(x,"--Hello Hi! World -9223372036854775808," "9223372036854775807--",60) == 0); printf("[%s]\n",x); sdsfree(x); x = sdsnew("--"); x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX); test_cond("sdscatfmt() seems working with unsigned numbers", sdslen(x) == 35 && memcmp(x,"--4294967295,18446744073709551615--",35) == 0); sdsfree(x); x = sdsnew(" x "); sdstrim(x," x"); test_cond("sdstrim() works when all chars match", sdslen(x) == 0); sdsfree(x); x = sdsnew(" x "); sdstrim(x," "); test_cond("sdstrim() works when a single char remains", sdslen(x) == 1 && x[0] == 'x'); sdsfree(x); x = sdsnew("xxciaoyyy"); sdstrim(x,"xy"); test_cond("sdstrim() correctly trims characters", sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0); y = sdsdup(x); sdsrange(y,1,1); test_cond("sdsrange(...,1,1)", sdslen(y) == 1 && memcmp(y,"i\0",2) == 0); sdsfree(y); y = sdsdup(x); sdsrange(y,1,-1); test_cond("sdsrange(...,1,-1)", sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0); sdsfree(y); y = sdsdup(x); sdsrange(y,-2,-1); test_cond("sdsrange(...,-2,-1)", sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0); sdsfree(y); y = sdsdup(x); sdsrange(y,2,1); test_cond("sdsrange(...,2,1)", sdslen(y) == 0 && memcmp(y,"\0",1) == 0); sdsfree(y); y = sdsdup(x); sdsrange(y,1,100); test_cond("sdsrange(...,1,100)", sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0); sdsfree(y); y = sdsdup(x); sdsrange(y,100,100); test_cond("sdsrange(...,100,100)", sdslen(y) == 0 && memcmp(y,"\0",1) == 0); sdsfree(y); y = sdsdup(x); sdsrange(y,4,6); test_cond("sdsrange(...,4,6)", sdslen(y) == 0 && memcmp(y,"\0",1) == 0); sdsfree(y); y = sdsdup(x); sdsrange(y,3,6); test_cond("sdsrange(...,3,6)", sdslen(y) == 1 && memcmp(y,"o\0",2) == 0); sdsfree(y); sdsfree(x); x = sdsnew("foo"); y = sdsnew("foa"); test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0); sdsfree(y); sdsfree(x); x = sdsnew("bar"); y = sdsnew("bar"); test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0); sdsfree(y); sdsfree(x); x = sdsnew("aar"); y = sdsnew("bar"); test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0); sdsfree(y); sdsfree(x); x = sdsnewlen("\a\n\0foo\r",7); y = sdscatrepr(sdsempty(),x,sdslen(x)); test_cond("sdscatrepr(...data...)", memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0); { unsigned int oldfree; char *p; int i; size_t step = 10, j; sdsfree(x); sdsfree(y); x = sdsnew("0"); test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0); /* Run the test a few times in order to hit the first two * SDS header types. */ for (i = 0; i < 10; i++) { size_t oldlen = sdslen(x); x = sdsMakeRoomFor(x,step); int type = x[-1]&SDS_TYPE_MASK; test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen); if (type != SDS_TYPE_5) { test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step); oldfree = sdsavail(x); UNUSED(oldfree); } p = x+oldlen; for (j = 0; j < step; j++) { p[j] = 'A'+j; } sdsIncrLen(x,step); } test_cond("sdsMakeRoomFor() content", memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0); test_cond("sdsMakeRoomFor() final length",sdslen(x)==101); sdsfree(x); } /* Simple template */ x = sdstemplate("v1={variable1} v2={variable2}", sdsTestTemplateCallback, NULL); test_cond("sdstemplate() normal flow", memcmp(x,"v1=value1 v2=value2",19) == 0); sdsfree(x); /* Template with callback error */ x = sdstemplate("v1={variable1} v3={doesnotexist}", sdsTestTemplateCallback, NULL); test_cond("sdstemplate() with callback error", x == NULL); /* Template with empty var name */ x = sdstemplate("v1={", sdsTestTemplateCallback, NULL); test_cond("sdstemplate() with empty var name", x == NULL); /* Template with truncated var name */ x = sdstemplate("v1={start", sdsTestTemplateCallback, NULL); test_cond("sdstemplate() with truncated var name", x == NULL); /* Template with quoting */ x = sdstemplate("v1={{{variable1}} {{} v2={variable2}", sdsTestTemplateCallback, NULL); test_cond("sdstemplate() with quoting", memcmp(x,"v1={value1} {} v2=value2",24) == 0); sdsfree(x); } test_report(); return 0;}#endif