+
+/* Use dirty flags for pointers that need to be cleaned up in the next
+ * iteration over the zsetopval. The dirty flag for the long long value is
+ * special, since long long values don't need cleanup. Instead, it means that
+ * we already checked that "ell" holds a long long, or tried to convert another
+ * representation into a long long value. When this was successful,
+ * OPVAL_VALID_LL is set as well. */
+#define OPVAL_DIRTY_ROBJ 1
+#define OPVAL_DIRTY_LL 2
+#define OPVAL_VALID_LL 4
+
+/* Store value retrieved from the iterator. */
+typedef struct {
+ int flags;
+ unsigned char _buf[32]; /* Private buffer. */
+ robj *ele;
+ unsigned char *estr;
+ unsigned int elen;
+ long long ell;
+ double score;
+} zsetopval;
+
+typedef union _iterset iterset;
+typedef union _iterzset iterzset;
+
+void zuiInitIterator(zsetopsrc *op) {
+ if (op->subject == NULL)
+ return;
+
+ if (op->type == REDIS_SET) {
+ iterset *it = &op->iter.set;
+ if (op->encoding == REDIS_ENCODING_INTSET) {
+ it->is.is = op->subject->ptr;
+ it->is.ii = 0;
+ } else if (op->encoding == REDIS_ENCODING_HT) {
+ it->ht.dict = op->subject->ptr;
+ it->ht.di = dictGetIterator(op->subject->ptr);
+ it->ht.de = dictNext(it->ht.di);
+ } else {
+ redisPanic("Unknown set encoding");
+ }
+ } else if (op->type == REDIS_ZSET) {
+ iterzset *it = &op->iter.zset;
+ if (op->encoding == REDIS_ENCODING_ZIPLIST) {
+ it->zl.zl = op->subject->ptr;
+ it->zl.eptr = ziplistIndex(it->zl.zl,0);
+ if (it->zl.eptr != NULL) {
+ it->zl.sptr = ziplistNext(it->zl.zl,it->zl.eptr);
+ redisAssert(it->zl.sptr != NULL);
+ }
+ } else if (op->encoding == REDIS_ENCODING_SKIPLIST) {
+ it->sl.zs = op->subject->ptr;
+ it->sl.node = it->sl.zs->zsl->header->level[0].forward;
+ } else {
+ redisPanic("Unknown sorted set encoding");
+ }
+ } else {
+ redisPanic("Unsupported type");
+ }
+}
+
+void zuiClearIterator(zsetopsrc *op) {
+ if (op->subject == NULL)
+ return;
+
+ if (op->type == REDIS_SET) {
+ iterset *it = &op->iter.set;
+ if (op->encoding == REDIS_ENCODING_INTSET) {
+ REDIS_NOTUSED(it); /* skip */
+ } else if (op->encoding == REDIS_ENCODING_HT) {
+ dictReleaseIterator(it->ht.di);
+ } else {
+ redisPanic("Unknown set encoding");
+ }
+ } else if (op->type == REDIS_ZSET) {
+ iterzset *it = &op->iter.zset;
+ if (op->encoding == REDIS_ENCODING_ZIPLIST) {
+ REDIS_NOTUSED(it); /* skip */
+ } else if (op->encoding == REDIS_ENCODING_SKIPLIST) {
+ REDIS_NOTUSED(it); /* skip */
+ } else {
+ redisPanic("Unknown sorted set encoding");
+ }
+ } else {
+ redisPanic("Unsupported type");
+ }
+}
+
+int zuiLength(zsetopsrc *op) {
+ if (op->subject == NULL)
+ return 0;
+
+ if (op->type == REDIS_SET) {
+ iterset *it = &op->iter.set;
+ if (op->encoding == REDIS_ENCODING_INTSET) {
+ return intsetLen(it->is.is);
+ } else if (op->encoding == REDIS_ENCODING_HT) {
+ return dictSize(it->ht.dict);
+ } else {
+ redisPanic("Unknown set encoding");
+ }
+ } else if (op->type == REDIS_ZSET) {
+ iterzset *it = &op->iter.zset;
+ if (op->encoding == REDIS_ENCODING_ZIPLIST) {
+ return zzlLength(it->zl.zl);
+ } else if (op->encoding == REDIS_ENCODING_SKIPLIST) {
+ return it->sl.zs->zsl->length;
+ } else {
+ redisPanic("Unknown sorted set encoding");
+ }
+ } else {
+ redisPanic("Unsupported type");
+ }
+}
+
+/* Check if the current value is valid. If so, store it in the passed structure
+ * and move to the next element. If not valid, this means we have reached the
+ * end of the structure and can abort. */
+int zuiNext(zsetopsrc *op, zsetopval *val) {
+ if (op->subject == NULL)
+ return 0;
+
+ if (val->flags & OPVAL_DIRTY_ROBJ)
+ decrRefCount(val->ele);
+
+ memset(val,0,sizeof(zsetopval));
+
+ if (op->type == REDIS_SET) {
+ iterset *it = &op->iter.set;
+ if (op->encoding == REDIS_ENCODING_INTSET) {
+ int64_t ell = val->ell;
+
+ if (!intsetGet(it->is.is,it->is.ii,&ell))
+ return 0;
+ val->score = 1.0;
+
+ /* Move to next element. */
+ it->is.ii++;
+ } else if (op->encoding == REDIS_ENCODING_HT) {
+ if (it->ht.de == NULL)
+ return 0;
+ val->ele = dictGetKey(it->ht.de);
+ val->score = 1.0;
+
+ /* Move to next element. */
+ it->ht.de = dictNext(it->ht.di);
+ } else {
+ redisPanic("Unknown set encoding");
+ }
+ } else if (op->type == REDIS_ZSET) {
+ iterzset *it = &op->iter.zset;
+ if (op->encoding == REDIS_ENCODING_ZIPLIST) {
+ /* No need to check both, but better be explicit. */
+ if (it->zl.eptr == NULL || it->zl.sptr == NULL)
+ return 0;
+ redisAssert(ziplistGet(it->zl.eptr,&val->estr,&val->elen,&val->ell));
+ val->score = zzlGetScore(it->zl.sptr);
+
+ /* Move to next element. */
+ zzlNext(it->zl.zl,&it->zl.eptr,&it->zl.sptr);
+ } else if (op->encoding == REDIS_ENCODING_SKIPLIST) {
+ if (it->sl.node == NULL)
+ return 0;
+ val->ele = it->sl.node->obj;
+ val->score = it->sl.node->score;
+
+ /* Move to next element. */
+ it->sl.node = it->sl.node->level[0].forward;
+ } else {
+ redisPanic("Unknown sorted set encoding");
+ }
+ } else {
+ redisPanic("Unsupported type");
+ }
+ return 1;
+}
+
+int zuiLongLongFromValue(zsetopval *val) {
+ if (!(val->flags & OPVAL_DIRTY_LL)) {
+ val->flags |= OPVAL_DIRTY_LL;
+
+ if (val->ele != NULL) {
+ if (val->ele->encoding == REDIS_ENCODING_INT) {
+ val->ell = (long)val->ele->ptr;
+ val->flags |= OPVAL_VALID_LL;
+ } else if (val->ele->encoding == REDIS_ENCODING_RAW) {
+ if (string2ll(val->ele->ptr,sdslen(val->ele->ptr),&val->ell))
+ val->flags |= OPVAL_VALID_LL;
+ } else {
+ redisPanic("Unsupported element encoding");
+ }
+ } else if (val->estr != NULL) {
+ if (string2ll((char*)val->estr,val->elen,&val->ell))
+ val->flags |= OPVAL_VALID_LL;
+ } else {
+ /* The long long was already set, flag as valid. */
+ val->flags |= OPVAL_VALID_LL;
+ }
+ }
+ return val->flags & OPVAL_VALID_LL;
+}
+
+robj *zuiObjectFromValue(zsetopval *val) {
+ if (val->ele == NULL) {
+ if (val->estr != NULL) {
+ val->ele = createStringObject((char*)val->estr,val->elen);
+ } else {
+ val->ele = createStringObjectFromLongLong(val->ell);
+ }
+ val->flags |= OPVAL_DIRTY_ROBJ;
+ }
+ return val->ele;
+}
+
+int zuiBufferFromValue(zsetopval *val) {
+ if (val->estr == NULL) {
+ if (val->ele != NULL) {
+ if (val->ele->encoding == REDIS_ENCODING_INT) {
+ val->elen = ll2string((char*)val->_buf,sizeof(val->_buf),(long)val->ele->ptr);
+ val->estr = val->_buf;
+ } else if (val->ele->encoding == REDIS_ENCODING_RAW) {
+ val->elen = sdslen(val->ele->ptr);
+ val->estr = val->ele->ptr;
+ } else {
+ redisPanic("Unsupported element encoding");
+ }
+ } else {
+ val->elen = ll2string((char*)val->_buf,sizeof(val->_buf),val->ell);
+ val->estr = val->_buf;
+ }
+ }
+ return 1;
+}
+
+/* Find value pointed to by val in the source pointer to by op. When found,
+ * return 1 and store its score in target. Return 0 otherwise. */
+int zuiFind(zsetopsrc *op, zsetopval *val, double *score) {
+ if (op->subject == NULL)
+ return 0;
+
+ if (op->type == REDIS_SET) {
+ iterset *it = &op->iter.set;
+
+ if (op->encoding == REDIS_ENCODING_INTSET) {
+ if (zuiLongLongFromValue(val) && intsetFind(it->is.is,val->ell)) {
+ *score = 1.0;
+ return 1;
+ } else {
+ return 0;
+ }
+ } else if (op->encoding == REDIS_ENCODING_HT) {
+ zuiObjectFromValue(val);
+ if (dictFind(it->ht.dict,val->ele) != NULL) {
+ *score = 1.0;
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ redisPanic("Unknown set encoding");
+ }
+ } else if (op->type == REDIS_ZSET) {
+ iterzset *it = &op->iter.zset;
+ zuiObjectFromValue(val);
+
+ if (op->encoding == REDIS_ENCODING_ZIPLIST) {
+ if (zzlFind(it->zl.zl,val->ele,score) != NULL) {
+ /* Score is already set by zzlFind. */
+ return 1;
+ } else {
+ return 0;
+ }
+ } else if (op->encoding == REDIS_ENCODING_SKIPLIST) {
+ dictEntry *de;
+ if ((de = dictFind(it->sl.zs->dict,val->ele)) != NULL) {
+ *score = *(double*)dictGetVal(de);
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ redisPanic("Unknown sorted set encoding");
+ }
+ } else {
+ redisPanic("Unsupported type");
+ }
+}
+
+int zuiCompareByCardinality(const void *s1, const void *s2) {
+ return zuiLength((zsetopsrc*)s1) - zuiLength((zsetopsrc*)s2);