redis源码从main开始11中讲了string对象类型,本章我们来看list对象类型

12 redis对象之list

目前list对象有两种编码方式,一种是链表,一种是压缩表
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */

以下为两种编码方式的list对象的创建方式
robj *createListObject(void) {
    list *l = listCreate();
    robj *o = createObject(REDIS_LIST,l);
    listSetFreeMethod(l,decrRefCountVoid);
    o->encoding = REDIS_ENCODING_LINKEDLIST;
    return o;
}

robj *createZiplistObject(void) {
    unsigned char *zl = ziplistNew();
    robj *o = createObject(REDIS_LIST,zl);
    o->encoding = REDIS_ENCODING_ZIPLIST;
    return o;
}


// 下面先来看编码方式为链表的list对象的创建过程
// 双端链表的相关内容主要在dlist.c中

//第一步,首先创建一个链表list结构
list *listCreate(void)
{
    struct list *list;
    if ((list = zmalloc(sizeof(*list))) == NULL)
        return NULL;
    list->head = list->tail = NULL;
    list->len = 0;
    list->dup = NULL;
    list->free = NULL;
    list->match = NULL;

    return list;
}


//链表结构体
typedef struct list {
    listNode *head; // 头结点
    listNode *tail; // 尾节点
    void *(*dup)(void *ptr); // 一个复制函数
    void (*free)(void *ptr); // 一个释放函数
    int (*match)(void *ptr, void *key); // 一个对比函数
    unsigned long len; // 链表节点数
} list;


//单个节点的结构
typedef struct listNode {
    struct listNode *prev; // 前置节点
    struct listNode *next; // 后继节点
    void *value; // 节点值
} listNode;

// 第二步,接下来创建一个REDIS_LIST类型的redis对象,并把刚刚创建的list传入
robj *o = createObject(REDIS_LIST,l);

//第三步,设置list的释放函数,decrRefCountVoid是一个包装,会根据传入的redis对象类型调用对应的释放方法,此处是list对象,会调用freeListObject方法
listSetFreeMethod(l,decrRefCountVoid);

//根据编码释放列表对象, 链表列表和压缩列表对应不同释放方法
void freeListObject(robj *o) {
    switch (o->encoding) {
    case REDIS_ENCODING_LINKEDLIST:
        listRelease((list*) o->ptr);
        break;
    case REDIS_ENCODING_ZIPLIST:
        zfree(o->ptr);
        break;
    default:
        redisPanic("Unknown list encoding type");
    }
}

//第四步,设置编码类型为链表编码
o->encoding = REDIS_ENCODING_LINKEDLIST;


// 下面来看压缩列表
// 压缩列表的主要内容在ziplist.c中

// 可以看出与链表列表不同的是,链表列表返回的是一个双端链表的结构体,这里返回的是无符号字节
// 第一步,创建压缩列表的机构
unsigned char *ziplistNew(void) {

    // 默认大小,是压缩列表头尾的大小,头大小为4+4+2字节,尾巴为1字节
    unsigned int bytes = ZIPLIST_HEADER_SIZE+1;
    // 分配初始化的大小
    unsigned char *zl = zmalloc(bytes);

    // 初始化表属性
    ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);
    ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);
    ZIPLIST_LENGTH(zl) = 0;

    // 设置这个压缩表的尾巴是255既二进制的11111111
    zl[bytes-1] = ZIP_END;

    return zl;
}

// 这里需要注意intrev32ifbe,这里redis做了一个转换
// 当宿主机是小端的时候intrev32ifbe不作处理,当宿主机是大端的时候会做大端向小端的转化
#if (BYTE_ORDER == LITTLE_ENDIAN)
#define memrev16ifbe(p)
#define memrev32ifbe(p)
#define memrev64ifbe(p)
#define intrev16ifbe(v) (v)
#define intrev32ifbe(v) (v)
#define intrev64ifbe(v) (v)
#else
#define memrev16ifbe(p) memrev16(p)
#define memrev32ifbe(p) memrev32(p)
#define memrev64ifbe(p) memrev64(p)
#define intrev16ifbe(v) intrev16(v)
#define intrev32ifbe(v) intrev32(v)
#define intrev64ifbe(v) intrev64(v)
#endif

基于版本3.0.0版本,点击下载https://download.redis.io/releases/redis-3.0.0.tar.gz

本文地址,https://www.ccagml.com/?p=410

发表评论