本文将详细说明Redis中简单动态字符串(sds)的实现。
在Redis源码(这里使用3.2.11版本)中,sds的实现在sds.h
和sds.c
中。
sds字符串和C原生字符串的对比 Redis并没有直接使用C语言的原生字符串,而是有一个专用的字符串实现:sds。sds相比于C语言原生字符串有很多优势:
sds获取字符串长度的效率高。要想获取C语言原生字符串的长度,需要遍历整个字符串对字符个数计数,直到遇到一个\0
为止,时间复杂度为O(n)。sds在头部保存了len
用来表示字符串的实际长度,获取sds字符串长度的时间复杂度为O(1)。
sds可以避免缓冲区溢出。一个简单的例子,字符串拼接,对C语言原生字符串str1和str2来说,把str2拼接到str1后,如果没有为str1申请好足够的内存,直接拼接可能造成str1后的内存区域被覆盖从而导致不可预知的后果。sds字符串在拼接时,会自动检查空间是否足够,如果不够会自动按照一定的规则进行分配,因此无需担心溢出问题。
sds的内存分配策略可以有效降低修改字符串时内存重分配的开销。在sdsMakeRoomFor
函数中有这么一段代码(只截取一小部分):
1 2 3 4 5 6 7 if (newlen < SDS_MAX_PREALLOC) newlen *= 2 ; else newlen += SDS_MAX_PREALLOC;
上面这段代码表示,在对一个sds字符串进行扩充时,Redis会认为这个字符串还有进一步被扩充的可能性,因此会根据一定规则来预先分配一部分空间来避免短期内再次申请内存分配。另外sds字符串在缩短内容时,也不会立即释放多出来的空间,sds字符串在alloc
属性中标识了占用的总空间大小,在需要的时候,Redis会进行释放。
4.sds是二进制安全的。C原生字符串的结尾是\0
,也就是说它在字符串内容中不能包含\0
,如果包含了会导致字符串被截断,因此C原生字符串只能用来保存文本数据,无法保存图片等包含\0
的数据。sds字符串使用len
属性来标识字符串长度而不是\0
,所以其内容可以是任意的二进制数据。
sds头文件详解 sds的定义表明sds实际上是一个char *
:
但这还不足以说明sds是什么,源码中还定义了五种sds header的类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 struct __attribute__ ((__packed__) ) sdshdr5 { unsigned char flags; char buf[]; }; struct __attribute__ ((__packed__) ) sdshdr8 { uint8_t len; uint8_t alloc; unsigned char flags; char buf[]; }; struct __attribute__ ((__packed__) ) sdshdr16 { uint16_t len; uint16_t alloc; unsigned char flags; char buf[]; }; struct __attribute__ ((__packed__) ) sdshdr32 { uint32_t len; uint32_t alloc; unsigned char flags; char buf[]; }; struct __attribute__ ((__packed__) ) sdshdr64 { uint64_t len; uint64_t alloc; unsigned char flags; char buf[]; }; #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 #define SDS_TYPE_MASK 7 // sds类型掩码 0b00000111,因为flags中只有3个最低有效位表示类型 #define SDS_TYPE_BITS 3 // 表示sds类型的比特位数,前面有提到:3个最低有效位表示类型
除了sdshdr5
不被使用以外,可以观察到其他四种类型的头部中len
和alloc
域的类型都不同,不同类型的头部支持的字符串长度不同,这是为了空间效率才这么做的,后面会有详细分析。
根据sds header的定义,来看看一个头部类型为sdshdr8的sds字符串的内存布局:
Redis的sds内存布局1
sdshdr8
中的len
表示sds字符串的实际长度,也就是buf字符数组的长度,alloc
表示分配给字符串的空间大小,注意这个大小不包含头部和结尾的终止符。也就是说alloc
是大于或等于len
的,当alloc
等于len
时,内存布局就如上图所示,如果当alloc
大于len
,在字符串和和结尾终止符(\0)之间,会用\0
填充,下面是一个len
等于12,alloc
等于15的sds字符串的内存布局示意图:
Redis的sds内存布局2
先看几个在sds实现中很常用的宏:
1 2 3 4 5 6 7 8 #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)
sdslen
函数获取一个sds的长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static inline size_t sdslen (const sds s) { unsigned char flags = s[-1 ]; switch (flags&SDS_TYPE_MASK) { case SDS_TYPE_5: return SDS_TYPE_5_LEN(flags); 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 ; }
sdsavail
函数获取一个sds的空闲空间,计算方式是:已分配的空间 - 字符串长度大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static inline size_t sdsavail (const sds s) { unsigned char flags = s[-1 ]; switch (flags&SDS_TYPE_MASK) { case SDS_TYPE_5: { return 0 ; } 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 ; }
sdssetlen
函数设置sds的字符串长度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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 ; *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 ; } }
sdsinclen
函数增加sds的长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 ; } }
sdsalloc
函数获取sds容量,sdsalloc() = sdsavail() + sdslen()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static inline size_t sdsalloc (const sds s) { unsigned char flags = s[-1 ]; switch (flags&SDS_TYPE_MASK) { case SDS_TYPE_5: 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 ; }
sdssetalloc
函数设置sds容量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static inline void sdssetalloc (sds s, size_t newlen) { unsigned char flags = s[-1 ]; switch (flags&SDS_TYPE_MASK) { case SDS_TYPE_5: break ; case SDS_TYPE_8: 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 ; } }
下面是sds.h
中声明的函数原型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 sds sdsnewlen (const void *init, size_t initlen) ; sds sdsnew (const char *init) ; sds sdsempty (void ) ; sds sdsdup (const sds s) ; void sdsfree (sds s) ; sds sdsgrowzero (sds s, size_t len) ; sds sdscatlen (sds s, const void *t, size_t len) ; sds sdscat (sds s, const char *t) ; sds sdscatsds (sds s, const sds t) ; sds sdscpylen (sds s, const char *t, size_t len) ; sds sdscpy (sds s, const char *t) ; sds sdscatvprintf (sds s, const char *fmt, va_list ap) ;#ifdef __GNUC__ sds sdscatprintf (sds s, const char *fmt, ...) __attribute__ ((format(printf, 2 , 3 ) )) ;#else sds sdscatprintf (sds s, const char *fmt, ...) ;#endif sds sdscatfmt (sds s, char const *fmt, ...) ; sds sdstrim (sds s, const char *cset) ; void sdsrange (sds s, int start, int end) ; void sdsupdatelen (sds s) ; void sdsclear (sds s) ; int sdscmp (const sds s1, const sds s2) ; sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); void sdsfreesplitres (sds *tokens, int count) ; void sdstolower (sds s) ; void sdstoupper (sds s) ; sds sdsfromlonglong (long long value) ; sds sdscatrepr (sds s, const char *p, size_t len) ; sds *sdssplitargs(const char *line, int *argc); sds sdsmapchars (sds s, const char *from, const char *to, size_t setlen) ; sds sdsjoin (char **argv, int argc, char *sep) ; sds sdsjoinsds (sds *argv, int argc, const char *sep, size_t seplen) ; sds sdsMakeRoomFor (sds s, size_t addlen) ; void sdsIncrLen (sds s, int incr) ; sds sdsRemoveFreeSpace (sds s) ; size_t sdsAllocSize (sds s) ; void *sdsAllocPtr(sds s); void *sds_malloc(size_t size); void *sds_realloc(void *ptr, size_t size); void sds_free (void *ptr) ;
sds实现详解 下面列举了一部分是sds.c
中的函数定义,由于sds.c
代码量较多(超过1500行),其中有一些函数是帮助函数,或测试代码,这里只列举比较重要的函数详细解释。
sdsHdrSize
函数获取sds header的大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 ; }
sdsReqType
函数根据字符串大小判断sds类型。
1 2 3 4 5 6 7 8 9 10 11 static inline char sdsReqType (size_t string_size) { if (string_size < 1 <<5 ) return SDS_TYPE_5; if (string_size < 1 <<8 ) return SDS_TYPE_8; if (string_size < 1 <<16 ) return SDS_TYPE_16; if (string_size < 1l l<<32 ) return SDS_TYPE_32; return SDS_TYPE_64; }
sdsnewlen
函数使用init指针指向的数据和initlen的长度创建一个新的sds字符串。如果init指针是NULL,字符串会被初始化为长度为initlen,内容全为0字节。 sds字符串总是以’\0’字符结尾的,所以即使你创建了如下的sds字符串: mystring = sdsnewlen(“abc”,3); 由于这个字符串在结尾隐式包含了一个’\0’,所以你可以使用printf()函数打印它。然而,sds字符串是二进制安全的,并且可以在中间包含’\0’字符,因为在sds字符串header中保存了字符串长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 sds sdsnewlen (const void *init, size_t initlen) { void *sh; sds s; char type = sdsReqType(initlen); if (type == SDS_TYPE_5 && initlen == 0 ) type = SDS_TYPE_8; int hdrlen = sdsHdrSize(type); unsigned char *fp; sh = s_malloc(hdrlen+initlen+1 ); if (!init) memset(sh, 0 , hdrlen+initlen+1 ); if (sh == NULL) return NULL; s = (char *)sh+hdrlen; fp = ((unsigned char *)s)-1 ; 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 = initlen; *fp = type; break ; } case SDS_TYPE_16: { SDS_HDR_VAR(16 ,s); sh->len = initlen; sh->alloc = initlen; *fp = type; break ; } case SDS_TYPE_32: { SDS_HDR_VAR(32 ,s); sh->len = initlen; sh->alloc = initlen; *fp = type; break ; } case SDS_TYPE_64: { SDS_HDR_VAR(64 ,s); sh->len = initlen; sh->alloc = initlen; *fp = type; break ; } } if (initlen && init) memcpy(s, init, initlen); s[initlen] = '\0' ; return s; }
sdsempty
函数创建一个空sds(字符串长度为0)字符串。即使在这种情况下,字符串也总是有一个隐式的’\0’结束符。
1 2 3 sds sdsempty (void ) { return sdsnewlen("" ,0 ); }
sdsnew
函数使用一个以’\0’为结束符的C字符串创建一个新的sds字符串。
1 2 3 4 sds sdsnew (const char *init) { size_t initlen = (init == NULL) ? 0 : strlen(init); return sdsnewlen(init, initlen); }
sdsdup
函数复制一个sds字符串
1 2 3 sds sdsdup (const sds s) { return sdsnewlen(s, sdslen(s)); }
sdsfree
函数释放一个sds字符串,如果该字符串是NULL则什么都不做。
1 2 3 4 void sdsfree (sds s) { if (s == NULL) return ; s_free((char *)s-sdsHdrSize(s[-1 ])); }
sdsupdatelen
函数使用通过strlen()获取的sds字符串长度来设置sds字符串的长度,所以只考虑到第一个空字符前的字符串长度。当sds字符串被手动修改的时候这个函数很有用,比如下面的例子: s = sdsnew(“foobar”); s[2] = ‘\0’; sdsupdatelen(s); printf(“%d\n”, sdslen(s)); 上面的代码输出是”2”,但是如果我们注释掉调用sdsupdatelen()的那行代码,输出则是’6’,因为字符串被强行修改了,但字符串的逻辑长度还是6个字节。
1 2 3 4 void sdsupdatelen (sds s) { int reallen = strlen(s); sdssetlen(s, reallen); }
sdsclear
函数就地修改一个sds字符串为空(长度为0)。然而,所有当前的缓冲区都不会被释放,而是设置成空闲空间,所以下一次追加操作可以使用原来的空闲空间而不需要分配空间。
1 2 3 4 void sdsclear (sds s) { sdssetlen(s, 0 ); s[0 ] = '\0' ; }
sdsMakeRoomFor
函数扩充sds字符串的空闲空间,调用此函数后,可以保证在原sds字符串后面扩充了addlen个字节的空间,外加1个字节的终止符。注意:这个函数不会改变调用sdslen()返回的字符串长度,仅仅改变了空闲空间的大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 sds sdsMakeRoomFor (sds s, size_t addlen) { void *sh, *newsh; size_t avail = sdsavail(s); size_t len, newlen; char type, oldtype = s[-1 ] & SDS_TYPE_MASK; int hdrlen; if (avail >= addlen) return s; len = sdslen(s); sh = (char *)s-sdsHdrSize(oldtype); newlen = (len+addlen); if (newlen < SDS_MAX_PREALLOC) newlen *= 2 ; else newlen += SDS_MAX_PREALLOC; type = sdsReqType(newlen); if (type == SDS_TYPE_5) type = SDS_TYPE_8; hdrlen = sdsHdrSize(type); if (oldtype==type) { newsh = s_realloc(sh, hdrlen+newlen+1 ); if (newsh == NULL) return NULL; s = (char *)newsh+hdrlen; } else { 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; }
sdsRemoveFreeSpace
函数重新分配sds字符串的空间,保证结尾没有空闲空间。其中包含的字符串不变,但下一次进行字符串连接操作时需要一次空间重新分配。调用此函数后,原来作为参数传入的sds字符串的指针不再是有效的,所有引用必须被替换为函数返回的新指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 sds sdsRemoveFreeSpace (sds s) { void *sh, *newsh; char type, oldtype = s[-1 ] & SDS_TYPE_MASK; int hdrlen; size_t len = sdslen(s); sh = (char *)s-sdsHdrSize(oldtype); type = sdsReqType(len); hdrlen = sdsHdrSize(type); if (oldtype==type) { newsh = s_realloc(sh, hdrlen+len+1 ); if (newsh == NULL) return NULL; s = (char *)newsh+hdrlen; } else { newsh = s_malloc(hdrlen+len+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, len); return s; }
sdsAllocSize
函数返回指定sds字符串的分配空间大小,包括: 1) sds header大小。 2) 字符串本身的大小。 3) 末尾的空闲空间大小(如果有的话)。 4) 隐式包含的终止符。
1 2 3 4 size_t sdsAllocSize (sds s) { size_t alloc = sdsalloc(s); return sdsHdrSize(s[-1 ])+alloc+1 ; }
sdsAllocPtr
函数返回sds分配空间的首地址(一般来说sds字符串的指针是其字符串缓冲区的首地址)
1 2 3 void *sdsAllocPtr(sds s) { return (void *) (s-sdsHdrSize(s[-1 ])); }
sdsIncrLen
函数取决于’incr’参数,此函数增加sds字符串的长度或减少剩余空闲空间的大小。同时也将在新字符串的末尾设置终止符。此函数用来修正调用sdsMakeRoomFor()函数之后字符串的长度,在当前字符串后追加数据这些需要设置字符串新长度的操作之后。注意:可以使用一个负的增量值来右对齐字符串。使用sdsIncrLen()和sdsMakeRoomFor()函数可以用来满足如下模式,从内核中直接复制一部分字节到一个sds字符串的末尾,且无须把数据先复制到一个中间缓冲区中: 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);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 void sdsIncrLen (sds s, int incr) { unsigned char flags = s[-1 ]; size_t len; switch (flags&SDS_TYPE_MASK) { case SDS_TYPE_5: { 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 ; } s[len] = '\0' ; }
sdsgrowzero
函数增长一个sds字符串到一个指定长度。扩充出来的不是原来字符串的空间会被设置为0。如果指定的长度比当前长度小,不做任何操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 sds sdsgrowzero (sds s, size_t len) { size_t curlen = sdslen(s); if (len <= curlen) return s; s = sdsMakeRoomFor(s,len-curlen); if (s == NULL) return NULL; memset(s+curlen,0 ,(len-curlen+1 )); sdssetlen(s, len); return s; }
sdscatlen
函数向指定的sds字符串’s’尾部追加由’t’指向的二进制安全的字符串,长度’len’字节。调用此函数后,原来作为参数传入的sds字符串的指针不再是有效的,所有引用必须被替换为函数返回的新指针。
1 2 3 4 5 6 7 8 9 10 sds sdscatlen (sds s, const void *t, size_t len) { size_t curlen = sdslen(s); s = sdsMakeRoomFor(s,len); if (s == NULL) return NULL; memcpy(s+curlen, t, len); sdssetlen(s, curlen+len); s[curlen+len] = '\0' ; return s; }
sdscat
函数追加指定的C字符串到sds字符串’s’的尾部。调用此函数后,原来作为参数传入的sds字符串的指针不再是有效的,所有引用必须被替换为函数返回的新指针。
1 2 3 sds sdscat (sds s, const char *t) { return sdscatlen(s, t, strlen(t)); }
sdscatsds
函数追加指定的sds字符串’t’到已经存在的sds字符串’s’末尾。调用此函数后,原来作为参数传入的sds字符串的指针不再是有效的,所有引用必须被替换为函数返回的新指针。
1 2 3 sds sdscatsds (sds s, const sds t) { return sdscatlen(s, t, sdslen(t)); }
sdscpylen
函数把由’t’指向的二进制安全的字符串复制到sds字符串’s’的内存空间中,长度为’len’,覆盖原来的数据。
1 2 3 4 5 6 7 8 9 10 sds sdscpylen (sds s, const char *t, size_t len) { if (sdsalloc(s) < len) { s = sdsMakeRoomFor(s,len-sdslen(s)); if (s == NULL) return NULL; } memcpy(s, t, len); s[len] = '\0' ; sdssetlen(s, len); return s; }
sdscpy
函数和sdscpylen()函数类似,但是’t’指向的必须是一个以’\0’结尾的字符串,所以可以用strlen()获取该字符串长度。
1 2 3 sds sdscpy (sds s, const char *t) { return sdscpylen(s, t, strlen(t)); }