1. 程式人生 > >redis原始碼分析之zipmap.c

redis原始碼分析之zipmap.c

/**
 * 建立空zipmap
 * @return
 */
/* Create a new empty zipmap. */
unsigned char *zipmapNew(void) {
    unsigned char *zm = zmalloc(2);

    zm[0] = 0; /* Length */
    zm[1] = ZIPMAP_END;
    return zm;
}

/**
 * 讀取zipmap元素的長度(1位元組或者5位元組,五位元組時,第一個位元組為標誌位)
 * @param p
 * @return
 */
/* Decode the encoded length pointed by 'p' */
static unsigned int zipmapDecodeLength(unsigned char *p) { unsigned int len = *p; if (len < ZIPMAP_BIGLEN) return len; memcpy(&len,p+1,sizeof(unsigned int)); memrev32ifbe(&len); return len; } /** * 計算編碼zipmap value長度所需要的位元組數,並且儲存在p中,若p為空則只返回佔用位元組數 * @param p * @param len * @return */
/* Encode the length 'l' writing it in 'p'. If p is NULL it just returns * the amount of bytes required to encode such a length. */ static unsigned int zipmapEncodeLength(unsigned char *p, unsigned int len) { if (p == NULL) { return ZIPMAP_LEN_BYTES(len); } else { if (len <
ZIPMAP_BIGLEN) { p[0] = len; return 1; } else { p[0] = ZIPMAP_BIGLEN; memcpy(p+1,&len,sizeof(len)); memrev32ifbe(p+1); return 1+sizeof(len); } } } /** * zipmap查詢key,如果totlen指標不為空,則返回zip整個長度 * @param zm * @param key * @param klen * @param totlen * @return */ /* Search for a matching key, returning a pointer to the entry inside the * zipmap. Returns NULL if the key is not found. * * If NULL is returned, and totlen is not NULL, it is set to the entire * size of the zimap, so that the calling function will be able to * reallocate the original zipmap to make room for more entries. */ static unsigned char *zipmapLookupRaw(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned int *totlen) { unsigned char *p = zm+1, *k = NULL; unsigned int l,llen; while(*p != ZIPMAP_END) { unsigned char free; /* Match or skip the key */ l = zipmapDecodeLength(p); llen = zipmapEncodeLength(NULL,l); if (key != NULL && k == NULL && l == klen && !memcmp(p+llen,key,l)) { /* Only return when the user doesn't care * for the total length of the zipmap. */ if (totlen != NULL) { k = p; } else { return p; } } //zmlen–len–“foo”–len–free–“bar”–len–“hello”–len–free–“world”-end //跳過key p += llen+l; //跳過value /* Skip the value as well */ l = zipmapDecodeLength(p); p += zipmapEncodeLength(NULL,l); free = p[0]; p += l+1+free; /* +1 to skip the free byte */ } if (totlen != NULL) *totlen = (unsigned int)(p-zm)+1;//算上end佔用的一個位元組 return k; } /** * 計算zipmap entry需要的長度 * @param klen * @param vlen * @return */ static unsigned long zipmapRequiredLength(unsigned int klen, unsigned int vlen) { unsigned int l; l = klen+vlen+3; if (klen >= ZIPMAP_BIGLEN) l += 4; if (vlen >= ZIPMAP_BIGLEN) l += 4; return l; } /** * zipmap entry key佔用的位元組數 * @param p * @return */ /* Return the total amount used by a key (encoded length + payload) */ static unsigned int zipmapRawKeyLength(unsigned char *p) { unsigned int l = zipmapDecodeLength(p); return zipmapEncodeLength(NULL,l) + l; } /** * zipmap entry value佔用的位元組數,包括空閒長度 * @param p * @return */ /* Return the total amount used by a value * (encoded length + single byte free count + payload) */ static unsigned int zipmapRawValueLength(unsigned char *p) { unsigned int l = zipmapDecodeLength(p); unsigned int used; used = zipmapEncodeLength(NULL,l); //p[used]為空閒長度 used += p[used] + 1 + l; return used; } /** * 計算zipmap entry的位元組數 * @param p * @return */ /* If 'p' points to a key, this function returns the total amount of * bytes used to store this entry (entry = key + associated value + trailing * free space if any). */ static unsigned int zipmapRawEntryLength(unsigned char *p) { unsigned int l = zipmapRawKeyLength(p); return l + zipmapRawValueLength(p+l); } /** * zipmap 空間重分配 * @param zm * @param len * @return */ static inline unsigned char *zipmapResize(unsigned char *zm, unsigned int len) { zm = zrealloc(zm, len); zm[len-1] = ZIPMAP_END; return zm; } /** * zipmap設定或者更新entry * @param zm * @param key * @param klen * @param val * @param vlen * @param update * @return */ /* Set key to value, creating the key if it does not already exist. * If 'update' is not NULL, *update is set to 1 if the key was * already preset, otherwise to 0. */ unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update) { unsigned int zmlen, offset; unsigned int freelen, reqlen = zipmapRequiredLength(klen,vlen); unsigned int empty, vempty; unsigned char *p; freelen = reqlen; if (update) *update = 0; p = zipmapLookupRaw(zm,key,klen,&zmlen); if (p == NULL) { /* Key not found: enlarge */ zm = zipmapResize(zm, zmlen+reqlen); p = zm+zmlen-1; zmlen = zmlen+reqlen; /* Increase zipmap length (this is an insert) */ if (zm[0] < ZIPMAP_BIGLEN) zm[0]++;//大於等於254就不起作用了 } else { /* Key found. Is there enough space for the new value? */ /* Compute the total length: */ if (update) *update = 1; freelen = zipmapRawEntryLength(p); if (freelen < reqlen) { /* Store the offset of this key within the current zipmap, so * it can be resized. Then, move the tail backwards so this * pair fits at the current position. */ offset = p-zm; zm = zipmapResize(zm, zmlen-freelen+reqlen); p = zm+offset; /* The +1 in the number of bytes to be moved is caused by the * end-of-zipmap byte. Note: the *original* zmlen is used. */ //移動末尾位元組位置到p+reqlen,在擴容的時候已經重置end,所以移動的時候少一個位元組 memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1)); zmlen = zmlen-freelen+reqlen; freelen = reqlen; } } /* We now have a suitable block where the key/value entry can * be written. If there is too much free space, move the tail * of the zipmap a few bytes to the front and shrink the zipmap, * as we want zipmaps to be very space efficient. */ //如果reqlen小的話,並且達到閾值(4位元組)的話,進行縮容操作 empty = freelen-reqlen; if (empty >= ZIPMAP_VALUE_MAX_FREE) { /* First, move the tail <empty> bytes to the front, then resize * the zipmap to be <empty> bytes smaller. */ offset = p-zm; memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1)); zmlen -= empty; zm = zipmapResize(zm, zmlen); p = zm+offset; vempty = 0; } else { //如果不縮容的話,需要記錄free空間佔用 vempty = empty; } /* Just write the key + value and we are done. */ /* Key: */ p += zipmapEncodeLength(p,klen); memcpy(p,key,klen); p += klen; /* Value: */ p += zipmapEncodeLength(p,vlen); *p++ = vempty; memcpy(p,val,vlen); return zm; } /** * zipmap 刪除指定key的entry * @param zm * @param key * @param klen * @param deleted * @return */ /* Remove the specified key. If 'deleted' is not NULL the pointed integer is * set to 0 if the key was not found, to 1 if it was found and deleted. */ unsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted) { unsigned int zmlen, freelen; unsigned char *p = zipmapLookupRaw(zm,key,klen,&zmlen); if (p) { freelen = zipmapRawEntryLength(p); memmove(p, p+freelen, zmlen-((p-zm)+freelen+1)); zm = zipmapResize(zm, zmlen-freelen); /* Decrease zipmap length */ if (zm[0] < ZIPMAP_BIGLEN) zm[0]--;//如果元素個數為254個,則不做修改 if (deleted) *deleted = 1; } else { if (deleted) *deleted = 0; } return zm; } /** * zipmap 跳過zipmap len * @param zm * @return */ /* Call before iterating through elements via zipmapNext() */ unsigned char *zipmapRewind(unsigned char *zm) { return zm+1; } /** * zipmap next元素 * @param zm * @param key * @param klen * @param value * @param vlen * @return */ /* This function is used to iterate through all the zipmap elements. * In the first call the first argument is the pointer to the zipmap + 1. * In the next calls what zipmapNext returns is used as first argument. * Example: * * unsigned char *i = zipmapRewind(my_zipmap); * while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) { * printf("%d bytes key at $p\n", klen, key); * printf("%d bytes value at $p\n", vlen, value); * } */ unsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen) { if (zm[0] == ZIPMAP_END) return NULL; if (key) { *key = zm; *klen = zipmapDecodeLength(zm); *key += ZIPMAP_LEN_BYTES(*klen); } zm += zipmapRawKeyLength(zm); if (value) { *value = zm+1; *vlen = zipmapDecodeLength(zm); *value += ZIPMAP_LEN_BYTES(*vlen); } zm += zipmapRawValueLength(zm); return zm; } /** * zipmap 獲取指定key的value * @param zm * @param key * @param klen * @param value * @param vlen * @return */ /* Search a key and retrieve the pointer and len of the associated value. * If the key is found the function returns 1, otherwise 0. */ int zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen) { unsigned char *p; if ((p = zipmapLookupRaw(zm,key,klen,NULL)) == NULL) return 0; p += zipmapRawKeyLength(p); *vlen = zipmapDecodeLength(p); *value = p + ZIPMAP_LEN_BYTES(*vlen) + 1; return 1; } /** * zipmap 判斷元素是否存在 * @param zm * @param key * @param klen * @return */ /* Return 1 if the key exists, otherwise 0 is returned. */ int zipmapExists(unsigned char *zm, unsigned char *key, unsigned int klen) { return zipmapLookupRaw(zm,key,klen,NULL) != NULL; } /** * zipmap獲取zipmap長度,如果超過了長度則重置長度(zipmap整個api中其實不強依賴於這個位元組,可能只是為了判別要轉換為dict的閾值) * @param zm * @return */ /* Return the number of entries inside a zipmap */ unsigned int zipmapLen(unsigned char *zm) { unsigned int len = 0; if (zm[0] < ZIPMAP_BIGLEN) { len = zm[0]; } else { unsigned char *p = zipmapRewind(zm); while((p = zipmapNext(p,NULL,NULL,NULL,NULL)) != NULL) len++; /* Re-store length if small enough */ if (len < ZIPMAP_BIGLEN) zm[0] = len; } return len; } /** * zipmap總佔用位元組數 * @param zm * @return */ /* Return the raw size in bytes of a zipmap, so that we can serialize * the zipmap on disk (or everywhere is needed) just writing the returned * amount of bytes of the C array starting at the zipmap pointer. */ size_t zipmapBlobLen(unsigned char *zm) { unsigned int totlen; zipmapLookupRaw(zm,NULL,0,&totlen); return totlen; } #ifdef REDIS_TEST static void zipmapRepr(unsigned char *p) { unsigned int l; printf("{status %u}",*p++); while(1) { if (p[0] == ZIPMAP_END) { printf("{end}"); break; } else { unsigned char e; l = zipmapDecodeLength(p); printf("{key %u}",l); p += zipmapEncodeLength(NULL,l); if (l != 0 && fwrite(p,l,1,stdout) == 0) perror("fwrite"); p += l; l = zipmapDecodeLength(p); printf("{value %u}",l); p += zipmapEncodeLength(NULL,l); e = *p++; if (l != 0 && fwrite(p,l,1,stdout) == 0) perror("fwrite"); p += l+e; if (e) { printf("["); while(e--) printf("."); printf("]"); } } } printf("\n"); } #define UNUSED(x) (void)(x) int zipmapTest(int argc, char *argv[]) { unsigned char *zm; UNUSED(argc); UNUSED(argv); zm = zipmapNew(); zm = zipmapSet(zm,(unsigned char*) "name",4, (unsigned char*) "foo",3,NULL); zm = zipmapSet(zm,(unsigned char*) "surname",7, (unsigned char*) "foo",3,NULL); zm = zipmapSet(zm,(unsigned

相關推薦

redis原始碼分析zipmap.c

/** * 建立空zipmap * @return */ /* Create a new empty zipmap. */ unsigned char *zipmapNew(void) { unsigned char *zm = zmalloc(2

redis原始碼分析intset.c

#include <stdio.h> #include <stdlib.h> #include <string.h> #include "intset.h" #include "zmalloc.h" #include "end

Open-iscs原始碼分析---iscsiadm.c

login處理... main(int argc, char **argv) { ...... printf("anycom: iscsiadmin.c 2332"); rc = exec_node_op(op, do_login, do_logout,

YOLO原始碼分析data.c

darknet裡樣本的儲存是以如下形式進行排列的 flag | class_info | box_info flag為0或者1,表示有沒有物體 class_info表示類別資訊,其長度是num_class box_info表示標註資訊,其長度是5(x, y, w, h, o

redis原始碼分析與思考(十七)——有序集合型別的命令實現(t_zset.c)

    有序集合是集合的延伸,它儲存著集合元素的不可重複性,但不同的是,它是有序的,它利用每一個元素的分數來作為有序集合的排序依據,現在列出有序集合的命令: 有序集合命令 命令 對應操作 時

redis原始碼分析與思考(十六)——集合型別的命令實現(t_set.c)

    集合型別是用來儲存多個字串的,與列表型別不一樣,集合中不允許有重複的元素,也不能以索引的方式來通過下標獲取值,集合中的元素還是無序的。在普通的集合上增刪查改外,集合型別還實現了多個集合的取交集、並集、差集,集合的命令如下表所示: 集合命

redis原始碼分析與思考(十五)——雜湊型別的命令實現(t_hash.c)

    雜湊型別又叫做字典,在redis中,雜湊型別本身是一個鍵值對,而雜湊型別裡面也存貯著鍵值對,其對應關係是,每個雜湊型別的值對應著一個鍵值對或多對鍵值對,如圖所示: 雜湊型別命令 命令 對應操

redis原始碼分析與思考(十四)——列表型別的命令實現(t_list.c)

    列表型別是用來存貯多個字串物件的結構。一個列表可以存貯232-1個元素,可以對列表兩端進行插入(push)、彈出(pop),還可以獲取指定範圍內的元素列表、獲取指定索引的元素等等,它可以靈活的充當棧和佇列的角色。下面列出列表的命令: 列

redis原始碼分析與思考(十三)——字串型別的命令實現(t_string.c)

    在對字串操作的命令中,主要有增加刪查該、批處理操作以及編碼的轉換命令,現在列出對字串物件操作的主要常用命令: 常用命令表 命令 對應操作 時間複雜度

redis原始碼分析與思考(十七)——有序集合型別的命令實現(t_set.c)

    有序集合是集合的延伸,它儲存著集合元素的不可重複性,但不同的是,它是有序的,它利用每一個元素的分數來作為有序集合的排序依據,現在列出有序集合的命令: 有序集合命令 命令 對應操作 時間複

Android Wi-Fi原始碼分析WifiService操作Wi-Fi(一):分析Wifi.c中的wifi_load_driver()函式

Wi-Fi原始碼分析之WifiService操作Wi-Fi(一) 分析Wifi.c中的wifi_load_driver()函式 int wifi_load_driver() { AL

ghostscript原始碼分析 scan_token()函式 (詞法分析器iscan.c)

scan_token()函式很重要,ghostscript寫得比較瑣碎難懂,裡面有些有英文解釋。 我只對我關注的部分加了些中文註釋。當然不是所有的都理解了。但是功能還是清楚了的,像某些函式介面。 如果讓我寫的話,我一定比他寫得更清晰。哈哈,當然他的scan_token基本的框架 設計還是

STL原始碼分析hash表(gnu-c++ 2.9)

1、基本概念 關於hash表的概念這裡就不再多說,hash表的變化一般都在雜湊函式和退避方法上。STL採用的是開鏈法,即每個hash桶裡面維持一個連結串列,hash函式計算出位置後,就將節點插入該位置的連結串列上,因此,底層實現為hash表的容器,迭代器的實現

Redis原始碼分析(三十五)--- redis.c服務端的實現分析(2)

       在Redis服務端的程式碼量真的是比較大,如果一個一個API的學習怎麼實現,無疑是一種效率很低的做法,所以我今天對服務端的實現程式碼的學習,重在他的執行流程上,而對於他的模組設計在上一篇中我已經分析過了,不明白的同學可以接著看上篇。所以我學習分析redis服務

HotSpot原始碼分析C++物件的記憶體佈局

HotSpot採用了OOP-Klass模型來描述Java類和物件。OOP(Ordinary Object Pointer)指的是普通物件指標,而Klass用來描述物件的具體型別。為了更好理解這個模型,首先要介紹一下C++的記憶體物件模型和虛擬函式。 1、C++類物件的記憶體佈局 我們使用Visual Stud

Spark原始碼分析Spark Shell(上)

https://www.cnblogs.com/xing901022/p/6412619.html 文中分析的spark版本為apache的spark-2.1.0-bin-hadoop2.7。 bin目錄結構: -rwxr-xr-x. 1 bigdata bigdata 1089 Dec

Netty 原始碼分析拆包器的奧祕

為什麼要粘包拆包 為什麼要粘包 首先你得了解一下TCP/IP協議,在使用者資料量非常小的情況下,極端情況下,一個位元組,該TCP資料包的有效載荷非常低,傳遞100位元組的資料,需要100次TCP傳送,100次ACK,在應用及時性要求不高的情況下,將這100個有效資料拼接成一個數據包,那會縮短到一個TCP資

Android原始碼分析為什麼在onCreate() 和 onResume() 獲取不到 View 的寬高

轉載自:https://www.jianshu.com/p/d7ab114ac1f7 先來看一段很熟悉的程式碼,可能在最開始接觸安卓的時候,大部分人都寫過的一段程式碼;即嘗試在 onCreate() 和 onResume() 方法中去獲取某個 View 的寬高資訊: 但是列印輸出後,我們會發

netty原始碼分析服務端啟動

ServerBootstrap與Bootstrap分別是netty中服務端與客戶端的引導類,主要負責服務端與客戶端初始化、配置及啟動引導等工作,接下來我們就通過netty原始碼中的示例對ServerBootstrap與Bootstrap的原始碼進行一個簡單的分析。首先我們知道這兩個類都繼承自AbstractB

SNMP原始碼分析(一)配置檔案部分

snmpd.conf想必不陌生。在程序啟動過程中會去讀取配置檔案中各個配置。其中幾個引數需要先知道是幹什麼的:   token:配置檔案的每行的開頭,例如 group MyROGroup v1 readSec 這行token的引數是group。