tigh 发表于 2015-7-20 09:14:09

redis源码笔记-sds

  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 = '\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';
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' && c0)
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
页: [1]
查看完整版本: redis源码笔记-sds