+/*-----------------------------------------------------------------------------
+ * Ziplist-backed sorted set API
+ *----------------------------------------------------------------------------*/
+
+double zzlGetScore(unsigned char *sptr) {
+ unsigned char *vstr;
+ unsigned int vlen;
+ long long vlong;
+ char buf[128];
+ double score;
+
+ redisAssert(sptr != NULL);
+ redisAssert(ziplistGet(sptr,&vstr,&vlen,&vlong));
+
+ if (vstr) {
+ memcpy(buf,vstr,vlen);
+ buf[vlen] = '\0';
+ score = strtod(buf,NULL);
+ } else {
+ score = vlong;
+ }
+
+ return score;
+}
+
+/* Compare element in sorted set with given element. */
+int zzlCompareElements(unsigned char *eptr, unsigned char *cstr, unsigned int clen) {
+ unsigned char *vstr;
+ unsigned int vlen;
+ long long vlong;
+ unsigned char vbuf[32];
+ int minlen, cmp;
+
+ redisAssert(ziplistGet(eptr,&vstr,&vlen,&vlong));
+ if (vstr == NULL) {
+ /* Store string representation of long long in buf. */
+ vlen = ll2string((char*)vbuf,sizeof(vbuf),vlong);
+ vstr = vbuf;
+ }
+
+ minlen = (vlen < clen) ? vlen : clen;
+ cmp = memcmp(vstr,cstr,minlen);
+ if (cmp == 0) return vlen-clen;
+ return cmp;
+}
+
+unsigned char *zzlFind(robj *zobj, robj *ele, double *score) {
+ unsigned char *zl = zobj->ptr;
+ unsigned char *eptr = ziplistIndex(zl,0), *sptr;
+
+ ele = getDecodedObject(ele);
+ while (eptr != NULL) {
+ sptr = ziplistNext(zl,eptr);
+ redisAssert(sptr != NULL);
+
+ if (ziplistCompare(eptr,ele->ptr,sdslen(ele->ptr))) {
+ /* Matching element, pull out score. */
+ *score = zzlGetScore(sptr);
+ decrRefCount(ele);
+ return eptr;
+ }
+
+ /* Move to next element. */
+ eptr = ziplistNext(zl,sptr);
+ }
+
+ decrRefCount(ele);
+ return NULL;
+}
+
+/* Delete (element,score) pair from ziplist. Use local copy of eptr because we
+ * don't want to modify the one given as argument. */
+int zzlDelete(robj *zobj, unsigned char *eptr) {
+ unsigned char *zl = zobj->ptr;
+ unsigned char *p = eptr;
+
+ /* TODO: add function to ziplist API to delete N elements from offset. */
+ zl = ziplistDelete(zl,&p);
+ zl = ziplistDelete(zl,&p);
+ zobj->ptr = zl;
+ return REDIS_OK;
+}
+
+int zzlInsertAt(robj *zobj, robj *ele, double score, unsigned char *eptr) {
+ unsigned char *zl = zobj->ptr;
+ unsigned char *sptr;
+ char scorebuf[128];
+ int scorelen;
+ int offset;
+
+ redisAssert(ele->encoding == REDIS_ENCODING_RAW);
+ scorelen = d2string(scorebuf,sizeof(scorebuf),score);
+ if (eptr == NULL) {
+ zl = ziplistPush(zl,ele->ptr,sdslen(ele->ptr),ZIPLIST_TAIL);
+ zl = ziplistPush(zl,(unsigned char*)scorebuf,scorelen,ZIPLIST_TAIL);
+ } else {
+ /* Keep offset relative to zl, as it might be re-allocated. */
+ offset = eptr-zl;
+ zl = ziplistInsert(zl,eptr,ele->ptr,sdslen(ele->ptr));
+ eptr = zl+offset;
+
+ /* Insert score after the element. */
+ redisAssert((sptr = ziplistNext(zl,eptr)) != NULL);
+ zl = ziplistInsert(zl,sptr,(unsigned char*)scorebuf,scorelen);
+ }
+
+ zobj->ptr = zl;
+ return REDIS_OK;
+}
+
+/* Insert (element,score) pair in ziplist. This function assumes the element is
+ * not yet present in the list. */
+int zzlInsert(robj *zobj, robj *ele, double score) {
+ unsigned char *zl = zobj->ptr;
+ unsigned char *eptr = ziplistIndex(zl,0), *sptr;
+ double s;
+ int insert = 0;
+
+ ele = getDecodedObject(ele);
+ while (eptr != NULL) {
+ sptr = ziplistNext(zl,eptr);
+ redisAssert(sptr != NULL);
+ s = zzlGetScore(sptr);
+
+ if (s > score) {
+ /* First element with score larger than score for element to be
+ * inserted. This means we should take its spot in the list to
+ * maintain ordering. */
+ insert = 1;
+ } else if (s == score) {
+ /* Ensure lexicographical ordering for elements. */
+ if (zzlCompareElements(eptr,ele->ptr,sdslen(ele->ptr)) < 0)
+ insert = 1;
+ }
+
+ if (insert) {
+ zzlInsertAt(zobj,ele,score,eptr);
+ break;
+ }
+
+ /* Move to next element. */
+ eptr = ziplistNext(zl,sptr);
+ }
+
+ /* Push on tail of list when it was not yet inserted. */
+ if (!insert)
+ zzlInsertAt(zobj,ele,score,eptr);
+
+ decrRefCount(ele);
+ return REDIS_OK;
+}