redis7.x源码分析:(1) sds动态字符串

sds(Simple Dynamic String)是redis中最基础也是最重要的数据结构之一,其内部使用的key、协议、回复等等都会用它来存储。sds主要设计被用来替代C原生字符串 char *(数组),以便更便捷、更高效、更安全的进行字符串操作管理。其实它和C++标准库中的string在一定程度上是比较类似的,都是用来完成对字符串缓冲区的动态分配、管理以及其它一些相应操作的。

// sds的定义:
typedef char *sds;

sds的定义非常简单,直接就是一个char*的别名,因此sds本身具备C字符串的特性,可以使用strcpy、strlen等函数。
sds相关数据结构中真正重要的是sdshdr的定义,最初老版本的定义如下:

struct sdshdr {int len;    // SDS字符串的长度int free;   // 未使用的空间大小char buf[]; // 字符串数据
};

现在的sdshdr已经重新定义成5个不同的结构了:

/* Note: sdshdr5 is never used, we just access the flags byte directly.* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {unsigned char flags; /* 3 lsb of type, and 5 msb of string length */char buf[];
};
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[];
};
struct __attribute__ ((__packed__)) sdshdr16 {uint16_t len; /* used */uint16_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {uint32_t len; /* used */uint32_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {uint64_t len; /* used */uint64_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4

这么定义的主要目的是节省存储空间,针对不同的字符串长度使用不同的头。另外结构中新增了flags标记,用来表示使用的是哪个头。如果flags类型是SDS_TYPE_5则高5bit还表示数据长度,因为sdshdr5中并没有长度的成员定义。
sds在分配空间时,是包含头结构的,但真正返回的却是buf成员的地址,这就是sds具备C字符串的特性的原因。由于结构体中设置了 attribute((packed)),表示按单字节对齐,因此可以通过sds[- 1]来获取flags的值,而后获取对应的结构体指针。

#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))// 获取SDS_TYPE_16对应的结构体指针
struct sdshdr16 *hdr = SDS_HDR(16,s)

接下来先看一下sds分配释放的主要实现 _sdsnewlen和 sdsfree,对外提供的分配函数最终都会调用它来实现。

sds _sdsnewlen(const void *init, size_t initlen, int trymalloc) {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. */// inilen为0时, 升级类型预留空间if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;int hdrlen = sdsHdrSize(type);unsigned char *fp; /* flags pointer. */size_t usable;// 分配: 头 + 数据长度 + 1的空间assert(initlen + hdrlen + 1 > initlen); /* Catch size_t overflow */sh = trymalloc?s_trymalloc_usable(hdrlen+initlen+1, &usable) :s_malloc_usable(hdrlen+initlen+1, &usable);if (sh == NULL) return NULL;// init不为空并且不是SDS_NOINIT, 则重置内存为0if (init==SDS_NOINIT)init = NULL;else if (!init)memset(sh, 0, hdrlen+initlen+1);// 对外返回的 sds 指针位置, 向后偏移头大小s = (char*)sh+hdrlen;// 存储flags的指针位置fp = ((unsigned char*)s)-1;usable = usable-hdrlen-1;if (usable > sdsTypeMaxSize(type))usable = sdsTypeMaxSize(type);// 根据类型,设置结构体中相应的值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 = usable;*fp = type;break;}case SDS_TYPE_16: {SDS_HDR_VAR(16,s);sh->len = initlen;sh->alloc = usable;*fp = type;break;}case SDS_TYPE_32: {SDS_HDR_VAR(32,s);sh->len = initlen;sh->alloc = usable;*fp = type;break;}case SDS_TYPE_64: {SDS_HDR_VAR(64,s);sh->len = initlen;sh->alloc = usable;*fp = type;break;}}// 拷贝需要初始化的内容if (initlen && init)memcpy(s, init, initlen);// buf末尾赋0s[initlen] = '\0';return s;
}......void sdsfree(sds s) {if (s == NULL) return;// 释放时,需要把指针重定向到相应结构体的起始位置s_free((char*)s-sdsHdrSize(s[-1]));
}

下面再看下扩容的函数 _sdsMakeRoomFor,它的实现也非常清晰,内部的一些扩容操作都会调用它。

sds _sdsMakeRoomFor(sds s, size_t addlen, int greedy) {void *sh, *newsh;// 获取剩余空间大小size_t avail = sdsavail(s);size_t len, newlen, reqlen;// 获取当前typechar type, oldtype = s[-1] & SDS_TYPE_MASK;int hdrlen;size_t usable;/* Return ASAP if there is enough space left. */// 空间够用则直接退出if (avail >= addlen) return s;len = sdslen(s);// 获取sds结构体分配内存的起始地址sh = (char*)s-sdsHdrSize(oldtype);// 新的需要分配的空间大小reqlen = newlen = (len+addlen);assert(newlen > len);   /* Catch size_t overflow */if (greedy == 1) {// greedy为1时需要预留空间, 如果新分配空间小于1MB, 则分配空间调整为2倍大小; 如果大于1MB则分配成 + 1MB大小if (newlen < SDS_MAX_PREALLOC)newlen *= 2;elsenewlen += SDS_MAX_PREALLOC;}// 按新长度获取新的typetype = 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);assert(hdrlen + newlen + 1 > reqlen);  /* Catch size_t overflow */if (oldtype==type) {// 如果类型不变,则直接按照新大小reallocnewsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable);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_usable(hdrlen+newlen+1, &usable);if (newsh == NULL) return NULL;memcpy((char*)newsh+hdrlen, s, len+1);s_free(sh);s = (char*)newsh+hdrlen;// 设置flagss[-1] = type;// 设置新的长度sdssetlen(s, len);}// 设置可用空间大小usable = usable-hdrlen-1;if (usable > sdsTypeMaxSize(type))usable = sdsTypeMaxSize(type);sdssetalloc(s, usable);return s;
}

另外,sds在设计中本身也是二进制安全的,而且sds会在末尾多分配1字节并且置’\0’,用于防止一些字符串操作的越界问题。因此它除了用作字符串外,还可以作为二进制数据的存储buf,在redis内部也有着广泛用途。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/471073.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1.7 JS性能优化

从输入url到页面加载完成都做了些什么 输入 URL - 资源定位符 http://www.zhaowa.com - http 协议 域名解析 https://www.zhaowa.com > ip 1. 切HOST&#xff1f; > 浏览器缓存映射、系统、路由、运营商、根服务器 2. 实际的静态文件存放&#xff1f; 大流量 > 多个…

Linux基础1

Linux基础1 Linux基础1学习笔记 ‍ 声明&#xff01; ​​​学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章 笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他…

【安全通信】告别信息泄露:搭建你的开源视频聊天系统briefing

文章目录 前言1.关于briefing2.本地部署briefing3.使用briefing4.cpolar内网穿透工具安装5.创建远程连接公网地址6.固定briefing公网地址 前言 在这个信息爆炸的时代&#xff0c;视频聊天几乎成了我们日常沟通的标配。但你是否曾在视频会议中感到不安&#xff0c;担心自己的私…

深度学习——优化算法、激活函数、归一化、正则化

文章目录 &#x1f33a;深度学习面试八股汇总&#x1f33a;优化算法方法梯度下降 (Gradient Descent, GD)动量法 (Momentum)AdaGrad (Adaptive Gradient Algorithm)RMSProp (Root Mean Square Propagation)Adam (Adaptive Moment Estimation)AdamW 优化算法总结 经验和实践建议…

Thread类及常见方法

目录 一、Thread常见构造方法 二、Thread常见属性 三、Thread常见方法 start() 获取当前线程 中断线程 join() 一、Thread常见构造方法 Thread类是JVM用来管理线程的一个类&#xff0c;每个线程都有唯一一个Thread对象与之对应&#xff0c;JVM会将这些对象组织起来&…

优化时钟网络之时钟抖动

Note&#xff1a;文章内容以Xilinx 7系列FPGA进行讲解 1、什么是时钟抖动 时钟抖动就是时钟周期之间出现的偏差。比如一个时钟周期为10ns的时钟&#xff0c;理想情况下&#xff0c;其上升沿会出现在0ns&#xff0c;10ns&#xff0c;20ns时刻&#xff0c;假设某个上升沿出现的时…

Vector 深度复制记录

有的时候数据得复制过去 有个疑问,自动分配内存吗? 不是估计有变化, 得在看看 指针作为值复制了 … … 挺好,修改原有的值 x86 的 SIM 程序 还有点问题 ; 无法直接绕过硬件错误 。。。 x86 gdb 没有问题 就是运行出现了问题&#xff0c;怎么解决&#xff1b;正常初始化没有问题…

贪心算法day03(最长递增序列问题)

目录 1.最长递增三元子序列 2.最长连续递增序列 1.最长递增三元子序列 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;我们只需要设置两个数进行比较就好。设a为nums[0]&#xff0c;b 为一个无穷大的数&#xff0c;只要有比a小的数字就赋值…

基于Java Web的传智播客crm企业管理系统的设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

马斯克万卡集群AI数据中心引发的科技涟漪:智算数据中心挑战与机遇的全景洞察

一、AI 爆发重塑数据中心格局 随着AI 技术的迅猛发展&#xff0c;尤其是大模型的崛起&#xff0c;其对数据中心产生了极为深远的影响。大模型以其数以亿计甚至更多的参数和对海量数据的处理需求&#xff0c;成为了 AI 发展的核心驱动力之一&#xff0c;同时也为数据中心带来了…

机器学习在医疗健康领域的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 机器学习在医疗健康领域的应用 机器学习在医疗健康领域的应用 机器学习在医疗健康领域的应用 引言 机器学习概述 定义与原理 发展…

学法减分交管12123模拟练习小程序源码前端和后端和搭建教程

交管推出个学法减分&#xff0c;每个驾驶员可以把被扣的6分&#xff0c;以看视频答题的形式学习回来&#xff0c;然后答题这个一共二十道题每道题60秒&#xff0c;有好多人不会&#xff0c;用咱们的小程序就可以模拟练习强化练习&#xff0c;还有拍照识别题目找到正确答案&…

AI大模型开发架构设计(18)——基于大模型构建企业知识库案例实战

文章目录 1 LLM 大模型在工作中的实际应用以及局限性LLM 大模型工作中实际应用大模型2点局限性 2 基于大模型和向量数据库的企业级知识库架构剖析向量数据库向量数据库选型知识库文档检索增强(Retrieval Augmented Generation)向量数据库应用技术总体架构向量数据库应用离线索引…

jmeter介绍、使用方法、性能测试、现参数化和数据驱动、分布式测试、压力测试、接口测试

目录 1.JMeter的组件介绍 2.JMeter介绍和使用方法 3.使用JMeter进行性能测试 4.JMeter如何实现参数化和数据驱动 5.使用JMeter进行分布式测试 6.使用JMeter完成压力测试 7.使用JMeter完成接口测试 下载并安装JMeter&#xff1a;从官方网站&#xff08;https://jmeter.ap…

Zotero 6.0 安装包及安装教程

Zotero的界面友好&#xff0c;操作简单&#xff0c;对于科研小白来说&#xff0c;是一款非常实用的文献管理软件。它不仅可以帮助用户精确获取、整理、引用文献&#xff0c;而且在学术实践中不可或缺的一环。 安 装 步 骤 压缩包文件&#xff0c;鼠标右击解压得到安装包。 仅用…

Docker 篇-Docker 详细安装、了解和使用 Docker 核心功能(数据卷、自定义镜像 Dockerfile、网络)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Docker 概述 1.1 Docker 主要组成部分 1.2 Docker 安装 2.0 Docker 常见命令 2.1 常见的命令介绍 2.2 常见的命令演示 3.0 数据卷 3.1 数据卷常见的命令 3.2 常见…

华为大变革?仓颉编程语言会代替ArkTS吗?

在华为鸿蒙生态系统中&#xff0c;编程语言的选择一直是开发者关注的焦点。近期&#xff0c;华为推出了自研的通用编程语言——仓颉编程语言&#xff0c;这引发了关于仓颉是否会取代ArkTS的讨论。本文将从多个角度分析这两种语言的特点、应用场景及未来趋势&#xff0c;探讨仓颉…

随时随地编码:香橙派Zero3上安装Code Server远程开发指南

文章目录 前言1. 添加镜像源2. 部署Code server3. 安装内网穿透工具4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在刷了CasaOS轻NAS系统的香橙派Orange Pi Zero3中&#xff0c;使用Docker本地部署Code server&#xff0c;并结合cpolar内网穿透实现远程使用浏览器…

npm list @types/node 命令用于列出当前项目中 @types/node 包及其依赖关系

文章目录 作用示例常用选项示例命令注意事项 1、实战举例**解决方法**1. **锁定唯一的 types/node 版本**2. **清理依赖并重新安装**3. **设置 tsconfig.json 的 types**4. **验证 Promise 类型支持** **总结** npm list types/node 命令用于列出当前项目中 types/node 包及其…

第一个 Flutter 项目(1)共46节

前端开发工具vs code&#xff0c;安装Flutter sdk&#xff0c;如果你的下载速度比较慢&#xff0c;可以选择这个&#x1f604; flutter sdk 解压码&#xff1a;stwq 配置可以看这Flutter 新建工程一直等待 解决办法-CSDN博客 如果你是新的 Flutter 开发者&#xff0c;我们建…