]> git.saurik.com Git - redis.git/blobdiff - redis.c
ZSETs memory leak #1 solved, another one missing
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index 0dfc36d9a66f68da0897f5436dff0b2f4979723e..c45beecff1d2c24e943f7ab85ecc6f27321d1b00 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -27,7 +27,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#define REDIS_VERSION "0.900"
+#define REDIS_VERSION "1.050"
 
 #include "fmacros.h"
 #include "config.h"
 #define REDIS_STRING 0
 #define REDIS_LIST 1
 #define REDIS_SET 2
-#define REDIS_HASH 3
+#define REDIS_ZSET 3
+#define REDIS_HASH 4
+
+/* Objects encoding */
+#define REDIS_ENCODING_RAW 0    /* Raw representation */
+#define REDIS_ENCODING_INT 1    /* Encoded as integer */
 
 /* Object types only used for dumping to disk */
 #define REDIS_EXPIRETIME 253
 /* Anti-warning macro... */
 #define REDIS_NOTUSED(V) ((void) V)
 
+#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^32 elements */
+#define ZSKIPLIST_P 0.25      /* Skiplist P = 1/4 */
 
 /*================================= Data types ============================== */
 
 /* A redis object, that is a type able to hold a string / list / set */
 typedef struct redisObject {
     void *ptr;
-    int type;
+    unsigned char type;
+    unsigned char encoding;
+    unsigned char notused[2];
     int refcount;
 } robj;
 
