首页 > 其他分享 >1. SDS

1. SDS

时间:2024-01-05 15:25:49浏览次数:20  
标签:__ sds SDS len char 字符串

相关文件:

sds.h
sds.c

1.定义

1.1 sds定义

typedef char* sds;
  1. sds:"Simple Dynamic String" ,简单动态字符串。

1.2 sdshdr定义

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[];
};
  1. sdshdr:"Simple Dynamic String Header" ,简单动态字符串的头部。

  2. __attribute__ ((__packed__)):告诉编译器以紧凑的方式排列结构体成员,不进行字节对齐(:https://www.cnblogs.com/INnoVationv2/p/17579732.html)

  3. 其他成员的说明如下

    struct __attribute__ ((__packed__)) sdshdr8 {
      	// 字符长度
        uint8_t len; 
      	// 已分配空间大小,不包括头部和终止符\0 
        uint8_t alloc; 
      	// 低3位表示SDS类型,高5位未使用
        unsigned char flags;
      	// 实际存储数据
        char buf[];
    };
    
  4. 不同类型的sdshdr16,对应不同的字符串,当扩容时,sdshdr的类型会改变

1.3 说明

sds的header是隐藏的

比如有一个sds s1变量,这个变量的实际类型是char* s1,指向的是header中的char buf[],当需要访问header的信息时,通过以下步骤进行访问

  1. 移动s1访问flags获取header类型
  2. 根据类型移动指针到header头部
  3. 将指针强转为对应header类型的指针
  4. 通过指针访问头部数据

比如获取sds的长度:

#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)

static inline size_t sdslen(const sds s) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            return SDS_TYPE_5_LEN(flags);
        case SDS_TYPE_8:
            return SDS_HDR(8,s)->len;
        case SDS_TYPE_16:
            return SDS_HDR(16,s)->len;
        case SDS_TYPE_32:
            return SDS_HDR(32,s)->len;
        case SDS_TYPE_64:
            return SDS_HDR(64,s)->len;
    }
    return 0;
}

假设s的长度是sdshdr8,SDS_HDR(8,s)->len就会被扩展为

((struct sdshdr8 *)((s)-(sizeof(struct sdshdr8))))

2.优点

2.1 减少内存分配次数

扩容逻辑

#define SDS_MAX_PREALLOC (1024*1024) //1MB

