HiRedis源码阅读(一)

Hiredis源码学习

用C/C++造轮子之前,先学学别人的轮子是怎么造的,就想看Redis源码,但网上只找到一本《Redis设计与实现》,讲的很粗放,不像《STL源码剖析》那么清楚明了,就自己去下了Hiredis看,地址:https://github.com/redis/hiredis

内存分配

按照学习《STL源码剖析》的过程来学HiRedis,先从内存分配入手.HiRedis关于内存分配的函数的声明放在 alloc.h中,定义在alloc.c:

// alloc.h
#ifndef HIREDIS_ALLOC_H
#define HIREDIS_ALLOC_H

#include <stdlib.h> //这个头文件是为了用size_t

#ifndef HIREDIS_OOM_HANDLER
#define HIREDIS_OOM_HANDLER abort()
#endif

#ifdef __cplusplus //如果是C++,把下面函数按C的方式编译下面函数
extern "C" {
#endif

void *hi_malloc(size_t size);
void *hi_calloc(size_t nmemb, size_t size);
void *hi_realloc(void *ptr, size_t size);
char *hi_strdup(const char *str);

#ifdef __cplusplus
}
#endif

#endif 

关于extern “C”,更多可以查看here,而关于size_t,可以看here

#include "fmacros.h"
#include "alloc.h"
#include <string.h>

void *hi_malloc(size_t size) {
    void *ptr = malloc(size);
    if (ptr == NULL)
        HIREDIS_OOM_HANDLER;
    return ptr;
}
void *hi_calloc(size_t nmemb, size_t size) {
    void *ptr = calloc(nmemb, size);
    if (ptr == NULL)
        HIREDIS_OOM_HANDLER;
   return ptr;
}
void *hi_realloc(void *ptr, size_t size) {
    void *newptr = realloc(ptr, size);
    if (newptr == NULL)
        HIREDIS_OOM_HANDLER;
    return newptr;
}
char *hi_strdup(const char *str) {
    char *newstr = strdup(str);
    if (newstr == NULL)
        HIREDIS_OOM_HANDLER;
    return newstr;
}

可以看出,HiRedis的alloc就是在原有的malloc、calloc、realloc、strdup外面加了一层封装(关于这几个内存分配函数,详见 here),在函数内部调用相应的C库函数,并且在内存分配失败时,调用abort()(关于abort(),详见here

跨平台

上面alloc.c中还包含了一个文件fmacros.h,它内部是这样的

#ifndef __HIREDIS_FMACRO_H
#define __HIREDIS_FMACRO_H

#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200112L

#if defined(__APPLE__) && defined(__MACH__)
/* Enable TCP_KEEPALIVE */
#define _DARWIN_C_SOURCE
#endif

#endif

其中定义的_XOPEN_SOURCE和_POSIX_C_SOURCE在这里有讲解,而下面的#if是处理了系统为macos的情况,详见here

简单动态字符串SDS(一)

HiRedis关于SDS的定义是这样的,

#ifndef __SDS_H
#define __SDS_H

#define SDS_MAX_PREALLOC (1024*1024)
#ifdef _MSC_VER
#define __attribute__(x)
#endif

#include <sys/types.h>
#include <stdarg.h>
#include <stdint.h>

typedef char *sds;
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[];
};

实际上sds就是char*,并且在sds.h中定义了sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64五种类型,其中sdshdr5是不使用的。定义中,结构体不使用对齐功能__attribute__ ((packed)),

#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
#define SDS_TYPE_MASK 7
#define SDS_TYPE_BITS 3
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) //因为sdshdr5不使用,所以长度直接返回0

