1.sds--string

作者: FrankerSung | 来源:发表于2019-02-01 11:13 被阅读169次

\color{red}{What}

SDS---A C dynamic strings library 一个动态字符串库

优点:

二进制安全[\0可在字符串内出现];
通过扩充属性,可快速获取字符串长度[空间换时间];
兼容C的字符串操作函数。

缺点:

  1. 许多函数传入字符串之后不确定是否改变原值,所以需要重新赋值,不然容易导致Bug,如str=sdscat(str, "test sdscat"),函数返回值需要赋值到str上。
  2. 字符串数据共享问题,一处修改,其余所有都需修改。
一个简单的结构(sdshdr8)
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
// sizeof(struct sdshdr8) = 3
注意,__attribute__ ((__packed__))可以让结构体按照紧凑排列的方式来占用内存,所以可以很方便的计算内存地址以获取指向sdshdr头部的起始地址的指针[sds减去头部大小]
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
又或,后移1个字节即可取到flags
sds s; unsigned char flags = s[-1];

\color{red}{How - And - Why}

sds定义了5种类似的结构---为了节省内存(为了节省内存真是无所不用至极,学习!!!),分别是sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64。
其中sdshdr5并不使用,使用flags的高5位说明字符串长度。(也就是说长度比较小时推荐使用C语言原生字符串)。

内联函数

// 获取buf中sdshdr存储的字符串的长度
static inline size_t sdslen(const sds s)sdsavail
// 可用存储空间长度
static inline size_t sdsavail(const sds s) 
// 设置使用长度
static inline void sdssetlen(sds s, size_t newlen) 
// 增加使用长度
static inline void sdsinclen(sds s, size_t inc)
//  sdsalloc() = sdsavail() + sdslen() 
static inline size_t sdsalloc(const sds s) 
// 设置alloc属性
static inline void sdssetalloc(sds s, size_t newlen)

一些比较重要的函数

1.根据指定内容和指定长度创建一个sds字符串
sds sdsnewlen(const void *init, size_t initlen) {
    void *sh;
    sds s;
    char type = sdsReqType(initlen);
    /* Empty strings are usually created in order to append. Use type 8
     * since type 5 is not good at this.
     * 空字符串和type 5默认设为SDS_TYPE_8
     */
    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
    int hdrlen = sdsHdrSize(type);
    unsigned char *fp; /* flags pointer. */

    sh = s_malloc(hdrlen+initlen+1);
    if (init==SDS_NOINIT)
        init = NULL;
    else if (!init)
        // 如果没有指定初始内容,分配空间,设置为0
        memset(sh, 0, hdrlen+initlen+1);
    if (sh == NULL) return NULL;
    // 地址查询-->buf[内存紧凑]
    s = (char*)sh+hdrlen;
    // flags指针
    fp = ((unsigned char*)s)-1;
    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;
        }
        //... 为了篇幅去掉类似的代码
    }
    // 赋值
    if (initlen && init)
        memcpy(s, init, initlen);
    s[initlen] = '\0';
    return s;
}

创建一个字符串:

sdsnewlen("redis", 5)
# sh=s_malloc(sizeof(struct hdrlen)+initlen+1); // initlen为参数的长度
------------
|5|0|redis|
------------
^     ^
sh  sh->buf
注意,sdsnewlen返回sh->buf
2.扩容函数(free属性)
/* Enlarge the free space at the end of the sds string so that the caller
 * is sure that after calling this function can overwrite up to addlen
 * bytes after the end of the string, plus one more byte for nul term.
 *
 * Note: this does not change the *length* of the sds string as returned
 * by sdslen(), but only the free buffer space we have.
 *
 * 扩容函数(free空间)
 */
sds sdsMakeRoomFor(sds s, size_t addlen) {
    void *sh, *newsh;
    size_t avail = sdsavail(s);
    size_t len, newlen;
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    int hdrlen;

    /* Return ASAP if there is enough space left. */
    if (avail >= addlen) return s;

    len = sdslen(s);
    sh = (char*)s-sdsHdrSize(oldtype);
    newlen = (len+addlen);
    // 扩容机制 小于1M,现有空间乘以2;超过1M,则扩容1M
    if (newlen < SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC;

    type = sdsReqType(newlen);

    /* Don't use type 5: the user is appending to the string and type 5 is
     * not able to remember empty space, so sdsMakeRoomFor() must be called
     * at every appending operation. */
    if (type == SDS_TYPE_5) type = SDS_TYPE_8;

    hdrlen = sdsHdrSize(type);
    if (oldtype==type) {
        newsh = s_realloc(sh, hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        s = (char*)newsh+hdrlen;
    } else {
        /* Since the header size changes, need to move the string forward,
         * and can't use realloc */
        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);
    }
    // 设置alloc字段
    sdssetalloc(s, newlen);
    return s;
}

\color{red}{SDS的使用方式}

参考源码尾部的sdsTest

\color{red}{总结与成长}

内存使用
空间与时间之间的平衡

参考资料
https://github.com/antirez/sds/blob/master/README.md

相关文章

  • 1.sds--string

    SDS---A C dynamic strings library 一个动态字符串库 优点: 二进制安全[\0可在...

网友评论

    本文标题:1.sds--string

    本文链接:https://www.haomeiwen.com/subject/rqnrsqtx.html