• Redis源码入门-字符串sds,sdshdr


    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: 关于内存补齐如果之前不知道,请自行百度。

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

    ThirdPartyImage_140dc1f3.png

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

    // s减去sdshdr长度 = 指向sdshdr结构体的指针
    (s)-(sizeof(struct sdshdr##T))) 、
    // s前一个位置 = flags
    s[-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是为了最后放一个,兼容传统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] = '';
        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宝典
    a

  • 相关阅读:
    app令牌的一个token实现
    velocity分页模板
    js基础-表单验证和提交
    做项目中没经验遇到的各种问题
    No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?
    oracle创建用户
    oracle创建表相关
    pe创建激活administrator后消除问题,删除用户问题
    spring学习遇到的问题汇总
    .NET MVC自定义错误处理页面的方法
  • 原文地址:https://www.cnblogs.com/java-bible/p/14439426.html
Copyright © 2020-2023  润新知