这一串宏就是定义了每个类型sds的标识码,将sdshdrx中的flag与SDS_TYPE_MASK做与运算就可以得出sds的类型。后面两个宏中使用的##详见here,它们的作用是根据指向sdshdrx内部buff的指针得到指向结构体的指针(通过指针减去长度实现)。
然后是一些内联函数。

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;
}
static inline size_t sdsavail(const sds s) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5: {
            return 0;
        }
        case SDS_TYPE_8: {
            SDS_HDR_VAR(8,s);
            return sh->alloc - sh->len;
        }
        case SDS_TYPE_16: {
            SDS_HDR_VAR(16,s);
            return sh->alloc - sh->len;
        }
        case SDS_TYPE_32: {
            SDS_HDR_VAR(32,s);
            return sh->alloc - sh->len;
        }
        case SDS_TYPE_64: {
            SDS_HDR_VAR(64,s);
            return sh->alloc - sh->len;
        }
    }
    return 0;
}
static inline size_t sdsalloc(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)->alloc;
        case SDS_TYPE_16:
            return SDS_HDR(16,s)->alloc;
        case SDS_TYPE_32:
            return SDS_HDR(32,s)->alloc;
        case SDS_TYPE_64:
            return SDS_HDR(64,s)->alloc;
    }
    return 0;
}

第一个函数的作用的求得s的长度,因为上面取消了对齐,所以可以直接用s[-1]取flag参数,然后与7做与运算,得到是sdshdrx,再调用结构体里的len,返回。第二个函数是求剩余空间,跟第一个差不多,返回分配的alloc-使用的len=剩余的。第三个是直接返回alloc即总空间。还有一些对结构体成员进行操作的函数

//修改长度为newlen
static inline void sdssetlen(sds s, size_t newlen) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            {
                unsigned char *fp = ((unsigned char*)s)-1;
                *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));
            }
            break;
        case SDS_TYPE_8:
            SDS_HDR(8,s)->len = (uint8_t)newlen;
            break;
        case SDS_TYPE_16:
            SDS_HDR(16,s)->len = (uint16_t)newlen;
            break;
        case SDS_TYPE_32:
            SDS_HDR(32,s)->len = (uint32_t)newlen;
            break;
        case SDS_TYPE_64:
            SDS_HDR(64,s)->len = (uint64_t)newlen;
            break;
    }
}
//增加长度为len+inc
static inline void sdsinclen(sds s, size_t inc) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            {
                unsigned char *fp = ((unsigned char*)s)-1;
                unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
            }
            break;
        case SDS_TYPE_8:
            SDS_HDR(8,s)->len += (uint8_t)inc;
            break;
        case SDS_TYPE_16:
            SDS_HDR(16,s)->len += (uint16_t)inc;
            break;
        case SDS_TYPE_32:
            SDS_HDR(32,s)->len += (uint32_t)inc;
            break;
        case SDS_TYPE_64:
            SDS_HDR(64,s)->len += (uint64_t)inc;
            break;
    }
}
//修改alloc为newlen
static inline void sdssetalloc(sds s, size_t newlen) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            /* Nothing to do, this type has no total allocation info. */
            break;
        case SDS_TYPE_8:
            SDS_HDR(8,s)->alloc = (uint8_t)newlen;
            break;
        case SDS_TYPE_16:
            SDS_HDR(16,s)->alloc = (uint16_t)newlen;
            break;
        case SDS_TYPE_32:
            SDS_HDR(32,s)->alloc = (uint32_t)newlen;
            break;
        case SDS_TYPE_64:
            SDS_HDR(64,s)->alloc = (uint64_t)newlen;
            break;
    }
}

在sds.h中还声明了很多函数,他们的定义在sds.c中

static inline int sdsHdrSize(char type) {
    switch(type&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            return sizeof(struct sdshdr5);
        case SDS_TYPE_8:
            return sizeof(struct sdshdr8);
        case SDS_TYPE_16:
            return sizeof(struct sdshdr16);
        case SDS_TYPE_32:
            return sizeof(struct sdshdr32);
        case SDS_TYPE_64:
            return sizeof(struct sdshdr64);
    }
    return 0;
}
static inline char sdsReqType(size_t string_size) {
    if (string_size < 32)
        return SDS_TYPE_5;
    if (string_size < 0xff)
        return SDS_TYPE_8;
    if (string_size < 0xffff)
        return SDS_TYPE_16;
    if (string_size < 0xffffffff)
        return SDS_TYPE_32;
    return SDS_TYPE_64;
}

