Redis设计与实现读书笔记——简单动态字符串
前言
数据类型定义
// 字符串类型的别名
typedef char *sds;
另一个是 sdshdr:
// 持有sds的结构
struct sdshdr {
// buf中已经被使用的字符串空间数量
int len;
// buf中预留字符串的空间数量
int free;
// 实际存储字符串的地方
char buf[];
};
其中,sds只是字符串数组类型char*的别名,而sdshdr用于持有和保存sds的信息
将sdshdr用作sds
sds sdsnewlen(const void *init, size_t initlen)
{
struct sdshdr *sh; if (init) {
// 创建
sh = malloc(sizeof(struct sdshdr) + initlen + 1);
} else {
// 重分配
sh = calloc(1, sizeof(struct sdshdr) + initlen + 1);
} if (sh == NULL) return NULL; sh->len = initlen;
sh->free = 0; // 刚开始free为0 if (initlen && init) {
memcpy(sh->buf, init, initlen);
}
sh->buf[initlen] = '\0'; // 只返回sh->buf这个字符串部分
return (char *)sh->buf;
}
通过使用变量持有一个sds的值,在遇到那些只处理sds值本身的函数时,可以直接将sds传给它们。比如说,sdstoupper 函数就是其中的一个例子:
static inline size_t sdslen(const sds s)
{
// 从sds中计算出相应的sdshdr结构
struct sdshdr *sh = (void *)(s - (sizeof(struct sdshdr))); return sh->len;
} void sdstoupper(sds s)
{
int len = sdslen(s), j; for (j = 0; j < len; j ++)
s[j] = toupper(s[j]);
}
这里有一个技巧,通过指针运算,可以从sds值中计算出相应的sdshdr结构:
static inline size_t sdsavail(const sds s)
{
struct sdshdr *sh = (void *)(s - (sizeof(struct sdshdr))); return sh->free;
}
内存分配函数实现
sds sdsMakeRoomFor(sds s, size_t addlen)
{
struct sdshdr *sh, *newsh;
size_t free = sdsavail(s);
size_t len, newlen; // 预留空间可以满足本地拼接
if (free >= addlen) return s; len = sdslen(s);
sh = (void *)(s - (sizeof(struct sdshdr))); // 设置新sds的字符串长度
// 这个长度比完成本次拼接实际所需的长度要大
// 通过预留空间优化下次拼接操作
newlen = (len + addlen);
if (newlen < 1024 * 1024)
newlen *= 2;
else
newlen += 1024; // 重新分配sdshdr
newsh = realloc(sh, sizeof(struct sdshdr) + newlen + 1);
if (newsh == NULL) return NULL; newsh->free = newlen - len; // 只返回字符串部分
return newsh->buf;
}
这种内存分配策略表明,在对sds 值进行扩展(expand)时,总会预留额外的空间,通过花费更多的内存,减少了对内存进行重分配(reallocate)的次数,并优化下次扩展操作的处理速度
/**
* 按长度len扩展sds,并将t拼接到sds的末尾
*/
sds sdscatlen(sds s, const void *t, size_t len)
{
struct sdshdr *sh; size_t curlen = sdslen(s); // O(N)
s = sdsMakeRoomFor(s, len);
if (s == NULL) return NULL; // 复制
memcpy(s + curlen, t, len); // 更新len和free属性
sh = (void *)(s - (sizeof(struct sdshdr)));
sh->len = curlen + len;
sh->free = sh->free - len; // 终结符
s[curlen + len] = '\0'; return s;
} /**
* 将一个char数组拼接到sds 末尾
*/
sds sdscat(sds s, const char *t)
{
return sdscatlen(s, t, strlen(t));
}
Redis设计与实现读书笔记——简单动态字符串的更多相关文章
- 【笔记】《Redis设计与实现》chapter2 简单动态字符串
------------恢复内容开始------------ 2.1 SDS的定义 struct sdshdr{ // 记录buf数组中已使用字节的数量 // 等于SDS所保存字符串的长度(不含'\0 ...
- 小白的Redis学习(一)-SDS简单动态字符串
本文为读<Redis设计与实现>的记录.该书以Redis2.9讲解Redis相关内容.请注意版本差异. Redis使用C语言实现,他对C语言中的char类型数据进行封装,构建了一种简单动态 ...
- Redis源码解析:01简单动态字符串SDS
Redis没有直接使用C字符串(以'\0'结尾的字符数组),而是构建了一种名为简单动态字符串( simple dynamic string, SDS)的抽象类型,并将SDS用作Redis的默认字符 ...
- Redis 设计与实现读书笔记一 Redis字符串
1 Redis 是C语言实现的 2 C字符串是 /0 结束的字符数组 3 Redis具体的动态字符串实现 /* * 保存字符串对象的结构 */ struct sdshdr { // buf 中已占用空 ...
- Redis设计与实现读书笔记(二) 链表
链表作为最基础的数据结构,在许多高级语言上已经有了很好的实现.由于redis采用C语言编写,需要自己实现链表,于是redis在adlist.h定义了链表类型.作者对于这部分没什么好说,源码比较简单,如 ...
- Redis设计与实现读书笔记(一) SDS
作为redis最基础的底层数据结构之一,SDS提供了许多C风格字符串所不具备的功能,为之后redis内存管理提供了许多方便.它们分别是: 二进制安全 减少字符串长度获取时间复杂度 杜绝字符串溢出 减少 ...
- <<redis设计和实现>>读书笔记
redis如何实现主从同步的高效率?? 主从复制的同步有一个命令数据的同步文本,然后利用两个不同服务器的偏移量来进行进行同步,避免每次都是全部同步(并非会保存所有的命令数据,而是会有一个缓冲区(比如1 ...
- Redis设计与实现读书笔记——双链表
前言 首先,贴一下参考链接: http://www.redisbook.com/en/latest/internal-datastruct/adlist.html, 另外真赞文章的作者,一个90后的小 ...
- Redis 设计与实现读书笔记一 Redis List
list结构体 adlist.h/list(源码位置) /* * 双端链表结构 */ typedef struct list { // 表头节点 listNode *head; // 表尾节点 lis ...
随机推荐
- 基于Laravel开发博客应用系列 —— 使用Bower+Gulp集成前端资源
本节我们将讨论如何将前端资源集成到项目中,包括前端资源的发布和引入.本项目将使用 Bower 和 Gulp 下载和集成jQuery.Bootstrap.Font Awesome 以及 DataTabl ...
- C# 动态类型与动态编译简介
关于C#的动态类型与动态编译的简介,主要是一个Demo. 动态类型 关键字: dynamic 这里有详细的介绍:[C#基础知识系列]专题十七:深入理解动态类型 动态类型的应用场景 可以减少强制转换(强 ...
- key Value
key 存值的编号 value 存放的数据 看来key 和value 可以为null~ public class Dog { private int id; private String name ...
- Django 模板中使用css, javascript
Django 模板中使用css, javascript (r'^css/(?Ppath.*)$', 'django.views.static.serve', {'document_root': '/v ...
- [ 原创 ] Java基础3--Java中的接口
一.使用接口(interface)的目的 Java只支持单继承,即一个类最多只能继承一个直接父类,接口的主要功能就是可以实现类似于类的多重继承的功能. 二.接口的性质 1.接口具有继承性,即子接口可继 ...
- LOJ P3960 列队 树状数组 vector
https://www.luogu.org/problemnew/show/P3960 树状数组预处理之后直接搞就可以了,也不是很好解释,反正就是一个模拟过程的暴力用树状数组维护,还挺巧妙的. 我为什 ...
- 【字符串哈希】The 16th UESTC Programming Contest Preliminary F - Zero One Problem
题意:给你一个零一矩阵,q次询问,每次给你两个长宽相同的子矩阵,问你它们是恰好有一位不同,还是完全相同,还是有多于一位不同. 对每行分别哈希,先一行一行地尝试匹配,如果恰好发现有一行无法对应,再对那一 ...
- codevs 1052 地鼠游戏 优先队列
1052 地鼠游戏 Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.codevs.cn/problem/1052/ Descriptio ...
- Xcode 小技巧
1.手动添加 #warning ,在不确定的 bug.错误.待定代码处,手动添加 #warning 行,在编译时间提醒自己需要处理的地方. 2.由于 arrayWithObjects: 和 initW ...
- RTSP交互过程
步骤一: 发送:OPTIONS rtsp://127.0.0.1/172.30.31.225:8000:HIK-DS8000HC:0:1:admin:hs123456:av_stream RTSP/1 ...