|
sds和adlist一样,是redis的基础数据结构之一,是其为自身实现的字符串类型。A C dynamic strings library
sds.h
1 #ifndef __SDS_H
2 #define __SDS_H
3
4 #define SDS_MAX_PREALLOC (1024*1024) //字符串最大的预分配长度是1M
5
6 #include
7 #include
8
9 typedef char *sds; //sds本身被typedef为char*,是后续绝大部分函数的参数(之一)
10
11 struct sdshdr {
12 int len;
13 int free;
14 char buf[];
15 }; //字符串的数据结构,记录了字符串长度、空闲字节,以及指向实际存储数据buf的指针
16
17 static inline size_t sdslen(const sds s) {
18 struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
19 return sh->len;
20 } //尽管在不考虑typedef的前提下,但从函数参数来看,非引用/指针类型,但毕竟底层实现是char*,因此作者也将所有函数内不改变的参数标记为了const
//传的参数指向实际字符串,因此需要先取得数据结构头的位置,然后返回字符串长度;
21
22 static inline size_t sdsavail(const sds s) {
23 struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
24 return sh->free;
25 } //同上,返回的是空闲字节数
26
27 sds sdsnewlen(const void *init, size_t initlen);
28 sds sdsnew(const char *init);
29 sds sdsempty(); //三个初始化函数
30 size_t sdslen(const sds s); //上述内联函数的函数声明
31 sds sdsdup(const sds s); //字符串复制函数
32 void sdsfree(sds s);
33 size_t sdsavail(sds s);
34 sds sdsgrowzero(sds s, size_t len);
35 sds sdscatlen(sds s, void *t, size_t len);
36 sds sdscat(sds s, char *t);
37 sds sdscatsds(sds s, sds t);
38 sds sdscpylen(sds s, char *t, size_t len);
39 sds sdscpy(sds s, char *t);
40
41 sds sdscatvprintf(sds s, const char *fmt, va_list ap);
42 #ifdef __GNUC__
43 sds sdscatprintf(sds s, const char *fmt, ...)
44 __attribute__((format(printf, 2, 3)));
45 #else
46 sds sdscatprintf(sds s, const char *fmt, ...);
47 #endif //为啥这么写,没看懂。。非重点,暂且略过
48
49 sds sdstrim(sds s, const char *cset);
50 sds sdsrange(sds s, int start, int end);
51 void sdsupdatelen(sds s);
52 void sdsclear(sds s);
53 int sdscmp(sds s1, sds s2);
54 sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count);
55 void sdsfreesplitres(sds *tokens, int count);
56 void sdstolower(sds s);
57 void sdstoupper(sds s);
58 sds sdsfromlonglong(long long value);
59 sds sdscatrepr(sds s, char *p, size_t len);
60 sds *sdssplitargs(char *line, int *argc);
61
62 #endif
对libc的string.h库熟悉的同学,根据上述函数名也大概能猜出来函数的作用,就不一一写了,具体参见sds.c的实现部分。
sds.c
看似很长,但绝大部分代码都很通俗易懂,大家不要恐惧。
1 #define SDS_ABORT_ON_OOM //在内存不够时的默认动作是终止进程(abort)
2
3 #include
4 #include
5 #include
6 #include
7 #include "sds.h"
8 #include "zmalloc.h" //同样用na个简单封装的内存分配库
9
10 static void sdsOomAbort(void) {
11 fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
12 abort();
13 } //abort前作的唯一的善后工作是告诉大家自己是由于在SDS时内存不够挂掉的
14
15 sds sdsnewlen(const void *init, size_t initlen) {
16 struct sdshdr *sh;
17
18 sh = zmalloc(sizeof(struct sdshdr)+initlen+1); //所分配的长度是头的长度sizeof(struct sdshdr)+字符串长度initlen+‘\0’的一个字节
//注意这里的sizeof(struct sdshdr)长度为8,剩下的都直接存在了sh->buf上
19 #ifdef SDS_ABORT_ON_OOM
20 if (sh == NULL) sdsOomAbort();
21 #else
22 if (sh == NULL) return NULL; //如果没有定义SDS_ABORT_ON_OOM,简单的返回NULL
23 #endif
24 sh->len = initlen;
25 sh->free = 0; //创建字符串时free的长度是0
26 if (initlen) {
27 if (init) memcpy(sh->buf, init, initlen); //若参数init不等于NULL,则拷贝initlen长度给sh->buf
28 else memset(sh->buf,0,initlen); //否则这块缓冲区被赋值为0
29 }
30 sh->buf[initlen] = '\0'; //用'\0'为字符串结尾
31 return (char*)sh->buf; //返回的是实际字符串的指针
32 }
33
34 sds sdsempty(void) {
35 return sdsnewlen("",0); //注意""字符串常量并不代表空,但0表示空了
36 }
37
38 sds sdsnew(const char *init) {
39 size_t initlen = (init == NULL) ? 0 : strlen(init);
40 return sdsnewlen(init, initlen);
41 }
42
43 sds sdsdup(const sds s) {
44 return sdsnewlen(s, sdslen(s));
45 } //dup复制函数也是调用newlen实现的
46
47 void sdsfree(sds s) {
48 if (s == NULL) return;
49 zfree(s-sizeof(struct sdshdr));
50 } //释放链接要先找到sds的header
51
52 void sdsupdatelen(sds s) {
53 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
54 int reallen = strlen(s);
55 sh->free += (sh->len-reallen);
56 sh->len = reallen;
57 } //更新长度是更新header的len和free两个字段
58
59 void sdsclear(sds s) {
60 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
61 sh->free += sh->len;
62 sh->len = 0;
63 sh->buf[0] = '\0';
64 } //将所有的len都给free,但并未释放内存空间
65
66 static sds sdsMakeRoomFor(sds s, size_t addlen) {
67 struct sdshdr *sh, *newsh;
68 size_t free = sdsavail(s);
69 size_t len, newlen;
70
71 if (free >= addlen) return s; //如果剩余空间仍足够,则直接将传进来的参数s返回
72 len = sdslen(s);
73 sh = (void*) (s-(sizeof(struct sdshdr)));
74 newlen = (len+addlen);
75 if (newlen < SDS_MAX_PREALLOC)
76 newlen *= 2;
77 else
78 newlen += SDS_MAX_PREALLOC; //在为newlen增加了addlen这么长的空余空间的同时,也在这次操作中额外申请了内存空间,当newlen小于系统定义的单次最大分配内存时,额外分配的长度是newlen,负责多分配SDS_MAX_PREALLOC这么大的空间。熟悉std::vector实现的同学对这种分配机制一定似曾相识。这里基于这样一种假设,这次make room的sds,也很有可能在不久的将来再次make room,所以预先分配了空间
79 newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1); //realloc保证原来分配空间的数据会拷贝过去,但执行此操作后,sh指针就失效了
80 #ifdef SDS_ABORT_ON_OOM
81 if (newsh == NULL) sdsOomAbort();
82 #else
83 if (newsh == NULL) return NULL;
84 #endif
85
86 newsh->free = newlen - len;
87 return newsh->buf;
88 }
89
90 /* Grow the sds to have the specified length. Bytes that were not part of
91 * the original length of the sds will be set to zero. */
92 sds sdsgrowzero(sds s, size_t len) { //len是一个to 参数,表示增长到
93 struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
94 size_t totlen, curlen = sh->len;
95
96 if (len = '0' && c = 'a' && c = 'A' && c 0)
618
619 sdsfree(y);
620 sdsfree(x);
621 x = sdsnew("bar");
622 y = sdsnew("bar");
623 test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
624
625 sdsfree(y);
626 sdsfree(x);
627 x = sdsnew("aar");
628 y = sdsnew("bar");
629 test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
630 }
631 test_report()
632 }
633 #endif |
|