예제 #1
0
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");
}
예제 #2
0
파일: zipmap.c 프로젝트: ericbbcc/redis
/* 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;

    // 遍历zipmap
    while(*p != ZIPMAP_END) {
        unsigned char free;
        // 返回len表示的长度
        /* Match or skip the key */
        l = zipmapDecodeLength(p);
        // 返回要编码l表示的长度需要的字节数,1还是5字节
        llen = zipmapEncodeLength(NULL,l);
        // 看看Key是不是相等
        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;
            }
        }
        // p到达下一个元素的位置
        p += llen+l;
        /* 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 */
    }
    // 返回zm的总字节长度
    if (totlen != NULL) *totlen = (unsigned int)(p-zm)+1;
    return k;
}
예제 #3
0
/* 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;
            }
        }
        p += llen+l;
        /* 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;
    return k;
}
/* Search for a matching key, returning a pointer to the entry inside the
 * zipmap. Returns NULL if the key is not found.
 *
 * 在 zipmap 中查找和给定 key 匹配的节点:
 *
 *  1)找到的话就返回节点的指针。
 *
 *  2)没找到则返回 NULL 。
 *
 * 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. 
 *
 * 如果没有找到相应的节点(函数返回 NULL),并且 totlen 不为 NULL ,
 * 那么 *totlen 的值将被设为整个 zipmap 的大小,
 * 这样调用者就可以根据 *totlen 的值,对 zipmap 进行内存重分配,
 * 从而让 zipmap 容纳更多节点。
 *
 * T = O(N^2)
 */
static unsigned char *zipmapLookupRaw(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned int *totlen) {

    // zm+1 略过 <zmlen> 属性,将 p 指向 zipmap 的首个节点
    unsigned char *p = zm+1, *k = NULL;
    unsigned int l,llen;

    // 遍历整个 zipmap 来寻找
    // T = O(N^2)
    while(*p != ZIPMAP_END) {
        unsigned char free;

        /* Match or skip the key */
        // 计算键的长度
        l = zipmapDecodeLength(p);
        // 计算编码键的长度所需的字节数
        llen = zipmapEncodeLength(NULL,l);
        // 对比 key
        // T = O(N)
        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) {
                // 如果调用者需要知道整个 zipmap 的长度,那么记录找到的指针到变量 k
                // 之后遍历时,程序只计算 zipmap 剩余节点的长度,不再用 memcmp 进行对比
                // 因为 k 已经不为 NULL 了
                k = p;
            } else {
                // 如果调用者不需要知道整个 zipmap 的长度,那么直接返回 p 
                return p;
            }
        }

        // 越过键节点,指向值节点
        p += llen+l;

        /* Skip the value as well */
        // 计算值的长度
        l = zipmapDecodeLength(p);
        // 计算编码值的长度所需的字节数,
        // 并移动指针 p ,越过该 <len> 属性,指向 <free> 属性
        p += zipmapEncodeLength(NULL,l);
        // 取出 <free> 属性的值
        free = p[0];
        // 略过值节点,指向下一节点
        p += l+1+free; /* +1 to skip the free byte */
    }

    // 计算并记录 zipmap 的空间长度
    // + 1 是将 ZIPMAP_END 也计算在内
    if (totlen != NULL) *totlen = (unsigned int)(p-zm)+1;

    // 返回找到 key 的指针
    return k;
}
예제 #5
0
/* 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);
    used += p[used] + 1 + l;
    return used;
}
예제 #6
0
/* 获取value节点占用的字节数,即len字段 + 1个字节free字段 + value字符串长度 + 空闲空间大小 */
static unsigned int zipmapRawValueLength(unsigned char *p) {
    // 获取value字符串的长度
    unsigned int l = zipmapDecodeLength(p);
    unsigned int used;

    // 获取保存value字符串长度所需要的字节数
    used = zipmapEncodeLength(NULL,l);
    // p[used]里面存储着空闲空间的大小
    used += p[used] + 1 + l;
    return used;
}
예제 #7
0
 /* 按关键字key查找zipmap,如果totlen不为NULL,函数返回后存放zipmap占用的字节数 */
