Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
422cf21f
Commit
422cf21f
authored
Jun 04, 2010
by
antirez
Browse files
Merge branch 'lists' of
git://github.com/pietern/redis
parents
4dc1218c
dda20542
Changes
6
Show whitespace changes
Inline
Side-by-side
Makefile
View file @
422cf21f
...
...
@@ -15,7 +15,7 @@ endif
CCOPT
=
$(CFLAGS)
$(CCLINK)
$(ARCH)
$(PROF)
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 zipmap.o sha1.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 sha1.o
ziplist.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 linenoise.o
CHECKDUMPOBJ
=
redis-check-dump.o lzf_c.o lzf_d.o
...
...
@@ -53,6 +53,7 @@ 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 zipmap.h staticsymbols.h sha1.h
sds.o
:
sds.c sds.h zmalloc.h
zipmap.o
:
zipmap.c zmalloc.h
ziplist.o
:
ziplist.c zmalloc.h
zmalloc.o
:
zmalloc.c config.h
redis-server
:
$(OBJ)
...
...
redis.c
View file @
422cf21f
...
...
@@ -75,6 +75,7 @@
#include "lzf.h" /* LZF compression library */
#include "pqsort.h" /* Partial qsort for SORT+LIMIT */
#include "zipmap.h" /* Compact dictionary-alike data structure */
#include "ziplist.h" /* Compact list data structure */
#include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
#include "release.h" /* Release and/or git repository information */
...
...
@@ -127,11 +128,13 @@
* is set to one of this fields for this object. */
#define REDIS_ENCODING_RAW 0 /* Raw representation */
#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 */
#define REDIS_ENCODING_HT 2 /* Encoded as hash table */
#define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
#define REDIS_ENCODING_LIST 4 /* Encoded as zipmap */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
static char* strencoding[] = {
"raw", "int", "
zipmap", "hashtable
"
"raw", "int", "
hashtable", "zipmap", "list", "ziplist
"
};
/* Object types only used for dumping to disk */
...
...
@@ -234,9 +237,11 @@ static char* strencoding[] = {
#define APPENDFSYNC_ALWAYS 1
#define APPENDFSYNC_EVERYSEC 2
/*
Hashes
related defaults */
/*
Zip structure
related defaults */
#define REDIS_HASH_MAX_ZIPMAP_ENTRIES 64
#define REDIS_HASH_MAX_ZIPMAP_VALUE 512
#define REDIS_LIST_MAX_ZIPLIST_ENTRIES 1024
#define REDIS_LIST_MAX_ZIPLIST_VALUE 32
/* We can print the stacktrace, so our assert is defined this way: */
#define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
...
...
@@ -422,9 +427,11 @@ struct redisServer {
off_t vm_page_size;
off_t vm_pages;
unsigned long long vm_max_memory;
/*
Hashes
config */
/*
Zip structure
config */
size_t hash_max_zipmap_entries;
size_t hash_max_zipmap_value;
size_t list_max_ziplist_entries;
size_t list_max_ziplist_value;
/* Virtual memory state */
FILE *vm_fp;
int vm_fd;
...
...
@@ -643,6 +650,7 @@ static struct redisCommand *lookupCommand(char *name);
static void call(redisClient *c, struct redisCommand *cmd);
static void resetClient(redisClient *c);
static void convertToRealHash(robj *o);
static void listTypeConvert(robj *o, int enc);
static int pubsubUnsubscribeAllChannels(redisClient *c, int notify);
static int pubsubUnsubscribeAllPatterns(redisClient *c, int notify);
static void freePubsubPattern(void *p);
...
...
@@ -1752,6 +1760,8 @@ static void initServerConfig() {
server.vm_blocked_clients = 0;
server.hash_max_zipmap_entries = REDIS_HASH_MAX_ZIPMAP_ENTRIES;
server.hash_max_zipmap_value = REDIS_HASH_MAX_ZIPMAP_VALUE;
server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES;
server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE;
server.shutdown_asap = 0;
resetServerSaveParams();
...
...
@@ -2030,6 +2040,10 @@ static void loadServerConfig(char *filename) {
server.hash_max_zipmap_entries = memtoll(argv[1], NULL);
} else if (!strcasecmp(argv[0],"hash-max-zipmap-value") && argc == 2){
server.hash_max_zipmap_value = memtoll(argv[1], NULL);
} else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){
server.list_max_ziplist_entries = memtoll(argv[1], NULL);
} else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2){
server.list_max_ziplist_value = memtoll(argv[1], NULL);
} else {
err = "Bad directive or wrong number of arguments"; goto loaderr;
}
...
...
@@ -3035,9 +3049,17 @@ static robj *dupStringObject(robj *o) {
static robj *createListObject(void) {
list *l = listCreate();
robj *o = createObject(REDIS_LIST,l);
listSetFreeMethod(l,decrRefCount);
return createObject(REDIS_LIST,l);
o->encoding = REDIS_ENCODING_LIST;
return o;
}
static robj *createZiplistObject(void) {
unsigned char *zl = ziplistNew();
robj *o = createObject(REDIS_LIST,zl);
o->encoding = REDIS_ENCODING_ZIPLIST;
return o;
}
static robj *createSetObject(void) {
...
...
@@ -3070,7 +3092,16 @@ static void freeStringObject(robj *o) {
}
static void freeListObject(robj *o) {
switch (o->encoding) {
case REDIS_ENCODING_LIST:
listRelease((list*) o->ptr);
break;
case REDIS_ENCODING_ZIPLIST:
zfree(o->ptr);
break;
default:
redisPanic("Unknown list encoding type");
}
}
static void freeSetObject(robj *o) {
...
...
@@ -3644,38 +3675,32 @@ static int rdbSaveRawString(FILE *fp, unsigned char *s, size_t len) {
return 0;
}
/* Save a long long value as either an encoded string or a string. */
static int rdbSaveLongLongAsStringObject(FILE *fp, long long value) {
unsigned char buf[32];
int enclen = rdbEncodeInteger(value,buf);
if (enclen > 0) {
if (fwrite(buf,enclen,1,fp) == 0) return -1;
} else {
/* Encode as string */
enclen = ll2string((char*)buf,32,value);
redisAssert(enclen < 32);
if (rdbSaveLen(fp,enclen) == -1) return -1;
if (fwrite(buf,enclen,1,fp) == 0) return -1;
}
return 0;
}
/* Like rdbSaveStringObjectRaw() but handle encoded objects */
static int rdbSaveStringObject(FILE *fp, robj *obj) {
int retval;
/* Avoid to decode the object, then encode it again, if the
* object is alrady integer encoded. */
if (obj->encoding == REDIS_ENCODING_INT) {
long val = (long) obj->ptr;
unsigned char buf[5];
int enclen;
if ((enclen = rdbEncodeInteger(val,buf)) > 0) {
if (fwrite(buf,enclen,1,fp) == 0) return -1;
return 0;
}
/* otherwise... fall throught and continue with the usual
* code path. */
}
/* Avoid incr/decr ref count business when possible.
* This plays well with copy-on-write given that we are probably
* in a child process (BGSAVE). Also this makes sure key objects
* of swapped objects are not incRefCount-ed (an assert does not allow
* this in order to avoid bugs) */
if (obj->encoding != REDIS_ENCODING_RAW) {
obj = getDecodedObject(obj);
retval = rdbSaveRawString(fp,obj->ptr,sdslen(obj->ptr));
decrRefCount(obj);
return rdbSaveLongLongAsStringObject(fp,(long)obj->ptr);
} else {
retval = rdbSaveRawString(fp,obj->ptr,sdslen(obj->ptr));
redisAssert(obj->encoding == REDIS_ENCODING_RAW);
return rdbSaveRawString(fp,obj->ptr,sdslen(obj->ptr));
}
return retval;
}
/* Save a double value. Doubles are saved as strings prefixed by an unsigned
...
...
@@ -3728,6 +3753,25 @@ static int rdbSaveObject(FILE *fp, robj *o) {
if (rdbSaveStringObject(fp,o) == -1) return -1;
} else if (o->type == REDIS_LIST) {
/* Save a list value */
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *p;
unsigned char *vstr;
unsigned int vlen;
long long vlong;
if (rdbSaveLen(fp,ziplistLen(o->ptr)) == -1) return -1;
p = ziplistIndex(o->ptr,0);
while(ziplistGet(p,&vstr,&vlen,&vlong)) {
if (vstr) {
if (rdbSaveRawString(fp,vstr,vlen) == -1)
return -1;
} else {
if (rdbSaveLongLongAsStringObject(fp,vlong) == -1)
return -1;
}
p = ziplistNext(o->ptr,p);
}
} else if (o->encoding == REDIS_ENCODING_LIST) {
list *list = o->ptr;
listIter li;
listNode *ln;
...
...
@@ -3736,9 +3780,11 @@ static int rdbSaveObject(FILE *fp, robj *o) {
listRewind(list,&li);
while((ln = listNext(&li))) {
robj *eleobj = listNodeValue(ln);
if (rdbSaveStringObject(fp,eleobj) == -1) return -1;
}
} else {
redisPanic("Unknown list encoding");
}
} else if (o->type == REDIS_SET) {
/* Save a set value */
dict *set = o->ptr;
...
...
@@ -4107,35 +4153,61 @@ static int rdbLoadDoubleValue(FILE *fp, double *val) {
/* Load a Redis object of the specified type from the specified file.
* On success a newly allocated object is returned, otherwise NULL. */
static robj *rdbLoadObject(int type, FILE *fp) {
robj *o;
robj *o, *ele, *dec;
size_t len;
redisLog(REDIS_DEBUG,"LOADING OBJECT %d (at %d)\n",type,ftell(fp));
if (type == REDIS_STRING) {
/* Read string value */
if ((o = rdbLoadEncodedStringObject(fp)) == NULL) return NULL;
o = tryObjectEncoding(o);
} else if (type == REDIS_LIST || type == REDIS_SET) {
/* Read list/set value */
uint32_t listlen;
} else if (type == REDIS_LIST) {
/* Read list value */
if ((len = rdbLoadLen(fp,NULL)) == REDIS_RDB_LENERR) return NULL;
/* Use a real list when there are too many entries */
if (len > server.list_max_ziplist_entries) {
o = createListObject();
} else {
o = createZiplistObject();
}
/* Load every single element of the list */
while(len--) {
if ((ele = rdbLoadEncodedStringObject(fp)) == NULL) return NULL;
if ((listlen = rdbLoadLen(fp,NULL)) == REDIS_RDB_LENERR) return NULL;
o = (type == REDIS_LIST) ? createListObject() : createSetObject();
/* If we are using a ziplist and the value is too big, convert
* the object to a real list. */
if (o->encoding == REDIS_ENCODING_ZIPLIST &&
ele->encoding == REDIS_ENCODING_RAW &&
sdslen(ele->ptr) > server.list_max_ziplist_value)
listTypeConvert(o,REDIS_ENCODING_LIST);
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
dec = getDecodedObject(ele);
o->ptr = ziplistPush(o->ptr,dec->ptr,sdslen(dec->ptr),REDIS_TAIL);
decrRefCount(dec);
decrRefCount(ele);
} else {
ele = tryObjectEncoding(ele);
listAddNodeTail(o->ptr,ele);
incrRefCount(ele);
}
}
} else if (type == REDIS_SET) {
/* Read list/set value */
if ((len = rdbLoadLen(fp,NULL)) == REDIS_RDB_LENERR) return NULL;
o = createSetObject();
/* It's faster to expand the dict to the right size asap in order
* to avoid rehashing */
if (
type == REDIS_SET && list
len > DICT_HT_INITIAL_SIZE)
dictExpand(o->ptr,
list
len);
if (len > DICT_HT_INITIAL_SIZE)
dictExpand(o->ptr,len);
/* Load every single element of the list/set */
while(listlen--) {
robj *ele;
while(len--) {
if ((ele = rdbLoadEncodedStringObject(fp)) == NULL) return NULL;
ele = tryObjectEncoding(ele);
if (type == REDIS_LIST) {
listAddNodeTail((list*)o->ptr,ele);
} else {
dictAdd((dict*)o->ptr,ele,NULL);
}
}
} else if (type == REDIS_ZSET) {
/* Read list/set value */
size_t zsetlen;
...
...
@@ -4833,24 +4905,251 @@ static void moveCommand(redisClient *c) {
}
/* =================================== Lists ================================ */
static void pushGenericCommand(redisClient *c, int where) {
robj *lobj;
list *list;
lobj = lookupKeyWrite(c->db,c->argv[1]);
/* Check the argument length to see if it requires us to convert the ziplist
* to a real list. Only check raw-encoded objects because integer encoded
* objects are never too long. */
static void listTypeTryConversion(robj *subject, robj *value) {
if (subject->encoding != REDIS_ENCODING_ZIPLIST) return;
if (value->encoding == REDIS_ENCODING_RAW &&
sdslen(value->ptr) > server.list_max_ziplist_value)
listTypeConvert(subject,REDIS_ENCODING_LIST);
}
static void listTypePush(robj *subject, robj *value, int where) {
/* Check if we need to convert the ziplist */
listTypeTryConversion(subject,value);
if (subject->encoding == REDIS_ENCODING_ZIPLIST &&
ziplistLen(subject->ptr) > server.list_max_ziplist_entries)
listTypeConvert(subject,REDIS_ENCODING_LIST);
if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
int pos = (where == REDIS_HEAD) ? ZIPLIST_HEAD : ZIPLIST_TAIL;
value = getDecodedObject(value);
subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),pos);
decrRefCount(value);
} else if (subject->encoding == REDIS_ENCODING_LIST) {
if (where == REDIS_HEAD) {
listAddNodeHead(subject->ptr,value);
} else {
listAddNodeTail(subject->ptr,value);
}
incrRefCount(value);
} else {
redisPanic("Unknown list encoding");
}
}
static robj *listTypePop(robj *subject, int where) {
robj *value = NULL;
if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *p;
unsigned char *vstr;
unsigned int vlen;
long long vlong;
int pos = (where == REDIS_HEAD) ? 0 : -1;
p = ziplistIndex(subject->ptr,pos);
if (ziplistGet(p,&vstr,&vlen,&vlong)) {
if (vstr) {
value = createStringObject((char*)vstr,vlen);
} else {
value = createStringObjectFromLongLong(vlong);
}
/* We only need to delete an element when it exists */
subject->ptr = ziplistDelete(subject->ptr,&p);
}
} else if (subject->encoding == REDIS_ENCODING_LIST) {
list *list = subject->ptr;
listNode *ln;
if (where == REDIS_HEAD) {
ln = listFirst(list);
} else {
ln = listLast(list);
}
if (ln != NULL) {
value = listNodeValue(ln);
incrRefCount(value);
listDelNode(list,ln);
}
} else {
redisPanic("Unknown list encoding");
}
return value;
}
static unsigned long listTypeLength(robj *subject) {
if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
return ziplistLen(subject->ptr);
} else if (subject->encoding == REDIS_ENCODING_LIST) {
return listLength((list*)subject->ptr);
} else {
redisPanic("Unknown list encoding");
}
}
/* Structure to hold set iteration abstraction. */
typedef struct {
robj *subject;
unsigned char encoding;
unsigned char direction; /* Iteration direction */
unsigned char *zi;
listNode *ln;
} listTypeIterator;
/* Structure for an entry while iterating over a list. */
typedef struct {
listTypeIterator *li;
unsigned char *zi; /* Entry in ziplist */
listNode *ln; /* Entry in linked list */
} listTypeEntry;
/* Initialize an iterator at the specified index. */
static listTypeIterator *listTypeInitIterator(robj *subject, int index, unsigned char direction) {
listTypeIterator *li = zmalloc(sizeof(listTypeIterator));
li->subject = subject;
li->encoding = subject->encoding;
li->direction = direction;
if (li->encoding == REDIS_ENCODING_ZIPLIST) {
li->zi = ziplistIndex(subject->ptr,index);
} else if (li->encoding == REDIS_ENCODING_LIST) {
li->ln = listIndex(subject->ptr,index);
} else {
redisPanic("Unknown list encoding");
}
return li;
}
/* Clean up the iterator. */
static void listTypeReleaseIterator(listTypeIterator *li) {
zfree(li);
}
/* Stores pointer to current the entry in the provided entry structure
* and advances the position of the iterator. Returns 1 when the current
* entry is in fact an entry, 0 otherwise. */
static int listTypeNext(listTypeIterator *li, listTypeEntry *entry) {
/* Protect from converting when iterating */
redisAssert(li->subject->encoding == li->encoding);
entry->li = li;
if (li->encoding == REDIS_ENCODING_ZIPLIST) {
entry->zi = li->zi;
if (entry->zi != NULL) {
if (li->direction == REDIS_TAIL)
li->zi = ziplistNext(li->subject->ptr,li->zi);
else
li->zi = ziplistPrev(li->subject->ptr,li->zi);
return 1;
}
} else if (li->encoding == REDIS_ENCODING_LIST) {
entry->ln = li->ln;
if (entry->ln != NULL) {
if (li->direction == REDIS_TAIL)
li->ln = li->ln->next;
else
li->ln = li->ln->prev;
return 1;
}
} else {
redisPanic("Unknown list encoding");
}
return 0;
}
/* Return entry or NULL at the current position of the iterator. */
static robj *listTypeGet(listTypeEntry *entry) {
listTypeIterator *li = entry->li;
robj *value = NULL;
if (li->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *vstr;
unsigned int vlen;
long long vlong;
redisAssert(entry->zi != NULL);
if (ziplistGet(entry->zi,&vstr,&vlen,&vlong)) {
if (vstr) {
value = createStringObject((char*)vstr,vlen);
} else {
value = createStringObjectFromLongLong(vlong);
}
}
} else if (li->encoding == REDIS_ENCODING_LIST) {
redisAssert(entry->ln != NULL);
value = listNodeValue(entry->ln);
incrRefCount(value);
} else {
redisPanic("Unknown list encoding");
}
return value;
}
/* Compare the given object with the entry at the current position. */
static int listTypeEqual(listTypeEntry *entry, robj *o) {
listTypeIterator *li = entry->li;
if (li->encoding == REDIS_ENCODING_ZIPLIST) {
redisAssert(o->encoding == REDIS_ENCODING_RAW);
return ziplistCompare(entry->zi,o->ptr,sdslen(o->ptr));
} else if (li->encoding == REDIS_ENCODING_LIST) {
return equalStringObjects(o,listNodeValue(entry->ln));
} else {
redisPanic("Unknown list encoding");
}
}
/* Delete the element pointed to. */
static void listTypeDelete(listTypeEntry *entry) {
listTypeIterator *li = entry->li;
if (li->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *p = entry->zi;
li->subject->ptr = ziplistDelete(li->subject->ptr,&p);
/* Update position of the iterator depending on the direction */
if (li->direction == REDIS_TAIL)
li->zi = p;
else
li->zi = ziplistPrev(li->subject->ptr,p);
} else if (entry->li->encoding == REDIS_ENCODING_LIST) {
listNode *next;
if (li->direction == REDIS_TAIL)
next = entry->ln->next;
else
next = entry->ln->prev;
listDelNode(li->subject->ptr,entry->ln);
li->ln = next;
} else {
redisPanic("Unknown list encoding");
}
}
static void listTypeConvert(robj *subject, int enc) {
listTypeIterator *li;
listTypeEntry entry;
redisAssert(subject->type == REDIS_LIST);
if (enc == REDIS_ENCODING_LIST) {
list *l = listCreate();
/* listTypeGet returns a robj with incremented refcount */
li = listTypeInitIterator(subject,0,REDIS_TAIL);
while (listTypeNext(li,&entry)) listAddNodeTail(l,listTypeGet(&entry));
listTypeReleaseIterator(li);
subject->encoding = REDIS_ENCODING_LIST;
zfree(subject->ptr);
subject->ptr = l;
} else {
redisPanic("Unsupported list conversion");
}
}
static void pushGenericCommand(redisClient *c, int where) {
robj *lobj = lookupKeyWrite(c->db,c->argv[1]);
if (lobj == NULL) {
if (handleClientsWaitingListPush(c,c->argv[1],c->argv[2])) {
addReply(c,shared.cone);
return;
}
lobj = createListObject();
list = lobj->ptr;
if (where == REDIS_HEAD) {
listAddNodeHead(list,c->argv[2]);
} else {
listAddNodeTail(list,c->argv[2]);
}
incrRefCount(c->argv[2]);
lobj = createZiplistObject();
dbAdd(c->db,c->argv[1],lobj);
} else {
if (lobj->type != REDIS_LIST) {
...
...
@@ -4861,16 +5160,10 @@ static void pushGenericCommand(redisClient *c, int where) {
addReply(c,shared.cone);
return;
}
list = lobj->ptr;
if (where == REDIS_HEAD) {
listAddNodeHead(list,c->argv[2]);
} else {
listAddNodeTail(list,c->argv[2]);
}
incrRefCount(c->argv[2]);
}
listTypePush(lobj,c->argv[2],where);
addReplyLongLong(c,listTypeLength(lobj));
server.dirty++;
addReplyLongLong(c,listLength(list));
}
static void lpushCommand(redisClient *c) {
...
...
@@ -4882,80 +5175,94 @@ static void rpushCommand(redisClient *c) {
}
static void llenCommand(redisClient *c) {
robj *o;
list *l;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_LIST)) return;
l = o->ptr;
addReplyUlong(c,listLength(l));
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
addReplyUlong(c,listTypeLength(o));
}
static void lindexCommand(redisClient *c) {
robj *o;
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk);
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
int index = atoi(c->argv[2]->ptr);
list *list;
listNode *ln;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,o,REDIS_LIST)) return;
list = o->ptr;
robj *value = NULL;
ln = listIndex(list, index);
if (ln == NULL) {
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *p;
unsigned char *vstr;
unsigned int vlen;
long long vlong;
p = ziplistIndex(o->ptr,index);
if (ziplistGet(p,&vstr,&vlen,&vlong)) {
if (vstr) {
value = createStringObject((char*)vstr,vlen);
} else {
value = createStringObjectFromLongLong(vlong);
}
addReplyBulk(c,value);
decrRefCount(value);
} else {
addReply(c,shared.nullbulk);
}
} else if (o->encoding == REDIS_ENCODING_LIST) {
listNode *ln = listIndex(o->ptr,index);
if (ln != NULL) {
value = listNodeValue(ln);
addReplyBulk(c,value);
} else {
robj *ele = listNodeValue(ln);
addReplyBulk(c,ele);
addReply(c,shared.nullbulk);
}
} else {
redisPanic("Unknown list encoding");
}
}
static void lsetCommand(redisClient *c) {
robj *o;
robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
int index = atoi(c->argv[2]->ptr);
list *list;
listNode *ln;
robj *value = c->argv[3];
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL ||
checkType(c,o,REDIS_LIST)) return;
list = o->ptr;
ln = listIndex(list, index);
listTypeTryConversion(o,value);
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *p, *zl = o->ptr;
p = ziplistIndex(zl,index);
if (p == NULL) {
addReply(c,shared.outofrangeerr);
} else {
o->ptr = ziplistDelete(o->ptr,&p);
value = getDecodedObject(value);
o->ptr = ziplistInsert(o->ptr,p,value->ptr,sdslen(value->ptr));
decrRefCount(value);
addReply(c,shared.ok);
server.dirty++;
}
} else if (o->encoding == REDIS_ENCODING_LIST) {
listNode *ln = listIndex(o->ptr,index);
if (ln == NULL) {
addReply(c,shared.outofrangeerr);
} else {
robj *ele = listNodeValue(ln);
decrRefCount(ele);
listNodeValue(ln) = c->argv[3];
incrRefCount(c->argv[3]);
decrRefCount((robj*)listNodeValue(ln));
listNodeValue(ln) = value;
incrRefCount(value);
addReply(c,shared.ok);
server.dirty++;
}
} else {
redisPanic("Unknown list encoding");
}
}
static void popGenericCommand(redisClient *c, int where) {
robj *o;
list *list;
listNode *ln;
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,o,REDIS_LIST)) return;
list = o->ptr;
if (where == REDIS_HEAD)
ln = listFirst(list);
else
ln = listLast(list);
robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk);
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
if (ln == NULL) {
robj *value = listTypePop(o,where);
if (value == NULL) {
addReply(c,shared.nullbulk);
} else {
robj *ele = listNodeValue(ln);
addReplyBulk(c,ele);
listDelNode(list,ln);
if (listLength(list) == 0) dbDelete(c->db,c->argv[1]);
addReplyBulk(c,value);
decrRefCount(value);
if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
server.dirty++;
}
}
...
...
@@ -4969,19 +5276,16 @@ static void rpopCommand(redisClient *c) {
}
static void lrangeCommand(redisClient *c) {
robj *o;
robj *o
, *value
;
int start = atoi(c->argv[2]->ptr);
int end = atoi(c->argv[3]->ptr);
int llen;
int rangelen, j;
list *list;
listNode *ln;
robj *ele;
listTypeEntry entry;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
|| checkType(c,o,REDIS_LIST)) return;
list = o->ptr;
llen = listLength(list);
llen = listTypeLength(o);
/* convert negative indexes */
if (start < 0) start = llen+start;
...
...
@@ -4999,13 +5303,15 @@ static void lrangeCommand(redisClient *c) {
rangelen = (end-start)+1;
/* Return the result in form of a multi-bulk reply */
ln = listIndex(list, start);
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen));
listTypeIterator *li = listTypeInitIterator(o,start,REDIS_TAIL);
for (j = 0; j < rangelen; j++) {
ele = listNodeValue(ln);
addReplyBulk(c,ele);
ln = ln->next;
redisAssert(listTypeNext(li,&entry));
value = listTypeGet(&entry);
addReplyBulk(c,value);
decrRefCount(value);
}
listTypeReleaseIterator(li);
}
static void ltrimCommand(redisClient *c) {
...
...
@@ -5019,8 +5325,7 @@ static void ltrimCommand(redisClient *c) {
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.ok)) == NULL ||
checkType(c,o,REDIS_LIST)) return;
list = o->ptr;
llen = listLength(list);
llen = listTypeLength(o);
/* convert negative indexes */
if (start < 0) start = llen+start;
...
...
@@ -5040,6 +5345,11 @@ static void ltrimCommand(redisClient *c) {
}
/* Remove list elements to perform the trim */
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
o->ptr = ziplistDeleteRange(o->ptr,0,ltrim);
o->ptr = ziplistDeleteRange(o->ptr,-rtrim,rtrim);
} else if (o->encoding == REDIS_ENCODING_LIST) {
list = o->ptr;
for (j = 0; j < ltrim; j++) {
ln = listFirst(list);
listDelNode(list,ln);
...
...
@@ -5048,41 +5358,50 @@ static void ltrimCommand(redisClient *c) {
ln = listLast(list);
listDelNode(list,ln);
}
if (listLength(list) == 0) dbDelete(c->db,c->argv[1]);
} else {
redisPanic("Unknown list encoding");
}
if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
server.dirty++;
addReply(c,shared.ok);
}
static void lremCommand(redisClient *c) {
robj *o;
list *list;
listNode *ln, *next;
robj *subject, *obj = c->argv[3];
int toremove = atoi(c->argv[2]->ptr);
int removed = 0;
int fromtail = 0
;
listTypeEntry entry
;
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_LIST)) return;
list = o->ptr;
subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero);
if (subject == NULL || checkType(c,subject,REDIS_LIST)) return;
/* Make sure obj is raw when we're dealing with a ziplist */
if (subject->encoding == REDIS_ENCODING_ZIPLIST)
obj = getDecodedObject(obj);
listTypeIterator *li;
if (toremove < 0) {
toremove = -toremove;
fromtail = 1;
li = listTypeInitIterator(subject,-1,REDIS_HEAD);
} else {
li = listTypeInitIterator(subject,0,REDIS_TAIL);
}
ln = fromtail ? list->tail : list->head;
while (ln) {
robj *ele = listNodeValue(ln);
next = fromtail ? ln->prev : ln->next;
if (
equalStringObjects(ele,c->argv[3]
)) {
list
DelNode(list,ln
);
while (listTypeNext(li,&entry)) {
if (
listTypeEqual(&entry,obj
)) {
list
TypeDelete(&entry
);
server.dirty++;
removed++;
if (toremove && removed == toremove) break;
}
ln = next;
}
if (listLength(list) == 0) dbDelete(c->db,c->argv[1]);
listTypeReleaseIterator(li);
/* Clean up raw encoded object */
if (subject->encoding == REDIS_ENCODING_ZIPLIST)
decrRefCount(obj);
if (listTypeLength(subject) == 0) dbDelete(c->db,c->argv[1]);
addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",removed));
}
...
...
@@ -5102,46 +5421,36 @@ static void lremCommand(redisClient *c) {
* as well. This command was originally proposed by Ezra Zygmuntowicz.
*/
static void rpoplpushcommand(redisClient *c) {
robj *sobj;
list *srclist;
listNode *ln;
robj *sobj, *value;
if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,sobj,REDIS_LIST)) return;
srclist = sobj->ptr;
ln = listLast(srclist);
if (l
n == NULL
) {
if (l
istTypeLength(sobj) == 0
) {
addReply(c,shared.nullbulk);
} else {
robj *dobj = lookupKeyWrite(c->db,c->argv[2]);
robj *ele = listNodeValue(ln);
list *dstlist;
if (dobj && dobj->type != REDIS_LIST) {
addReply(c,shared.wrongtypeerr);
return;
}
if (dobj && checkType(c,dobj,REDIS_LIST)) return;
value = listTypePop(sobj,REDIS_TAIL);
/* Add the element to the target list (unless it's directly
* passed to some BLPOP-ing client */
if (!handleClientsWaitingListPush(c,c->argv[2],ele)) {
if (dobj == NULL) {
if (!handleClientsWaitingListPush(c,c->argv[2],value)) {
/* Create the list if the key does not exist */
dobj = createListObject();
if (!dobj) {
dobj = createZiplistObject();
dbAdd(c->db,c->argv[2],dobj);
}
dstlist = dobj->ptr;
listAddNodeHead(dstlist,ele);
incrRefCount(ele);
listTypePush(dobj,value,REDIS_HEAD);
}
/* Send the element to the client as reply as well */
addReplyBulk(c,ele);
addReplyBulk(c,value);
/* listTypePop returns an object with its refcount incremented */
decrRefCount(value);
/* Finally remove the element from the source list */
listDelNode(srclist,ln);
if (listLength(srclist) == 0) dbDelete(c->db,c->argv[1]);
/* Delete the source list when it is empty */
if (listTypeLength(sobj) == 0) dbDelete(c->db,c->argv[1]);
server.dirty++;
}
}
...
...
@@ -5764,7 +6073,7 @@ static zskiplistNode *zslFirstWithScore(zskiplist *zsl, double score) {
* Returns 0 when the element cannot be found, rank otherwise.
* Note that the rank is 1-based due to the span of zsl->header to the
* first element. */
static unsigned long zslGetRank(zskiplist *zsl, double score, robj *o) {
static unsigned long zsl
istType
GetRank(zskiplist *zsl, double score, robj *o) {
zskiplistNode *x;
unsigned long rank = 0;
int i;
...
...
@@ -5788,7 +6097,7 @@ static unsigned long zslGetRank(zskiplist *zsl, double score, robj *o) {
}
/* Finds an element by its rank. The rank argument needs to be 1-based. */
zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) {
zskiplistNode* zsl
istType
GetElementByRank(zskiplist *zsl, unsigned long rank) {
zskiplistNode *x;
unsigned long traversed = 0;
int i;
...
...
@@ -6254,10 +6563,10 @@ static void zrangeGenericCommand(redisClient *c, int reverse) {
/* check if starting point is trivial, before searching
* the element in log(N) time */
if (reverse) {
ln = start == 0 ? zsl->tail : zslGetElementByRank(zsl, llen-start);
ln = start == 0 ? zsl->tail : zsl
istType
GetElementByRank(zsl, llen-start);
} else {
ln = start == 0 ?
zsl->header->forward[0] : zslGetElementByRank(zsl, start+1);
zsl->header->forward[0] : zsl
istType
GetElementByRank(zsl, start+1);
}
/* Return the result in form of a multi-bulk reply */
...
...
@@ -6454,7 +6763,7 @@ static void zrankGenericCommand(redisClient *c, int reverse) {
}
score = dictGetEntryVal(de);
rank = zslGetRank(zsl, *score, c->argv[2]);
rank = zsl
istType
GetRank(zsl, *score, c->argv[2]);
if (rank) {
if (reverse) {
addReplyLongLong(c, zsl->length - rank);
...
...
@@ -7031,7 +7340,7 @@ static int sortCompare(const void *s1, const void *s2) {
* is optimized for speed and a bit less for readability */
static void sortCommand(redisClient *c) {
list *operations;
int outputlen = 0;
unsigned
int outputlen = 0;
int desc = 0, alpha = 0;
int limit_start = 0, limit_count = -1, start, end;
int j, dontsort = 0, vectorlen;
...
...
@@ -7101,7 +7410,7 @@ static void sortCommand(redisClient *c) {
/* Load the sorting vector with all the objects to sort */
switch(sortval->type) {
case REDIS_LIST: vectorlen = listLength(
(list*)
sortval
->ptr
); break;
case REDIS_LIST: vectorlen = list
Type
Length(sortval); break;
case REDIS_SET: vectorlen = dictSize((dict*)sortval->ptr); break;
case REDIS_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;
default: vectorlen = 0; redisPanic("Bad SORT type"); /* Avoid GCC warning */
...
...
@@ -7110,18 +7419,15 @@ static void sortCommand(redisClient *c) {
j = 0;
if (sortval->type == REDIS_LIST) {
list *list = sortval->ptr;
listNode *ln;
listIter li;
listRewind(list,&li);
while((ln = listNext(&li))) {
robj *ele = ln->value;
vector[j].obj = ele;
listTypeIterator *li = listTypeInitIterator(sortval,0,REDIS_TAIL);
listTypeEntry entry;
while(listTypeNext(li,&entry)) {
vector[j].obj = listTypeGet(&entry);
vector[j].u.score = 0;
vector[j].u.cmpobj = NULL;
j++;
}
listTypeReleaseIterator(li);
} else {
dict *set;
dictIterator *di;
...
...
@@ -7231,8 +7537,7 @@ static void sortCommand(redisClient *c) {
}
}
} else {
robj *listObject = createListObject();
list *listPtr = (list*) listObject->ptr;
robj *sobj = createZiplistObject();
/* STORE option specified, set the sorting result as a List object */
for (j = start; j <= end; j++) {
...
...
@@ -7240,9 +7545,8 @@ static void sortCommand(redisClient *c) {
listIter li;
if (!getop) {
listAddNodeTail(listPtr,vector[j].obj);
incrRefCount(vector[j].obj);
}
listTypePush(sobj,vector[j].obj,REDIS_TAIL);
} else {
listRewind(operations,&li);
while((ln = listNext(&li))) {
redisSortOperation *sop = ln->value;
...
...
@@ -7250,21 +7554,21 @@ static void sortCommand(redisClient *c) {
vector[j].obj);
if (sop->type == REDIS_SORT_GET) {
if (!val) {
listAddNodeTail(listPtr,createStringObject("",0));
if (!val) val = createStringObject("",0);
/* listTypePush does an incrRefCount, so we should take care
* care of the incremented refcount caused by either
* lookupKeyByPattern or createStringObject("",0) */
listTypePush(sobj,val,REDIS_TAIL);
decrRefCount(val);
} else {
/* We should do a incrRefCount on val because it is
* added to the list, but also a decrRefCount because
* it is returned by lookupKeyByPattern. This results
* in doing nothing at all. */
listAddNodeTail(listPtr,val);
/* always fails */
redisAssert(sop->type == REDIS_SORT_GET);
}
} else {
redisAssert(sop->type == REDIS_SORT_GET); /* always fails */
}
}
}
dbReplace(c->db,storekey,
listObject
);
dbReplace(c->db,storekey,
sobj
);
/* Note: we add 1 because the DB is dirty anyway since even if the
* SORT result is empty a new key is set and maybe the old content
* replaced. */
...
...
@@ -7273,6 +7577,9 @@ static void sortCommand(redisClient *c) {
}
/* Cleanup */
if (sortval->type == REDIS_LIST)
for (j = 0; j < vectorlen; j++)
decrRefCount(vector[j].obj);
decrRefCount(sortval);
listRelease(operations);
for (j = 0; j < vectorlen; j++) {
...
...
@@ -8597,40 +8904,17 @@ fmterr:
exit(1);
}
/* Write an object into a file in the bulk format $<count>\r\n<payload>\r\n */
static int fwriteBulkObject(FILE *fp, robj *obj) {
char buf[128];
int decrrc = 0;
/* Avoid the incr/decr ref count business if possible to help
* copy-on-write (we are often in a child process when this function
* is called).
* Also makes sure that key objects don't get incrRefCount-ed when VM
* is enabled */
if (obj->encoding != REDIS_ENCODING_RAW) {
obj = getDecodedObject(obj);
decrrc = 1;
}
snprintf(buf,sizeof(buf),"$%ld\r\n",(long)sdslen(obj->ptr));
if (fwrite(buf,strlen(buf),1,fp) == 0) goto err;
if (sdslen(obj->ptr) && fwrite(obj->ptr,sdslen(obj->ptr),1,fp) == 0)
goto err;
if (fwrite("\r\n",2,1,fp) == 0) goto err;
if (decrrc) decrRefCount(obj);
return 1;
err:
if (decrrc) decrRefCount(obj);
return 0;
}
/* Write binary-safe string into a file in the bulkformat
* $<count>\r\n<payload>\r\n */
static int fwriteBulkString(FILE *fp, char *s, unsigned long len) {
char buf[128];
snprintf(buf,sizeof(buf),"$%ld\r\n",(unsigned long)len);
if (fwrite(buf,strlen(buf),1,fp) == 0) return 0;
if (len && fwrite(s,len,1,fp) == 0) return 0;
char cbuf[128];
int clen;
cbuf[0] = '$';
clen = 1+ll2string(cbuf+1,sizeof(cbuf)-1,len);
cbuf[clen++] = '\r';
cbuf[clen++] = '\n';
if (fwrite(cbuf,clen,1,fp) == 0) return 0;
if (len > 0 && fwrite(s,len,1,fp) == 0) return 0;
if (fwrite("\r\n",2,1,fp) == 0) return 0;
return 1;
}
...
...
@@ -8647,16 +8931,28 @@ static int fwriteBulkDouble(FILE *fp, double d) {
}
/* Write a long value in bulk format $<count>\r\n<payload>\r\n */
static int fwriteBulkLong(FILE *fp, long l) {
char buf[128], lbuf[128];
snprintf(lbuf,sizeof(lbuf),"%ld\r\n",l);
snprintf(buf,sizeof(buf),"$%lu\r\n",(unsigned long)strlen(lbuf)-2);
if (fwrite(buf,strlen(buf),1,fp) == 0) return 0;
if (fwrite(lbuf,strlen(lbuf),1,fp) == 0) return 0;
static int fwriteBulkLongLong(FILE *fp, long long l) {
char bbuf[128], lbuf[128];
unsigned int blen, llen;
llen = ll2string(lbuf,32,l);
blen = snprintf(bbuf,sizeof(bbuf),"$%u\r\n%s\r\n",llen,lbuf);
if (fwrite(bbuf,blen,1,fp) == 0) return 0;
return 1;
}
/* Delegate writing an object to writing a bulk string or bulk long long. */
static int fwriteBulkObject(FILE *fp, robj *obj) {
/* Avoid using getDecodedObject to help copy-on-write (we are often
* in a child process when this function is called). */
if (obj->encoding == REDIS_ENCODING_INT) {
return fwriteBulkLongLong(fp,(long)obj->ptr);
} else if (obj->encoding == REDIS_ENCODING_RAW) {
return fwriteBulkString(fp,obj->ptr,sdslen(obj->ptr));
} else {
redisPanic("Unknown string encoding");
}
}
/* Write a sequence of commands able to fully rebuild the dataset into
* "filename". Used both by REWRITEAOF and BGREWRITEAOF. */
static int rewriteAppendOnlyFile(char *filename) {
...
...
@@ -8688,7 +8984,7 @@ static int rewriteAppendOnlyFile(char *filename) {
/* SELECT the new DB */
if (fwrite(selectcmd,sizeof(selectcmd)-1,1,fp) == 0) goto werr;
if (fwriteBulkLong(fp,j) == 0) goto werr;
if (fwriteBulkLong
Long
(fp,j) == 0) goto werr;
/* Iterate this DB writing every entry */
while((de = dictNext(di)) != NULL) {
...
...
@@ -8723,19 +9019,42 @@ static int rewriteAppendOnlyFile(char *filename) {
if (fwriteBulkObject(fp,o) == 0) goto werr;
} else if (o->type == REDIS_LIST) {
/* Emit the RPUSHes needed to rebuild the list */
char cmd[]="*3\r\n$5\r\nRPUSH\r\n";
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *zl = o->ptr;
unsigned char *p = ziplistIndex(zl,0);
unsigned char *vstr;
unsigned int vlen;
long long vlong;
while(ziplistGet(p,&vstr,&vlen,&vlong)) {
if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
if (fwriteBulkObject(fp,&key) == 0) goto werr;
if (vstr) {
if (fwriteBulkString(fp,(char*)vstr,vlen) == 0)
goto werr;
} else {
if (fwriteBulkLongLong(fp,vlong) == 0)
goto werr;
}
p = ziplistNext(zl,p);
}
} else if (o->encoding == REDIS_ENCODING_LIST) {
list *list = o->ptr;
listNode *ln;
listIter li;
listRewind(list,&li);
while((ln = listNext(&li))) {
char cmd[]="*3\r\n$5\r\nRPUSH\r\n";
robj *eleobj = listNodeValue(ln);
if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
if (fwriteBulkObject(fp,&key) == 0) goto werr;
if (fwriteBulkObject(fp,eleobj) == 0) goto werr;
}
} else {
redisPanic("Unknown list encoding");
}
} else if (o->type == REDIS_SET) {
/* Emit the SADDs needed to rebuild the set */
dict *set = o->ptr;
...
...
@@ -8810,7 +9129,7 @@ static int rewriteAppendOnlyFile(char *filename) {
if (expiretime < now) continue;
if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
if (fwriteBulkObject(fp,&key) == 0) goto werr;
if (fwriteBulkLong(fp,expiretime) == 0) goto werr;
if (fwriteBulkLong
Long
(fp,expiretime) == 0) goto werr;
}
if (swapped) decrRefCount(o);
}
...
...
@@ -10759,16 +11078,14 @@ static void computeDatasetDigest(unsigned char *final) {
if (o->type == REDIS_STRING) {
mixObjectDigest(digest,o);
} else if (o->type == REDIS_LIST) {
list *list = o->ptr;
listNode *ln;
listIter li;
listRewind(list,&li);
while((ln = listNext(&li))) {
robj *eleobj = listNodeValue(ln);
listTypeIterator *li = listTypeInitIterator(o,0,REDIS_TAIL);
listTypeEntry entry;
while(listTypeNext(li,&entry)) {
robj *eleobj = listTypeGet(&entry);
mixObjectDigest(digest,eleobj);
decrRefCount(eleobj);
}
listTypeReleaseIterator(li);
} else if (o->type == REDIS_SET) {
dict *set = o->ptr;
dictIterator *di = dictGetIterator(set);
...
...
staticsymbols.h
View file @
422cf21f
...
...
@@ -100,7 +100,7 @@ static struct redisFunctionSym symsTable[] = {
{
"freeStringObject"
,(
unsigned
long
)
freeStringObject
},
{
"freeZsetObject"
,(
unsigned
long
)
freeZsetObject
},
{
"fwriteBulkDouble"
,(
unsigned
long
)
fwriteBulkDouble
},
{
"fwriteBulkLong"
,(
unsigned
long
)
fwriteBulkLong
},
{
"fwriteBulkLong
Long
"
,(
unsigned
long
)
fwriteBulkLong
Long
},
{
"fwriteBulkObject"
,(
unsigned
long
)
fwriteBulkObject
},
{
"fwriteBulkString"
,(
unsigned
long
)
fwriteBulkString
},
{
"genRedisInfoString"
,(
unsigned
long
)
genRedisInfoString
},
...
...
ziplist.c
0 → 100644
View file @
422cf21f
/* Memory layout of a ziplist, containing "foo", "bar", "quux":
* <zlbytes><zllen><len>"foo"<len>"bar"<len>"quux"
*
* <zlbytes> is an unsigned integer to hold the number of bytes that
* the ziplist occupies. This is stored to not have to traverse the ziplist
* to know the new length when pushing.
*
* <zllen> is the number of items in the ziplist. When this value is
* greater than 254, we need to traverse the entire list to know
* how many items it holds.
*
* <len> is the number of bytes occupied by a single entry. When this
* number is greater than 253, the length will occupy 5 bytes, where
* the extra bytes contain an unsigned integer to hold the length.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <limits.h>
#include "zmalloc.h"
#include "sds.h"
#include "ziplist.h"
/* Important note: the ZIP_END value is used to depict the end of the
* ziplist structure. When a pointer contains an entry, the first couple
* of bytes contain the encoded length of the previous entry. This length
* is encoded as ZIP_ENC_RAW length, so the first two bits will contain 00
* and the byte will therefore never have a value of 255. */
#define ZIP_END 255
#define ZIP_BIGLEN 254
/* Entry encoding */
#define ZIP_ENC_RAW 0
#define ZIP_ENC_INT16 1
#define ZIP_ENC_INT32 2
#define ZIP_ENC_INT64 3
#define ZIP_ENCODING(p) ((p)[0] >> 6)
/* Length encoding for raw entries */
#define ZIP_LEN_INLINE 0
#define ZIP_LEN_UINT16 1
#define ZIP_LEN_UINT32 2
/* Utility macros */
#define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl)))
#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
#define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
#define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))
#define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)
#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+ZIPLIST_TAIL_OFFSET(zl))
#define ZIPLIST_ENTRY_END(zl) ((zl)+ZIPLIST_BYTES(zl)-1)
/* We know a positive increment can only be 1 because entries can only be
* pushed one at a time. */
#define ZIPLIST_INCR_LENGTH(zl,incr) { \
if (ZIPLIST_LENGTH(zl) < UINT16_MAX) ZIPLIST_LENGTH(zl)+=incr; }
typedef
struct
zlentry
{
unsigned
int
prevrawlensize
,
prevrawlen
;
unsigned
int
lensize
,
len
;
unsigned
int
headersize
;
unsigned
char
encoding
;
unsigned
char
*
p
;
}
zlentry
;
/* Return bytes needed to store integer encoded by 'encoding' */
static
unsigned
int
zipEncodingSize
(
unsigned
char
encoding
)
{
if
(
encoding
==
ZIP_ENC_INT16
)
{
return
sizeof
(
int16_t
);
}
else
if
(
encoding
==
ZIP_ENC_INT32
)
{
return
sizeof
(
int32_t
);
}
else
if
(
encoding
==
ZIP_ENC_INT64
)
{
return
sizeof
(
int64_t
);
}
assert
(
NULL
);
}
/* Decode the encoded length pointed by 'p'. If a pointer to 'lensize' is
* provided, it is set to the number of bytes required to encode the length. */
static
unsigned
int
zipDecodeLength
(
unsigned
char
*
p
,
unsigned
int
*
lensize
)
{
unsigned
char
encoding
=
ZIP_ENCODING
(
p
),
lenenc
;
unsigned
int
len
;
if
(
encoding
==
ZIP_ENC_RAW
)
{
lenenc
=
(
p
[
0
]
>>
4
)
&
0x3
;
if
(
lenenc
==
ZIP_LEN_INLINE
)
{
len
=
p
[
0
]
&
0xf
;
if
(
lensize
)
*
lensize
=
1
;
}
else
if
(
lenenc
==
ZIP_LEN_UINT16
)
{
len
=
p
[
1
]
|
(
p
[
2
]
<<
8
);
if
(
lensize
)
*
lensize
=
3
;
}
else
{
len
=
p
[
1
]
|
(
p
[
2
]
<<
8
)
|
(
p
[
3
]
<<
16
)
|
(
p
[
4
]
<<
24
);
if
(
lensize
)
*
lensize
=
5
;
}
}
else
{
len
=
zipEncodingSize
(
encoding
);
if
(
lensize
)
*
lensize
=
1
;
}
return
len
;
}
/* Encode the length 'l' writing it in 'p'. If p is NULL it just returns
* the amount of bytes required to encode such a length. */
static
unsigned
int
zipEncodeLength
(
unsigned
char
*
p
,
char
encoding
,
unsigned
int
rawlen
)
{
unsigned
char
len
=
1
,
lenenc
,
buf
[
5
];
if
(
encoding
==
ZIP_ENC_RAW
)
{
if
(
rawlen
<=
0xf
)
{
if
(
!
p
)
return
len
;
lenenc
=
ZIP_LEN_INLINE
;
buf
[
0
]
=
rawlen
;
}
else
if
(
rawlen
<=
0xffff
)
{
len
+=
2
;
if
(
!
p
)
return
len
;
lenenc
=
ZIP_LEN_UINT16
;
buf
[
1
]
=
(
rawlen
)
&
0xff
;
buf
[
2
]
=
(
rawlen
>>
8
)
&
0xff
;
}
else
{
len
+=
4
;
if
(
!
p
)
return
len
;
lenenc
=
ZIP_LEN_UINT32
;
buf
[
1
]
=
(
rawlen
)
&
0xff
;
buf
[
2
]
=
(
rawlen
>>
8
)
&
0xff
;
buf
[
3
]
=
(
rawlen
>>
16
)
&
0xff
;
buf
[
4
]
=
(
rawlen
>>
24
)
&
0xff
;
}
buf
[
0
]
=
(
lenenc
<<
4
)
|
(
buf
[
0
]
&
0xf
);
}
if
(
!
p
)
return
len
;
/* Apparently we need to store the length in 'p' */
buf
[
0
]
=
(
encoding
<<
6
)
|
(
buf
[
0
]
&
0x3f
);
memcpy
(
p
,
buf
,
len
);
return
len
;
}
/* Decode the length of the previous element stored at "p". */
static
unsigned
int
zipPrevDecodeLength
(
unsigned
char
*
p
,
unsigned
int
*
lensize
)
{
unsigned
int
len
=
*
p
;
if
(
len
<
ZIP_BIGLEN
)
{
if
(
lensize
)
*
lensize
=
1
;
}
else
{
if
(
lensize
)
*
lensize
=
1
+
sizeof
(
len
);
memcpy
(
&
len
,
p
+
1
,
sizeof
(
len
));
}
return
len
;
}
/* Encode the length of the previous entry and write it to "p". Return the
* number of bytes needed to encode this length if "p" is NULL. */
static
unsigned
int
zipPrevEncodeLength
(
unsigned
char
*
p
,
unsigned
int
len
)
{
if
(
p
==
NULL
)
{
return
(
len
<
ZIP_BIGLEN
)
?
1
:
sizeof
(
len
)
+
1
;
}
else
{
if
(
len
<
ZIP_BIGLEN
)
{
p
[
0
]
=
len
;
return
1
;
}
else
{
p
[
0
]
=
ZIP_BIGLEN
;
memcpy
(
p
+
1
,
&
len
,
sizeof
(
len
));
return
1
+
sizeof
(
len
);
}
}
}
/* Return the difference in number of bytes needed to store the new length
* "len" on the entry pointed to by "p". */
static
int
zipPrevLenByteDiff
(
unsigned
char
*
p
,
unsigned
int
len
)
{
unsigned
int
prevlensize
;
zipPrevDecodeLength
(
p
,
&
prevlensize
);
return
zipPrevEncodeLength
(
NULL
,
len
)
-
prevlensize
;
}
/* Check if string pointed to by 'entry' can be encoded as an integer.
* Stores the integer value in 'v' and its encoding in 'encoding'.
* Warning: this function requires a NULL-terminated string! */
static
int
zipTryEncoding
(
unsigned
char
*
entry
,
long
long
*
v
,
unsigned
char
*
encoding
)
{
long
long
value
;
char
*
eptr
;
if
(
entry
[
0
]
==
'-'
||
(
entry
[
0
]
>=
'0'
&&
entry
[
0
]
<=
'9'
))
{
value
=
strtoll
((
char
*
)
entry
,
&
eptr
,
10
);
if
(
eptr
[
0
]
!=
'\0'
)
return
0
;
if
(
value
>=
INT16_MIN
&&
value
<=
INT16_MAX
)
{
*
encoding
=
ZIP_ENC_INT16
;
}
else
if
(
value
>=
INT32_MIN
&&
value
<=
INT32_MAX
)
{
*
encoding
=
ZIP_ENC_INT32
;
}
else
{
*
encoding
=
ZIP_ENC_INT64
;
}
*
v
=
value
;
return
1
;
}
return
0
;
}
/* Store integer 'value' at 'p', encoded as 'encoding' */
static
void
zipSaveInteger
(
unsigned
char
*
p
,
int64_t
value
,
unsigned
char
encoding
)
{
int16_t
i16
;
int32_t
i32
;
int64_t
i64
;
if
(
encoding
==
ZIP_ENC_INT16
)
{
i16
=
value
;
memcpy
(
p
,
&
i16
,
sizeof
(
i16
));
}
else
if
(
encoding
==
ZIP_ENC_INT32
)
{
i32
=
value
;
memcpy
(
p
,
&
i32
,
sizeof
(
i32
));
}
else
if
(
encoding
==
ZIP_ENC_INT64
)
{
i64
=
value
;
memcpy
(
p
,
&
i64
,
sizeof
(
i64
));
}
else
{
assert
(
NULL
);
}
}
/* Read integer encoded as 'encoding' from 'p' */
static
int64_t
zipLoadInteger
(
unsigned
char
*
p
,
unsigned
char
encoding
)
{
int16_t
i16
;
int32_t
i32
;
int64_t
i64
,
ret
;
if
(
encoding
==
ZIP_ENC_INT16
)
{
memcpy
(
&
i16
,
p
,
sizeof
(
i16
));
ret
=
i16
;
}
else
if
(
encoding
==
ZIP_ENC_INT32
)
{
memcpy
(
&
i32
,
p
,
sizeof
(
i32
));
ret
=
i32
;
}
else
if
(
encoding
==
ZIP_ENC_INT64
)
{
memcpy
(
&
i64
,
p
,
sizeof
(
i64
));
ret
=
i64
;
}
else
{
assert
(
NULL
);
}
return
ret
;
}
/* Return a struct with all information about an entry. */
static
zlentry
zipEntry
(
unsigned
char
*
p
)
{
zlentry
e
;
e
.
prevrawlen
=
zipPrevDecodeLength
(
p
,
&
e
.
prevrawlensize
);
e
.
len
=
zipDecodeLength
(
p
+
e
.
prevrawlensize
,
&
e
.
lensize
);
e
.
headersize
=
e
.
prevrawlensize
+
e
.
lensize
;
e
.
encoding
=
ZIP_ENCODING
(
p
+
e
.
prevrawlensize
);
e
.
p
=
p
;
return
e
;
}
/* Return the total number of bytes used by the entry at "p". */
static
unsigned
int
zipRawEntryLength
(
unsigned
char
*
p
)
{
zlentry
e
=
zipEntry
(
p
);
return
e
.
headersize
+
e
.
len
;
}
/* Create a new empty ziplist. */
unsigned
char
*
ziplistNew
(
void
)
{
unsigned
int
bytes
=
ZIPLIST_HEADER_SIZE
+
1
;
unsigned
char
*
zl
=
zmalloc
(
bytes
);
ZIPLIST_BYTES
(
zl
)
=
bytes
;
ZIPLIST_TAIL_OFFSET
(
zl
)
=
ZIPLIST_HEADER_SIZE
;
ZIPLIST_LENGTH
(
zl
)
=
0
;
zl
[
bytes
-
1
]
=
ZIP_END
;
return
zl
;
}
/* Resize the ziplist. */
static
unsigned
char
*
ziplistResize
(
unsigned
char
*
zl
,
unsigned
int
len
)
{
zl
=
zrealloc
(
zl
,
len
);
ZIPLIST_BYTES
(
zl
)
=
len
;
zl
[
len
-
1
]
=
ZIP_END
;
return
zl
;
}
/* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
static
unsigned
char
*
__ziplistDelete
(
unsigned
char
*
zl
,
unsigned
char
*
p
,
unsigned
int
num
)
{
unsigned
int
i
,
totlen
,
deleted
=
0
;
int
nextdiff
=
0
;
zlentry
first
=
zipEntry
(
p
);
for
(
i
=
0
;
p
[
0
]
!=
ZIP_END
&&
i
<
num
;
i
++
)
{
p
+=
zipRawEntryLength
(
p
);
deleted
++
;
}
totlen
=
p
-
first
.
p
;
if
(
totlen
>
0
)
{
if
(
p
[
0
]
!=
ZIP_END
)
{
/* Tricky: storing the prevlen in this entry might reduce or
* increase the number of bytes needed, compared to the current
* prevlen. Note that we can always store this length because
* it was previously stored by an entry that is being deleted. */
nextdiff
=
zipPrevLenByteDiff
(
p
,
first
.
prevrawlen
);
zipPrevEncodeLength
(
p
-
nextdiff
,
first
.
prevrawlen
);
/* Update offset for tail */
ZIPLIST_TAIL_OFFSET
(
zl
)
-=
totlen
+
nextdiff
;
/* Move tail to the front of the ziplist */
memmove
(
first
.
p
,
p
-
nextdiff
,
ZIPLIST_BYTES
(
zl
)
-
(
p
-
zl
)
-
1
+
nextdiff
);
}
else
{
/* The entire tail was deleted. No need to move memory. */
ZIPLIST_TAIL_OFFSET
(
zl
)
=
(
first
.
p
-
zl
)
-
first
.
prevrawlen
;
}
/* Resize and update length */
zl
=
ziplistResize
(
zl
,
ZIPLIST_BYTES
(
zl
)
-
totlen
+
nextdiff
);
ZIPLIST_INCR_LENGTH
(
zl
,
-
deleted
);
}
return
zl
;
}
/* Insert item at "p". */
static
unsigned
char
*
__ziplistInsert
(
unsigned
char
*
zl
,
unsigned
char
*
p
,
unsigned
char
*
s
,
unsigned
int
slen
)
{
unsigned
int
curlen
=
ZIPLIST_BYTES
(
zl
),
reqlen
,
prevlen
=
0
;
unsigned
int
offset
,
nextdiff
=
0
;
unsigned
char
*
tail
;
unsigned
char
encoding
=
ZIP_ENC_RAW
;
long
long
value
;
zlentry
entry
;
/* Find out prevlen for the entry that is inserted. */
if
(
p
[
0
]
!=
ZIP_END
)
{
entry
=
zipEntry
(
p
);
prevlen
=
entry
.
prevrawlen
;
}
else
{
tail
=
ZIPLIST_ENTRY_TAIL
(
zl
);
if
(
tail
[
0
]
!=
ZIP_END
)
{
prevlen
=
zipRawEntryLength
(
tail
);
}
}
/* See if the entry can be encoded */
if
(
zipTryEncoding
(
s
,
&
value
,
&
encoding
))
{
reqlen
=
zipEncodingSize
(
encoding
);
}
else
{
reqlen
=
slen
;
}
/* We need space for both the length of the previous entry and
* the length of the payload. */
reqlen
+=
zipPrevEncodeLength
(
NULL
,
prevlen
);
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. */
nextdiff
=
(
p
[
0
]
!=
ZIP_END
)
?
zipPrevLenByteDiff
(
p
,
reqlen
)
:
0
;
/* Store offset because a realloc may change the address of zl. */
offset
=
p
-
zl
;
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 */
memmove
(
p
+
reqlen
,
p
-
nextdiff
,
curlen
-
offset
-
1
+
nextdiff
);
/* Encode this entry's raw length in the next entry. */
zipPrevEncodeLength
(
p
+
reqlen
,
reqlen
);
/* Update offset for tail */
ZIPLIST_TAIL_OFFSET
(
zl
)
+=
reqlen
+
nextdiff
;
}
else
{
/* This element will be the new tail. */
ZIPLIST_TAIL_OFFSET
(
zl
)
=
p
-
zl
;
}
/* Write the entry */
p
+=
zipPrevEncodeLength
(
p
,
prevlen
);
p
+=
zipEncodeLength
(
p
,
encoding
,
slen
);
if
(
encoding
!=
ZIP_ENC_RAW
)
{
zipSaveInteger
(
p
,
value
,
encoding
);
}
else
{
memcpy
(
p
,
s
,
slen
);
}
ZIPLIST_INCR_LENGTH
(
zl
,
1
);
return
zl
;
}
unsigned
char
*
ziplistPush
(
unsigned
char
*
zl
,
unsigned
char
*
s
,
unsigned
int
slen
,
int
where
)
{
unsigned
char
*
p
;
p
=
(
where
==
ZIPLIST_HEAD
)
?
ZIPLIST_ENTRY_HEAD
(
zl
)
:
ZIPLIST_ENTRY_END
(
zl
);
return
__ziplistInsert
(
zl
,
p
,
s
,
slen
);
}
unsigned
char
*
ziplistPop
(
unsigned
char
*
zl
,
sds
*
target
,
int
where
)
{
zlentry
entry
;
unsigned
char
*
p
;
long
long
value
;
if
(
target
)
*
target
=
NULL
;
/* Get pointer to element to remove */
p
=
(
where
==
ZIPLIST_HEAD
)
?
ZIPLIST_ENTRY_HEAD
(
zl
)
:
ZIPLIST_ENTRY_TAIL
(
zl
);
if
(
*
p
==
ZIP_END
)
return
zl
;
entry
=
zipEntry
(
p
);
if
(
target
)
{
if
(
entry
.
encoding
==
ZIP_ENC_RAW
)
{
*
target
=
sdsnewlen
(
p
+
entry
.
headersize
,
entry
.
len
);
}
else
{
value
=
zipLoadInteger
(
p
+
entry
.
headersize
,
entry
.
encoding
);
*
target
=
sdscatprintf
(
sdsempty
(),
"%lld"
,
value
);
}
}
zl
=
__ziplistDelete
(
zl
,
p
,
1
);
return
zl
;
}
/* Returns an offset to use for iterating with ziplistNext. When the given
* index is negative, the list is traversed back to front. When the list
* doesn't contain an element at the provided index, NULL is returned. */
unsigned
char
*
ziplistIndex
(
unsigned
char
*
zl
,
int
index
)
{
unsigned
char
*
p
;
zlentry
entry
;
if
(
index
<
0
)
{
index
=
(
-
index
)
-
1
;
p
=
ZIPLIST_ENTRY_TAIL
(
zl
);
if
(
p
[
0
]
!=
ZIP_END
)
{
entry
=
zipEntry
(
p
);
while
(
entry
.
prevrawlen
>
0
&&
index
--
)
{
p
-=
entry
.
prevrawlen
;
entry
=
zipEntry
(
p
);
}
}
}
else
{
p
=
ZIPLIST_ENTRY_HEAD
(
zl
);
while
(
p
[
0
]
!=
ZIP_END
&&
index
--
)
{
p
+=
zipRawEntryLength
(
p
);
}
}
return
(
p
[
0
]
==
ZIP_END
||
index
>
0
)
?
NULL
:
p
;
}
/* Return pointer to next entry in ziplist. */
unsigned
char
*
ziplistNext
(
unsigned
char
*
zl
,
unsigned
char
*
p
)
{
((
void
)
zl
);
/* "p" could be equal to ZIP_END, caused by ziplistDelete,
* and we should return NULL. Otherwise, we should return NULL
* when the *next* element is ZIP_END (there is no next entry). */
if
(
p
[
0
]
==
ZIP_END
)
{
return
NULL
;
}
else
{
p
=
p
+
zipRawEntryLength
(
p
);
return
(
p
[
0
]
==
ZIP_END
)
?
NULL
:
p
;
}
}
/* Return pointer to previous entry in ziplist. */
unsigned
char
*
ziplistPrev
(
unsigned
char
*
zl
,
unsigned
char
*
p
)
{
zlentry
entry
;
/* Iterating backwards from ZIP_END should return the tail. When "p" is
* equal to the first element of the list, we're already at the head,
* and should return NULL. */
if
(
p
[
0
]
==
ZIP_END
)
{
p
=
ZIPLIST_ENTRY_TAIL
(
zl
);
return
(
p
[
0
]
==
ZIP_END
)
?
NULL
:
p
;
}
else
if
(
p
==
ZIPLIST_ENTRY_HEAD
(
zl
))
{
return
NULL
;
}
else
{
entry
=
zipEntry
(
p
);
return
p
-
entry
.
prevrawlen
;
}
}
/* Get entry pointer to by 'p' and store in either 'e' or 'v' depending
* on the encoding of the entry. 'e' is always set to NULL to be able
* to find out whether the string pointer or the integer value was set.
* Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */
unsigned
int
ziplistGet
(
unsigned
char
*
p
,
unsigned
char
**
sstr
,
unsigned
int
*
slen
,
long
long
*
sval
)
{
zlentry
entry
;
if
(
p
==
NULL
||
p
[
0
]
==
ZIP_END
)
return
0
;
if
(
sstr
)
*
sstr
=
NULL
;
entry
=
zipEntry
(
p
);
if
(
entry
.
encoding
==
ZIP_ENC_RAW
)
{
if
(
sstr
)
{
*
slen
=
entry
.
len
;
*
sstr
=
p
+
entry
.
headersize
;
}
}
else
{
if
(
sval
)
{
*
sval
=
zipLoadInteger
(
p
+
entry
.
headersize
,
entry
.
encoding
);
}
}
return
1
;
}
/* Insert an entry at "p". */
unsigned
char
*
ziplistInsert
(
unsigned
char
*
zl
,
unsigned
char
*
p
,
unsigned
char
*
s
,
unsigned
int
slen
)
{
return
__ziplistInsert
(
zl
,
p
,
s
,
slen
);
}
/* Delete a single entry from the ziplist, pointed to by *p.
* Also update *p in place, to be able to iterate over the
* ziplist, while deleting entries. */
unsigned
char
*
ziplistDelete
(
unsigned
char
*
zl
,
unsigned
char
**
p
)
{
unsigned
int
offset
=
*
p
-
zl
;
zl
=
__ziplistDelete
(
zl
,
*
p
,
1
);
/* Store pointer to current element in p, because ziplistDelete will
* do a realloc which might result in a different "zl"-pointer.
* When the delete direction is back to front, we might delete the last
* entry and end up with "p" pointing to ZIP_END, so check this. */
*
p
=
zl
+
offset
;
return
zl
;
}
/* Delete a range of entries from the ziplist. */
unsigned
char
*
ziplistDeleteRange
(
unsigned
char
*
zl
,
unsigned
int
index
,
unsigned
int
num
)
{
unsigned
char
*
p
=
ziplistIndex
(
zl
,
index
);
return
(
p
==
NULL
)
?
zl
:
__ziplistDelete
(
zl
,
p
,
num
);
}
/* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */
unsigned
int
ziplistCompare
(
unsigned
char
*
p
,
unsigned
char
*
sstr
,
unsigned
int
slen
)
{
zlentry
entry
;
unsigned
char
sencoding
;
long
long
zval
,
sval
;
if
(
p
[
0
]
==
ZIP_END
)
return
0
;
entry
=
zipEntry
(
p
);
if
(
entry
.
encoding
==
ZIP_ENC_RAW
)
{
/* Raw compare */
if
(
entry
.
len
==
slen
)
{
return
memcmp
(
p
+
entry
.
headersize
,
sstr
,
slen
)
==
0
;
}
else
{
return
0
;
}
}
else
{
/* Try to compare encoded values */
if
(
zipTryEncoding
(
sstr
,
&
sval
,
&
sencoding
))
{
if
(
entry
.
encoding
==
sencoding
)
{
zval
=
zipLoadInteger
(
p
+
entry
.
headersize
,
entry
.
encoding
);
return
zval
==
sval
;
}
}
}
return
0
;
}
/* Return length of ziplist. */
unsigned
int
ziplistLen
(
unsigned
char
*
zl
)
{
unsigned
int
len
=
0
;
if
(
ZIPLIST_LENGTH
(
zl
)
<
UINT16_MAX
)
{
len
=
ZIPLIST_LENGTH
(
zl
);
}
else
{
unsigned
char
*
p
=
zl
+
ZIPLIST_HEADER_SIZE
;
while
(
*
p
!=
ZIP_END
)
{
p
+=
zipRawEntryLength
(
p
);
len
++
;
}
/* Re-store length if small enough */
if
(
len
<
UINT16_MAX
)
ZIPLIST_LENGTH
(
zl
)
=
len
;
}
return
len
;
}
/* Return size in bytes of ziplist. */
unsigned
int
ziplistSize
(
unsigned
char
*
zl
)
{
return
ZIPLIST_BYTES
(
zl
);
}
void
ziplistRepr
(
unsigned
char
*
zl
)
{
unsigned
char
*
p
;
zlentry
entry
;
printf
(
"{total bytes %d} {length %u}
\n
"
,
ZIPLIST_BYTES
(
zl
),
ZIPLIST_LENGTH
(
zl
));
p
=
ZIPLIST_ENTRY_HEAD
(
zl
);
while
(
*
p
!=
ZIP_END
)
{
entry
=
zipEntry
(
p
);
printf
(
"{offset %ld, header %u, payload %u} "
,
p
-
zl
,
entry
.
headersize
,
entry
.
len
);
p
+=
entry
.
headersize
;
if
(
entry
.
encoding
==
ZIP_ENC_RAW
)
{
fwrite
(
p
,
entry
.
len
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
zipLoadInteger
(
p
,
entry
.
encoding
));
}
printf
(
"
\n
"
);
p
+=
entry
.
len
;
}
printf
(
"{end}
\n\n
"
);
}
#ifdef ZIPLIST_TEST_MAIN
#include <sys/time.h>
unsigned
char
*
createList
()
{
unsigned
char
*
zl
=
ziplistNew
();
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
"foo"
,
3
,
ZIPLIST_TAIL
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
"quux"
,
4
,
ZIPLIST_TAIL
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
"hello"
,
5
,
ZIPLIST_HEAD
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
"1024"
,
4
,
ZIPLIST_TAIL
);
return
zl
;
}
unsigned
char
*
createIntList
()
{
unsigned
char
*
zl
=
ziplistNew
();
char
buf
[
32
];
sprintf
(
buf
,
"100"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_TAIL
);
sprintf
(
buf
,
"128000"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_TAIL
);
sprintf
(
buf
,
"-100"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_HEAD
);
sprintf
(
buf
,
"4294967296"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_HEAD
);
sprintf
(
buf
,
"non integer"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_TAIL
);
sprintf
(
buf
,
"much much longer non integer"
);
zl
=
ziplistPush
(
zl
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
),
ZIPLIST_TAIL
);
return
zl
;
}
long
long
usec
(
void
)
{
struct
timeval
tv
;
gettimeofday
(
&
tv
,
NULL
);
return
(((
long
long
)
tv
.
tv_sec
)
*
1000000
)
+
tv
.
tv_usec
;
}
void
stress
(
int
pos
,
int
num
,
int
maxsize
,
int
dnum
)
{
int
i
,
j
,
k
;
unsigned
char
*
zl
;
char
posstr
[
2
][
5
]
=
{
"HEAD"
,
"TAIL"
};
long
long
start
;
for
(
i
=
0
;
i
<
maxsize
;
i
+=
dnum
)
{
zl
=
ziplistNew
();
for
(
j
=
0
;
j
<
i
;
j
++
)
{
zl
=
ziplistPush
(
zl
,(
unsigned
char
*
)
"quux"
,
4
,
ZIPLIST_TAIL
);
}
/* Do num times a push+pop from pos */
start
=
usec
();
for
(
k
=
0
;
k
<
num
;
k
++
)
{
zl
=
ziplistPush
(
zl
,(
unsigned
char
*
)
"quux"
,
4
,
pos
);
zl
=
ziplistDeleteRange
(
zl
,
0
,
1
);
}
printf
(
"List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec
\n
"
,
i
,
ZIPLIST_BYTES
(
zl
),
num
,
posstr
[
pos
],
usec
()
-
start
);
zfree
(
zl
);
}
}
int
main
(
int
argc
,
char
**
argv
)
{
unsigned
char
*
zl
,
*
p
;
unsigned
char
*
entry
;
unsigned
int
elen
;
long
long
value
;
sds
s
;
zl
=
createIntList
();
ziplistRepr
(
zl
);
zl
=
createList
();
ziplistRepr
(
zl
);
zl
=
ziplistPop
(
zl
,
&
s
,
ZIPLIST_TAIL
);
printf
(
"Pop tail: %s (length %ld)
\n
"
,
s
,
sdslen
(
s
));
ziplistRepr
(
zl
);
zl
=
ziplistPop
(
zl
,
&
s
,
ZIPLIST_HEAD
);
printf
(
"Pop head: %s (length %ld)
\n
"
,
s
,
sdslen
(
s
));
ziplistRepr
(
zl
);
zl
=
ziplistPop
(
zl
,
&
s
,
ZIPLIST_TAIL
);
printf
(
"Pop tail: %s (length %ld)
\n
"
,
s
,
sdslen
(
s
));
ziplistRepr
(
zl
);
zl
=
ziplistPop
(
zl
,
&
s
,
ZIPLIST_TAIL
);
printf
(
"Pop tail: %s (length %ld)
\n
"
,
s
,
sdslen
(
s
));
ziplistRepr
(
zl
);
printf
(
"Get element at index 3:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
3
);
if
(
!
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"ERROR: Could not access index 3
\n
"
);
return
1
;
}
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
printf
(
"
\n
"
);
}
else
{
printf
(
"%lld
\n
"
,
value
);
}
printf
(
"
\n
"
);
}
printf
(
"Get element at index 4 (out of range):
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
4
);
if
(
p
==
NULL
)
{
printf
(
"No entry
\n
"
);
}
else
{
printf
(
"ERROR: Out of range index should return NULL, returned offset: %ld
\n
"
,
p
-
zl
);
return
1
;
}
printf
(
"
\n
"
);
}
printf
(
"Get element at index -1 (last element):
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
-
1
);
if
(
!
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"ERROR: Could not access index -1
\n
"
);
return
1
;
}
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
printf
(
"
\n
"
);
}
else
{
printf
(
"%lld
\n
"
,
value
);
}
printf
(
"
\n
"
);
}
printf
(
"Get element at index -4 (first element):
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
-
4
);
if
(
!
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"ERROR: Could not access index -4
\n
"
);
return
1
;
}
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
printf
(
"
\n
"
);
}
else
{
printf
(
"%lld
\n
"
,
value
);
}
printf
(
"
\n
"
);
}
printf
(
"Get element at index -5 (reverse out of range):
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
-
5
);
if
(
p
==
NULL
)
{
printf
(
"No entry
\n
"
);
}
else
{
printf
(
"ERROR: Out of range index should return NULL, returned offset: %ld
\n
"
,
p
-
zl
);
return
1
;
}
printf
(
"
\n
"
);
}
printf
(
"Iterate list from 0 to end:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
0
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
p
=
ziplistNext
(
zl
,
p
);
printf
(
"
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Iterate list from 1 to end:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
1
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
p
=
ziplistNext
(
zl
,
p
);
printf
(
"
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Iterate list from 2 to end:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
2
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
p
=
ziplistNext
(
zl
,
p
);
printf
(
"
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Iterate starting out of range:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
4
);
if
(
!
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"No entry
\n
"
);
}
else
{
printf
(
"ERROR
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Iterate from back to front:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
-
1
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
p
=
ziplistPrev
(
zl
,
p
);
printf
(
"
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Iterate from back to front, deleting all items:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
-
1
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
zl
=
ziplistDelete
(
zl
,
&
p
);
p
=
ziplistPrev
(
zl
,
p
);
printf
(
"
\n
"
);
}
printf
(
"
\n
"
);
}
printf
(
"Delete inclusive range 0,0:
\n
"
);
{
zl
=
createList
();
zl
=
ziplistDeleteRange
(
zl
,
0
,
1
);
ziplistRepr
(
zl
);
}
printf
(
"Delete inclusive range 0,1:
\n
"
);
{
zl
=
createList
();
zl
=
ziplistDeleteRange
(
zl
,
0
,
2
);
ziplistRepr
(
zl
);
}
printf
(
"Delete inclusive range 1,2:
\n
"
);
{
zl
=
createList
();
zl
=
ziplistDeleteRange
(
zl
,
1
,
2
);
ziplistRepr
(
zl
);
}
printf
(
"Delete with start index out of range:
\n
"
);
{
zl
=
createList
();
zl
=
ziplistDeleteRange
(
zl
,
5
,
1
);
ziplistRepr
(
zl
);
}
printf
(
"Delete with num overflow:
\n
"
);
{
zl
=
createList
();
zl
=
ziplistDeleteRange
(
zl
,
1
,
5
);
ziplistRepr
(
zl
);
}
printf
(
"Delete foo while iterating:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
0
);
while
(
ziplistGet
(
p
,
&
entry
,
&
elen
,
&
value
))
{
if
(
entry
&&
strncmp
(
"foo"
,(
char
*
)
entry
,
elen
)
==
0
)
{
printf
(
"Delete foo
\n
"
);
zl
=
ziplistDelete
(
zl
,
&
p
);
}
else
{
printf
(
"Entry: "
);
if
(
entry
)
{
fwrite
(
entry
,
elen
,
1
,
stdout
);
}
else
{
printf
(
"%lld"
,
value
);
}
p
=
ziplistNext
(
zl
,
p
);
printf
(
"
\n
"
);
}
}
printf
(
"
\n
"
);
ziplistRepr
(
zl
);
}
printf
(
"Create long list and check indices:
\n
"
);
{
zl
=
ziplistNew
();
char
buf
[
32
];
int
i
,
len
;
for
(
i
=
0
;
i
<
1000
;
i
++
)
{
len
=
sprintf
(
buf
,
"%d"
,
i
);
zl
=
ziplistPush
(
zl
,(
unsigned
char
*
)
buf
,
len
,
ZIPLIST_TAIL
);
}
for
(
i
=
0
;
i
<
1000
;
i
++
)
{
p
=
ziplistIndex
(
zl
,
i
);
assert
(
ziplistGet
(
p
,
NULL
,
NULL
,
&
value
));
assert
(
i
==
value
);
p
=
ziplistIndex
(
zl
,
-
i
-
1
);
assert
(
ziplistGet
(
p
,
NULL
,
NULL
,
&
value
));
assert
(
999
-
i
==
value
);
}
printf
(
"SUCCESS
\n\n
"
);
}
printf
(
"Compare strings with ziplist entries:
\n
"
);
{
zl
=
createList
();
p
=
ziplistIndex
(
zl
,
0
);
if
(
!
ziplistCompare
(
p
,(
unsigned
char
*
)
"hello"
,
5
))
{
printf
(
"ERROR: not
\"
hello
\"\n
"
);
return
1
;
}
if
(
ziplistCompare
(
p
,(
unsigned
char
*
)
"hella"
,
5
))
{
printf
(
"ERROR:
\"
hella
\"\n
"
);
return
1
;
}
p
=
ziplistIndex
(
zl
,
3
);
if
(
!
ziplistCompare
(
p
,(
unsigned
char
*
)
"1024"
,
4
))
{
printf
(
"ERROR: not
\"
1024
\"\n
"
);
return
1
;
}
if
(
ziplistCompare
(
p
,(
unsigned
char
*
)
"1025"
,
4
))
{
printf
(
"ERROR:
\"
1025
\"\n
"
);
return
1
;
}
printf
(
"SUCCESS
\n
"
);
}
printf
(
"Stress with variable ziplist size:
\n
"
);
{
stress
(
ZIPLIST_HEAD
,
100000
,
16384
,
256
);
stress
(
ZIPLIST_TAIL
,
100000
,
16384
,
256
);
}
return
0
;
}
#endif
ziplist.h
0 → 100644
View file @
422cf21f
#define ZIPLIST_HEAD 0
#define ZIPLIST_TAIL 1
unsigned
char
*
ziplistNew
(
void
);
unsigned
char
*
ziplistPush
(
unsigned
char
*
zl
,
unsigned
char
*
s
,
unsigned
int
slen
,
int
where
);
unsigned
char
*
ziplistPop
(
unsigned
char
*
zl
,
sds
*
target
,
int
where
);
unsigned
char
*
ziplistIndex
(
unsigned
char
*
zl
,
int
index
);
unsigned
char
*
ziplistNext
(
unsigned
char
*
zl
,
unsigned
char
*
p
);
unsigned
char
*
ziplistPrev
(
unsigned
char
*
zl
,
unsigned
char
*
p
);
unsigned
int
ziplistGet
(
unsigned
char
*
p
,
unsigned
char
**
sval
,
unsigned
int
*
slen
,
long
long
*
lval
);
unsigned
char
*
ziplistInsert
(
unsigned
char
*
zl
,
unsigned
char
*
p
,
unsigned
char
*
s
,
unsigned
int
slen
);
unsigned
char
*
ziplistDelete
(
unsigned
char
*
zl
,
unsigned
char
**
p
);
unsigned
char
*
ziplistDeleteRange
(
unsigned
char
*
zl
,
unsigned
int
index
,
unsigned
int
num
);
unsigned
int
ziplistCompare
(
unsigned
char
*
p
,
unsigned
char
*
s
,
unsigned
int
slen
);
unsigned
int
ziplistLen
(
unsigned
char
*
zl
);
unsigned
int
ziplistSize
(
unsigned
char
*
zl
);
zipmap.c
View file @
422cf21f
...
...
@@ -176,7 +176,6 @@ static unsigned long zipmapRequiredLength(unsigned int klen, unsigned int vlen)
/* 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
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment