sds,全称Simple Dynamic Strings,是Redis自定义的一个字符串类型。

typedef char *sds;

看到这你必定心田感觉Redis在逗你,这不就是一个字符数组么,怎么就Simple Dynamic Strings了呢 !没错,我过后也是这么感觉的,然而仔细阅读源码后发现sds并不是一个人在战斗,它还有战友sdshdr,sdshdr是个五胞胎,别离是sdshdr5,sdshdr8,sdshd16,sdshdr32,sdshd64。块头从小到大。

sdshdr 全称 Simple Dynamic Strings Header

/* 因为生的跟他人不一样(内部结构不一样),老五(sdshdr5)从来不被应用 */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[];};
知识点!这个很要害!!
\_\_attribute\_\_ ((\_\_packed\_\_))
待会你会看到如下代码:
(s)-(sizeof(struct sdshdr\#\#T))) 、s[-1]、(char*)s-sdsHdrSize(s[-1])
这些指针之所以能够走位如此风骚,都归功于 \_\_attribute\_\_ ((\_\_packed\_\_))

这个命令的意思是 勾销编译阶段的内存优化对齐性能.
ps: 对于内存补齐如果之前不晓得,请自行百度。

所以,该构造在内存中的构造如下:

这样看,之前那些风骚的走位就很明了了。

// s减去sdshdr长度 = 指向sdshdr构造体的指针(s)-(sizeof(struct sdshdr##T))) 、// s前一个地位 = flagss[-1]// 与1雷同成果(char*)s-sdsHdrSize(s[-1])

有了下面的根底,看sds.c和sds.h里的代码就曾经很容易了,咱们重点看两个函数

  1. 创立sds字符串
sds sdsnewlen(const void *init, size_t initlen) {    void *sh;    sds s;    /* 依据字符串的长度来决定sds的类型 */    char type = sdsReqType(initlen);    /* 老五被歧视了 */    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;    /* 计算sdsHeader的长度 */    int hdrlen = sdsHdrSize(type);    /* 对应flags */    unsigned char *fp;     /* 开拓内存空间,+1是为了最初放一个\0,兼容传统C语言,入乡随俗 */    sh = s_malloc(hdrlen+initlen+1);    if (!init)        memset(sh, 0, hdrlen+initlen+1);    if (sh == NULL) return NULL;    /* 这走位,指向字符串开始的中央 */    s = (char*)sh+hdrlen;    /* 这走位,到flags了 */    fp = ((unsigned char*)s)-1;    /* 依据不同的类型,初始化sdsHeader */    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;}
  1. 动静扩大sds空间
sds sdsMakeRoomFor(sds s, size_t addlen) {    void *sh, *newsh;    /* avail = alloc-len */    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);    /* Redis认为一旦被扩容了,     * 那这个字符串被再次扩容的几率就很大,所以会在此基础上多加一些空间,     * 避免频繁扩容      */    if (newlen < SDS_MAX_PREALLOC)        newlen *= 2;    else        newlen += SDS_MAX_PREALLOC;    /* 从新计算type */    type = sdsReqType(newlen);    /* 老五又被歧视了 */    if (type == SDS_TYPE_5) type = SDS_TYPE_8;    hdrlen = sdsHdrSize(type);    if (oldtype==type) {        /* 当原类型与新类型统一,则在原有根底是realloc空间即可 */        newsh = s_realloc(sh, hdrlen+newlen+1);        if (newsh == NULL) return NULL;        s = (char*)newsh+hdrlen;    } else {        /* 否则须要从新malloc一整块空间,而后拷贝 */        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;}
关注公众号:java宝典