unsigned lru:22; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr;
- /* VM fields, this are only allocated if VM is active, otherwise the
+ /* VM fields are only allocated if VM is active, otherwise the
* object allocation function will just allocate
* sizeof(redisObjct) minus sizeof(redisObjectVM), so using
* Redis without VM active will not have any overhead. */
static void call(redisClient *c, struct redisCommand *cmd);
static void resetClient(redisClient *c);
static void convertToRealHash(robj *o);
-static void convertList(robj *o, int enc);
+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);
static void renamenxCommand(redisClient *c);
static void lpushCommand(redisClient *c);
static void rpushCommand(redisClient *c);
+static void lpushxCommand(redisClient *c);
+static void rpushxCommand(redisClient *c);
+static void linsertCommand(redisClient *c);
static void lpopCommand(redisClient *c);
static void rpopCommand(redisClient *c);
static void llenCommand(redisClient *c);
{"mget",mgetCommand,-2,REDIS_CMD_INLINE,NULL,1,-1,1},
{"rpush",rpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
{"lpush",lpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"rpushx",rpushxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"lpushx",lpushxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"linsert",linsertCommand,5,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
{"rpop",rpopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
{"lpop",lpopCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
{"brpop",brpopCommand,-3,REDIS_CMD_INLINE,NULL,1,1,1},
if (o->encoding == REDIS_ENCODING_ZIPLIST &&
ele->encoding == REDIS_ENCODING_RAW &&
sdslen(ele->ptr) > server.list_max_ziplist_value)
- convertList(o,REDIS_ENCODING_LIST);
+ listTypeConvert(o,REDIS_ENCODING_LIST);
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
dec = getDecodedObject(ele);
} else {
ele = tryObjectEncoding(ele);
listAddNodeTail(o->ptr,ele);
- incrRefCount(ele);
}
}
} else if (type == REDIS_SET) {
/* 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 listTryConversion(robj *subject, robj *value) {
+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)
- convertList(subject,REDIS_ENCODING_LIST);
+ listTypeConvert(subject,REDIS_ENCODING_LIST);
}
-static void lPush(robj *subject, robj *value, int where) {
+static void listTypePush(robj *subject, robj *value, int where) {
/* Check if we need to convert the ziplist */
- listTryConversion(subject,value);
+ listTypeTryConversion(subject,value);
if (subject->encoding == REDIS_ENCODING_ZIPLIST &&
- ziplistLen(subject->ptr) > server.list_max_ziplist_entries)
- convertList(subject,REDIS_ENCODING_LIST);
+ 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;
}
}
-static robj *lPop(robj *subject, int where) {
+static robj *listTypePop(robj *subject, int where) {
robj *value = NULL;
if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *p;
return value;
}
-static unsigned long lLength(robj *subject) {
+static unsigned long listTypeLength(robj *subject) {
if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
return ziplistLen(subject->ptr);
} else if (subject->encoding == REDIS_ENCODING_LIST) {
unsigned char direction; /* Iteration direction */
unsigned char *zi;
listNode *ln;
-} lIterator;
+} listTypeIterator;
/* Structure for an entry while iterating over a list. */
typedef struct {
- lIterator *li;
+ listTypeIterator *li;
unsigned char *zi; /* Entry in ziplist */
listNode *ln; /* Entry in linked list */
-} lEntry;
+} listTypeEntry;
/* Initialize an iterator at the specified index. */
-static lIterator *lInitIterator(robj *subject, int index, unsigned char direction) {
- lIterator *li = zmalloc(sizeof(lIterator));
+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;
}
/* Clean up the iterator. */
-static void lReleaseIterator(lIterator *li) {
+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 lNext(lIterator *li, lEntry *entry) {
+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;
}
/* Return entry or NULL at the current position of the iterator. */
-static robj *lGet(lEntry *entry) {
- lIterator *li = entry->li;
+static robj *listTypeGet(listTypeEntry *entry) {
+ listTypeIterator *li = entry->li;
robj *value = NULL;
if (li->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *vstr;
return value;
}
+static void listTypeInsert(listTypeEntry *entry, robj *value, int where) {
+ robj *subject = entry->li->subject;
+ if (entry->li->encoding == REDIS_ENCODING_ZIPLIST) {
+ value = getDecodedObject(value);
+ if (where == REDIS_TAIL) {
+ unsigned char *next = ziplistNext(subject->ptr,entry->zi);
+
+ /* When we insert after the current element, but the current element
+ * is the tail of the list, we need to do a push. */
+ if (next == NULL) {
+ subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),REDIS_TAIL);
+ } else {
+ subject->ptr = ziplistInsert(subject->ptr,next,value->ptr,sdslen(value->ptr));
+ }
+ } else {
+ subject->ptr = ziplistInsert(subject->ptr,entry->zi,value->ptr,sdslen(value->ptr));
+ }
+ decrRefCount(value);
+ } else if (entry->li->encoding == REDIS_ENCODING_LIST) {
+ if (where == REDIS_TAIL) {
+ listInsertNode(subject->ptr,entry->ln,value,AL_START_TAIL);
+ } else {
+ listInsertNode(subject->ptr,entry->ln,value,AL_START_HEAD);
+ }
+ incrRefCount(value);
+ } else {
+ redisPanic("Unknown list encoding");
+ }
+}
+
/* Compare the given object with the entry at the current position. */
-static int lEqual(lEntry *entry, robj *o) {
- lIterator *li = entry->li;
+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));
}
/* Delete the element pointed to. */
-static void lDelete(lEntry *entry) {
- lIterator *li = entry->li;
+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);
}
}
-static void convertList(robj *subject, int enc) {
- lIterator *li;
- lEntry entry;
+static void listTypeConvert(robj *subject, int enc) {
+ listTypeIterator *li;
+ listTypeEntry entry;
redisAssert(subject->type == REDIS_LIST);
if (enc == REDIS_ENCODING_LIST) {
list *l = listCreate();
+ listSetFreeMethod(l,decrRefCount);
- /* lGet returns a robj with incremented refcount */
- li = lInitIterator(subject,0,REDIS_TAIL);
- while (lNext(li,&entry)) listAddNodeTail(l,lGet(&entry));
- lReleaseIterator(li);
+ /* 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);
return;
}
}
- lPush(lobj,c->argv[2],where);
- addReplyLongLong(c,lLength(lobj));
+ listTypePush(lobj,c->argv[2],where);
+ addReplyLongLong(c,listTypeLength(lobj));
server.dirty++;
}
pushGenericCommand(c,REDIS_TAIL);
}
+static void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
+ robj *subject;
+ listTypeIterator *iter;
+ listTypeEntry entry;
+ int inserted = 0;
+
+ if ((subject = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
+ checkType(c,subject,REDIS_LIST)) return;
+ if (handleClientsWaitingListPush(c,c->argv[1],val)) {
+ addReply(c,shared.cone);
+ return;
+ }
+
+ if (refval != NULL) {
+ /* Note: we expect refval to be string-encoded because it is *not* the
+ * last argument of the multi-bulk LINSERT. */
+ redisAssert(refval->encoding == REDIS_ENCODING_RAW);
+
+ /* We're not sure if this value can be inserted yet, but we cannot
+ * convert the list inside the iterator. We don't want to loop over
+ * the list twice (once to see if the value can be inserted and once
+ * to do the actual insert), so we assume this value can be inserted
+ * and convert the ziplist to a regular list if necessary. */
+ listTypeTryConversion(subject,val);
+
+ /* Seek refval from head to tail */
+ iter = listTypeInitIterator(subject,0,REDIS_TAIL);
+ while (listTypeNext(iter,&entry)) {
+ if (listTypeEqual(&entry,refval)) {
+ listTypeInsert(&entry,val,where);
+ inserted = 1;
+ break;
+ }
+ }
+ listTypeReleaseIterator(iter);
+
+ if (inserted) {
+ /* Check if the length exceeds the ziplist length threshold. */
+ if (subject->encoding == REDIS_ENCODING_ZIPLIST &&
+ ziplistLen(subject->ptr) > server.list_max_ziplist_entries)
+ listTypeConvert(subject,REDIS_ENCODING_LIST);
+ server.dirty++;
+ }
+ } else {
+ listTypePush(subject,val,where);
+ server.dirty++;
+ }
+
+ addReplyUlong(c,listTypeLength(subject));
+}
+
+static void lpushxCommand(redisClient *c) {
+ pushxGenericCommand(c,NULL,c->argv[2],REDIS_HEAD);
+}
+
+static void rpushxCommand(redisClient *c) {
+ pushxGenericCommand(c,NULL,c->argv[2],REDIS_TAIL);
+}
+
+static void linsertCommand(redisClient *c) {
+ if (strcasecmp(c->argv[2]->ptr,"after") == 0) {
+ pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_TAIL);
+ } else if (strcasecmp(c->argv[2]->ptr,"before") == 0) {
+ pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_HEAD);
+ } else {
+ addReply(c,shared.syntaxerr);
+ }
+}
+
static void llenCommand(redisClient *c) {
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
- addReplyUlong(c,lLength(o));
+ addReplyUlong(c,listTypeLength(o));
}
static void lindexCommand(redisClient *c) {
int index = atoi(c->argv[2]->ptr);
robj *value = c->argv[3];
- listTryConversion(o,value);
+ listTypeTryConversion(o,value);
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *p, *zl = o->ptr;
p = ziplistIndex(zl,index);
robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk);
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
- robj *value = lPop(o,where);
+ robj *value = listTypePop(o,where);
if (value == NULL) {
addReply(c,shared.nullbulk);
} else {
addReplyBulk(c,value);
decrRefCount(value);
- if (lLength(o) == 0) dbDelete(c->db,c->argv[1]);
+ if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
server.dirty++;
}
}
int end = atoi(c->argv[3]->ptr);
int llen;
int rangelen, j;
- lEntry entry;
+ listTypeEntry entry;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
|| checkType(c,o,REDIS_LIST)) return;
- llen = lLength(o);
+ llen = listTypeLength(o);
/* convert negative indexes */
if (start < 0) start = llen+start;
/* Return the result in form of a multi-bulk reply */
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen));
- lIterator *li = lInitIterator(o,start,REDIS_TAIL);
+ listTypeIterator *li = listTypeInitIterator(o,start,REDIS_TAIL);
for (j = 0; j < rangelen; j++) {
- redisAssert(lNext(li,&entry));
- value = lGet(&entry);
+ redisAssert(listTypeNext(li,&entry));
+ value = listTypeGet(&entry);
addReplyBulk(c,value);
decrRefCount(value);
}
- lReleaseIterator(li);
+ listTypeReleaseIterator(li);
}
static void ltrimCommand(redisClient *c) {
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.ok)) == NULL ||
checkType(c,o,REDIS_LIST)) return;
- llen = lLength(o);
+ llen = listTypeLength(o);
/* convert negative indexes */
if (start < 0) start = llen+start;
} else {
redisPanic("Unknown list encoding");
}
- if (lLength(o) == 0) dbDelete(c->db,c->argv[1]);
+ if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
server.dirty++;
addReply(c,shared.ok);
}
robj *subject, *obj = c->argv[3];
int toremove = atoi(c->argv[2]->ptr);
int removed = 0;
- lEntry entry;
+ listTypeEntry entry;
subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero);
if (subject == NULL || checkType(c,subject,REDIS_LIST)) return;
if (subject->encoding == REDIS_ENCODING_ZIPLIST)
obj = getDecodedObject(obj);
- lIterator *li;
+ listTypeIterator *li;
if (toremove < 0) {
toremove = -toremove;
- li = lInitIterator(subject,-1,REDIS_HEAD);
+ li = listTypeInitIterator(subject,-1,REDIS_HEAD);
} else {
- li = lInitIterator(subject,0,REDIS_TAIL);
+ li = listTypeInitIterator(subject,0,REDIS_TAIL);
}
- while (lNext(li,&entry)) {
- if (lEqual(&entry,obj)) {
- lDelete(&entry);
+ while (listTypeNext(li,&entry)) {
+ if (listTypeEqual(&entry,obj)) {
+ listTypeDelete(&entry);
server.dirty++;
removed++;
if (toremove && removed == toremove) break;
}
}
- lReleaseIterator(li);
+ listTypeReleaseIterator(li);
/* Clean up raw encoded object */
if (subject->encoding == REDIS_ENCODING_ZIPLIST)
decrRefCount(obj);
- if (lLength(subject) == 0) dbDelete(c->db,c->argv[1]);
+ if (listTypeLength(subject) == 0) dbDelete(c->db,c->argv[1]);
addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",removed));
}
if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,sobj,REDIS_LIST)) return;
- if (lLength(sobj) == 0) {
+ if (listTypeLength(sobj) == 0) {
addReply(c,shared.nullbulk);
} else {
robj *dobj = lookupKeyWrite(c->db,c->argv[2]);
if (dobj && checkType(c,dobj,REDIS_LIST)) return;
- value = lPop(sobj,REDIS_TAIL);
+ value = listTypePop(sobj,REDIS_TAIL);
/* Add the element to the target list (unless it's directly
* passed to some BLPOP-ing client */
dobj = createZiplistObject();
dbAdd(c->db,c->argv[2],dobj);
}
- lPush(dobj,value,REDIS_HEAD);
+ listTypePush(dobj,value,REDIS_HEAD);
}
/* Send the element to the client as reply as well */
addReplyBulk(c,value);
- /* lPop returns an object with its refcount incremented */
+ /* listTypePop returns an object with its refcount incremented */
decrRefCount(value);
/* Delete the source list when it is empty */
- if (lLength(sobj) == 0) dbDelete(c->db,c->argv[1]);
+ if (listTypeLength(sobj) == 0) dbDelete(c->db,c->argv[1]);
server.dirty++;
}
}
* 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 zslistTypeGetRank(zskiplist *zsl, double score, robj *o) {
zskiplistNode *x;
unsigned long rank = 0;
int i;
}
/* Finds an element by its rank. The rank argument needs to be 1-based. */
-zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) {
+zskiplistNode* zslistTypeGetElementByRank(zskiplist *zsl, unsigned long rank) {
zskiplistNode *x;
unsigned long traversed = 0;
int i;
/* 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 : zslistTypeGetElementByRank(zsl, llen-start);
} else {
ln = start == 0 ?
- zsl->header->forward[0] : zslGetElementByRank(zsl, start+1);
+ zsl->header->forward[0] : zslistTypeGetElementByRank(zsl, start+1);
}
/* Return the result in form of a multi-bulk reply */
}
score = dictGetEntryVal(de);
- rank = zslGetRank(zsl, *score, c->argv[2]);
+ rank = zslistTypeGetRank(zsl, *score, c->argv[2]);
if (rank) {
if (reverse) {
addReplyLongLong(c, zsl->length - rank);
/* Check the length of a number of objects to see if we need to convert a
* zipmap to a real hash. Note that we only check string encoded objects
* as their string length can be queried in constant time. */
-static void hashTryConversion(robj *subject, robj **argv, int start, int end) {
+static void hashTypeTryConversion(robj *subject, robj **argv, int start, int end) {
int i;
if (subject->encoding != REDIS_ENCODING_ZIPMAP) return;
}
/* Encode given objects in-place when the hash uses a dict. */
-static void hashTryObjectEncoding(robj *subject, robj **o1, robj **o2) {
+static void hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2) {
if (subject->encoding == REDIS_ENCODING_HT) {
if (o1) *o1 = tryObjectEncoding(*o1);
if (o2) *o2 = tryObjectEncoding(*o2);
/* Get the value from a hash identified by key. Returns either a string
* object or NULL if the value cannot be found. The refcount of the object
* is always increased by 1 when the value was found. */
-static robj *hashGet(robj *o, robj *key) {
+static robj *hashTypeGet(robj *o, robj *key) {
robj *value = NULL;
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
unsigned char *v;
/* Test if the key exists in the given hash. Returns 1 if the key
* exists and 0 when it doesn't. */
-static int hashExists(robj *o, robj *key) {
+static int hashTypeExists(robj *o, robj *key) {
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
key = getDecodedObject(key);
if (zipmapExists(o->ptr,key->ptr,sdslen(key->ptr))) {
/* Add an element, discard the old if the key already exists.
* Return 0 on insert and 1 on update. */
-static int hashSet(robj *o, robj *key, robj *value) {
+static int hashTypeSet(robj *o, robj *key, robj *value) {
int update = 0;
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
key = getDecodedObject(key);
/* Delete an element from a hash.
* Return 1 on deleted and 0 on not found. */
-static int hashDelete(robj *o, robj *key) {
+static int hashTypeDelete(robj *o, robj *key) {
int deleted = 0;
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
key = getDecodedObject(key);
}
/* Return the number of elements in a hash. */
-static unsigned long hashLength(robj *o) {
+static unsigned long hashTypeLength(robj *o) {
return (o->encoding == REDIS_ENCODING_ZIPMAP) ?
zipmapLen((unsigned char*)o->ptr) : dictSize((dict*)o->ptr);
}
dictIterator *di;
dictEntry *de;
-} hashIterator;
+} hashTypeIterator;
-static hashIterator *hashInitIterator(robj *subject) {
- hashIterator *hi = zmalloc(sizeof(hashIterator));
+static hashTypeIterator *hashTypeInitIterator(robj *subject) {
+ hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator));
hi->encoding = subject->encoding;
if (hi->encoding == REDIS_ENCODING_ZIPMAP) {
hi->zi = zipmapRewind(subject->ptr);
return hi;
}
-static void hashReleaseIterator(hashIterator *hi) {
+static void hashTypeReleaseIterator(hashTypeIterator *hi) {
if (hi->encoding == REDIS_ENCODING_HT) {
dictReleaseIterator(hi->di);
}
/* Move to the next entry in the hash. Return REDIS_OK when the next entry
* could be found and REDIS_ERR when the iterator reaches the end. */
-static int hashNext(hashIterator *hi) {
+static int hashTypeNext(hashTypeIterator *hi) {
if (hi->encoding == REDIS_ENCODING_ZIPMAP) {
if ((hi->zi = zipmapNext(hi->zi, &hi->zk, &hi->zklen,
&hi->zv, &hi->zvlen)) == NULL) return REDIS_ERR;
/* Get key or value object at current iteration position.
* This increases the refcount of the field object by 1. */
-static robj *hashCurrent(hashIterator *hi, int what) {
+static robj *hashTypeCurrent(hashTypeIterator *hi, int what) {
robj *o;
if (hi->encoding == REDIS_ENCODING_ZIPMAP) {
if (what & REDIS_HASH_KEY) {
return o;
}
-static robj *hashLookupWriteOrCreate(redisClient *c, robj *key) {
+static robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key) {
robj *o = lookupKeyWrite(c->db,key);
if (o == NULL) {
o = createHashObject();
int update;
robj *o;
- if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
- hashTryConversion(o,c->argv,2,3);
- hashTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
- update = hashSet(o,c->argv[2],c->argv[3]);
+ if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
+ hashTypeTryConversion(o,c->argv,2,3);
+ hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
+ update = hashTypeSet(o,c->argv[2],c->argv[3]);
addReply(c, update ? shared.czero : shared.cone);
server.dirty++;
}
static void hsetnxCommand(redisClient *c) {
robj *o;
- if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
- hashTryConversion(o,c->argv,2,3);
+ if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
+ hashTypeTryConversion(o,c->argv,2,3);
- if (hashExists(o, c->argv[2])) {
+ if (hashTypeExists(o, c->argv[2])) {
addReply(c, shared.czero);
} else {
- hashTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
- hashSet(o,c->argv[2],c->argv[3]);
+ hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
+ hashTypeSet(o,c->argv[2],c->argv[3]);
addReply(c, shared.cone);
server.dirty++;
}
return;
}
- if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
- hashTryConversion(o,c->argv,2,c->argc-1);
+ if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
+ hashTypeTryConversion(o,c->argv,2,c->argc-1);
for (i = 2; i < c->argc; i += 2) {
- hashTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]);
- hashSet(o,c->argv[i],c->argv[i+1]);
+ hashTypeTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]);
+ hashTypeSet(o,c->argv[i],c->argv[i+1]);
}
addReply(c, shared.ok);
server.dirty++;
robj *o, *current, *new;
if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return;
- if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
- if ((current = hashGet(o,c->argv[2])) != NULL) {
+ if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
+ if ((current = hashTypeGet(o,c->argv[2])) != NULL) {
if (getLongLongFromObjectOrReply(c,current,&value,
"hash value is not an integer") != REDIS_OK) {
decrRefCount(current);
value += incr;
new = createStringObjectFromLongLong(value);
- hashTryObjectEncoding(o,&c->argv[2],NULL);
- hashSet(o,c->argv[2],new);
+ hashTypeTryObjectEncoding(o,&c->argv[2],NULL);
+ hashTypeSet(o,c->argv[2],new);
decrRefCount(new);
addReplyLongLong(c,value);
server.dirty++;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
- if ((value = hashGet(o,c->argv[2])) != NULL) {
+ if ((value = hashTypeGet(o,c->argv[2])) != NULL) {
addReplyBulk(c,value);
decrRefCount(value);
} else {
* an empty hash. The reply should then be a series of NULLs. */
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-2));
for (i = 2; i < c->argc; i++) {
- if (o != NULL && (value = hashGet(o,c->argv[i])) != NULL) {
+ if (o != NULL && (value = hashTypeGet(o,c->argv[i])) != NULL) {
addReplyBulk(c,value);
decrRefCount(value);
} else {
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
- if (hashDelete(o,c->argv[2])) {
- if (hashLength(o) == 0) dbDelete(c->db,c->argv[1]);
+ if (hashTypeDelete(o,c->argv[2])) {
+ if (hashTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
addReply(c,shared.cone);
server.dirty++;
} else {
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
- addReplyUlong(c,hashLength(o));
+ addReplyUlong(c,hashTypeLength(o));
}
static void genericHgetallCommand(redisClient *c, int flags) {
robj *o, *lenobj, *obj;
unsigned long count = 0;
- hashIterator *hi;
+ hashTypeIterator *hi;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
|| checkType(c,o,REDIS_HASH)) return;
addReply(c,lenobj);
decrRefCount(lenobj);
- hi = hashInitIterator(o);
- while (hashNext(hi) != REDIS_ERR) {
+ hi = hashTypeInitIterator(o);
+ while (hashTypeNext(hi) != REDIS_ERR) {
if (flags & REDIS_HASH_KEY) {
- obj = hashCurrent(hi,REDIS_HASH_KEY);
+ obj = hashTypeCurrent(hi,REDIS_HASH_KEY);
addReplyBulk(c,obj);
decrRefCount(obj);
count++;
}
if (flags & REDIS_HASH_VALUE) {
- obj = hashCurrent(hi,REDIS_HASH_VALUE);
+ obj = hashTypeCurrent(hi,REDIS_HASH_VALUE);
addReplyBulk(c,obj);
decrRefCount(obj);
count++;
}
}
- hashReleaseIterator(hi);
+ hashTypeReleaseIterator(hi);
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",count);
}
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
- addReply(c, hashExists(o,c->argv[2]) ? shared.cone : shared.czero);
+ addReply(c, hashTypeExists(o,c->argv[2]) ? shared.cone : shared.czero);
}
static void convertToRealHash(robj *o) {
/* Retrieve value from hash by the field name. This operation
* already increases the refcount of the returned object. */
initStaticStringObject(fieldobj,((char*)&fieldname)+(sizeof(long)*2));
- o = hashGet(o, &fieldobj);
+ o = hashTypeGet(o, &fieldobj);
} else {
if (o->type != REDIS_STRING) return NULL;
/* Load the sorting vector with all the objects to sort */
switch(sortval->type) {
- case REDIS_LIST: vectorlen = lLength(sortval); break;
+ case REDIS_LIST: vectorlen = listTypeLength(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 */
j = 0;
if (sortval->type == REDIS_LIST) {
- lIterator *li = lInitIterator(sortval,0,REDIS_TAIL);
- lEntry entry;
- while(lNext(li,&entry)) {
- vector[j].obj = lGet(&entry);
+ 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++;
}
- lReleaseIterator(li);
+ listTypeReleaseIterator(li);
} else {
dict *set;
dictIterator *di;
listIter li;
if (!getop) {
- lPush(sobj,vector[j].obj,REDIS_TAIL);
+ listTypePush(sobj,vector[j].obj,REDIS_TAIL);
} else {
listRewind(operations,&li);
while((ln = listNext(&li))) {
if (sop->type == REDIS_SORT_GET) {
if (!val) val = createStringObject("",0);
- /* lPush does an incrRefCount, so we should take care
+ /* listTypePush does an incrRefCount, so we should take care
* care of the incremented refcount caused by either
* lookupKeyByPattern or createStringObject("",0) */
- lPush(sobj,val,REDIS_TAIL);
+ listTypePush(sobj,val,REDIS_TAIL);
decrRefCount(val);
} else {
/* always fails */
/* actual age can be >= minage, but not < minage. As we use wrapping
* 21 bit clocks with minutes resolution for the LRU. */
time_t minage = abs(server.lruclock - o->lru);
- long asize = 0;
+ long asize = 0, elesize;
+ robj *ele;
list *l;
+ listNode *ln;
dict *d;
struct dictEntry *de;
int z;
}
break;
case REDIS_LIST:
- l = o->ptr;
- listNode *ln = listFirst(l);
-
- asize = sizeof(list);
- if (ln) {
- robj *ele = ln->value;
- long elesize;
-
- elesize = (ele->encoding == REDIS_ENCODING_RAW) ?
- (sizeof(*o)+sdslen(ele->ptr)) : sizeof(*o);
- asize += (sizeof(listNode)+elesize)*listLength(l);
+ if (o->encoding == REDIS_ENCODING_ZIPLIST) {
+ asize = sizeof(*o)+ziplistSize(o->ptr);
+ } else {
+ l = o->ptr;
+ ln = listFirst(l);
+ asize = sizeof(list);
+ if (ln) {
+ ele = ln->value;
+ elesize = (ele->encoding == REDIS_ENCODING_RAW) ?
+ (sizeof(*o)+sdslen(ele->ptr)) : sizeof(*o);
+ asize += (sizeof(listNode)+elesize)*listLength(l);
+ }
}
break;
case REDIS_SET:
asize = sizeof(dict)+(sizeof(struct dictEntry*)*dictSlots(d));
if (z) asize += sizeof(zset)-sizeof(dict);
if (dictSize(d)) {
- long elesize;
- robj *ele;
-
de = dictGetRandomKey(d);
ele = dictGetEntryKey(de);
elesize = (ele->encoding == REDIS_ENCODING_RAW) ?
d = o->ptr;
asize = sizeof(dict)+(sizeof(struct dictEntry*)*dictSlots(d));
if (dictSize(d)) {
- long elesize;
- robj *ele;
-
de = dictGetRandomKey(d);
ele = dictGetEntryKey(de);
elesize = (ele->encoding == REDIS_ENCODING_RAW) ?
if (o->type == REDIS_STRING) {
mixObjectDigest(digest,o);
} else if (o->type == REDIS_LIST) {
- lIterator *li = lInitIterator(o,0,REDIS_TAIL);
- lEntry entry;
- while(lNext(li,&entry)) {
- robj *eleobj = lGet(&entry);
+ listTypeIterator *li = listTypeInitIterator(o,0,REDIS_TAIL);
+ listTypeEntry entry;
+ while(listTypeNext(li,&entry)) {
+ robj *eleobj = listTypeGet(&entry);
mixObjectDigest(digest,eleobj);
decrRefCount(eleobj);
}
- lReleaseIterator(li);
+ listTypeReleaseIterator(li);
} else if (o->type == REDIS_SET) {
dict *set = o->ptr;
dictIterator *di = dictGetIterator(set);
}
dictReleaseIterator(di);
} else if (o->type == REDIS_HASH) {
- hashIterator *hi;
+ hashTypeIterator *hi;
robj *obj;
- hi = hashInitIterator(o);
- while (hashNext(hi) != REDIS_ERR) {
+ hi = hashTypeInitIterator(o);
+ while (hashTypeNext(hi) != REDIS_ERR) {
unsigned char eledigest[20];
memset(eledigest,0,20);
- obj = hashCurrent(hi,REDIS_HASH_KEY);
+ obj = hashTypeCurrent(hi,REDIS_HASH_KEY);
mixObjectDigest(eledigest,obj);
decrRefCount(obj);
- obj = hashCurrent(hi,REDIS_HASH_VALUE);
+ obj = hashTypeCurrent(hi,REDIS_HASH_VALUE);
mixObjectDigest(eledigest,obj);
decrRefCount(obj);
xorDigest(digest,eledigest,20);
}
- hashReleaseIterator(hi);
+ hashTypeReleaseIterator(hi);
} else {
redisPanic("Unknown object type");
}