static unsigned char *zipmapLookupRaw(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned int *totlen) {
    // zipmap中第1个字节是zmlen字段,zm+1跳过第1个字节
    unsigned char *p = zm+1, *k = NULL;
    unsigned int l,llen;

    // 从前往后查找
    while(*p != ZIPMAP_END) {
        unsigned char free;

        /* Match or skip the key */
        // 确定key字符串的长度
        l = zipmapDecodeLength(p);
        // 确定保存key字符串长度所需要的字节数,也就是len字段所需要的字节数
        llen = zipmapEncodeLength(NULL,l);
        // 比较当前key与给定key是否匹配
        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. */
            // 如果totlen为NULL,表示函数调用者不关心zipmap占用的字节数,此时直接返回p,否则先记录下p指针然后继续遍历
            if (totlen != NULL) {
                k = p;
            } else {
                return p;
            }
        }
        // p加上llen和l,到了value节点处
        p += llen+l;
        /* Skip the value as well */
        // 确定value字符串的长度
        l = zipmapDecodeLength(p);
        // 确定保存value字符串长度所需要的字节数,也就是len字段所需要的字节数
        p += zipmapEncodeLength(NULL,l);
        // 读出free字段的值(前面我们讲过:free只占用一个字节)
        free = p[0];
        // 跳到下一个key节点的
        p += l+1+free; /* +1 to skip the free byte */
    }
    // 到这里遍历完整个zipmap,得到其占用的字节数
    if (totlen != NULL) *totlen = (unsigned int)(p-zm)+1;
    return k;
}
/* Return the total amount used by a value
 * (encoded length + single byte free count + payload) 
 *
 * 返回值所占用的字节总数
 *
 * 包括编码长度值所需的字节数,单个字节的 <free> 属性,以及值的长度本身
 *
 * T = O(1)
 */
static unsigned int zipmapRawValueLength(unsigned char *p) {

    // 取出值的长度
    unsigned int l = zipmapDecodeLength(p);
    unsigned int used;
    
    // 编码长度所需的字节数
    used = zipmapEncodeLength(NULL,l);
    // 计算总和
    used += p[used] + 1 + l;

    return used;
}
예제 #9
0
파일: zipmap.c 프로젝트: mallipeddi/redis
/* 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.
 *
 * If NULL is returned, and freeoff and freelen are not NULL, they are set
 * to the offset of the first empty space that can hold '*freelen' bytes
 * (freelen is an integer pointer used both to signal the required length
 * and to get the reply from the function). If there is not a suitable
 * free space block to hold the requested bytes, *freelen is set to 0. */
