zipmaps functions to get, iterate, test for existence. Initial works for Hash data type

This commit is contained in:
antirez 2010-03-05 14:04:17 +01:00
parent b1ad58ed2f
commit 5234952bee
4 changed files with 136 additions and 24 deletions

View File

@ -14,7 +14,7 @@ endif
CCOPT= $(CFLAGS) $(CCLINK) $(ARCH) $(PROF) CCOPT= $(CFLAGS) $(CCLINK) $(ARCH) $(PROF)
DEBUG?= -g -rdynamic -ggdb DEBUG?= -g -rdynamic -ggdb
OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o
BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o
@ -39,8 +39,9 @@ redis-benchmark.o: redis-benchmark.c fmacros.h ae.h anet.h sds.h adlist.h \
zmalloc.h zmalloc.h
redis-cli.o: redis-cli.c fmacros.h anet.h sds.h adlist.h zmalloc.h redis-cli.o: redis-cli.c fmacros.h anet.h sds.h adlist.h zmalloc.h
redis.o: redis.c fmacros.h config.h redis.h ae.h sds.h anet.h dict.h \ redis.o: redis.c fmacros.h config.h redis.h ae.h sds.h anet.h dict.h \
adlist.h zmalloc.h lzf.h pqsort.h staticsymbols.h adlist.h zmalloc.h lzf.h pqsort.h zipmap.h staticsymbols.h
sds.o: sds.c sds.h zmalloc.h sds.o: sds.c sds.h zmalloc.h
zipmap.o: zipmap.c zmalloc.h
zmalloc.o: zmalloc.c config.h zmalloc.o: zmalloc.c config.h
redis-server: $(OBJ) redis-server: $(OBJ)

31
redis.c
View File

