staticunsignedchar *__ziplistInsert(unsignedchar *zl, unsignedchar *p, unsignedchar *s, unsignedint slen) { // 记录当前 ziplist 的长度 size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen, prevlen = 0; size_t offset; int nextdiff = 0; unsignedchar encoding = 0; longlong value = 123456789; /* initialized to avoid warning. Using a value that is easy to see if for some reason we use it uninitialized. */ zlentry entry, tail;
/* Find out prevlen for the entry that is inserted. */ if (p[0] != ZIP_END) { // 如果 p[0] 不指向列表末端,说明列表非空,并且 p 正指向列表的其中一个节点 // 那么取出 p 所指向节点的信息,并将它保存到 entry 结构中 // 然后用 prevlen 变量记录前置节点的长度 // (当插入新节点之后 p 所指向的节点就成了新节点的前置节点) // T = O(1) entry = zipEntry(p); prevlen = entry.prevrawlen; } else { // 如果 p 指向表尾末端,那么程序需要检查列表是否为: // 1)如果 ptail 也指向 ZIP_END ,那么列表为空; // 2)如果列表不为空,那么 ptail 将指向列表的最后一个节点。 unsignedchar *ptail = ZIPLIST_ENTRY_TAIL(zl); if (ptail[0] != ZIP_END) { // 表尾节点为新节点的前置节点
/* See if the entry can be encoded */ // 尝试看能否将输入字符串转换为整数,如果成功的话: // 1)value 将保存转换后的整数值 // 2)encoding 则保存适用于 value 的编码方式 // 无论使用什么编码, reqlen 都保存节点值的长度 // T = O(N) if (zipTryEncoding(s,slen,&value,&encoding)) { /* 'encoding' is set to the appropriate integer encoding */ reqlen = zipIntSize(encoding); } else { /* 'encoding' is untouched, however zipEncodeLength will use the * string length to figure out how to encode it. */ reqlen = slen; } /* We need space for both the length of the previous entry and * the length of the payload. */ // 计算编码前置节点的长度所需的大小 // T = O(1) reqlen += zipPrevEncodeLength(NULL,prevlen); // 计算编码当前节点值所需的大小 // T = O(1) reqlen += zipEncodeLength(NULL,encoding,slen);
/* When the insert position is not equal to the tail, we need to * make sure that the next entry can hold this entry's length in * its prevlen field. */ // 只要新节点不是被添加到列表末端, // 那么程序就需要检查看 p 所指向的节点(的 header)能否编码新节点的长度。 // nextdiff 保存了新旧编码之间的字节大小差,如果这个值大于 0 // 那么说明需要对 p 所指向的节点(的 header )进行扩展 // T = O(1) nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
/* Store offset because a realloc may change the address of zl. */ // 因为重分配空间可能会改变 zl 的地址 // 所以在分配之前,需要记录 zl 到 p 的偏移量,然后在分配之后依靠偏移量还原 p offset = p-zl; // curlen 是 ziplist 原来的长度 // reqlen 是整个新节点的长度 // nextdiff 是新节点的后继节点扩展 header 的长度(要么 0 字节,要么 4 个字节) // T = O(N) zl = ziplistResize(zl,curlen+reqlen+nextdiff); p = zl+offset;
/* Apply memory move when necessary and update tail offset. */ if (p[0] != ZIP_END) { // 新元素之后还有节点,因为新元素的加入,需要对这些原有节点进行调整
/* Subtract one because of the ZIP_END bytes */ // 移动现有元素,为新元素的插入空间腾出位置 // T = O(N) memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);
/* Encode this entry's raw length in the next entry. */ // 将新节点的长度编码至后置节点 // p+reqlen 定位到后置节点 // reqlen 是新节点的长度 // T = O(1) zipPrevEncodeLength(p+reqlen,reqlen);
/* When the tail contains more than one entry, we need to take * "nextdiff" in account as well. Otherwise, a change in the * size of prevlen doesn't have an effect on the *tail* offset. */ // 如果新节点的后面有多于一个节点 // 那么程序需要将 nextdiff 记录的字节数也计算到表尾偏移中 // 这样才能让表尾偏移量正确对齐表尾节点 // T = O(1) tail = zipEntry(p+reqlen); if (p[reqlen+tail.headersize+tail.len] != ZIP_END) { ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff); } } else { /* This element will be the new tail. */ // 新元素是新的表尾节点 ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl); }
/* When nextdiff != 0, the raw length of the next entry has changed, so * we need to cascade the update throughout the ziplist */ // 当 nextdiff != 0 时,新节点的后继节点的(header 部分)长度已经被改变, // 所以需要级联地更新后续的节点 if (nextdiff != 0) { offset = p-zl; // T = O(N^2) zl = __ziplistCascadeUpdate(zl,p+reqlen); p = zl+offset; }
/* Write the entry */ // 一切搞定,将前置节点的长度写入新节点的 header p += zipPrevEncodeLength(p,prevlen); // 将节点值的长度写入新节点的 header p += zipEncodeLength(p,encoding,slen); // 写入节点值 if (ZIP_IS_STR(encoding)) { // T = O(N) memcpy(p,s,slen); } else { // T = O(1) zipSaveInteger(p,value,encoding); }
// 更新列表的节点数量计数器 // T = O(1) ZIPLIST_INCR_LENGTH(zl,1);
// 取出表尾节点的长度 // T = O(1) prevlen = zipRawEntryLength(ptail); }
接着尝试整数编码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* See if the entry can be encoded */ // 尝试看能否将输入字符串转换为整数,如果成功的话: // 1)value 将保存转换后的整数值 // 2)encoding 则保存适用于 value 的编码方式 // 无论使用什么编码, reqlen 都保存节点值的长度 // T = O(N) if (zipTryEncoding(s,slen,&value,&encoding)) { /* 'encoding' is set to the appropriate integer encoding */ reqlen = zipIntSize(encoding); } else { /* 'encoding' is untouched, however zipEncodeLength will use the * string length to figure out how to encode it. */ reqlen = slen; }
switch(encoding) { case ZIP_INT_8B: return1; case ZIP_INT_16B: return2; case ZIP_INT_24B: return3; case ZIP_INT_32B: return4; case ZIP_INT_64B: return8; default: return0; /* 4 bit immediate */ }
// 编码字符串 if (ZIP_IS_STR(encoding)) { /* Although encoding is given it may not be set for strings, * so we determine it here using the raw length. */ if (rawlen <= 0x3f) { // 1B if (!p) return len; buf[0] = ZIP_STR_06B | rawlen; } elseif (rawlen <= 0x3fff) { // 2B len += 1; if (!p) return len; buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f); buf[1] = rawlen & 0xff; } else { // 5B len += 4; if (!p) return len; buf[0] = ZIP_STR_32B; buf[1] = (rawlen >> 24) & 0xff; buf[2] = (rawlen >> 16) & 0xff; buf[3] = (rawlen >> 8) & 0xff; buf[4] = rawlen & 0xff; }
// 编码整数 } else { /* Implies integer encoding, so length is always 1. */ if (!p) return len; buf[0] = encoding; }
/* Store this length at p */ // 将编码后的长度写入 p memcpy(p,buf,len);
/* Find out prevlen for the entry that is inserted. */ if (p[0] != ZIP_END) { // 如果 p[0] 不指向列表末端,说明列表非空,并且 p 正指向列表的其中一个节点 // 那么取出 p 所指向节点的信息,并将它保存到 entry 结构中 // 然后用 prevlen 变量记录前置节点的长度 // (当插入新节点之后 p 所指向的节点就成了新节点的前置节点) // T = O(1) entry = zipEntry(p); prevlen = entry.prevrawlen; }
/* Return a struct with all information about an entry. * * 将 p 所指向的列表节点的信息全部保存到 zlentry 中,并返回该 zlentry 。 * * T = O(1) */ static zlentry zipEntry(unsignedchar *p) { zlentry e;
/* When the insert position is not equal to the tail, we need to * make sure that the next entry can hold this entry's length in * its prevlen field. */ // 只要新节点不是被添加到列表末端, // 那么程序就需要检查看 p 所指向的节点(的 header)能否编码新节点的长度。 // nextdiff 保存了新旧编码之间的字节大小差,如果这个值大于 0 // 那么说明需要对 p 所指向的节点(的 header )进行扩展 // T = O(1) nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
/* Subtract one because of the ZIP_END bytes */ // 移动现有元素,为新元素的插入空间腾出位置 // T = O(N) memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);
/* Encode this entry's raw length in the next entry. */ // 将新节点的长度编码至后置节点 // p+reqlen 定位到后置节点 // reqlen 是新节点的长度 // T = O(1) zipPrevEncodeLength(p+reqlen,reqlen);
/* When the tail contains more than one entry, we need to take * "nextdiff" in account as well. Otherwise, a change in the * size of prevlen doesn't have an effect on the *tail* offset. */ // 如果新节点的后面有多于一个节点 // 那么程序需要将 nextdiff 记录的字节数也计算到表尾偏移量中 // 这样才能让表尾偏移量正确对齐表尾节点 // T = O(1) tail = zipEntry(p+reqlen); if (p[reqlen+tail.headersize+tail.len] != ZIP_END) { ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff); }
/* When nextdiff != 0, the raw length of the next entry has changed, so * we need to cascade the update throughout the ziplist */ // 当 nextdiff != 0 时,新节点的后继节点的(header 部分)长度已经被改变, // 所以需要级联地更新后续的节点 if (nextdiff != 0) { offset = p-zl; // T = O(N^2) zl = __ziplistCascadeUpdate(zl,p+reqlen); p = zl+offset; }