static unsigned char *zipmapLookupRaw(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned int *totlen, unsigned int *freeoff, unsigned int *freelen) {
    unsigned char *p = zm+1;
    unsigned int l;
    unsigned int reqfreelen = 0; /* initialized just to prevent warning */

    if (freelen) {
        reqfreelen = *freelen;
        *freelen = 0;
        assert(reqfreelen != 0);
    }
    while(*p != ZIPMAP_END) {
        if (*p == ZIPMAP_EMPTY) {
            l = zipmapDecodeLength(p+1);
            /* if the user want a free space report, and this space is
             * enough, and we did't already found a suitable space... */
            if (freelen && l >= reqfreelen && *freelen == 0) {
                *freelen = l;
                *freeoff = p-zm;
            }
            p += l;
            zm[0] |= ZIPMAP_STATUS_FRAGMENTED;
        } else {
            unsigned char free;

            /* Match or skip the key */
            l = zipmapDecodeLength(p);
            if (l == klen && !memcmp(p+1,key,l)) return p;
            p += zipmapEncodeLength(NULL,l) + l;
            /* 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;
    return NULL;
}
예제 #10
0
파일: zipmap.c 프로젝트: mallipeddi/redis
/* 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 char *p = zipmapLookupRaw(zm,key,klen,NULL,NULL,NULL);
    if (p) {
        unsigned int freelen = zipmapRawEntryLength(p);

        p[0] = ZIPMAP_EMPTY;
        zipmapEncodeLength(p+1,freelen);
        zm[0] |= ZIPMAP_STATUS_FRAGMENTED;
        if (deleted) *deleted = 1;
    } else {
        if (deleted) *deleted = 0;
    }
    return zm;
}
예제 #11
0
파일: zipmap.c 프로젝트: mallipeddi/redis
void zipmapRepr(unsigned char *p) {
    unsigned int l;

    printf("{status %u}",*p++);
    while(1) {
        if (p[0] == ZIPMAP_END) {
            printf("{end}");
            break;
        } else if (p[0] == ZIPMAP_EMPTY) {
            l = zipmapDecodeLength(p+1);
            printf("{%u empty block}", l);
            p += l;
        } else {
            unsigned char e;

            l = zipmapDecodeLength(p);
            printf("{key %u}",l);
            p += zipmapEncodeLength(NULL,l);
            fwrite(p,l,1,stdout);
            p += l;

            l = zipmapDecodeLength(p);
            printf("{value %u}",l);
            p += zipmapEncodeLength(NULL,l);
            e = *p++;
            fwrite(p,l,1,stdout);
            p += l+e;
            if (e) {
                printf("[");
                while(e--) printf(".");
                printf("]");
            }
        }
    }
    printf("\n");
}
예제 #12
0
/* 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]++;
    } 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. */
            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. */
    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 {
        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;
}
예제 #13
0
/* 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;
}
예제 #14
0
/* 根据key设置value,如果key不存在则创建相应的键值对,参数update用来辨别更新操作和添加操作。 */
unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update) {
    unsigned int zmlen, offset;
    // 计算存储key和value所需要的字节数
    unsigned int freelen, reqlen = zipmapRequiredLength(klen,vlen);
    unsigned int empty, vempty;
    unsigned char *p;

    /************************************************************************
     *  下面这段代码用于在zipmap留出足够的空间来容纳新插入的键值对或新的value值,尚未写入
     ************************************************************************/
    freelen = reqlen;
    if (update) *update = 0;
    // 在zipmap中查找key,函数返回后zmlen中保存了zipmap所占用的字节数。
    p = zipmapLookupRaw(zm,key,klen,&zmlen);
    if (p == NULL) {
        /* Key not found: enlarge */
        // 如果key指定的键值对不存在,则对zipmap扩容,为容纳新的键值对准备内存空间
        // zipmapResize执行的是realloc操作
        zm = zipmapResize(zm, zmlen+reqlen);
        // 此时p指向扩容前zipmap的结尾符,将从这里添加新的键值对
        p = zm+zmlen-1;
        // 更新zipmap所占用的内存空间大小
        zmlen = zmlen+reqlen;

        /* Increase zipmap length (this is an insert) */
        // 更新zipmap中保存的键值对数量,即zmlen字段
        if (zm[0] < ZIPMAP_BIGLEN) zm[0]++;

    } else {
        /* Key found. Is there enough space for the new value? */
        /* 找到可对应的键值对,执行更新操作。这里需要考虑value节点的空间大小是否能够容纳新值 */
        /* Compute the total length: */
        if (update) *update = 1;
        // 求出旧value节点的空间大小
        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. */
             // 旧节点的空间太小,需要扩容操作,zipmapResize函数会重新分配空间,所以需要记录p指针的偏移量
            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. */
            // 移动旧value节点以后的元素以确保有足够的空间容纳新值( +1是将尾部结尾符一起移动)
            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. */
    // freelen表示经上步骤后流出来的空余空间大小,reqlen表示插入或更新键值对所需要的空间,两者的差就是free字段的
    // 的值,如果该值过大zipmap会自动调整。下面这段代码就是完成调整功能。
    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 {
        vempty = empty;
    }

    /******************************************
     *  下面的操作是讲key和value写入zipmap指定位置
     *******************************************/
    /* Just write the key + value and we are done. */
    /* Key: */
    // 对key的长度编码并写入zipmap中
    p += zipmapEncodeLength(p,klen);
    // 写入key字符串
    memcpy(p,key,klen);
    // 移动指针到value写入位置
    p += klen;
    /* Value: */
    // 对value的长度编码并写入zipmap中
    p += zipmapEncodeLength(p,vlen);
    // 写入free字段
    *p++ = vempty;
    // 写入value
    memcpy(p,val,vlen);
    return zm;
}
예제 #15
0
/* 获取key节点占用的字节数,即len字段 + key字符串长度 */
static unsigned int zipmapRawKeyLength(unsigned char *p) {
    // 获取key字符串的长度
    unsigned int l = zipmapDecodeLength(p);
    // 加上保存key字符串长度所需要的字节数
    return zipmapEncodeLength(NULL,l) + l;
}
예제 #16
0
파일: zipmap.c 프로젝트: mallipeddi/redis
/* Set key to value, creating the key if it does not already exist. */
unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen) {
    unsigned int oldlen = 0, freeoff = 0, freelen;
    unsigned int reqlen = zipmapRequiredLength(klen,vlen);
    unsigned int empty, vempty;
    unsigned char *p;
   
    freelen = reqlen;
    p = zipmapLookupRaw(zm,key,klen,&oldlen,&freeoff,&freelen);
    if (p == NULL && freelen == 0) {
        printf("HERE oldlen:%u required:%u\n",oldlen,reqlen);
        /* Key not found, and not space for the new key. Enlarge */
        zm = zrealloc(zm,oldlen+reqlen);
        p = zm+oldlen-1;
        zm[oldlen+reqlen-1] = ZIPMAP_END;
        freelen = reqlen;
        printf("New total length is: %u\n", oldlen+reqlen);
    } else if (p == NULL) {
        /* Key not found, but there is enough free space. */
        p = zm+freeoff;
        /* note: freelen is already set in this case */
    } else {
        unsigned char *b = p;

        /* Key found. Is there enough space for the new value? */
        /* Compute the total length: */
        freelen = zipmapRawKeyLength(b);
        b += freelen;
        freelen += zipmapRawValueLength(b);
        if (freelen < reqlen) {
            /* Mark this entry as free and recurse */
            p[0] = ZIPMAP_EMPTY;
            zipmapEncodeLength(p+1,freelen);
            zm[0] |= ZIPMAP_STATUS_FRAGMENTED;
            return zipmapSet(zm,key,klen,val,vlen);
        }
    }

    /* Ok we have a suitable block where to write the new key/value
     * entry. */
    empty = freelen-reqlen;
    /* If there is too much free space mark it as a free block instead
     * of adding it as trailing empty space for the value, as we want
     * zipmaps to be very space efficient. */
    if (empty > ZIPMAP_VALUE_MAX_FREE) {
        unsigned char *e;

        e = p+reqlen;
        e[0] = ZIPMAP_EMPTY;
        zipmapEncodeLength(e+1,empty);
        vempty = 0;
        zm[0] |= ZIPMAP_STATUS_FRAGMENTED;
    } else {
        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;
}
/* Set key to value, creating the key if it does not already exist.
 *
 * 将 key 的值设置为 value ,如果 key 不存在于 zipmap 中,那么新创建一个。
 *
 * If 'update' is not NULL, *update is set to 1 if the key was
 * already preset, otherwise to 0. 
 *
 * 如果 update 不为 NULL :
 *
 *  1) 那么在 key 已经存在时,将 *update 设为 1 。
 *
 *  2) 如果 key 未存在,将 *update 设为 0 。
 *
 * T = O(N^2)
 */
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;
    // 按 key 在 zipmap 中查找节点
    // T = O(N^2)
    p = zipmapLookupRaw(zm,key,klen,&zmlen);
    if (p == NULL) {

        /* Key not found: enlarge */
        // key 不存在,扩展 zipmap

        // T = O(N)
        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]++;
    } else {

        /* Key found. Is there enough space for the new value? */
        /* Compute the total length: */
        // 键已经存在,检查旧的值空间大小能否满足新值
        // 如果不满足的话,扩展 zipmap 并移动数据

        if (update) *update = 1;
        // T = O(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. */
            // 如果已有空间不满足新值所需空间,那么对 zipmap 进行扩展
            // T = O(N)
            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. */
            // 向后移动数据,为节点空出足以存放新值的空间
            // T = O(N)
            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. */
    // 计算节点空余空间的长度,如果空余空间太大了,就进行缩短
    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;
        // 前移数据,覆盖空余空间
        // T = O(N)
        memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));
        zmlen -= empty;
        // 缩小 zipmap ,移除多余的空间
        // T = O(N)
        zm = zipmapResize(zm, zmlen);
        p = zm+offset;
        vempty = 0;
    } else {
        vempty = empty;
    }

    /* Just write the key + value and we are done. */

    /* Key: */
    // 写入键
    // T = O(N)
    p += zipmapEncodeLength(p,klen);
    memcpy(p,key,klen);
    p += klen;

    /* Value: */
    // 写入值
    // T = O(N)
    p += zipmapEncodeLength(p,vlen);
    *p++ = vempty;
    memcpy(p,val,vlen);

    return zm;
}