[SDS阅读理解/5]源码中的函数/2

接着讲函数

       第十个。该函数的作用是为一个sds字符串变量申请更多的剩余空间,作者注释里有讲。学习使用别人的代码一定要看源码注释!函数里头有用到两个宏SDS_TYPE_MASK SDS_MAX_PREALLOC,它们的作用在宏那节有讲。下面直接在代码中讲解要点。

// 作者注释
/* 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. */

sds sdsMakeRoomFor(sds s, size_t addlen) {
    void *sh, *newsh;
    // 获取字符串的剩余可用空间,sdsavail()是头文件中定义的一个内联函数
    // 它的作用在第四节有讲
    size_t avail = sdsavail(s);
    size_t len, newlen;
    // 获取字符串原来的类型(flags),字符串首地址向左偏移一个字节就可得到
    // 按位与运算后flags的结果不变
    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);
    // 下面这行代码的意思就是用s的首地址减去对应结构体的大小
    // 就是该结构体的首地址了。其实就是指针偏移。用到的函数前
    // 一节有讲
    sh = (char*)s-sdsHdrSize(oldtype);
    // 字符串已使用的长度加上新增加的长度就是字符串的总空间大小
    newlen = (len+addlen);
    // 下面的if else语句在宏那节有讲。作用是为了避免我们可以
    // 申请无线大的空间
    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) {
        // 则申请对应结构体大小加新的总字符串空间再加一个字节的
        // 结束符空间的内存。s_realloc()其实就是realloc()
        newsh = s_realloc(sh, hdrlen+newlen+1);
        // 申请失败则返回NULL
        if (newsh == NULL) return NULL;
        // 成功则将字符串指向新申请的空间地址。newsh是新空间的首地址,
        // 加上该结构体的大小就是结构体里头那个零长度数组的首地址
        s = (char*)newsh+hdrlen;
    } else { // 新字符串的类型与原字符串类型不同
        /* Since the header size changes, need to move the string forward,
         * and can't use realloc */
        // 作者说不能使用s_realloc(),因为头结构体的大小改变了,继续
        // 使用的话会造成数据储存错位,因为realloc()会自动复制原地址
        // 里头的内容到新空间中去。s_malloc()就是malloc()。
        newsh = s_malloc(hdrlen+newlen+1);
        // 申请失败,返回NULL
        if (newsh == NULL) return NULL;
        // 成功则将原字符串的内容拷贝到零数组首地址后面的空间中去
        // 大小就是字符个数加1,结尾放'\0'字符。
        memcpy((char*)newsh+hdrlen, s, len+1);
        // sh指向原字符串的头结构体的首地址,已经不需要了,释放它
        s_free(sh);
        // 将s指向新的字符串空间
        s = (char*)newsh+hdrlen;
        // 设置字符串的类型
        s[-1] = type;
        // 设置字符串的真实长度,不是总的空间大小
        sdssetlen(s, len);
    }
    // 设置新字符串的总空间大小。该函数的原理前面章节有讲
    sdssetalloc(s, newlen);
    return s;
}

       第十一个。该函数的作用是去除多余的可用空间,使得字符串的总的空间大小就是实际储存的字符串个数加一个字节的结尾符。它的原理就是新申请一段刚好能容纳字符串的空间,将内容拷贝过去,释放原来的空间。

// 作者注释
/* Reallocate the sds string so that it has no free space at the end. The
 * contained string remains not altered, but next concatenation operations
 * will require a reallocation.
 *
 * After the call, the passed sds string is no longer valid and all the
 * references must be substituted with the new pointer returned by the call. */