len = sdslen(s);
// newlen=原有字符串长度+新增字符串长度
newlen = (len+addlen);
assert(newlen > len);   /* Catch size_t overflow */
if (greedy == 1) {
    // 如果小于1MB,每次分配两倍大小的空间
    if (newlen < SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        // 如果大于1MB,那每次多分配1MB
        newlen += SDS_MAX_PREALLOC;
}

如果启用greedy模式

  1. 目标字符串长度小于1MB,每次分配两倍大小的空间
  2. 如果大于1MB,那每次多分配1MB

比如现有长度是3,要追加一个长度为5的字符串,那么扩容后的长度就是16

好处

内存预分配,减少内存分配次数

缩容

当删除字符串中的部分字符时,默认情况下,在删除字符后,不会回收空间,之后如果再次写入,就无需分配空间,

也可以使用特定函数强制释放空间

2.2 杜绝缓冲区溢出

sds在追加字符串时,会检查原有sds的空间是否充足,如果不足,就会先分配好内存再追加,防止缓冲区溢出的发生

sds sdscatlen(sds s, const void *t, size_t len) {
    size_t curlen = sdslen(s);

  	// 检查空间是否充足,len是待插入字符串t的长度
    s = sdsMakeRoomFor(s,len);
    if (s == NULL) return NULL;
    memcpy(s+curlen, t, len);
    sdssetlen(s, curlen+len);
    s[curlen+len] = '\0';
    return s;
}

2.3 二进制安全

C语言字符串只能用于保存ascii字符,而不能保存任意二进制文件,原因是C语言字符串使用\0结尾,10进制表示就是0,很多函数都依赖这一特性,比如strcmp函数

int strcmp(const char *s1, const char *s2)
{
    for ( ; *s1 == *s2; s1++, s2++)
      if (*s1 == '\0')
        return 0;
    return ((*(unsigned char *)s1 < *(unsigned char *)s2) ? -1 : +1);
}

而二进制文件中0字符经常出现,所以不兼容

而在SDS中,header中保存了字符串长度,这样就杜绝了C语言字符串的问题,比如对比两个字符串,sdsde函数如下,使用memcmp + 数据长度进行比较,因此实现了二进制安全

int sdscmp(const sds s1, const sds s2) {
    size_t l1, l2, minlen;
    int cmp;

    l1 = sdslen(s1);
    l2 = sdslen(s2);
    minlen = (l1 < l2) ? l1 : l2;
    cmp = memcmp(s1,s2,minlen);
    if (cmp == 0) return l1>l2? 1: (l1<l2? -1: 0);
    return cmp;
}

2.4 保持C语言字符串格式,兼容部分C字符串函数

sds在保存字符串时,仍然使用\0作为结尾,所以兼容部分c语言字符串函数,可以直接调用

3.总结

  1. 获取字符串长度,只需要O(1)时间复杂度
  2. 杜绝缓冲区溢出
  3. 减少修改字符串长度所需内存重分配次数
  4. 二进制安全
  5. 保持C语言字符串格式,兼容部分C字符串函数

标签:__,sds,SDS,len,char,字符串
From: https://www.cnblogs.com/INnoVationv2/p/17947299

相关文章

  • Redis数据结构2:REDIS_STRING(SDS)
    REDIS_STRING(SDS)SDS全称SimpleDynamicString(简单动态字符串),是专为Redis设计的简易字符串实现。Redis并未采用C语言传统字符串char*,而是自己设计了一套字符串实现标准。传统字符串的缺陷C语言字符串实际上就是一个以'\0'结尾的字符数组。例如:char*myName="ErickRen";......
  • CodeforcesDS1
    CodeforcesDS1CF387EGeorgeandCards(2200)Problem给出一个\(1\)到\(n\)的排列(输入中的数组\(p\)),并给出一个长为\(k\)的数组\(b\),要求从\(p\)中删去\(n-k\)个元素后得到数组\(b\)。删除操作的定义:选取一个区间\([l,r]\),删去其中最小的元素,并获得\(r-l......
  • Redis数据结构之动态字符串SDS
    动态字符串SDS我们都知道Redis中保存的Key是字符串,value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。不过Redis没有直接使用C语言中的字符串,因为C语言字符串存在很多问题:V获取字符串长度的需要通过运算V非二进制安全V不可修改Redis构建了一种新的字......
  • 常见面试题-Redis底层的SDS、ZipList、ListPack
    Redis的SDS了解吗?答:Redis创建了SDS(simpledynamicstring)的抽象类型作为String的默认实现SDS的结构如下:structsdshdr{//字节数组,用于保存字符串charbuf[];//buf[]中已使用字节数量,称为SDS的长度intlen;//buf[]中尚未使用的字节数量intfree;}......
  • Redis系列 - Redis底层数据结构(简单动态字符串(SDS)、链表、字典、跳跃表、整数集合
    转自:https://blog.csdn.net/u011485472/article/details/109460490Redis系列-Redis底层数据结构(简单动态字符串(SDS)、链表、字典、跳跃表、整数集合、压缩列表)简单动态字符串(simpledynamicstring,SDS)链表字典跳跃表整数集合压缩列表RedisObject在介绍Redis底......
  • 亚马逊 化妆品/护肤品检HRIPT,BCOP,COA,MSDS,FDA,CPNP认证
    亚马逊化妆品/护肤品检HRIPT,BCOP,COA,MSDS,FDA,CPNP认证亚马逊作为全球最大的电子商务平台之一,对平台上销售的产品有着严格的质量要求。为了保证消费者的权益所以亚马逊会要求卖家提供认证,化妆品比较常见的认证有哪些?下面小编就为大家介绍一下:亚马逊化妆品/护肤品检HRIPT,BCOP,COA,M......
  • redis数据结构sds
    简单字符串sds数据结构structsdshdr{//记录buf数组中已使用字节的数量//等于SDS所保存字符串的长度intlen;//记录buf数组中未使用字节的数量intfree;//字节数组,用于保存字符串charbuf[];};特性空间预分配空间预分配用于优化SDS的字符串年增长操作:当SD......
  • 【Redis】字符串sds
    sds,即SimpleDynamicStrings,是Redis中存储绝大部分字符串所采用的数据结构。typedefchar*sds;一、类型sds的类型包括SDS_TYPE_5,SDS_TYPE_8,SDS_TYPE_16,SDS_TYPE_32,SDS_TYPE_64五种:#defineSDS_TYPE_50#defineSDS_TYPE_81#defineSDS_TYPE_162#defineSD......
  • redis数据结构-String(SDS)
    redis数据结构(一)注:以下源码部分,来自redis-7.0.12,redis-3.0redis有一个核心的对象,叫做redisObject,用来标识所有的key和value,用结构体reidsObject来标识String、Hash、List、Set、Zset五种数据结构。源码位置在server.h。/*Objectsencoding.Somekindofobjects......
  • Redis数据结构——简单动态字符串SDS
    前言相信用过Redis的人都知道,Redis提供了一个逻辑上的对象系统构建了一个键值对数据库以供客户端用户使用。这个对象系统包括字符串对象、哈希对象、列表对象、集合对象、有序集合对象等。但是Redis面向内存并没有直接使用这些对象。而是使用了简单动态字符串,链表、字典(散列表)、......