第一个函数根据类型返回结构体大小。第二个则根据大小返回需要的结构体,在创建新的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. */
    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);//#define s_malloc malloc
    if (sh == NULL) return NULL;
    if (!init)
        memset(sh, 0, hdrlen+initlen+1);
    s = (char*)sh+hdrlen;
    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;
        }
        case SDS_TYPE_16: {
            SDS_HDR_VAR(16,s);
            sh->len = initlen;
            sh->alloc = initlen;
            *fp = type;
            break;
        }
        case SDS_TYPE_32: {
            SDS_HDR_VAR(32,s);
            sh->len = initlen;
            sh->alloc = initlen;
            *fp = type;
            break;
        }
        case SDS_TYPE_64: {
            SDS_HDR_VAR(64,s);
            sh->len = initlen;
            sh->alloc = initlen;
            *fp = type;
            break;
        }
    }
    if (initlen && init)
        memcpy(s, init, initlen);
    s[initlen] = '\0';
    return s;
}

这个函数的作用是创建一个新的sdshdrx,其长度为sizeof(sdshdrx)+initlen+1字节并初始化为0,最后一个字节是’\0’,因为结构中储存了长度len,所以字符串当中有\0也不会影响。如果init不为空指针,则将其所指向的长度为ininlen的字符串拷贝到新分配空间的initlen长度这一段,再把最后一位置’\0’

//创建一个空的sds,里面有一个'\0'
sds sdsempty(void) {
    return sdsnewlen("",0);
}
//根据传入的字符串调用sdsnewlen()创建新的sds
sds sdsnew(const char *init) {
    size_t initlen = (init == NULL) ? 0 : strlen(init);
    return sdsnewlen(init, initlen);
}
//sds的拷贝构造函数
sds sdsdup(const sds s) {
    return sdsnewlen(s, sdslen(s));
}
//释放sds
void sdsfree(sds s) {
    if (s == NULL) return;
    s_free((char*)s-sdsHdrSize(s[-1]));
}
//更新长度len
void sdsupdatelen(sds s) {
    int reallen = strlen(s);
    sdssetlen(s, reallen);
}
//将sds变为空串,但是实际分配的内存没释放
//当再append时直接加,无需分配内存
void sdsclear(sds s) {
    sdssetlen(s, 0);
    s[0] = '\0';
}

下面这个函数的作用是为sds s增加空间,在append操作时需要调用

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;

    if (avail >= addlen) return s;//剩余空间足够,直接返回s

    len = sdslen(s);
    sh = (char*)s-sdsHdrSize(oldtype);
    newlen = (len+addlen);//append之后的新长度
    
    /*这里包含的sds的扩增规则,在达到阈值以前,sds每次增加导致内存不够,
    重新分配内存时就会分配所需内存的两倍,如果需要的达到了阈值,就只会多
    分配阈值这么多内存
    */
    if (newlen < SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC;

    type = sdsReqType(newlen);
    
    //如果是sdshdr5,转成sdshdr8
    if (type == SDS_TYPE_5) type = SDS_TYPE_8;
    
    hdrlen = sdsHdrSize(type);
/*如果原始的sds和append之后的sds同类型,则调用s_realloc(),否则调用
s_malloc()并拷贝buff[]到新的newsh中
*/
    if (oldtype==type) {
        newsh = s_realloc(sh, hdrlen+newlen+1);
        if (newsh == NULL) {
            s_free(sh);
            return NULL;
        }
        s = (char*)newsh+hdrlen;
    } else {
        /* 当前后类型不一时,创建新类型的sds,并把s中字符串拷贝到newsh,
        令s指向新的sdshdr,设置len和alloc*/
        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);
    }
    sdssetalloc(s, newlen);
    return s;
}

所以sds实际上内存扩展有点类似于vector,在达到阈值之前都是翻倍增加的,不过是需要的翻倍,比如,原先有个“ab”,我们再appen “cde”,此时该串的总长度=alloc应该为10+最后给的一位’\0’=11.而vector则是依次压入,超出时翻倍,它的大小应该是8,因为vector最后不会有’\0’。

因为这个写起来太长了,而且我马上要准备找实习,所以决定把内容分成几篇,欢迎各位评论探讨。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值