此結(jié)構(gòu)在Stream中扮演著存儲(chǔ)消息的角色。
//listpack 頭部大小為 6 其中total bytes 為4個(gè)字節(jié) numelement 為2個(gè)字節(jié)
#define LP_HDR_SIZE 6
//結(jié)束符
#define LP_EOF 0xFF
//設(shè)置total值
//p自然是 listpack的指針 而他的前四個(gè)字節(jié)存儲(chǔ)的便是其字節(jié)的總數(shù)
//所以此處需要將一個(gè)四個(gè)字節(jié)的 int 賦值給四個(gè)byte 此處需要注意 存儲(chǔ)方式為小端
//因?yàn)閷⒌臀淮鎯?chǔ)到了 0字節(jié) 依次類推
#define lpSetTotalBytes(p,v) do { \
(p)[0] = (v)&0xff; \
(p)[1] = ((v)>>8)&0xff; \
(p)[2] = ((v)>>16)&0xff; \
(p)[3] = ((v)>>24)&0xff; \
} while(0)
//設(shè)置listpack的元素?cái)?shù)
//與上方相同 存儲(chǔ)為小端
#define lpSetNumElements(p,v) do { \
(p)[4] = (v)&0xff; \
(p)[5] = ((v)>>8)&0xff; \
} while(0)
//創(chuàng)建一個(gè)listpack
unsigned char *lpNew(void) {
//聲明一個(gè)7字節(jié)的空間 其中+1 是因?yàn)?listpack有個(gè)結(jié)束符 0xFF占用一個(gè)字節(jié)
unsigned char *lp = lp_malloc(LP_HDR_SIZE+1);
//如果聲明失敗返回null
if (lp == NULL) return NULL;
//設(shè)置lp的total bytes初始化數(shù)據(jù)為 7
lpSetTotalBytes(lp,LP_HDR_SIZE+1);
//設(shè)置默認(rèn)element數(shù)為 0
lpSetNumElements(lp,0);
//設(shè)置結(jié)束符為 0xFF
lp[LP_HDR_SIZE] = LP_EOF;
return lp;
}
//listpack中 entry的最大編碼長(zhǎng)度為 9
#define LP_MAX_INT_ENCODING_LEN 9
//listpack中 entry長(zhǎng)度描述最大為5個(gè)字節(jié)
#define LP_MAX_BACKLEN_SIZE 5
//前插
#define LP_BEFORE 0
//后插
#define LP_AFTER 1
//替換
#define LP_REPLACE 2
//listpack 插入數(shù)據(jù)、刪除數(shù)據(jù)、替換數(shù)據(jù) 根據(jù)參數(shù)調(diào)用來確定
// lp listpack的指針 ele 新數(shù)據(jù) size 數(shù)據(jù)長(zhǎng)度 p 如果是插入則此數(shù)據(jù)為需要插如到的那個(gè)數(shù)據(jù),如果是刪除則為需要?jiǎng)h除的數(shù)據(jù)
// where 需要操作的位置LP_BEFORE(前插) 、 LP_AFTER(后插) 、LP_REPLACE(替換) newp 操作完數(shù)據(jù)的下一個(gè)數(shù)據(jù)指針
//如果是刪除 則ele為NULL
unsigned char *lpInsert(unsigned char *lp, unsigned char *ele, uint32_t size, unsigned char *p, int where, unsigned char **newp) {
//首先聲明當(dāng)前entry的 需要編碼大小 和 長(zhǎng)度描述大小 此處backlen用于后往前遍歷 所以此處長(zhǎng)度只包含 編碼長(zhǎng)度+數(shù)據(jù)長(zhǎng)度
unsigned char intenc[LP_MAX_INT_ENCODING_LEN];
unsigned char backlen[LP_MAX_BACKLEN_SIZE];
//計(jì)算當(dāng)前編碼的長(zhǎng)度
uint64_t enclen;
//如果ele為null說明需要?jiǎng)h除 而此處的刪除是將當(dāng)前p占用的字節(jié)替換為空字節(jié)
if (ele == NULL) where = LP_REPLACE;
//如果是在之后插入 則將其轉(zhuǎn)換為向前插入 這樣做少些邏輯代碼 值得學(xué)習(xí)
if (where == LP_AFTER) {
//首先跳轉(zhuǎn)到下一個(gè)節(jié)點(diǎn)
p = lpSkip(p);
//設(shè)置where為before
where = LP_BEFORE;
}
//計(jì)算當(dāng)前p 在lp的偏移值
unsigned long poff = p-lp;
//根據(jù)ele 計(jì)算需要的編碼類型 共 9中類型
//1、 7 位的無符號(hào)整型 加上內(nèi)容共占 1個(gè)字節(jié) 結(jié)構(gòu)為 0xxx xxxx x是存儲(chǔ)數(shù)據(jù)的地方 0標(biāo)示位
//2、 63長(zhǎng)度以下的字符串 結(jié)構(gòu)為 10LL LLLL L是存儲(chǔ)字符串長(zhǎng)度的地方 10 標(biāo)識(shí)位 后續(xù)便是內(nèi)容
//3、 13位的有符號(hào)整型 加上內(nèi)容共占 2個(gè)字節(jié) 結(jié)構(gòu)為 110x xxxx xxxx xxxx x是存儲(chǔ)數(shù)據(jù)的地方 110 標(biāo)示位
//4、 4095長(zhǎng)度以下 63 長(zhǎng)度以上的字符串 結(jié)構(gòu)為 1110 LLLL LLLL LLLL L存儲(chǔ)字符串長(zhǎng)度的地方 1110 標(biāo)識(shí)位 后續(xù)便是L計(jì)算出長(zhǎng)度大小的內(nèi)容
//5、 4294967295 長(zhǎng)度以下 4095 長(zhǎng)度以上的字符串 結(jié)構(gòu)為 1111 0000 后續(xù)四個(gè)字節(jié)存儲(chǔ) 字符串長(zhǎng)度 1111 0000 標(biāo)示位
//6、 16位整型 結(jié)構(gòu)為 1111 0001 也是為標(biāo)示位 后續(xù)兩個(gè)字節(jié)為 數(shù)據(jù)值 共 3個(gè)字節(jié)大小
//7、 24位整型 結(jié)構(gòu)為 1111 0010 也是為標(biāo)示位 后續(xù)三個(gè)字節(jié)為 數(shù)據(jù)值 共 4個(gè)字節(jié)大小
//8、 32位整型 結(jié)構(gòu)為 1111 0100 也是為標(biāo)示位 后續(xù)四個(gè)字節(jié)為 數(shù)據(jù)值 共 5個(gè)字節(jié)大小
//9、 64位整型 結(jié)構(gòu)為 1111 1000 也是為標(biāo)示位 后續(xù)八個(gè)字節(jié)為 數(shù)據(jù)值 共 9個(gè)字節(jié)大小 然而也是已知占用最多的類型所以intenc為9
int enctype;
if (ele) {
//便是使用上方的規(guī)則計(jì)算當(dāng)前ele的類型編碼 存儲(chǔ)到 intenc中 在根據(jù)編碼的類型和數(shù)據(jù)獲取對(duì)應(yīng)的長(zhǎng)度 enclen
enctype = lpEncodeGetType(ele,size,intenc,&enclen);
} else {
//如果為NULL 則設(shè)置type為-1 長(zhǎng)度為 0
enctype = -1;
enclen = 0;
}
//計(jì)算當(dāng)前backlen需要占用的大小 根據(jù)enclen計(jì)算 規(guī)則如下
//1、enclen <= 127 占用 1 個(gè)字節(jié)
//2、enclen < 16383 占用 2 個(gè)字節(jié)
//3、enclen < 2097151 占用 3 個(gè)字節(jié)
//4、enclen < 268435455 占用4個(gè)字節(jié)
//5、如果不滿足上方的條件則占用五個(gè)字節(jié) 并且將enclen 寫入到backlen中
unsigned long backlen_size = ele ? lpEncodeBacklen(backlen,enclen) : 0;
//獲取當(dāng)前的字節(jié)數(shù)
uint64_t old_listpack_bytes = lpGetTotalBytes(lp);
//如果是替換則計(jì)算需要需要替換的字節(jié)數(shù) 即 encoding size + content size + backlen size 便是一個(gè)entry的總共大小
uint32_t replaced_len = 0;
if (where == LP_REPLACE) {
//解析當(dāng)前p的 encoding size + content size
replaced_len = lpCurrentEncodedSize(p);
//根據(jù)獲取到len 計(jì)算下backlen占用的size
replaced_len += lpEncodeBacklen(NULL,replaced_len);
}
//計(jì)算一下最新listpack需要的字節(jié)數(shù) 當(dāng)前字節(jié)數(shù) + 新ele的字節(jié)數(shù) - 需要替換元素的字節(jié)數(shù)
uint64_t new_listpack_bytes = old_listpack_bytes + enclen + backlen_size
- replaced_len;
//獲取到最新的字節(jié)數(shù)后 判斷當(dāng)前字節(jié)數(shù)是否超過了限制 即 4294967295 因?yàn)閘istpack的長(zhǎng)度描述 total bytes 是四個(gè)字節(jié) 無符號(hào)的最大數(shù)便是4294967295
//如果超過那么不在執(zhí)行 返回NULL 因?yàn)槌绦虺鲥e(cuò)
if (new_listpack_bytes > UINT32_MAX) return NULL;
//獲取需要被替換的目標(biāo)地址
unsigned char *dst = lp + poff;
//如果上方計(jì)算的最新大小超過了原有的大小
if (new_listpack_bytes > old_listpack_bytes) {
//那么遍進(jìn)行realloc 重新分配內(nèi)存空間
if ((lp = lp_realloc(lp,new_listpack_bytes)) == NULL) return NULL;
//分配完成后再次計(jì)算需要操作的目標(biāo)位置
dst = lp + poff;
}
//判斷當(dāng)前位置 因?yàn)橹皩fter修改為了before所以此處只有 before和replace
if (where == LP_BEFORE) {
//如果是before操作 那么此處將 dst 位置的數(shù)據(jù) 移動(dòng)到 dst+enclen+backlen_size的位置
//這樣做變空出了當(dāng)前ele大小的位置方便后續(xù)操作
memmove(dst+enclen+backlen_size,dst,old_listpack_bytes-poff);
} else {
//如果是替換 那么計(jì)算當(dāng)前元素大小和 原先元素大小的差 如果是正數(shù) 則往后移 如果是負(fù)數(shù)則向前移
long lendiff = (enclen+backlen_size)-replaced_len;
//dst + replacelen 便是 當(dāng)前元素的下一個(gè)元素地址 然后在加上上方計(jì)算的差 這樣便計(jì)算出 下一個(gè)元素需要位移的具體位置
//然后設(shè)置需要位移的元素首地址 再次計(jì)算此元素的長(zhǎng)度 總長(zhǎng)度-當(dāng)前替換元素的偏移-當(dāng)前替換元素的大小 便是后面所有元素的占用大小
memmove(dst+replaced_len+lendiff,
dst+replaced_len,
old_listpack_bytes-poff-replaced_len);
}
//如果當(dāng)前l(fā)istpack的大小小于了原先的大小 則需要丟棄后面空出來的字節(jié)
//因?yàn)樯戏揭呀?jīng)進(jìn)行了插入/替換的操作所以此處尾部的字節(jié)數(shù) 必然是多余的
if (new_listpack_bytes < old_listpack_bytes) {
if ((lp = lp_realloc(lp,new_listpack_bytes)) == NULL) return NULL;
//根據(jù)偏移 再次計(jì)算當(dāng)前元素的首地址 因?yàn)樯戏浇?jīng)過了插入 和 替換所以此處指針一定是新元素的地址
dst = lp + poff;
}
//如果需要回傳新元素的地址 則設(shè)置為dst
if (newp) {
*newp = dst;
//如果是刪除操作并且當(dāng)前刪除的元素是最后一個(gè) 那么 不存在新元素所以為NULL
if (!ele && dst[0] == LP_EOF) *newp = NULL;
}
//如果ele不為空 要么是替換 要么是 添加 但是之前都是對(duì)原先內(nèi)存的移動(dòng)并沒有賦值
//所以此處是將新元素的具體數(shù)據(jù)賦值到內(nèi)存中
if (ele) {
//如果是整型 那么直接copy即可
if (enctype == LP_ENCODING_INT) {
memcpy(dst,intenc,enclen);
} else {
//否則需要將字符串編碼為指定的encoding 然后在復(fù)制
//此方法的操作是根據(jù)size編碼為上方的對(duì)應(yīng)類型然后memcpy拷貝到dst中
lpEncodeString(dst,ele,size);
}
//將dst偏移指定的長(zhǎng)度
dst += enclen;
//將backlen也拷貝進(jìn)dst中
memcpy(dst,backlen,backlen_size);
//dst back的長(zhǎng)度
dst += backlen_size;
}
//如果當(dāng)前操作類型不是replace 或者 ele == null
//都是需要對(duì)元素個(gè)數(shù)進(jìn)行操作的 因?yàn)椴皇莚eplace說明是插入 需要 ++ 如果ele為null 說明是刪除需要 --
if (where != LP_REPLACE || ele == NULL) {
//獲取當(dāng)前元素個(gè)數(shù)
uint32_t num_elements = lpGetNumElements(lp);
//如果當(dāng)前長(zhǎng)度不是65535 即 short的最大值 無符號(hào) 那么就對(duì)num進(jìn)行設(shè)置
if (num_elements != LP_HDR_NUMELE_UNKNOWN) {
if (ele)
//如果存在ele說明是添加則 +1
lpSetNumElements(lp,num_elements+1);
else
//如果不存在說明是刪除則 --
lpSetNumElements(lp,num_elements-1);
}
}
//將當(dāng)前最新的bytes大小設(shè)置到lp中
lpSetTotalBytes(lp,new_listpack_bytes);
//到此處可以總結(jié) 下newp 如果是插入或者替換 那么此值為當(dāng)前p的偏移首地址 如果是刪除則為當(dāng)前p的下一個(gè)元素的地址
return lp;
}
//獲取當(dāng)前l(fā)istpack的第一個(gè)元素
unsigned char *lpFirst(unsigned char *lp) {
//在listpack的首地址基礎(chǔ)上跳過頭部 便是第一個(gè)元素的首地址
lp += LP_HDR_SIZE;
//如果首地址的值是0xff 那么說明list為空 直接返回null
if (lp[0] == LP_EOF) return NULL;
//否則返回首地址
return lp;
}
//獲取當(dāng)前l(fā)istpack的最后一個(gè)元素
unsigned char *lpLast(unsigned char *lp) {
//根據(jù)當(dāng)前l(fā)istpack的首地址 加上 當(dāng)前l(fā)istpack的總字節(jié)數(shù) 便超過了當(dāng)前l(fā)istpack的內(nèi)存空間 進(jìn)行-1 回到EOF 0xff的地址
unsigned char *p = lp+lpGetTotalBytes(lp)-1;
//根據(jù)最后一個(gè)字節(jié)向前根據(jù)backlen算出前一個(gè)字節(jié)的長(zhǎng)度 根據(jù)長(zhǎng)度進(jìn)行 p - backlen 便是最后一個(gè)元素的首地址
return lpPrev(lp,p);
}
//根據(jù)當(dāng)前元素的首地址 計(jì)算前一個(gè)元素的首地址
unsigned char *lpPrev(unsigned char *lp, unsigned char *p) {
//如果當(dāng)前元素的地址 減去 listpack的地址 等于6 說明當(dāng)前l(fā)p為空 則不進(jìn)行計(jì)算
if (p-lp == LP_HDR_SIZE) return NULL;
//不為空則跳過當(dāng)前元素的首地址 到達(dá)上一個(gè)元素的backlen的位置
p--;
//根據(jù)backlen向前計(jì)算 獲取到元素的長(zhǎng)度
uint64_t prevlen = lpDecodeBacklen(p);
//當(dāng)前獲取到的元素長(zhǎng)度是編碼的長(zhǎng)度+數(shù)據(jù)長(zhǎng)度 還缺少了backlen的長(zhǎng)度所以此處根據(jù)上方解析出的backlen在計(jì)算下backlen的長(zhǎng)度
//從而獲取到當(dāng)前p 到上一個(gè)元素長(zhǎng)度
prevlen += lpEncodeBacklen(NULL,prevlen);
//但是之前為了計(jì)算backlen所以p進(jìn)行了--已經(jīng)不是原來的那個(gè)位置 但是我們計(jì)算出的長(zhǎng)度是根據(jù)原先的位置計(jì)算的
//所以此處需要+1 才是上一個(gè)元素的真正位置
return p-prevlen+1;
}
//如果需要獲取下一個(gè)元素那么必須需要當(dāng)前的元素
//lp listpack的指針地址 p 需要查詢下個(gè)元素的指針地址
unsigned char *lpNext(unsigned char *lp, unsigned char *p) {
//此處暫時(shí)沒有使用到了lp 但是在編譯是會(huì)提示未使用 所以此處使用此方法欺騙編譯器
((void) lp);
//next使用的是skip跳過當(dāng)前p
p = lpSkip(p);
//如果跳過的結(jié)果是EOF 結(jié)束符 那么返回null 否則返回跳過結(jié)果
if (p[0] == LP_EOF) return NULL;
return p;
}
//跳過當(dāng)前p 獲取到下一個(gè)元素的地址
unsigned char *lpSkip(unsigned char *p) {
//首先計(jì)算當(dāng)前元素占用的長(zhǎng)度
unsigned long entrylen = lpCurrentEncodedSize(p);
//根據(jù)長(zhǎng)度計(jì)算出back的長(zhǎng)度
entrylen += lpEncodeBacklen(NULL,entrylen);
//根據(jù)計(jì)算出的 backlen的長(zhǎng)度 + entrylen的長(zhǎng)度便是下一個(gè)元素的長(zhǎng)度
//所以此處p加上上方的計(jì)算結(jié)果便可
p += entrylen;
return p;
}
//獲取當(dāng)前元素的數(shù)據(jù)
// p 為元素的首地址 count 如果元素存儲(chǔ)的是整型并且復(fù)合之前編碼的 那么count為返回值 但是此處返回值優(yōu)勢(shì)char* 所以需要一個(gè)參數(shù)做為結(jié)果
//inbuff如果想獲取到當(dāng)前整型的buff數(shù)據(jù) 那么此值為返回值 如果需要inbuff 則count語義改成為寫入到inbuff的字節(jié)個(gè)數(shù)
unsigned char *lpGet(unsigned char *p, int64_t *count, unsigned char *intbuf) {
//如果為整型 此處便是計(jì)算 count的臨時(shí)變量
int64_t val;
//uval 根據(jù)char計(jì)算出的值 negstart存儲(chǔ)的值是否為負(fù)數(shù)根據(jù)此值進(jìn)行判別
//negmax 如果是負(fù)數(shù) 需要此值將當(dāng)前正數(shù)計(jì)算為負(fù)數(shù)
uint64_t uval, negstart, negmax;
//如果當(dāng)前是無符號(hào)整型 那么直接計(jì)算uval即可
if (LP_ENCODING_IS_7BIT_UINT(p[0])) {
negstart = UINT64_MAX;
negmax = 0;
//當(dāng)前char & 一個(gè)0x7f 便是具體值
uval = p[0] & 0x7f;
//如果當(dāng)前是六位的字符串 那么此處 p +1 就是數(shù)據(jù)體 因?yàn)閑ncoding站1個(gè)字節(jié) 之前編碼有詳細(xì)說明過
} else if (LP_ENCODING_IS_6BIT_STR(p[0])) {
*count = LP_ENCODING_6BIT_STR_LEN(p);
return p+1;
//如果當(dāng)前是13位的有符號(hào)整型
} else if (LP_ENCODING_IS_13BIT_INT(p[0])) {
//根據(jù)&0x1f 獲取到高位值 在位移 8位 設(shè)置為高位數(shù)據(jù) 然后 或 | p1 低位值
uval = ((p[0]&0x1f)<<8) | p[1];
//他的最大值不能超過的 4096 如果超過那么便是負(fù)數(shù)
negstart = (uint64_t)1<<12;
//計(jì)算負(fù)數(shù)的基數(shù) 基數(shù)減去 uval -1 便是最終的結(jié)果
negmax = 8191;
//與13同理
} else if (LP_ENCODING_IS_16BIT_INT(p[0])) {
uval = (uint64_t)p[1] |
(uint64_t)p[2]<<8;
negstart = (uint64_t)1<<15;
negmax = UINT16_MAX;
//如果13同理
} else if (LP_ENCODING_IS_24BIT_INT(p[0])) {
uval = (uint64_t)p[1] |
(uint64_t)p[2]<<8 |
(uint64_t)p[3]<<16;
negstart = (uint64_t)1<<23;
negmax = UINT32_MAX>>8;
//與13同理
} else if (LP_ENCODING_IS_32BIT_INT(p[0])) {
uval = (uint64_t)p[1] |
(uint64_t)p[2]<<8 |
(uint64_t)p[3]<<16 |
(uint64_t)p[4]<<24;
negstart = (uint64_t)1<<31;
negmax = UINT32_MAX;
//與13同理
} else if (LP_ENCODING_IS_64BIT_INT(p[0])) {
uval = (uint64_t)p[1] |
(uint64_t)p[2]<<8 |
(uint64_t)p[3]<<16 |
(uint64_t)p[4]<<24 |
(uint64_t)p[5]<<32 |
(uint64_t)p[6]<<40 |
(uint64_t)p[7]<<48 |
(uint64_t)p[8]<<56;
negstart = (uint64_t)1<<63;
negmax = UINT64_MAX;
//如果是12位的字符串 那么需要p+2 encoding長(zhǎng)度為 2
} else if (LP_ENCODING_IS_12BIT_STR(p[0])) {
*count = LP_ENCODING_12BIT_STR_LEN(p);
return p+2;
//如果是32位的字符串 那么需要p+5 encoding長(zhǎng)度為 5
} else if (LP_ENCODING_IS_32BIT_STR(p[0])) {
*count = LP_ENCODING_32BIT_STR_LEN(p);
return p+5;
} else {
//如果都不是那么設(shè)置默認(rèn)值 方便調(diào)試
uval = 12345678900000000ULL + p[0];
negstart = UINT64_MAX;
negmax = 0;
}
//如果當(dāng)前值超過了限定的值 則說明 到達(dá)了負(fù)數(shù)
if (uval >= negstart) {
//之前設(shè)置的基數(shù) 減去當(dāng)前值
uval = negmax-uval;
val = uval;
//取反-1 就是當(dāng)時(shí)存儲(chǔ)的具體值
val = -val-1;
} else {
//否則將讀出的數(shù)據(jù)存儲(chǔ)到val中
val = uval;
}
//如果需要字符串的buff那么snpringf存儲(chǔ)進(jìn)去
if (intbuf) {
//此時(shí)count為寫入到inbuff中的字節(jié)長(zhǎng)度
*count = snprintf((char*)intbuf,LP_INTBUF_SIZE,"%lld",(long long)val);
//然后返回intbuff
return intbuf;
} else {
//否則設(shè)置count 此為具體value的值
*count = val;
return NULL;
}
}
//追加元素 其實(shí)就是添加最后一個(gè)元素
//lp listpack 指針 ele 當(dāng)前元素的指針 size 當(dāng)前元素的大小
unsigned char *lpAppend(unsigned char *lp, unsigned char *ele, uint32_t size) {
//首先獲取當(dāng)前元素的占用字節(jié)數(shù)
uint64_t listpack_bytes = lpGetTotalBytes(lp);
//當(dāng)前l(fā)p首地址加上字節(jié)數(shù) 減去 1 就是 eof的地址
unsigned char *eofptr = lp + listpack_bytes - 1;
//只要在調(diào)用insert的時(shí)候 指定位before 設(shè)置需要插入的元素為eof便為append
return lpInsert(lp,ele,size,eofptr,LP_BEFORE,NULL);
}
//元素的刪除 之前在insert中講解過 lp listpack 指針 p 元素指針 newp如果需要?jiǎng)h除元素的下一個(gè)指針則不為空
unsigned char *lpDelete(unsigned char *lp, unsigned char *p, unsigned char **newp) {
//設(shè)置插入值為NULL 類型為REPLACE 便是刪除
return lpInsert(lp,NULL,0,p,LP_REPLACE,newp);
}