/* 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. * * 从 zipmap 中删除包含给定 key 的节点。 * * 如果 deleted 参数不为 NULL ,那么: * * 1) 如果因为 key 没找到而导致删除失败,那么将 *deleted 设为 0 。 * * 2) 如果 key 找到了,并且成功将它删除了,那么将 *deleted 设为 1 。 * * T = O(N^2) */ unsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted) { unsigned int zmlen, freelen; // T = O(N^2) unsigned char *p = zipmapLookupRaw(zm,key,klen,&zmlen); if (p) { // 找到,进行删除 // 计算节点的总长 freelen = zipmapRawEntryLength(p); // 移动内存,覆盖被删除的数据 // T = O(N) memmove(p, p+freelen, zmlen-((p-zm)+freelen+1)); // 缩小 zipmap // T = O(N) zm = zipmapResize(zm, zmlen-freelen); /* Decrease zipmap length */ // 减少 zipmap 的节点数量 // 注意,如果节点数量已经大于等于 ZIPMAP_BIGLEN // 那么这里不会进行减少,只有在调用 zipmapLen 的时候 // 如果有需要的话,正确的节点数量才会被设置 // 具体请看 zipmapLen 的源码 if (zm[0] < ZIPMAP_BIGLEN) zm[0]--; if (deleted) *deleted = 1; } else { if (deleted) *deleted = 0; } return zm; }
/* 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; }
/* 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. * * 返回整个 zipmap 占用的字节大小 * * T = O(N) */ size_t zipmapBlobLen(unsigned char *zm) { unsigned int totlen; // 虽然 zipmapLookupRaw 一般情况下的复杂度为 O(N^2) // 但是当 key 参数为 NULL 时,无须使用 memcmp 来进行字符串对比 // zipmapLookupRaw 退化成一个单纯的计算长度的函数来使用 // 这种情况下, zipmapLookupRaw 的复杂度为 O(N) zipmapLookupRaw(zm,NULL,0,&totlen); return totlen; }
/* 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; }
/* 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]--; if (deleted) *deleted = 1; } else { if (deleted) *deleted = 0; } return zm; }
/* Search a key and retrieve the pointer and len of the associated value. * If the key is found the function returns 1, otherwise 0. * * 在 zipmap 中按 key 进行查找, * 将值的指针保存到 *value 中,并将值的长度保存到 *vlen 中。 * * 成功找到值时函数返回 1 ,没找到则返回 0 。 * * T = O(N^2) */ int zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen) { unsigned char *p; // 在 zipmap 中按 key 查找 // 没找到直接返回 0 // T = O(N^2) if ((p = zipmapLookupRaw(zm,key,klen,NULL)) == NULL) return 0; // 越过键,指向值 p += zipmapRawKeyLength(p); // 取出值的长度 *vlen = zipmapDecodeLength(p); // 将 *value 指向值, +1 为越过 <free> 属性 *value = p + ZIPMAP_LEN_BYTES(*vlen) + 1; // 找到,返回 1 return 1; }
/* 根据key删除指定的键值对 */ unsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted) { unsigned int zmlen, freelen; // 看判断该键值对是否在zipmap中,如果不存在则直接返回 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]--; if (deleted) *deleted = 1; } else { if (deleted) *deleted = 0; } return zm; }
/* 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]--; if (deleted) *deleted = 1; } else { if (deleted) *deleted = 0; } return zm; }
/* 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; }
/* 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; }
/* 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; }
/* 根据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; }
/* 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; }