@ -75,6 +75,7 @@
#include "zmalloc.h" /* total memory usage aware version of malloc/free */ #include "zmalloc.h" /* total memory usage aware version of malloc/free */
#include "lzf.h" /* LZF compression library */ #include "lzf.h" /* LZF compression library */
#include "pqsort.h" /* Partial qsort for SORT+LIMIT */ #include "pqsort.h" /* Partial qsort for SORT+LIMIT */
#include "zipmap.h"
/* Error codes */ /* Error codes */
#define REDIS_OK 0 #define REDIS_OK 0
@ -118,9 +119,13 @@
#define REDIS_ZSET 3 #define REDIS_ZSET 3
#define REDIS_HASH 4 #define REDIS_HASH 4
/* Objects encoding */ /* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object
* is set to one of this fields for this object. */
#define REDIS_ENCODING_RAW 0 /* Raw representation */ #define REDIS_ENCODING_RAW 0 /* Raw representation */
#define REDIS_ENCODING_INT 1 /* Encoded as integer */ #define REDIS_ENCODING_INT 1 /* Encoded as integer */
#define REDIS_ENCODING_ZIPMAP 2 /* Encoded as zipmap */
#define REDIS_ENCODING_HT 3 /* Encoded as an hash table */
/* Object types only used for dumping to disk */ /* Object types only used for dumping to disk */
#define REDIS_EXPIRETIME 253 #define REDIS_EXPIRETIME 253
@ -1011,7 +1016,7 @@ static dictType zsetDictType = {
}; };
/* Db->dict */ /* Db->dict */
static dictType hashDictType = { static dictType dbDictType = {
dictObjHash, /* hash function */ dictObjHash, /* hash function */
NULL, /* key dup */ NULL, /* key dup */
NULL, /* val dup */ NULL, /* val dup */
@ -1030,6 +1035,16 @@ static dictType keyptrDictType = {
NULL /* val destructor */ NULL /* val destructor */
}; };
/* Hash type hash table (note that small hashes are represented with zimpaps) */
static dictType hashDictType = {
dictEncObjHash, /* hash function */
NULL, /* key dup */
NULL, /* val dup */
dictEncObjKeyCompare, /* key compare */
dictRedisObjectDestructor, /* key destructor */
dictRedisObjectDestructor /* val destructor */
};
/* Keylist hash table type has unencoded redis objects as keys and /* Keylist hash table type has unencoded redis objects as keys and
* lists as values. It's used for blocking operations (BLPOP) and to * lists as values. It's used for blocking operations (BLPOP) and to
* map swapped keys to a list of clients waiting for this keys to be loaded. */ * map swapped keys to a list of clients waiting for this keys to be loaded. */
@ -1493,7 +1508,7 @@ static void initServer() {
exit(1); exit(1);
} }
for (j = 0; j < server.dbnum; j++) { for (j = 0; j < server.dbnum; j++) {
server.db[j].dict = dictCreate(&hashDictType,NULL); server.db[j].dict = dictCreate(&dbDictType,NULL);
server.db[j].expires = dictCreate(&keyptrDictType,NULL); server.db[j].expires = dictCreate(&keyptrDictType,NULL);
server.db[j].blockingkeys = dictCreate(&keylistDictType,NULL); server.db[j].blockingkeys = dictCreate(&keylistDictType,NULL);
if (server.vm_enabled) if (server.vm_enabled)
@ -2543,6 +2558,16 @@ static robj *createSetObject(void) {
return createObject(REDIS_SET,d); return createObject(REDIS_SET,d);
} }
static robj *createHashObject(void) {
/* All the Hashes start as zipmaps. Will be automatically converted
* into hash tables if there are enough elements or big elements
* inside. */
unsigned char *zm = zipmapNew();
robj *o = createObject(REDIS_HASH,zm);
o->encoding = REDIS_ENCODING_ZIPMAP;
return o;
}
static robj *createZsetObject(void) { static robj *createZsetObject(void) {
zset *zs = zmalloc(sizeof(*zs)); zset *zs = zmalloc(sizeof(*zs));

View File

@ -97,6 +97,11 @@
* comments above, that is, the max number of trailing bytes in a value. */ * comments above, that is, the max number of trailing bytes in a value. */
#define ZIPMAP_VALUE_MAX_FREE 5 #define ZIPMAP_VALUE_MAX_FREE 5
/* The following macro returns the number of bytes needed to encode the length
* for the integer value _l, that is, 1 byte for lengths < ZIPMAP_BIGLEN and
* 5 bytes for all the other lengths. */
#define ZIPMAP_LEN_BYTES(_l) (((_l) < ZIPMAP_BIGLEN) ? 1 : sizeof(unsigned int)+1)
/* Create a new empty zipmap. */ /* Create a new empty zipmap. */
unsigned char *zipmapNew(void) { unsigned char *zipmapNew(void) {
unsigned char *zm = zmalloc(2); unsigned char *zm = zmalloc(2);
@ -119,7 +124,7 @@ static unsigned int zipmapDecodeLength(unsigned char *p) {
* the amount of bytes required to encode such a length. */ * the amount of bytes required to encode such a length. */
static unsigned int zipmapEncodeLength(unsigned char *p, unsigned int len) { static unsigned int zipmapEncodeLength(unsigned char *p, unsigned int len) {
if (p == NULL) { if (p == NULL) {
return (len < ZIPMAP_BIGLEN) ? 1 : 1+sizeof(unsigned int); return ZIPMAP_LEN_BYTES(len);
} else { } else {
if (len < ZIPMAP_BIGLEN) { if (len < ZIPMAP_BIGLEN) {
p[0] = len; p[0] = len;
@ -219,23 +224,24 @@ static unsigned int zipmapRawEntryLength(unsigned char *p) {
return l + zipmapRawValueLength(p+l); return l + zipmapRawValueLength(p+l);
} }
/* Set key to value, creating the key if it does not already exist. */ /* 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) { * 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 oldlen = 0, freeoff = 0, freelen; unsigned int oldlen = 0, freeoff = 0, freelen;
unsigned int reqlen = zipmapRequiredLength(klen,vlen); unsigned int reqlen = zipmapRequiredLength(klen,vlen);
unsigned int empty, vempty; unsigned int empty, vempty;
unsigned char *p; unsigned char *p;
freelen = reqlen; freelen = reqlen;
if (update) *update = 0;
p = zipmapLookupRaw(zm,key,klen,&oldlen,&freeoff,&freelen); p = zipmapLookupRaw(zm,key,klen,&oldlen,&freeoff,&freelen);
if (p == NULL && freelen == 0) { 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 */ /* Key not found, and not space for the new key. Enlarge */
zm = zrealloc(zm,oldlen+reqlen); zm = zrealloc(zm,oldlen+reqlen);
p = zm+oldlen-1; p = zm+oldlen-1;
zm[oldlen+reqlen-1] = ZIPMAP_END; zm[oldlen+reqlen-1] = ZIPMAP_END;
freelen = reqlen; freelen = reqlen;
printf("New total length is: %u\n", oldlen+reqlen);
} else if (p == NULL) { } else if (p == NULL) {
/* Key not found, but there is enough free space. */ /* Key not found, but there is enough free space. */
p = zm+freeoff; p = zm+freeoff;
@ -245,6 +251,7 @@ unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int kle
/* Key found. Is there enough space for the new value? */ /* Key found. Is there enough space for the new value? */
/* Compute the total length: */ /* Compute the total length: */
if (update) *update = 1;
freelen = zipmapRawKeyLength(b); freelen = zipmapRawKeyLength(b);
b += freelen; b += freelen;
freelen += zipmapRawValueLength(b); freelen += zipmapRawValueLength(b);
@ -253,7 +260,7 @@ unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int kle
p[0] = ZIPMAP_EMPTY; p[0] = ZIPMAP_EMPTY;
zipmapEncodeLength(p+1,freelen); zipmapEncodeLength(p+1,freelen);
zm[0] |= ZIPMAP_STATUS_FRAGMENTED; zm[0] |= ZIPMAP_STATUS_FRAGMENTED;
return zipmapSet(zm,key,klen,val,vlen); return zipmapSet(zm,key,klen,val,vlen,NULL);
} }
} }
@ -304,37 +311,58 @@ unsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int kle
return zm; return zm;
} }
/* Call it before to iterate trought elements via zipmapNext() */
unsigned char *zipmapRewind(unsigned char *zm) {
return zm+1;
}
/* This function is used to iterate through all the zipmap elements. /* 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 first call the first argument is the pointer to the zipmap + 1.
* In the next calls what zipmapNext returns is used as first argument. * In the next calls what zipmapNext returns is used as first argument.
* Example: * Example:
* *
* unsigned char *i = my_zipmap+1; * unsigned char *i = zipmapRewind(my_zipmap);
* while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) { * while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) {
* printf("%d bytes key at $p\n", klen, key); * printf("%d bytes key at $p\n", klen, key);
* printf("%d bytes value at $p\n", vlen, value); * printf("%d bytes value at $p\n", vlen, value);
* } * }
*/ */
unsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen) {
unsigned int *klen, unsigned char **value, unsigned int *vlen) {
while(zm[0] == ZIPMAP_EMPTY) while(zm[0] == ZIPMAP_EMPTY)
zm += zipmapDecodeLength(zm+1); zm += zipmapDecodeLength(zm+1);
if (zm[0] == ZIPMAP_END) return NULL; if (zm[0] == ZIPMAP_END) return NULL;
if (key) { if (key) {
*key = zm; *key = zm;
*klen = zipmapDecodeLength(zm); *klen = zipmapDecodeLength(zm);
*key += (*klen < ZIPMAP_BIGLEN) ? 1 : sizeof(unsigned int); *key += ZIPMAP_LEN_BYTES(*klen);
} }
zm += zipmapRawKeyLength(zm); zm += zipmapRawKeyLength(zm);
if (value) { if (value) {
*value = zm+1; *value = zm+1;
*vlen = zipmapDecodeLength(zm); *vlen = zipmapDecodeLength(zm);
*value += (*vlen < ZIPMAP_BIGLEN) ? 1 : sizeof(unsigned int); *value += ZIPMAP_LEN_BYTES(*vlen);
} }
zm += zipmapRawValueLength(zm); zm += zipmapRawValueLength(zm);
return zm; 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,NULL)) == NULL) return 0;
p += zipmapRawKeyLength(p);
*vlen = zipmapDecodeLength(p);
*value = p + ZIPMAP_LEN_BYTES(*vlen) + 1;
return 1;
}
/* 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,NULL) != NULL;
}
void zipmapRepr(unsigned char *p) { void zipmapRepr(unsigned char *p) {
unsigned int l; unsigned int l;
@ -372,29 +400,41 @@ void zipmapRepr(unsigned char *p) {
printf("\n"); printf("\n");
} }
#ifdef ZIPMAP_TEST_MAIN
int main(void) { int main(void) {
unsigned char *zm; unsigned char *zm;
zm = zipmapNew(); zm = zipmapNew();
zm = zipmapSet(zm,(unsigned char*) "hello",5, (unsigned char*) "world!",6); zm = zipmapSet(zm,(unsigned char*) "hello",5, (unsigned char*) "world!",6,NULL);
zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "bar",3); zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "bar",3,NULL);
zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "!",1); zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "!",1,NULL);
zipmapRepr(zm); zipmapRepr(zm);
zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "12345",5); zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "12345",5,NULL);
zipmapRepr(zm); zipmapRepr(zm);
zm = zipmapSet(zm,(unsigned char*) "new",3, (unsigned char*) "xx",2); zm = zipmapSet(zm,(unsigned char*) "new",3, (unsigned char*) "xx",2,NULL);
zipmapRepr(zm); zipmapRepr(zm);
zm = zipmapDel(zm,(unsigned char*) "new",3,NULL); zm = zipmapDel(zm,(unsigned char*) "new",3,NULL);
zipmapRepr(zm); zipmapRepr(zm);
printf("Iterate trought elements:\n"); printf("\nPerform a direct lookup:\n");
{ {
unsigned char *i = zm+1; unsigned char *value;
unsigned int vlen;
if (zipmapGet(zm,(unsigned char*) "foo",3,&value,&vlen)) {
printf(" foo is associated to the %d bytes value: %.*s\n",
vlen, vlen, value);
}
}
printf("\nIterate trought elements:\n");
{
unsigned char *i = zipmapRewind(zm);
unsigned char *key, *value; unsigned char *key, *value;
unsigned int klen, vlen; unsigned int klen, vlen;
while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) { while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) {
printf("%d:%.*s => %d:%.*s\n", klen, klen, key, vlen, vlen, value); printf(" %d:%.*s => %d:%.*s\n", klen, klen, key, vlen, vlen, value);
} }
} }
return 0; return 0;
} }
#endif

46
zipmap.h Normal file
View File

@ -0,0 +1,46 @@
/* String -> String Map data structure optimized for size.
*
* See zipmap.c for more info.
*
* --------------------------------------------------------------------------
*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _ZIMMAP_H
#define _ZIPMAP_H
unsigned char *zipmapNew(void);
unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update);
unsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted);
unsigned char *zipmapRewind(unsigned char *zm);
unsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen);
int zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen);
int zipmapExists(unsigned char *zm, unsigned char *key, unsigned int klen);
#endif