zoukankan      html  css  js  c++  java
  • redis源码分析(2)-- 基本数据结构sds

    一、sds格式

    sds header定义:

    1 struct sdshdr {
    2     unsigned int len;
    3     unsigned int free;
    4     char buf[];
    5 };

    sizeof(struct sdshdr)= 2*sizeof(unsigned int), char buf[]等价于char buf[0], 仅对编译器有效,并不实际占用存储。

    其中len是使用的长度,free是剩余的长度,再加上一个C语言中的''结束符

    sizeof(buf) = len + free + 1, 格式如下:

    二、sds基本操作

     1、创建sds对象

     1 sds sdsnewlen(const void *init, size_t initlen) {
     2     struct sdshdr *sh;
     3 
     4     if (init) {
     5         sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
     6     } else {
     7         sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
     8     }
     9     if (sh == NULL) return NULL;
    10     sh->len = initlen;
    11     sh->free = 0;
    12     if (initlen && init)
    13         memcpy(sh->buf, init, initlen);
    14     sh->buf[initlen] = '';
    15     return (char*)sh->buf;
    16 }
    17 
    18 /* Create a new sds string starting from a null terminated C string. */
    19 sds sdsnew(const char *init) {
    20     size_t initlen = (init == NULL) ? 0 : strlen(init);
    21     return sdsnewlen(init, initlen);
    22 }

    sdsnew根据输入C字符串init,创建新的sds对象,typedef char *sds; sds即char *类型。

    sds s = sdsnew("abc");
    当执行上面这行代码,会在堆内存上面开辟一段连续的空间,具体的空间大小是11个字节的空间。
    最前面4个字节存的是len ,也就是sds的长度
    其次4个字节存的是free,剩余的空间。
    最后才是 3个才是存的是我们实际的char的信息
    也就是每创建一个sds,都会执行上面的操作,都会申请额外的8个字节才存len和free信息。

    既然是一段连续的空间,那么只要已知一个指针,那么就能拿出struct结构里面的任何数据。
    还是用上面那个例子
    sds s = sdsnew("abc");

    这个时候s实际上存的是buf首个char数据的地址。也就是说向前 移8个字节就能到struct sdshdr的len的地址(准确的说是int len的首地址)。8个字节刚好就是struct sdshdr占的空间字节的大小。
    所以下面这个是成立的

    static inline size_t sdslen(const sds s) {
        struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));//把buf的首地址向前移动8个,也是到了len
        return sh->len;
    }

    s - (sizeof(struct sdshdr)) 表示将指针向前移动到 struct sdshdr 的起点,从而得出一个指向 sdshdr 结构的指针:    

    sds中函数比较简单,这里不做一一介绍,感兴趣可以直接阅读sds.c中的源码。

    三、sds特点

    1、可以通过O(1)获取字符串长度,直接返回sds->len即可,C字符串用strlen需要O(N)

    2、杜绝缓冲区溢出。同样因为 sds已经记录长度信息

    3、空间预分配和惰性空间释放

    sds中空间预分配相关函数:

     1 sds sdsMakeRoomFor(sds s, size_t addlen) {
     2     struct sdshdr *sh, *newsh;
     3     size_t free = sdsavail(s);
     4     size_t len, newlen;
     5 
     6     if (free >= addlen) return s; // 需要增加的长度小于free,直接返回,避免了频繁内存申请
     7     len = sdslen(s);
     8     sh = (void*) (s-(sizeof(struct sdshdr)));
     9     newlen = (len+addlen);
    10     if (newlen < SDS_MAX_PREALLOC) // 总长度(len+addlen)小于SDS_MAX_PREALLOC(1MB), 新申请长度扩大为2*newlen
    11         newlen *= 2;
    12     else
    13         newlen += SDS_MAX_PREALLOC; // 否则,只比需求量增加1MB
    14     newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
    15     if (newsh == NULL) return NULL;
    16 
    17     newsh->free = newlen - len;
    18     return newsh->buf;
    19 }

    惰性空间释放用于优化字符串的缩短操作,不用每次缩短都释放内存,仅修改free和len的大小即可。

    例如使用sdstrim操作删除sds中的的特定字符,最终只要修改free/len大小,不涉及free操作。

    真正需要释放sds中的free空间,可以调用:

    1 sds sdsRemoveFreeSpace(sds s) {
    2     struct sdshdr *sh;
    3 
    4     sh = (void*) (s-(sizeof(struct sdshdr)));
    5     sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1);
    6     sh->free = 0;
    7     return sh->buf;
    8 }

    通过realloc来重新分配一块不包含free的空间,原空间realloc会自动释放。

    4、sds主要api信息

  • 相关阅读:
    英语影视台词---经典电影台词(世间万物有始皆有终。)
    js数组,字符串,json互相转换函数有哪些
    php set_time_limit()的作用是什么
    界面分析---如何做美观的网页
    legend2---开发日志7(vue的使用场景有哪些,或者说使用的优缺点)
    legend2---开发日志1(legend的数据库整体设计思路是什么)
    legend2---开发日志2(注释和函数比较好的写法)
    legend2---开发日志3(thinkphp的入口目录是public的体现是什么)
    legend2---开发日志4(常用的链接传值方式有哪些)
    BZOJ 1823 JSOI 2010 盛宴 2-SAT
  • 原文地址:https://www.cnblogs.com/ym65536/p/7138785.html
Copyright © 2011-2022 走看看