sds sdsRemoveFreeSpace(sds s) {
    void *sh, *newsh;
    // 获取原字符串类型(原理和上面的差不多,就不详细说了)
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    // 获取原字符串的头结构体大小
    int hdrlen, oldhdrlen = sdsHdrSize(oldtype);
    // 字符串实际长度
    size_t len = sdslen(s);
    // sh指向原结构体的首地址
    sh = (char*)s-oldhdrlen;

    /* Check what would be the minimum SDS header that is just good enough to
     * fit this string. */
    // 检测哪种类型的结构体(大小最小)适合该字符串,上面原注释的意思。
    // 根据长度获字符串取类型
    type = sdsReqType(len);
    // 根据字符串类型获取结构体大小
    hdrlen = sdsHdrSize(type);

    /* If the type is the same, or at least a large enough type is still
     * required, we just realloc(), letting the allocator to do the copy
     * only if really needed. Otherwise if the change is huge, we manually
     * reallocate the string to use the different header type. */
    // 下面这一大段原理和上面那个函数结尾的差不多,就不一一解释了。
    // 简要翻译下原注释的意思:如果字符串类型没变或需要一个更大
    // 的类型(type 8,type 16或type32等),我们就用realloc()
    // 帮我们申请空间并拷贝内容,否则我们就自己用malloc(),申请
    // 空间,拷贝内容
    if (oldtype==type || type > SDS_TYPE_8) {
        newsh = s_realloc(sh, oldhdrlen+len+1);
        if (newsh == NULL) return NULL;
        s = (char*)newsh+oldhdrlen;
    } else {
        newsh = s_malloc(hdrlen+len+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, len);
    return s;
}

       第十二个。该函数的作用是获取申请一个sds字符串变量所申请的总空间大小,包括头结构体的大小、字符串实际长度加剩余可用空间、和结尾的一个字节。

// 作者注释
/* Return the total size of the allocation of the specifed sds string,
 * including:
 * 1) The sds header before the pointer.
 * 2) The string.
 * 3) The free buffer at the end if any.
 * 4) The implicit null term.
 */

size_t sdsAllocSize(sds s) {
    // 获取字符串的实际长度和剩余可用空间大小。该函数的原理在前面章节有讲
    size_t alloc = sdsalloc(s);
    // 返回对应头结构体的大小和字符串的总大小和一个字节的内存空间
    return sdsHdrSize(s[-1])+alloc+1;
}

       第十三个。该函数的作用是获取一个字符串变量的头结构体的首地址。

/* Return the pointer of the actual SDS allocation (normally SDS strings
 * are referenced by the start of the string buffer). */
void *sdsAllocPtr(sds s) {
    // 字符串的首地址减去对应结构体的大小就是头结构体的首地址
    // 原理就是指针偏移
    return (void*) (s-sdsHdrSize(s[-1]));
}

       好吧,今天就记录到这里。:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis 5有五种主要的数据结构,它们分别是动态字符串SDS、整数集合lntSet、字典Dict、压缩列表ZipList、快速列表QuickList和跳跃表SkipList。动态字符串SDS是一种由Redis自己实现的字符串结构,它解决了C语言字符串存在的一些问题,如获取字符串长度的运算复杂度、非二进制安全以及不可修改的问题。整数集合lntSet是一种可以储存整数的集合结构,它能够高效地存储和操作整数,相较于一般的集合结构,它能够节省内存空间。字典Dict是一种键值对映射的数据结构,它使用哈希表来实现,可以高效地进行插入、删除和查找操作。压缩列表ZipList是一种紧凑的列表结构,可以在节省内存的同时存储多个元素,并支持快速地访问和修改。快速列表QuickList是一种将多个压缩列表连接起来形成的链表式结构,可以高效地处理列表的前端和后端插入、删除和查找操作。跳跃表SkipList是一种有序集合结构,类似于Java的SortedSet和HashMap的结合体,它使用一种叫作"跳跃列表"的数据结构来实现,可以高效地进行元素的插入、删除和查找操作。这些数据结构在Redis被广泛应用,每种数据结构都有其特定的优势和用途。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Redis5种数据结构解析](https://blog.csdn.net/weixin_46129192/article/details/125817565)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Redis的五种基础数据结构](https://blog.csdn.net/yuyuanlai/article/details/106745757)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值