@@ -200,9 +209,10 @@ typedef struct redisClient {
     redisDb *db;
     int dictid;
     sds querybuf;
-    robj **argv;
-    int argc;
+    robj **argv, **mbargv;
+    int argc, mbargc;
     int bulklen;            /* bulk read len. -1 if not in bulk read mode */
+    int multibulk;          /* multi bulk command format active */
     list *reply;
     int sentlen;
     time_t lastinteraction; /* time of the last interaction, used for timeout */
@@ -263,7 +273,7 @@ struct redisServer {
     redisClient *master;    /* client that is master for this slave */
     int replstate;
     unsigned int maxclients;
-    unsigned int maxmemory;
+    unsigned long maxmemory;
     /* Sort parameters - qsort_r() is only available under BSD so we
      * have to take this state global, in order to pass it to sortCompare() */
     int sort_desc;
@@ -297,6 +307,28 @@ typedef struct _redisSortOperation {
     robj *pattern;
 } redisSortOperation;
 
+/* ZSETs use a specialized version of Skiplists */
+
+typedef struct zskiplistNode {
+    struct zskiplistNode **forward;
+    struct zskiplistNode *backward;
+    double score;
+    robj *obj;
+} zskiplistNode;
+
+typedef struct zskiplist {
+    struct zskiplistNode *header, *tail;
+    long length;
+    int level;
+} zskiplist;
+
+typedef struct zset {
+    dict *dict;
+    zskiplist *zsl;
+} zset;
+
+/* Our shared "common" objects */
+
 struct sharedObjectsStruct {
     robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *pong, *space,
     *colon, *nullbulk, *nullmultibulk,
@@ -323,6 +355,8 @@ static robj *createStringObject(char *ptr, size_t len);
 static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int dictid, robj **argv, int argc);
 static int syncWithMaster(void);
 static robj *tryObjectSharing(robj *o);
+static int tryObjectEncoding(robj *o);
+static robj *getDecodedObject(const robj *o);
 static int removeExpire(redisDb *db, robj *key);
 static int expireIfNeeded(redisDb *db, robj *key);
 static int deleteIfVolatile(redisDb *db, robj *key);
@@ -334,6 +368,10 @@ static void freeMemoryIfNeeded(void);
 static int processCommand(redisClient *c);
 static void setupSigSegvAction(void);
 static void rdbRemoveTempFile(pid_t childpid);
+static size_t stringObjectLen(robj *o);
+static void processInputBuffer(redisClient *c);
+static zskiplist *zslCreate(void);
+static void zslFree(zskiplist *zsl);
 
 static void authCommand(redisClient *c);
 static void pingCommand(redisClient *c);
@@ -374,6 +412,7 @@ static void smoveCommand(redisClient *c);
 static void sismemberCommand(redisClient *c);
 static void scardCommand(redisClient *c);
 static void spopCommand(redisClient *c);
+static void srandmemberCommand(redisClient *c);
 static void sinterCommand(redisClient *c);
 static void sinterstoreCommand(redisClient *c);
 static void sunionCommand(redisClient *c);
@@ -389,10 +428,18 @@ static void infoCommand(redisClient *c);
 static void mgetCommand(redisClient *c);
 static void monitorCommand(redisClient *c);
 static void expireCommand(redisClient *c);
-static void getSetCommand(redisClient *c);
+static void getsetCommand(redisClient *c);
 static void ttlCommand(redisClient *c);
 static void slaveofCommand(redisClient *c);
 static void debugCommand(redisClient *c);
+static void msetCommand(redisClient *c);
+static void msetnxCommand(redisClient *c);
+static void zaddCommand(redisClient *c);
+static void zrangeCommand(redisClient *c);
+static void zrevrangeCommand(redisClient *c);
+static void zlenCommand(redisClient *c);
+static void zremCommand(redisClient *c);
+
 /*================================= Globals ================================= */
 
 /* Global vars */
@@ -422,6 +469,7 @@ static struct redisCommand cmdTable[] = {
     {"sismember",sismemberCommand,3,REDIS_CMD_BULK},
     {"scard",scardCommand,2,REDIS_CMD_INLINE},
     {"spop",spopCommand,2,REDIS_CMD_INLINE},
+    {"srandmember",srandmemberCommand,2,REDIS_CMD_INLINE},
     {"sinter",sinterCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
     {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
     {"sunion",sunionCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
@@ -429,9 +477,16 @@ static struct redisCommand cmdTable[] = {
     {"sdiff",sdiffCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
     {"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
     {"smembers",sinterCommand,2,REDIS_CMD_INLINE},
+    {"zadd",zaddCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
+    {"zrem",zremCommand,3,REDIS_CMD_BULK},
+    {"zrange",zrangeCommand,4,REDIS_CMD_INLINE},
+    {"zrevrange",zrevrangeCommand,4,REDIS_CMD_INLINE},
+    {"zlen",zlenCommand,2,REDIS_CMD_INLINE},
     {"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
     {"decrby",decrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
-    {"getset",getSetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
+    {"getset",getsetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
+    {"mset",msetCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
+    {"msetnx",msetnxCommand,-3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
     {"randomkey",randomkeyCommand,1,REDIS_CMD_INLINE},
     {"select",selectCommand,2,REDIS_CMD_INLINE},
     {"move",moveCommand,3,REDIS_CMD_INLINE},
@@ -615,6 +670,12 @@ static void redisLog(int level, const char *fmt, ...) {
  * keys and radis objects as values (objects can hold SDS strings,
  * lists, sets). */
 
+static void dictVanillaFree(void *privdata, void *val)
+{
+    DICT_NOTUSED(privdata);
+    zfree(val);
+}
+
 static int sdsDictKeyCompare(void *privdata, const void *key1,
         const void *key2)
 {
@@ -634,32 +695,77 @@ static void dictRedisObjectDestructor(void *privdata, void *val)
     decrRefCount(val);
 }
 
-static int dictSdsKeyCompare(void *privdata, const void *key1,
+static int dictObjKeyCompare(void *privdata, const void *key1,
         const void *key2)
 {
     const robj *o1 = key1, *o2 = key2;
     return sdsDictKeyCompare(privdata,o1->ptr,o2->ptr);
 }
 
-static unsigned int dictSdsHash(const void *key) {
+static unsigned int dictObjHash(const void *key) {
     const robj *o = key;
     return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));
 }
 
+static int dictEncObjKeyCompare(void *privdata, const void *key1,
+        const void *key2)
+{
+    const robj *o1 = key1, *o2 = key2;
+
+    if (o1->encoding == REDIS_ENCODING_RAW &&
+        o2->encoding == REDIS_ENCODING_RAW)
+        return sdsDictKeyCompare(privdata,o1->ptr,o2->ptr);
+    else {
+        robj *dec1, *dec2;
+        int cmp;
+
+        dec1 = o1->encoding != REDIS_ENCODING_RAW ?
+            getDecodedObject(o1) : (robj*)o1;
+        dec2 = o2->encoding != REDIS_ENCODING_RAW ?
+            getDecodedObject(o2) : (robj*)o2;
+        cmp = sdsDictKeyCompare(privdata,dec1->ptr,dec2->ptr);
+        if (dec1 != o1) decrRefCount(dec1);
+        if (dec2 != o2) decrRefCount(dec2);
+        return cmp;
+    }
+}
+
+static unsigned int dictEncObjHash(const void *key) {
+    const robj *o = key;
+
+    if (o->encoding == REDIS_ENCODING_RAW)
+        return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));
+    else {
+        robj *dec = getDecodedObject(o);
+        unsigned int hash = dictGenHashFunction(dec->ptr, sdslen((sds)dec->ptr));
+        decrRefCount(dec);
+        return hash;
+    }
+}
+
 static dictType setDictType = {
-    dictSdsHash,               /* hash function */
+    dictEncObjHash,            /* hash function */
     NULL,                      /* key dup */
     NULL,                      /* val dup */
-    dictSdsKeyCompare,         /* key compare */
+    dictEncObjKeyCompare,      /* key compare */
     dictRedisObjectDestructor, /* key destructor */
     NULL                       /* val destructor */
 };
 
+static dictType zsetDictType = {
+    dictEncObjHash,            /* hash function */
+    NULL,                      /* key dup */
+    NULL,                      /* val dup */
+    dictEncObjKeyCompare,      /* key compare */
+    dictRedisObjectDestructor, /* key destructor */
+    dictVanillaFree            /* val destructor */
+};
+
 static dictType hashDictType = {
-    dictSdsHash,                /* hash function */
+    dictObjHash,                /* hash function */
     NULL,                       /* key dup */
     NULL,                       /* val dup */
-    dictSdsKeyCompare,          /* key compare */
+    dictObjKeyCompare,          /* key compare */
     dictRedisObjectDestructor,  /* key destructor */
     dictRedisObjectDestructor   /* val destructor */
 };
@@ -876,7 +982,6 @@ static void createSharedObjects(void) {
 
 static void appendServerSaveParams(time_t seconds, int changes) {
     server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1));
-    if (server.saveparams == NULL) oom("appendServerSaveParams");
     server.saveparams[server.saveparamslen].seconds = seconds;
     server.saveparams[server.saveparamslen].changes = changes;
     server.saveparamslen++;
@@ -933,8 +1038,6 @@ static void initServer() {
     server.el = aeCreateEventLoop();
     server.db = zmalloc(sizeof(redisDb)*server.dbnum);
     server.sharingpool = dictCreate(&setDictType,NULL);
-    if (!server.db || !server.clients || !server.slaves || !server.monitors || !server.el || !server.objfreelist)
-        oom("server initialization"); /* Fatal OOM */
     server.fd = anetTcpServer(server.neterr, server.port, server.bindaddr);
     if (server.fd == -1) {
         redisLog(REDIS_WARNING, "Opening TCP port: %s", server.neterr);
@@ -1072,7 +1175,7 @@ static void loadServerConfig(char *filename) {
         } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
             server.maxclients = atoi(argv[1]);
         } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
-            server.maxmemory = atoi(argv[1]);
+            server.maxmemory = strtoll(argv[1], NULL, 10);
         } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
             server.masterhost = sdsnew(argv[1]);
             server.masterport = atoi(argv[2]);
@@ -1124,7 +1227,10 @@ static void freeClientArgv(redisClient *c) {
 
     for (j = 0; j < c->argc; j++)
         decrRefCount(c->argv[j]);
+    for (j = 0; j < c->mbargc; j++)
+        decrRefCount(c->mbargv[j]);
     c->argc = 0;
+    c->mbargc = 0;
 }
 
 static void freeClient(redisClient *c) {
@@ -1152,6 +1258,7 @@ static void freeClient(redisClient *c) {
         server.replstate = REDIS_REPL_CONNECT;
     }
     zfree(c->argv);
+    zfree(c->mbargv);
     zfree(c);
 }
 
@@ -1181,7 +1288,7 @@ static void glueReplyBuffersIfNeeded(redisClient *c) {
         }
         /* Now the output buffer is empty, add the new single element */
         o = createObject(REDIS_STRING,sdsnewlen(buf,totlen));
-        if (!listAddNodeTail(c->reply,o)) oom("listAddNodeTail");
+        listAddNodeTail(c->reply,o);
     }
 }
 
@@ -1254,6 +1361,7 @@ static struct redisCommand *lookupCommand(char *name) {
 static void resetClient(redisClient *c) {
     freeClientArgv(c);
     c->bulklen = -1;
+    c->multibulk = 0;
 }
 
 /* If this function gets called we already read a whole
@@ -1271,6 +1379,74 @@ static int processCommand(redisClient *c) {
     /* Free some memory if needed (maxmemory setting) */
     if (server.maxmemory) freeMemoryIfNeeded();
 
+    /* Handle the multi bulk command type. This is an alternative protocol
+     * supported by Redis in order to receive commands that are composed of
+     * multiple binary-safe "bulk" arguments. The latency of processing is
+     * a bit higher but this allows things like multi-sets, so if this
+     * protocol is used only for MSET and similar commands this is a big win. */
+    if (c->multibulk == 0 && c->argc == 1 && ((char*)(c->argv[0]->ptr))[0] == '*') {
+        c->multibulk = atoi(((char*)c->argv[0]->ptr)+1);
+        if (c->multibulk <= 0) {
+            resetClient(c);
+            return 1;
+        } else {
+            decrRefCount(c->argv[c->argc-1]);
+            c->argc--;
+            return 1;
+        }
+    } else if (c->multibulk) {
+        if (c->bulklen == -1) {
+            if (((char*)c->argv[0]->ptr)[0] != '$') {
+                addReplySds(c,sdsnew("-ERR multi bulk protocol error\r\n"));
+                resetClient(c);
+                return 1;
+            } else {
+                int bulklen = atoi(((char*)c->argv[0]->ptr)+1);
+                decrRefCount(c->argv[0]);
+                if (bulklen < 0 || bulklen > 1024*1024*1024) {
+                    c->argc--;
+                    addReplySds(c,sdsnew("-ERR invalid bulk write count\r\n"));
+                    resetClient(c);
+                    return 1;
+                }
+                c->argc--;
+                c->bulklen = bulklen+2; /* add two bytes for CR+LF */
+                return 1;
+            }
+        } else {
+            c->mbargv = zrealloc(c->mbargv,(sizeof(robj*))*(c->mbargc+1));
+            c->mbargv[c->mbargc] = c->argv[0];
+            c->mbargc++;
+            c->argc--;
+            c->multibulk--;
+            if (c->multibulk == 0) {
+                robj **auxargv;
+                int auxargc;
+
+                /* Here we need to swap the multi-bulk argc/argv with the
+                 * normal argc/argv of the client structure. */
+                auxargv = c->argv;
+                c->argv = c->mbargv;
+                c->mbargv = auxargv;
+
+                auxargc = c->argc;
+                c->argc = c->mbargc;
+                c->mbargc = auxargc;
+
+                /* We need to set bulklen to something different than -1
+                 * in order for the code below to process the command without
+                 * to try to read the last argument of a bulk command as
+                 * a special argument. */
+                c->bulklen = 0;
+                /* continue below and process the command */
+            } else {
+                c->bulklen = -1;
+                return 1;
+            }
+        }
+    }
+    /* -- end of multi bulk commands processing -- */
+
     /* The QUIT command is handled as a special case. Normal command
      * procs are unable to close the client connection safely */
     if (!strcasecmp(c->argv[0]->ptr,"quit")) {
@@ -1304,7 +1480,10 @@ static int processCommand(redisClient *c) {
         c->argc--;
         c->bulklen = bulklen+2; /* add two bytes for CR+LF */
         /* It is possible that the bulk read is already in the
-         * buffer. Check this condition and handle it accordingly */
+         * buffer. Check this condition and handle it accordingly.
+         * This is just a fast path, alternative to call processInputBuffer().
+         * It's a good idea since the code is small and this condition
+         * happens most of the times. */
         if ((signed)sdslen(c->querybuf) >= c->bulklen) {
             c->argv[c->argc] = createStringObject(c->querybuf,c->bulklen-2);
             c->argc++;
@@ -1319,6 +1498,10 @@ static int processCommand(redisClient *c) {
         for(j = 1; j < c->argc; j++)
             c->argv[j] = tryObjectSharing(c->argv[j]);
     }
+    /* Let's try to encode the bulk object to save space. */
+    if (cmd->flags & REDIS_CMD_BULK)
+        tryObjectEncoding(c->argv[c->argc-1]);
+
     /* Check if the user is authenticated */
     if (server.requirepass && !c->authenticated && cmd->proc != authCommand) {
         addReplySds(c,sdsnew("-ERR operation not permitted\r\n"));
@@ -1355,7 +1538,6 @@ static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int di
         outv = static_outv;
     } else {
         outv = zmalloc(sizeof(robj*)*(argc*2+1));
-        if (!outv) oom("replicationFeedSlaves");
     }
     
     for (j = 0; j < argc; j++) {
@@ -1364,7 +1546,8 @@ static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int di
             robj *lenobj;
 
             lenobj = createObject(REDIS_STRING,
-                sdscatprintf(sdsempty(),"%d\r\n",sdslen(argv[j]->ptr)));
+                sdscatprintf(sdsempty(),"%d\r\n",
+                    stringObjectLen(argv[j])));
             lenobj->refcount = 0;
             outv[outc++] = lenobj;
         }
@@ -1413,34 +1596,7 @@ static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int di
     if (outv != static_outv) zfree(outv);
 }
 
-static void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
-    redisClient *c = (redisClient*) privdata;
-    char buf[REDIS_IOBUF_LEN];
-    int nread;
-    REDIS_NOTUSED(el);
-    REDIS_NOTUSED(mask);
-
-    nread = read(fd, buf, REDIS_IOBUF_LEN);
-    if (nread == -1) {
-        if (errno == EAGAIN) {
-            nread = 0;
-        } else {
-            redisLog(REDIS_DEBUG, "Reading from client: %s",strerror(errno));
-            freeClient(c);
-            return;
-        }
-    } else if (nread == 0) {
-        redisLog(REDIS_DEBUG, "Client closed connection");
-        freeClient(c);
-        return;
-    }
-    if (nread) {
-        c->querybuf = sdscatlen(c->querybuf, buf, nread);
-        c->lastinteraction = time(NULL);
-    } else {
-        return;
-    }
-
+static void processInputBuffer(redisClient *c) {
 again:
     if (c->bulklen == -1) {
         /* Read the first line of the query */
@@ -1469,12 +1625,10 @@ again:
                 return;
             }
             argv = sdssplitlen(query,sdslen(query)," ",1,&argc);
-            if (argv == NULL) oom("sdssplitlen");
             sdsfree(query);
 
             if (c->argv) zfree(c->argv);
             c->argv = zmalloc(sizeof(robj*)*argc);
-            if (c->argv == NULL) oom("allocating arguments list for client");
 
             for (j = 0; j < argc; j++) {
                 if (sdslen(argv[j])) {
@@ -1488,7 +1642,7 @@ again:
             /* Execute the command. If the client is still valid
              * after processCommand() return and there is something
              * on the query buffer try to process the next command. */
-            if (processCommand(c) && sdslen(c->querybuf)) goto again;
+            if (c->argc && processCommand(c) && sdslen(c->querybuf)) goto again;
             return;
         } else if (sdslen(c->querybuf) >= REDIS_REQUEST_MAX_SIZE) {
             redisLog(REDIS_DEBUG, "Client protocol error");
@@ -1507,12 +1661,45 @@ again:
             c->argv[c->argc] = createStringObject(c->querybuf,c->bulklen-2);
             c->argc++;
             c->querybuf = sdsrange(c->querybuf,c->bulklen,-1);
-            processCommand(c);
+            /* Process the command. If the client is still valid after
+             * the processing and there is more data in the buffer
+             * try to parse it. */
+            if (processCommand(c) && sdslen(c->querybuf)) goto again;
             return;
         }
     }
 }
 
+static void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
+    redisClient *c = (redisClient*) privdata;
+    char buf[REDIS_IOBUF_LEN];
+    int nread;
+    REDIS_NOTUSED(el);
+    REDIS_NOTUSED(mask);
+
+    nread = read(fd, buf, REDIS_IOBUF_LEN);
+    if (nread == -1) {
+        if (errno == EAGAIN) {
+            nread = 0;
+        } else {
+            redisLog(REDIS_DEBUG, "Reading from client: %s",strerror(errno));
+            freeClient(c);
+            return;
+        }
+    } else if (nread == 0) {
+        redisLog(REDIS_DEBUG, "Client closed connection");
+        freeClient(c);
+        return;
+    }
+    if (nread) {
+        c->querybuf = sdscatlen(c->querybuf, buf, nread);
+        c->lastinteraction = time(NULL);
+    } else {
+        return;
+    }
+    processInputBuffer(c);
+}
+
 static int selectDb(redisClient *c, int id) {
     if (id < 0 || id >= server.dbnum)
         return REDIS_ERR;
@@ -1537,12 +1724,15 @@ static redisClient *createClient(int fd) {
     c->argc = 0;
     c->argv = NULL;
     c->bulklen = -1;
+    c->multibulk = 0;
+    c->mbargc = 0;
+    c->mbargv = NULL;
     c->sentlen = 0;
     c->flags = 0;
     c->lastinteraction = time(NULL);
     c->authenticated = 0;
     c->replstate = REDIS_REPL_NONE;
-    if ((c->reply = listCreate()) == NULL) oom("listCreate");
+    c->reply = listCreate();
     listSetFreeMethod(c->reply,decrRefCount);
     listSetDupMethod(c->reply,dupClientReplyValue);
     if (aeCreateFileEvent(server.el, c->fd, AE_READABLE,
@@ -1550,7 +1740,7 @@ static redisClient *createClient(int fd) {
         freeClient(c);
         return NULL;
     }
-    if (!listAddNodeTail(server.clients,c)) oom("listAddNodeTail");
+    listAddNodeTail(server.clients,c);
     return c;
 }
 
@@ -1560,8 +1750,12 @@ static void addReply(redisClient *c, robj *obj) {
          c->replstate == REDIS_REPL_ONLINE) &&
         aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
         sendReplyToClient, c, NULL) == AE_ERR) return;
-    if (!listAddNodeTail(c->reply,obj)) oom("listAddNodeTail");
-    incrRefCount(obj);
+    if (obj->encoding != REDIS_ENCODING_RAW) {
+        obj = getDecodedObject(obj);
+    } else {
+        incrRefCount(obj);
+    }
+    listAddNodeTail(c->reply,obj);
 }
 
 static void addReplySds(redisClient *c, sds s) {
@@ -1570,6 +1764,26 @@ static void addReplySds(redisClient *c, sds s) {
     decrRefCount(o);
 }
 
+static void addReplyBulkLen(redisClient *c, robj *obj) {
+    size_t len;
+
+    if (obj->encoding == REDIS_ENCODING_RAW) {
+        len = sdslen(obj->ptr);
+    } else {
+        long n = (long)obj->ptr;
+
+        len = 1;
+        if (n < 0) {
+            len++;
+            n = -n;
+        }
+        while((n = n/10) != 0) {
+            len++;
+        }
+    }
+    addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",len));
+}
+
 static void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
     int cport, cfd;
     char cip[128];
@@ -1616,8 +1830,8 @@ static robj *createObject(int type, void *ptr) {
     } else {
         o = zmalloc(sizeof(*o));
     }
-    if (!o) oom("createObject");
     o->type = type;
+    o->encoding = REDIS_ENCODING_RAW;
     o->ptr = ptr;
     o->refcount = 1;
     return o;
@@ -1630,19 +1844,27 @@ static robj *createStringObject(char *ptr, size_t len) {
 static robj *createListObject(void) {
     list *l = listCreate();
 
-    if (!l) oom("listCreate");
     listSetFreeMethod(l,decrRefCount);
     return createObject(REDIS_LIST,l);
 }
 
 static robj *createSetObject(void) {
     dict *d = dictCreate(&setDictType,NULL);
-    if (!d) oom("dictCreate");
     return createObject(REDIS_SET,d);
 }
 
+static robj *createZsetObject(void) {
+    zset *zs = zmalloc(sizeof(*zs));
+
+    zs->dict = dictCreate(&zsetDictType,NULL);
+    zs->zsl = zslCreate();
+    return createObject(REDIS_ZSET,zs);
+}
+
 static void freeStringObject(robj *o) {
-    sdsfree(o->ptr);
+    if (o->encoding == REDIS_ENCODING_RAW) {
+        sdsfree(o->ptr);
+    }
 }
 
 static void freeListObject(robj *o) {
@@ -1653,6 +1875,14 @@ static void freeSetObject(robj *o) {
     dictRelease((dict*) o->ptr);
 }
 
+static void freeZsetObject(robj *o) {
+    zset *zs = o->ptr;
+
+    dictRelease(zs->dict);
+    zslFree(zs->zsl);
+    zfree(zs);
+}
+
 static void freeHashObject(robj *o) {
     dictRelease((dict*) o->ptr);
 }
@@ -1677,6 +1907,7 @@ static void decrRefCount(void *obj) {
         case REDIS_STRING: freeStringObject(o); break;
         case REDIS_LIST: freeListObject(o); break;
         case REDIS_SET: freeSetObject(o); break;
+        case REDIS_ZSET: freeZsetObject(o); break;
         case REDIS_HASH: freeHashObject(o); break;
         default: assert(0 != 0); break;
         }
@@ -1686,6 +1917,36 @@ static void decrRefCount(void *obj) {
     }
 }
 
+static robj *lookupKey(redisDb *db, robj *key) {
+    dictEntry *de = dictFind(db->dict,key);
+    return de ? dictGetEntryVal(de) : NULL;
+}
+
+static robj *lookupKeyRead(redisDb *db, robj *key) {
+    expireIfNeeded(db,key);
+    return lookupKey(db,key);
+}
+
+static robj *lookupKeyWrite(redisDb *db, robj *key) {
+    deleteIfVolatile(db,key);
+    return lookupKey(db,key);
+}
+
+static int deleteKey(redisDb *db, robj *key) {
+    int retval;
+
+    /* We need to protect key from destruction: after the first dictDelete()
+     * it may happen that 'key' is no longer valid if we don't increment
+     * it's count. This may happen when we get the object reference directly
+     * from the hash table with dictRandomKey() or dict iterators */
+    incrRefCount(key);
+    if (dictSize(db->expires)) dictDelete(db->expires,key);
+    retval = dictDelete(db->dict,key);
+    decrRefCount(key);
+
+    return retval == DICT_OK;
+}
+
 /* Try to share an object against the shared objects pool */
 static robj *tryObjectSharing(robj *o) {
     struct dictEntry *de;
@@ -1731,34 +1992,99 @@ static robj *tryObjectSharing(robj *o) {
     }
 }
 
-static robj *lookupKey(redisDb *db, robj *key) {
-    dictEntry *de = dictFind(db->dict,key);
-    return de ? dictGetEntryVal(de) : NULL;
+/* Check if the nul-terminated string 's' can be represented by a long
+ * (that is, is a number that fits into long without any other space or
+ * character before or after the digits).
+ *
+ * If so, the function returns REDIS_OK and *longval is set to the value
+ * of the number. Otherwise REDIS_ERR is returned */
+static int isStringRepresentableAsLong(sds s, long *longval) {
+    char buf[32], *endptr;
+    long value;
+    int slen;
+    
+    value = strtol(s, &endptr, 10);
+    if (endptr[0] != '\0') return REDIS_ERR;
+    slen = snprintf(buf,32,"%ld",value);
+
+    /* If the number converted back into a string is not identical
+     * then it's not possible to encode the string as integer */
+    if (sdslen(s) != (unsigned)slen || memcmp(buf,s,slen)) return REDIS_ERR;
+    if (longval) *longval = value;
+    return REDIS_OK;
 }
 
-static robj *lookupKeyRead(redisDb *db, robj *key) {
-    expireIfNeeded(db,key);
-    return lookupKey(db,key);
+/* Try to encode a string object in order to save space */
+static int tryObjectEncoding(robj *o) {
+    long value;
+    sds s = o->ptr;
+
+    if (o->encoding != REDIS_ENCODING_RAW)
+        return REDIS_ERR; /* Already encoded */
+
+    /* It's not save to encode shared objects: shared objects can be shared
+     * everywhere in the "object space" of Redis. Encoded objects can only
+     * appear as "values" (and not, for instance, as keys) */
+     if (o->refcount > 1) return REDIS_ERR;
+
+    /* Currently we try to encode only strings */
+    assert(o->type == REDIS_STRING);
+
+    /* Check if we can represent this string as a long integer */
+    if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return REDIS_ERR;
+
+    /* Ok, this object can be encoded */
+    o->encoding = REDIS_ENCODING_INT;
+    sdsfree(o->ptr);
+    o->ptr = (void*) value;
+    return REDIS_OK;
 }
 
-static robj *lookupKeyWrite(redisDb *db, robj *key) {
-    deleteIfVolatile(db,key);
-    return lookupKey(db,key);
+/* Get a decoded version of an encoded object (returned as a new object) */
+static robj *getDecodedObject(const robj *o) {
+    robj *dec;
+    
+    assert(o->encoding != REDIS_ENCODING_RAW);
+    if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
+        char buf[32];
+
+        snprintf(buf,32,"%ld",(long)o->ptr);
+        dec = createStringObject(buf,strlen(buf));
+        return dec;
+    } else {
+        assert(1 != 1);
+    }
 }
 
-static int deleteKey(redisDb *db, robj *key) {
-    int retval;
+static int compareStringObjects(robj *a, robj *b) {
+    assert(a->type == REDIS_STRING && b->type == REDIS_STRING);
 
-    /* We need to protect key from destruction: after the first dictDelete()
-     * it may happen that 'key' is no longer valid if we don't increment
-     * it's count. This may happen when we get the object reference directly
-     * from the hash table with dictRandomKey() or dict iterators */
-    incrRefCount(key);
-    if (dictSize(db->expires)) dictDelete(db->expires,key);
-    retval = dictDelete(db->dict,key);
-    decrRefCount(key);
+    if (a == b) return 0;
+    if (a->encoding == REDIS_ENCODING_INT && b->encoding == REDIS_ENCODING_INT){
+        return (long)a->ptr - (long)b->ptr;
+    } else {
+        int retval;
 
-    return retval == DICT_OK;
+        incrRefCount(a);
+        incrRefCount(b);
+        if (a->encoding != REDIS_ENCODING_RAW) a = getDecodedObject(a);
+        if (b->encoding != REDIS_ENCODING_RAW) b = getDecodedObject(a);
+        retval = sdscmp(a->ptr,b->ptr);
+        decrRefCount(a);
+        decrRefCount(b);
+        return retval;
+    }
+}
+
+static size_t stringObjectLen(robj *o) {
+    assert(o->type == REDIS_STRING);
+    if (o->encoding == REDIS_ENCODING_RAW) {
+        return sdslen(o->ptr);
+    } else {
+        char buf[32];
+
+        return snprintf(buf,32,"%ld",(long)o->ptr);
+    }
 }
 
 /*============================ DB saving/loading ============================ */
@@ -1865,10 +2191,12 @@ writeerr:
 
 /* Save a string objet as [len][data] on disk. If the object is a string
  * representation of an integer value we try to safe it in a special form */
-static int rdbSaveStringObject(FILE *fp, robj *obj) {
-    size_t len = sdslen(obj->ptr);
+static int rdbSaveStringObjectRaw(FILE *fp, robj *obj) {
+    size_t len;
     int enclen;
 
+    len = sdslen(obj->ptr);
+
     /* Try integer encoding */
     if (len <= 11) {
         unsigned char buf[5];
@@ -1880,7 +2208,7 @@ static int rdbSaveStringObject(FILE *fp, robj *obj) {
 
     /* Try LZF compression - under 20 bytes it's unable to compress even
      * aaaaaaaaaaaaaaaaaa so skip it */
-    if (1 && len > 20) {
+    if (len > 20) {
         int retval;
 
         retval = rdbSaveLzfStringObject(fp,obj);
@@ -1895,6 +2223,21 @@ static int rdbSaveStringObject(FILE *fp, robj *obj) {
     return 0;
 }
 
+/* Like rdbSaveStringObjectRaw() but handle encoded objects */
+static int rdbSaveStringObject(FILE *fp, robj *obj) {
+    int retval;
+    robj *dec;
+
+    if (obj->encoding != REDIS_ENCODING_RAW) {
+        dec = getDecodedObject(obj);
+        retval = rdbSaveStringObjectRaw(fp,dec);
+        decrRefCount(dec);
+        return retval;
+    } else {
+        return rdbSaveStringObjectRaw(fp,obj);
+    }
+}
+
 /* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success */
 static int rdbSave(char *filename) {
     dictIterator *di = NULL;
@@ -1962,7 +2305,6 @@ static int rdbSave(char *filename) {
                 dictIterator *di = dictGetIterator(set);
                 dictEntry *de;
 
-                if (!set) oom("dictGetIteraotr");
                 if (rdbSaveLen(fp,dictSize(set)) == -1) goto werr;
                 while((de = dictNext(di)) != NULL) {
                     robj *eleobj = dictGetEntryKey(de);
@@ -1987,7 +2329,7 @@ static int rdbSave(char *filename) {
     /* Use RENAME to make sure the DB file is changed atomically only
      * if the generate DB file is ok. */
     if (rename(tmpfile,filename) == -1) {
-        redisLog(REDIS_WARNING,"Error moving temp DB file on the final destionation: %s", strerror(errno));
+        redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno));
         unlink(tmpfile);
         return REDIS_ERR;
     }
@@ -2212,6 +2554,7 @@ static int rdbLoad(char *filename) {
         if (type == REDIS_STRING) {
             /* Read string value */
             if ((o = rdbLoadStringObject(fp,rdbver)) == NULL) goto eoferr;
+            tryObjectEncoding(o);
         } else if (type == REDIS_LIST || type == REDIS_SET) {
             /* Read list/set value */
             uint32_t listlen;
@@ -2224,12 +2567,11 @@ static int rdbLoad(char *filename) {
                 robj *ele;
 
                 if ((ele = rdbLoadStringObject(fp,rdbver)) == NULL) goto eoferr;
+                tryObjectEncoding(ele);
                 if (type == REDIS_LIST) {
-                    if (!listAddNodeTail((list*)o->ptr,ele))
-                        oom("listAddNodeTail");
+                    listAddNodeTail((list*)o->ptr,ele);
                 } else {
-                    if (dictAdd((dict*)o->ptr,ele,NULL) == DICT_ERR)
-                        oom("dictAdd");
+                    dictAdd((dict*)o->ptr,ele,NULL);
                 }
             }
         } else {
@@ -2277,8 +2619,7 @@ static void pingCommand(redisClient *c) {
 }
 
 static void echoCommand(redisClient *c) {
-    addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",
-        (int)sdslen(c->argv[1]->ptr)));
+    addReplyBulkLen(c,c->argv[1]);
     addReply(c,c->argv[1]);
     addReply(c,shared.crlf);
 }
@@ -2323,14 +2664,14 @@ static void getCommand(redisClient *c) {
         if (o->type != REDIS_STRING) {
             addReply(c,shared.wrongtypeerr);
         } else {
-            addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",(int)sdslen(o->ptr)));
+            addReplyBulkLen(c,o);
             addReply(c,o);
             addReply(c,shared.crlf);
         }
     }
 }
 
-static void getSetCommand(redisClient *c) {
+static void getsetCommand(redisClient *c) {
     getCommand(c);
     if (dictAdd(c->db->dict,c->argv[1],c->argv[2]) == DICT_ERR) {
         dictReplace(c->db->dict,c->argv[1],c->argv[2]);
@@ -2354,7 +2695,7 @@ static void mgetCommand(redisClient *c) {
             if (o->type != REDIS_STRING) {
                 addReply(c,shared.nullbulk);
             } else {
-                addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",(int)sdslen(o->ptr)));
+                addReplyBulkLen(c,o);
                 addReply(c,o);
                 addReply(c,shared.crlf);
             }
@@ -2376,12 +2717,18 @@ static void incrDecrCommand(redisClient *c, long long incr) {
         } else {
             char *eptr;
 
-            value = strtoll(o->ptr, &eptr, 10);
+            if (o->encoding == REDIS_ENCODING_RAW)
+                value = strtoll(o->ptr, &eptr, 10);
+            else if (o->encoding == REDIS_ENCODING_INT)
+                value = (long)o->ptr;
+            else
+                assert(1 != 1);
         }
     }
 
     value += incr;
     o = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
+    tryObjectEncoding(o);
     retval = dictAdd(c->db->dict,c->argv[1],o);
     if (retval == DICT_ERR) {
         dictReplace(c->db->dict,c->argv[1],o);
@@ -2477,7 +2824,6 @@ static void keysCommand(redisClient *c) {
     robj *lenobj = createObject(REDIS_STRING,NULL);
 
     di = dictGetIterator(c->db->dict);
-    if (!di) oom("dictGetIterator");
     addReply(c,lenobj);
     decrRefCount(lenobj);
     while((de = dictNext(di)) != NULL) {
@@ -2673,9 +3019,9 @@ static void pushGenericCommand(redisClient *c, int where) {
         lobj = createListObject();
         list = lobj->ptr;
         if (where == REDIS_HEAD) {
-            if (!listAddNodeHead(list,c->argv[2])) oom("listAddNodeHead");
+            listAddNodeHead(list,c->argv[2]);
         } else {
-            if (!listAddNodeTail(list,c->argv[2])) oom("listAddNodeTail");
+            listAddNodeTail(list,c->argv[2]);
         }
         dictAdd(c->db->dict,c->argv[1],lobj);
         incrRefCount(c->argv[1]);
@@ -2687,9 +3033,9 @@ static void pushGenericCommand(redisClient *c, int where) {
         }
         list = lobj->ptr;
         if (where == REDIS_HEAD) {
-            if (!listAddNodeHead(list,c->argv[2])) oom("listAddNodeHead");
+            listAddNodeHead(list,c->argv[2]);
         } else {
-            if (!listAddNodeTail(list,c->argv[2])) oom("listAddNodeTail");
+            listAddNodeTail(list,c->argv[2]);
         }
         incrRefCount(c->argv[2]);
     }
@@ -2742,7 +3088,7 @@ static void lindexCommand(redisClient *c) {
                 addReply(c,shared.nullbulk);
             } else {
                 robj *ele = listNodeValue(ln);
-                addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",(int)sdslen(ele->ptr)));
+                addReplyBulkLen(c,ele);
                 addReply(c,ele);
                 addReply(c,shared.crlf);
             }
@@ -2802,7 +3148,7 @@ static void popGenericCommand(redisClient *c, int where) {
                 addReply(c,shared.nullbulk);
             } else {
                 robj *ele = listNodeValue(ln);
-                addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",(int)sdslen(ele->ptr)));
+                addReplyBulkLen(c,ele);
                 addReply(c,ele);
                 addReply(c,shared.crlf);
                 listDelNode(list,ln);
@@ -2858,7 +3204,7 @@ static void lrangeCommand(redisClient *c) {
             addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen));
             for (j = 0; j < rangelen; j++) {
                 ele = listNodeValue(ln);
-                addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",(int)sdslen(ele->ptr)));
+                addReplyBulkLen(c,ele);
                 addReply(c,ele);
                 addReply(c,shared.crlf);
                 ln = ln->next;
@@ -2941,7 +3287,7 @@ static void lremCommand(redisClient *c) {
                 robj *ele = listNodeValue(ln);
 
                 next = fromtail ? ln->prev : ln->next;
-                if (sdscmp(ele->ptr,c->argv[3]->ptr) == 0) {
+                if (compareStringObjects(ele,c->argv[3]) == 0) {
                     listDelNode(list,ln);
                     server.dirty++;
                     removed++;
@@ -3090,7 +3436,7 @@ static void spopCommand(redisClient *c) {
         } else {
             robj *ele = dictGetEntryKey(de);
 
-            addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",sdslen(ele->ptr)));
+            addReplyBulkLen(c,ele);
             addReply(c,ele);
             addReply(c,shared.crlf);
             dictDelete(set->ptr,ele);
@@ -3100,6 +3446,31 @@ static void spopCommand(redisClient *c) {
     }
 }
 
+static void srandmemberCommand(redisClient *c) {
+    robj *set;
+    dictEntry *de;
+
+    set = lookupKeyRead(c->db,c->argv[1]);
+    if (set == NULL) {
+        addReply(c,shared.nullbulk);
+    } else {
+        if (set->type != REDIS_SET) {
+            addReply(c,shared.wrongtypeerr);
+            return;
+        }
+        de = dictGetRandomKey(set->ptr);
+        if (de == NULL) {
+            addReply(c,shared.nullbulk);
+        } else {
+            robj *ele = dictGetEntryKey(de);
+
+            addReplyBulkLen(c,ele);
+            addReply(c,ele);
+            addReply(c,shared.crlf);
+        }
+    }
+}
+
 static int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
     dict **d1 = (void*) s1, **d2 = (void*) s2;
 
@@ -3113,7 +3484,6 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, r
     robj *lenobj = NULL, *dstset = NULL;
     int j, cardinality = 0;
 
-    if (!dv) oom("sinterGenericCommand");
     for (j = 0; j < setsnum; j++) {
         robj *setobj;
 
@@ -3160,7 +3530,6 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, r
      * the element against all the other sets, if at least one set does
      * not include the element it is discarded */
     di = dictGetIterator(dv[0]);
-    if (!di) oom("dictGetIterator");
 
     while((de = dictNext(di)) != NULL) {
         robj *ele;
@@ -3171,7 +3540,7 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, r
             continue; /* at least one set does not contain the member */
         ele = dictGetEntryKey(de);
         if (!dstkey) {
-            addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",sdslen(ele->ptr)));
+            addReplyBulkLen(c,ele);
             addReply(c,ele);
             addReply(c,shared.crlf);
             cardinality++;
@@ -3217,7 +3586,6 @@ static void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnu
     robj *dstset = NULL;
     int j, cardinality = 0;
 
-    if (!dv) oom("sunionDiffGenericCommand");
     for (j = 0; j < setsnum; j++) {
         robj *setobj;
 
@@ -3248,7 +3616,6 @@ static void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnu
         if (!dv[j]) continue; /* non existing keys are like empty sets */
 
         di = dictGetIterator(dv[j]);
-        if (!di) oom("dictGetIterator");
 
         while((de = dictNext(di)) != NULL) {
             robj *ele;
@@ -3275,13 +3642,11 @@ static void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnu
     if (!dstkey) {
         addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",cardinality));
         di = dictGetIterator(dstset->ptr);
-        if (!di) oom("dictGetIterator");
         while((de = dictNext(di)) != NULL) {
             robj *ele;
 
             ele = dictGetEntryKey(de);
-            addReplySds(c,sdscatprintf(sdsempty(),
-                    "$%d\r\n",sdslen(ele->ptr)));
+            addReplyBulkLen(c,ele);
             addReply(c,ele);
             addReply(c,shared.crlf);
         }
@@ -3321,6 +3686,312 @@ static void sdiffstoreCommand(redisClient *c) {
     sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_DIFF);
 }
 
+/* ==================================== ZSets =============================== */
+
+/* ZSETs are ordered sets using two data structures to hold the same elements
+ * in order to get O(log(N)) INSERT and REMOVE operations into a sorted
+ * data structure.
+ *
+ * The elements are added to an hash table mapping Redis objects to scores.
+ * At the same time the elements are added to a skip list mapping scores
+ * to Redis objects (so objects are sorted by scores in this "view"). */
+
+/* This skiplist implementation is almost a C translation of the original
+ * algorithm described by William Pugh in "Skip Lists: A Probabilistic
+ * Alternative to Balanced Trees", modified in three ways:
+ * a) this implementation allows for repeated values.
+ * b) the comparison is not just by key (our 'score') but by satellite data.
+ * c) there is a back pointer, so it's a doubly linked list with the back
+ * pointers being only at "level 1". This allows to traverse the list
+ * from tail to head, useful for ZREVRANGE. */
+
+static zskiplistNode *zslCreateNode(int level, double score, robj *obj) {
+    zskiplistNode *zn = zmalloc(sizeof(*zn));
+
+    zn->forward = zmalloc(sizeof(zskiplistNode*) * level);
+    zn->score = score;
+    zn->obj = obj;
+    return zn;
+}
+
+static zskiplist *zslCreate(void) {
+    int j;
+    zskiplist *zsl;
+    
+    zsl = zmalloc(sizeof(*zsl));
+    zsl->level = 1;
+    zsl->length = 0;
+    zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL);
+    for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++)
+        zsl->header->forward[j] = NULL;
+    zsl->header->backward = NULL;
+    zsl->tail = NULL;
+    return zsl;
+}
+
+static void zslFreeNode(zskiplistNode *node) {
+    decrRefCount(node->obj);
+    zfree(node);
+}
+
+static void zslFree(zskiplist *zsl) {
+    zskiplistNode *node = zsl->header->forward[1], *next;
+
+    while(node) {
+        next = node->forward[0];
+        zslFreeNode(node);
+        node = next;
+    }
+}
+
+static int zslRandomLevel(void) {
+    int level = 1;
+    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
+        level += 1;
+    return level;
+}
+
+static void zslInsert(zskiplist *zsl, double score, robj *obj) {
+    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
+    int i, level;
+
+    x = zsl->header;
+    for (i = zsl->level-1; i >= 0; i--) {
+        while (x->forward[i] && x->forward[i]->score < score)
+            x = x->forward[i];
+        update[i] = x;
+    }
+    /* we assume the key is not already inside, since we allow duplicated
+     * scores, and the re-insertion of score and redis object should never
+     * happpen since the caller of zslInsert() should test in the hash table
+     * if the element is already inside or not. */
+    level = zslRandomLevel();
+    if (level > zsl->level) {
+        for (i = zsl->level; i < level; i++)
+            update[i] = zsl->header;
+        zsl->level = level;
+    }
+    x = zslCreateNode(level,score,obj);
+    for (i = 0; i < level; i++) {
+        x->forward[i] = update[i]->forward[i];
+        update[i]->forward[i] = x;
+    }
+    x->backward = (update[0] == zsl->header) ? NULL : update[0];
+    if (x->forward[0])
+        x->forward[0]->backward = x;
+    else
+        zsl->tail = x;
+    zsl->length++;
+}
+
+static int zslDelete(zskiplist *zsl, double score, robj *obj) {
+    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
+    int i;
+
+    x = zsl->header;
+    for (i = zsl->level-1; i >= 0; i--) {
+        while (x->forward[i] && x->forward[i]->score < score)
+            x = x->forward[i];
+        update[i] = x;
+    }
+    /* We may have multiple elements with the same score, what we need
+     * is to find the element with both the right score and object. */
+    x = x->forward[0];
+    while(x->score == score) {
+        if (compareStringObjects(x->obj,obj) == 0) {
+            for (i = 0; i < zsl->level; i++) {
+                if (update[i]->forward[i] != x) break;
+                update[i]->forward[i] = x->forward[i];
+            }
+            if (x->forward[0]) {
+                x->forward[0]->backward = (x->backward == zsl->header) ?
+                                            NULL : x->backward;
+            } else {
+                zsl->tail = x->backward;
+            }
+            zslFreeNode(x);
+            while(zsl->level > 1 && zsl->header->forward[zsl->level-1] == NULL)
+                zsl->level--;
+            zsl->length--;
+            return 1;
+        } else {
+            x = x->forward[0];
+            if (!x) return 0; /* end of the list reached, not found */
+        }
+    }
+    return 0; /* not found */
+}
+
+/* The actual Z-commands implementations */
+
+static void zaddCommand(redisClient *c) {
+    robj *zsetobj;
+    zset *zs;
+    double *score;
+
+    zsetobj = lookupKeyWrite(c->db,c->argv[1]);
+    if (zsetobj == NULL) {
+        zsetobj = createZsetObject();
+        dictAdd(c->db->dict,c->argv[1],zsetobj);
+        incrRefCount(c->argv[1]);
+    } else {
+        if (zsetobj->type != REDIS_ZSET) {
+            addReply(c,shared.wrongtypeerr);
+            return;
+        }
+    }
+    score = zmalloc(sizeof(double));
+    *score = strtod(c->argv[2]->ptr,NULL);
+    zs = zsetobj->ptr;
+    if (dictAdd(zs->dict,c->argv[3],score) == DICT_OK) {
+        /* case 1: New element */
+        incrRefCount(c->argv[3]); /* added to hash */
+        zslInsert(zs->zsl,*score,c->argv[3]);
+        incrRefCount(c->argv[3]); /* added to skiplist */
+        server.dirty++;
+        addReply(c,shared.cone);
+    } else {
+        dictEntry *de;
+        double *oldscore;
+        
+        /* case 2: Score update operation */
+        de = dictFind(zs->dict,c->argv[3]);
+        assert(de != NULL);
+        oldscore = dictGetEntryVal(de);
+        if (*score != *oldscore) {
+            int deleted;
+
+            deleted = zslDelete(zs->zsl,*oldscore,c->argv[3]);
+            assert(deleted != 0);
+            zslInsert(zs->zsl,*score,c->argv[3]);
+            incrRefCount(c->argv[3]);
+            server.dirty++;
+        }
+        addReply(c,shared.czero);
+    }
+}
+
+static void zremCommand(redisClient *c) {
+    robj *zsetobj;
+    zset *zs;
+
+    zsetobj = lookupKeyWrite(c->db,c->argv[1]);
+    if (zsetobj == NULL) {
+        addReply(c,shared.czero);
+    } else {
+        dictEntry *de;
+        double *oldscore;
+        int deleted;
+
+        if (zsetobj->type != REDIS_ZSET) {
+            addReply(c,shared.wrongtypeerr);
+            return;
+        }
+        zs = zsetobj->ptr;
+        de = dictFind(zs->dict,c->argv[2]);
+        if (de == NULL) {
+            addReply(c,shared.czero);
+            return;
+        }
+        /* Delete from the skiplist */
+        oldscore = dictGetEntryVal(de);
+        deleted = zslDelete(zs->zsl,*oldscore,c->argv[2]);
+        assert(deleted != 0);
+
+        /* Delete from the hash table */
+        dictDelete(zs->dict,c->argv[2]);
+        if (htNeedsResize(zs->dict)) dictResize(zs->dict);
+        server.dirty++;
+        addReply(c,shared.cone);
+    }
+}
+
+static void zrangeGenericCommand(redisClient *c, int reverse) {
+    robj *o;
+    int start = atoi(c->argv[2]->ptr);
+    int end = atoi(c->argv[3]->ptr);
+
+    o = lookupKeyRead(c->db,c->argv[1]);
+    if (o == NULL) {
+        addReply(c,shared.nullmultibulk);
+    } else {
+        if (o->type != REDIS_ZSET) {
+            addReply(c,shared.wrongtypeerr);
+        } else {
+            zset *zsetobj = o->ptr;
+            zskiplist *zsl = zsetobj->zsl;
+            zskiplistNode *ln;
+
+            int llen = zsl->length;
+            int rangelen, j;
+            robj *ele;
+
+            /* convert negative indexes */
+            if (start < 0) start = llen+start;
+            if (end < 0) end = llen+end;
+            if (start < 0) start = 0;
+            if (end < 0) end = 0;
+
+            /* indexes sanity checks */
+            if (start > end || start >= llen) {
+                /* Out of range start or start > end result in empty list */
+                addReply(c,shared.emptymultibulk);
+                return;
+            }
+            if (end >= llen) end = llen-1;
+            rangelen = (end-start)+1;
+
+            /* Return the result in form of a multi-bulk reply */
+            if (reverse) {
+                ln = zsl->tail;
+                while (start--)
+                    ln = ln->backward;
+            } else {
+                ln = zsl->header->forward[0];
+                while (start--)
+                    ln = ln->forward[0];
+            }
+
+            addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen));
+            for (j = 0; j < rangelen; j++) {
+                ele = ln->obj;
+                addReplyBulkLen(c,ele);
+                addReply(c,ele);
+                addReply(c,shared.crlf);
+                ln = reverse ? ln->backward : ln->forward[0];
+            }
+        }
+    }
+}
+
+static void zrangeCommand(redisClient *c) {
+    zrangeGenericCommand(c,0);
+}
+
+static void zrevrangeCommand(redisClient *c) {
+    zrangeGenericCommand(c,1);
+}
+
+static void zlenCommand(redisClient *c) {
+    robj *o;
+    zset *zs;
+    
+    o = lookupKeyRead(c->db,c->argv[1]);
+    if (o == NULL) {
+        addReply(c,shared.czero);
+        return;
+    } else {
+        if (o->type != REDIS_ZSET) {
+            addReply(c,shared.wrongtypeerr);
+        } else {
+            zs = o->ptr;
+            addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",zs->zsl->length));
+        }
+    }
+}
+
+/* ========================= Non type-specific commands  ==================== */
+
 static void flushdbCommand(redisClient *c) {
     server.dirty += dictSize(c->db->dict);
     dictEmpty(c->db->dict);
@@ -3337,7 +4008,6 @@ static void flushallCommand(redisClient *c) {
 
 static redisSortOperation *createSortOperation(int type, robj *pattern) {
     redisSortOperation *so = zmalloc(sizeof(*so));
-    if (!so) oom("createSortOperation");
     so->type = type;
     so->pattern = pattern;
     return so;
@@ -3357,6 +4027,12 @@ static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
         char buf[REDIS_SORTKEY_MAX+1];
     } keyname;
 
+    if (subst->encoding == REDIS_ENCODING_RAW)
+        incrRefCount(subst);
+    else {
+        subst = getDecodedObject(subst);
+    }
+
     spat = pattern->ptr;
     ssub = subst->ptr;
     if (sdslen(spat)+sdslen(ssub)-1 > REDIS_SORTKEY_MAX) return NULL;
@@ -3376,6 +4052,8 @@ static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
     keyobj.type = REDIS_STRING;
     keyobj.ptr = ((char*)&keyname)+(sizeof(long)*2);
 
+    decrRefCount(subst);
+
     /* printf("lookup '%s' => %p\n", keyname.buf,de); */
     return lookupKeyRead(db,&keyobj);
 }
@@ -3413,7 +4091,20 @@ static int sortCompare(const void *s1, const void *s2) {
             }
         } else {
             /* Compare elements directly */
-            cmp = strcoll(so1->obj->ptr,so2->obj->ptr);
+            if (so1->obj->encoding == REDIS_ENCODING_RAW &&
+                so2->obj->encoding == REDIS_ENCODING_RAW) {
+                cmp = strcoll(so1->obj->ptr,so2->obj->ptr);
+            } else {
+                robj *dec1, *dec2;
+
+                dec1 = so1->obj->encoding == REDIS_ENCODING_RAW ?
+                    so1->obj : getDecodedObject(so1->obj);
+                dec2 = so2->obj->encoding == REDIS_ENCODING_RAW ?
+                    so2->obj : getDecodedObject(so2->obj);
+                cmp = strcoll(dec1->ptr,dec2->ptr);
+                if (dec1 != so1->obj) decrRefCount(dec1);
+                if (dec2 != so2->obj) decrRefCount(dec2);
+            }
         }
     }
     return server.sort_desc ? -cmp : cmp;
@@ -3503,7 +4194,6 @@ static void sortCommand(redisClient *c) {
         listLength((list*)sortval->ptr) :
         dictSize((dict*)sortval->ptr);
     vector = zmalloc(sizeof(redisSortObject)*vectorlen);
-    if (!vector) oom("allocating objects vector for SORT");
     j = 0;
     if (sortval->type == REDIS_LIST) {
         list *list = sortval->ptr;
@@ -3523,7 +4213,6 @@ static void sortCommand(redisClient *c) {
         dictEntry *setele;
 
         di = dictGetIterator(set);
-        if (!di) oom("dictGetIterator");
         while((setele = dictNext(di)) != NULL) {
             vector[j].obj = dictGetEntryKey(setele);
             vector[j].u.score = 0;
@@ -3543,13 +4232,33 @@ static void sortCommand(redisClient *c) {
                 byval = lookupKeyByPattern(c->db,sortby,vector[j].obj);
                 if (!byval || byval->type != REDIS_STRING) continue;
                 if (alpha) {
-                    vector[j].u.cmpobj = byval;
-                    incrRefCount(byval);
+                    if (byval->encoding == REDIS_ENCODING_RAW) {
+                        vector[j].u.cmpobj = byval;
+                        incrRefCount(byval);
+                    } else {
+                        vector[j].u.cmpobj = getDecodedObject(byval);
+                    }
                 } else {
-                    vector[j].u.score = strtod(byval->ptr,NULL);
+                    if (byval->encoding == REDIS_ENCODING_RAW) {
+                        vector[j].u.score = strtod(byval->ptr,NULL);
+                    } else {
+                        if (byval->encoding == REDIS_ENCODING_INT) {
+                            vector[j].u.score = (long)byval->ptr;
+                        } else
+                            assert(1 != 1);
+                    }
                 }
             } else {
-                if (!alpha) vector[j].u.score = strtod(vector[j].obj->ptr,NULL);
+                if (!alpha) {
+                    if (vector[j].obj->encoding == REDIS_ENCODING_RAW)
+                        vector[j].u.score = strtod(vector[j].obj->ptr,NULL);
+                    else {
+                        if (vector[j].obj->encoding == REDIS_ENCODING_INT)
+                            vector[j].u.score = (long) vector[j].obj->ptr;
+                        else
+                            assert(1 != 1);
+                    }
+                }
             }
         }
     }
@@ -3581,8 +4290,7 @@ static void sortCommand(redisClient *c) {
     for (j = start; j <= end; j++) {
         listNode *ln;
         if (!getop) {
-            addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",
-                sdslen(vector[j].obj->ptr)));
+            addReplyBulkLen(c,vector[j].obj);
             addReply(c,vector[j].obj);
             addReply(c,shared.crlf);
         }
@@ -3596,8 +4304,7 @@ static void sortCommand(redisClient *c) {
                 if (!val || val->type != REDIS_STRING) {
                     addReply(c,shared.nullbulk);
                 } else {
-                    addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",
-                        sdslen(val->ptr)));
+                    addReplyBulkLen(c,val);
                     addReply(c,val);
                     addReply(c,shared.crlf);
                 }
@@ -3624,6 +4331,7 @@ static void infoCommand(redisClient *c) {
     
     info = sdscatprintf(sdsempty(),
         "redis_version:%s\r\n"
+        "arch_bits:%s\r\n"
         "uptime_in_seconds:%d\r\n"
         "uptime_in_days:%d\r\n"
         "connected_clients:%d\r\n"
@@ -3636,6 +4344,7 @@ static void infoCommand(redisClient *c) {
         "total_commands_processed:%lld\r\n"
         "role:%s\r\n"
         ,REDIS_VERSION,
+        (sizeof(long) == 8) ? "64" : "32",
         uptime,
         uptime/(3600*24),
         listLength(server.clients)-listLength(server.slaves),
@@ -3682,7 +4391,7 @@ static void monitorCommand(redisClient *c) {
 
     c->flags |= (REDIS_SLAVE|REDIS_MONITOR);
     c->slaveseldb = 0;
-    if (!listAddNodeTail(server.monitors,c)) oom("listAddNodeTail");
+    listAddNodeTail(server.monitors,c);
     addReply(c,shared.ok);
 }
 
@@ -3782,6 +4491,49 @@ static void ttlCommand(redisClient *c) {
     addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",ttl));
 }
 
+static void msetGenericCommand(redisClient *c, int nx) {
+    int j;
+
+    if ((c->argc % 2) == 0) {
+        addReplySds(c,sdsnew("-ERR wrong number of arguments\r\n"));
+        return;
+    }
+    /* Handle the NX flag. The MSETNX semantic is to return zero and don't
+     * set nothing at all if at least one already key exists. */
+    if (nx) {
+        for (j = 1; j < c->argc; j += 2) {
+            if (dictFind(c->db->dict,c->argv[j]) != NULL) {
+                addReply(c, shared.czero);
+                return;
+            }
+        }
+    }
+
+    for (j = 1; j < c->argc; j += 2) {
+        int retval;
+
+        retval = dictAdd(c->db->dict,c->argv[j],c->argv[j+1]);
+        if (retval == DICT_ERR) {
+            dictReplace(c->db->dict,c->argv[j],c->argv[j+1]);
+            incrRefCount(c->argv[j+1]);
+        } else {
+            incrRefCount(c->argv[j]);
+            incrRefCount(c->argv[j+1]);
+        }
+        removeExpire(c->db,c->argv[j]);
+    }
+    server.dirty += (c->argc-1)/2;
+    addReply(c, nx ? shared.cone : shared.ok);
+}
+
+static void msetCommand(redisClient *c) {
+    msetGenericCommand(c,0);
+}
+
+static void msetnxCommand(redisClient *c) {
+    msetGenericCommand(c,1);
+}
+
 /* =============================== Replication  ============================= */
 
 static int syncWrite(int fd, char *ptr, ssize_t size, int timeout) {
@@ -3879,7 +4631,6 @@ static void syncCommand(redisClient *c) {
              * another slave. Set the right state, and copy the buffer. */
             listRelease(c->reply);
             c->reply = listDup(slave->reply);
-            if (!c->reply) oom("listDup copying slave reply list");
             c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
             redisLog(REDIS_NOTICE,"Waiting for end of BGSAVE for SYNC");
         } else {
@@ -3901,7 +4652,7 @@ static void syncCommand(redisClient *c) {
     c->repldbfd = -1;
     c->flags |= REDIS_SLAVE;
     c->slaveseldb = 0;
-    if (!listAddNodeTail(server.slaves,c)) oom("listAddNodeTail");
+    listAddNodeTail(server.slaves,c);
     return;
 }
 
@@ -4178,8 +4929,8 @@ static void debugCommand(redisClient *c) {
         key = dictGetEntryKey(de);
         val = dictGetEntryVal(de);
         addReplySds(c,sdscatprintf(sdsempty(),
-            "+Key at:%p refcount:%d, value at:%p refcount:%d\r\n",
-                key, key->refcount, val, val->refcount));
+            "+Key at:%p refcount:%d, value at:%p refcount:%d encoding:%d\r\n",
+                key, key->refcount, val, val->refcount, val->encoding));
     } else {
         addReplySds(c,sdsnew(
             "-ERR Syntax error, try DEBUG [SEGFAULT|OBJECT <key>]\r\n"));
@@ -4188,6 +4939,11 @@ static void debugCommand(redisClient *c) {
 
 #ifdef HAVE_BACKTRACE
 static struct redisFunctionSym symsTable[] = {
+{"compareStringObjects", (unsigned long)compareStringObjects},
+{"isStringRepresentableAsLong", (unsigned long)isStringRepresentableAsLong},
+{"dictEncObjKeyCompare", (unsigned long)dictEncObjKeyCompare},
+{"dictEncObjHash", (unsigned long)dictEncObjHash},
+{"incrDecrCommand", (unsigned long)incrDecrCommand},
 {"freeStringObject", (unsigned long)freeStringObject},
 {"freeListObject", (unsigned long)freeListObject},
 {"freeSetObject", (unsigned long)freeSetObject},
@@ -4195,6 +4951,8 @@ static struct redisFunctionSym symsTable[] = {
 {"createObject", (unsigned long)createObject},
 {"freeClient", (unsigned long)freeClient},
 {"rdbLoad", (unsigned long)rdbLoad},
+{"rdbSaveStringObject", (unsigned long)rdbSaveStringObject},
+{"rdbSaveStringObjectRaw", (unsigned long)rdbSaveStringObjectRaw},
 {"addReply", (unsigned long)addReply},
 {"addReplySds", (unsigned long)addReplySds},
 {"incrRefCount", (unsigned long)incrRefCount},
@@ -4203,6 +4961,8 @@ static struct redisFunctionSym symsTable[] = {
 {"replicationFeedSlaves", (unsigned long)replicationFeedSlaves},
 {"syncWithMaster", (unsigned long)syncWithMaster},
 {"tryObjectSharing", (unsigned long)tryObjectSharing},
+{"tryObjectEncoding", (unsigned long)tryObjectEncoding},
+{"getDecodedObject", (unsigned long)getDecodedObject},
 {"removeExpire", (unsigned long)removeExpire},
 {"expireIfNeeded", (unsigned long)expireIfNeeded},
 {"deleteIfVolatile", (unsigned long)deleteIfVolatile},
@@ -4250,6 +5010,7 @@ static struct redisFunctionSym symsTable[] = {
 {"sismemberCommand", (unsigned long)sismemberCommand},
 {"scardCommand", (unsigned long)scardCommand},
 {"spopCommand", (unsigned long)spopCommand},
+{"srandmemberCommand", (unsigned long)srandmemberCommand},
 {"sinterCommand", (unsigned long)sinterCommand},
 {"sinterstoreCommand", (unsigned long)sinterstoreCommand},
 {"sunionCommand", (unsigned long)sunionCommand},
@@ -4265,7 +5026,7 @@ static struct redisFunctionSym symsTable[] = {
 {"mgetCommand", (unsigned long)mgetCommand},
 {"monitorCommand", (unsigned long)monitorCommand},
 {"expireCommand", (unsigned long)expireCommand},
-{"getSetCommand", (unsigned long)getSetCommand},
+{"getsetCommand", (unsigned long)getsetCommand},
 {"ttlCommand", (unsigned long)ttlCommand},
 {"slaveofCommand", (unsigned long)slaveofCommand},
 {"debugCommand", (unsigned long)debugCommand},
@@ -4273,6 +5034,22 @@ static struct redisFunctionSym symsTable[] = {
 {"setupSigSegvAction", (unsigned long)setupSigSegvAction},
 {"readQueryFromClient", (unsigned long)readQueryFromClient},
 {"rdbRemoveTempFile", (unsigned long)rdbRemoveTempFile},
+{"msetGenericCommand", (unsigned long)msetGenericCommand},
+{"msetCommand", (unsigned long)msetCommand},
+{"msetnxCommand", (unsigned long)msetnxCommand},
+{"zslCreateNode", (unsigned long)zslCreateNode},
+{"zslCreate", (unsigned long)zslCreate},
+{"zslFreeNode",(unsigned long)zslFreeNode},
+{"zslFree",(unsigned long)zslFree},
+{"zslRandomLevel",(unsigned long)zslRandomLevel},
+{"zslInsert",(unsigned long)zslInsert},
+{"zslDelete",(unsigned long)zslDelete},
+{"createZsetObject",(unsigned long)createZsetObject},
+{"zaddCommand",(unsigned long)zaddCommand},
+{"zrangeGenericCommand",(unsigned long)zrangeGenericCommand},
+{"zrangeCommand",(unsigned long)zrangeCommand},
+{"zrevrangeCommand",(unsigned long)zrevrangeCommand},
+{"zremCommand",(unsigned long)zremCommand},
 {NULL,0}
 };
 
@@ -4305,8 +5082,14 @@ static void *getMcontextEip(ucontext_t *uc) {
     return (void*) uc->uc_mcontext.mc_eip;
 #elif defined(__dietlibc__)
     return (void*) uc->uc_mcontext.eip;
-#elif defined(__APPLE__)
+#elif defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
+    return (void*) uc->uc_mcontext->__ss.__eip;
+#elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
+  #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
+    return (void*) uc->uc_mcontext->__ss.__rip;
+  #else
     return (void*) uc->uc_mcontext->__ss.__eip;
+  #endif 
 #elif defined(__i386__) || defined(__X86_64__) /* Linux x86 */
     return (void*) uc->uc_mcontext.gregs[REG_EIP];
 #elif defined(__ia64__) /* Linux IA64 */