void freeListObject(robj *o) { switch (o->encoding) { case REDIS_ENCODING_LINKEDLIST: listRelease((list*) o->ptr); break; case REDIS_ENCODING_ZIPLIST: zfree(o->ptr); break; default: redisPanic("Unknown list encoding type"); } }
/* Return random element from a non empty set. * The returned element can be a int64_t value if the set is encoded * as an "intset" blob of integers, or a redis object if the set * is a regular set. * * The caller provides both pointers to be populated with the right * object. The return value of the function is the object->encoding * field of the object and is used by the caller to check if the * int64_t pointer or the redis object pointer was populated. * * When an object is returned (the set was a real set) the ref count * of the object is not incremented so this function can be considered * copy on write friendly. */ int setTypeRandomElement(robj* setobj, robj** objele, int64_t* llele) { if (setobj->encoding == REDIS_ENCODING_HT) { dictEntry* de = dictGetRandomKey(setobj->ptr); *objele = dictGetKey(de); } else if (setobj->encoding == REDIS_ENCODING_INTSET) { *llele = intsetRandom(setobj->ptr); } else { redisPanic("Unknown set encoding"); } return setobj->encoding; }
/* 执行后台的job,参数内包含着哪种type */ void *bioProcessBackgroundJobs(void *arg) { struct bio_job *job; unsigned long type = (unsigned long) arg; sigset_t sigset; /* Make the thread killable at any time, so that bioKillThreads() * can work reliably. */ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); pthread_mutex_lock(&bio_mutex[type]); /* Block SIGALRM so we are sure that only the main thread will * receive the watchdog signal. */ sigemptyset(&sigset); sigaddset(&sigset, SIGALRM); if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) redisLog(REDIS_WARNING, "Warning: can't mask SIGALRM in bio.c thread: %s", strerror(errno)); while(1) { listNode *ln; /* The loop always starts with the lock hold. */ if (listLength(bio_jobs[type]) == 0) { pthread_cond_wait(&bio_condvar[type],&bio_mutex[type]); continue; } /* Pop the job from the queue. */ //从工作列表中取出第一个job ln = listFirst(bio_jobs[type]); job = ln->value; /* It is now possible to unlock the background system as we know have * a stand alone job structure to process.*/ pthread_mutex_unlock(&bio_mutex[type]); /* Process the job accordingly to its type. */ //执行具体的工作 if (type == REDIS_BIO_CLOSE_FILE) { close((long)job->arg1); } else if (type == REDIS_BIO_AOF_FSYNC) { aof_fsync((long)job->arg1); } else { redisPanic("Wrong job type in bioProcessBackgroundJobs()."); } zfree(job); /* Lock again before reiterating the loop, if there are no longer * jobs to process we'll block again in pthread_cond_wait(). */ pthread_mutex_lock(&bio_mutex[type]); listDelNode(bio_jobs[type],ln); bio_pending[type]--; } }
void freeSetObject(robj *o) { switch (o->encoding) { case REDIS_ENCODING_HT: dictRelease((dict*) o->ptr); break; case REDIS_ENCODING_INTSET: zfree(o->ptr); break; default: redisPanic("Unknown set encoding type"); } }
void decrRefCount(void *obj) { robj *o = obj; /* Object is a swapped out value, or in the process of being loaded. */ if (server.vm_enabled && (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING)) { vmpointer *vp = obj; if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(o); vmMarkPagesFree(vp->page,vp->usedpages); server.vm_stats_swapped_objects--; zfree(vp); return; } if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0"); /* Object is in memory, or in the process of being swapped out. * * If the object is being swapped out, abort the operation on * decrRefCount even if the refcount does not drop to 0: the object * is referenced at least two times, as value of the key AND as * job->val in the iojob. So if we don't invalidate the iojob, when it is * done but the relevant key was removed in the meantime, the * complete jobs handler will not find the key about the job and the * assert will fail. */ if (server.vm_enabled && o->storage == REDIS_VM_SWAPPING) vmCancelThreadedIOJob(o); if (--(o->refcount) == 0) { switch(o->type) { 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: redisPanic("Unknown object type"); break; } o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */ zfree(o); } }
void ltrimCommand(redisClient *c) { robj *o; long start, end, llen, j, ltrim, rtrim; list *list; listNode *ln; if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) || (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return; if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.ok)) == NULL || checkType(c,o,REDIS_LIST)) return; llen = listTypeLength(o); /* convert negative indexes */ if (start < 0) start = llen+start; if (end < 0) end = llen+end; if (start < 0) start = 0; /* Invariant: start >= 0, so this test will be true when end < 0. * The range is empty when start > end or start >= length. */ if (start > end || start >= llen) { /* Out of range start or start > end result in empty list */ ltrim = llen; rtrim = 0; } else { if (end >= llen) end = llen-1; ltrim = start; rtrim = llen-end-1; } /* Remove list elements to perform the trim */ if (o->encoding == REDIS_ENCODING_ZIPLIST) { o->ptr = ziplistDeleteRange(o->ptr,0,ltrim); o->ptr = ziplistDeleteRange(o->ptr,-rtrim,rtrim); } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { list = o->ptr; for (j = 0; j < ltrim; j++) { ln = listFirst(list); listDelNode(list,ln); } for (j = 0; j < rtrim; j++) { ln = listLast(list); listDelNode(list,ln); } } else { redisPanic("Unknown list encoding"); } if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]); signalModifiedKey(c->db,c->argv[1]); server.dirty++; addReply(c,shared.ok); }
void freeHashObject(robj *o) { switch (o->encoding) { case REDIS_ENCODING_HT: dictRelease((dict*) o->ptr); break; case REDIS_ENCODING_ZIPLIST: zfree(o->ptr); break; default: redisPanic("Unknown hash encoding type"); break; } }
int setTypeIsMember(robj *subject, robj *value) { long long llval; if (subject->encoding == REDIS_ENCODING_HT) { return dictFind((dict*)subject->ptr,value) != NULL; } else if (subject->encoding == REDIS_ENCODING_INTSET) { if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) { return intsetFind((intset*)subject->ptr,llval); } } else { redisPanic("Unknown set encoding"); } return 0; }
setTypeIterator *setTypeInitIterator(robj *subject) { setTypeIterator *si = zmalloc(sizeof(setTypeIterator)); si->subject = subject; si->encoding = subject->encoding; if (si->encoding == REDIS_ENCODING_HT) { si->di = dictGetIterator(subject->ptr); } else if (si->encoding == REDIS_ENCODING_INTSET) { si->ii = 0; } else { redisPanic("Unknown set encoding"); } return si; }
void *bioProcessBackgroundJobs(void *arg) { struct bio_job *job; #ifdef _WIN32 size_t type = (size_t) arg; #else unsigned long type = (unsigned long) arg; #endif sigset_t sigset; pthread_detach(pthread_self()); pthread_mutex_lock(&bio_mutex[type]); /* Block SIGALRM so we are sure that only the main thread will * receive the watchdog signal. */ sigemptyset(&sigset); sigaddset(&sigset, SIGALRM); if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) redisLog(REDIS_WARNING, "Warning: can't mask SIGALRM in bio.c thread: %s", strerror(errno)); while(1) { listNode *ln; /* The loop always starts with the lock hold. */ if (listLength(bio_jobs[type]) == 0) { pthread_cond_wait(&bio_condvar[type],&bio_mutex[type]); continue; } /* Pop the job from the queue. */ ln = listFirst(bio_jobs[type]); job = ln->value; /* It is now possible to unlock the background system as we know have * a stand alone job structure to process.*/ pthread_mutex_unlock(&bio_mutex[type]); /* Process the job accordingly to its type. */ if (type == REDIS_BIO_CLOSE_FILE) { close((long)job->arg1); } else if (type == REDIS_BIO_AOF_FSYNC) { aof_fsync((long)job->arg1); } else { redisPanic("Wrong job type in bioProcessBackgroundJobs()."); } zfree(job); /* Lock again before reiterating the loop, if there are no longer * jobs to process we'll block again in pthread_cond_wait(). */ pthread_mutex_lock(&bio_mutex[type]); listDelNode(bio_jobs[type],ln); bio_pending[type]--; } }
void *bioProcessBackgroundJobs(void *arg) { //bioInit创建一个进程,用来刷文件。arg参数就是这个线程对应应该处理的任务号,用来索引bio_jobs[type] struct bio_job *job; unsigned long type = (unsigned long) arg;//实际上就是jobid。序号 sigset_t sigset; pthread_detach(pthread_self()); pthread_mutex_lock(&bio_mutex[type]);//先锁一下,待会pthread_cond_wait /* Block SIGALRM so we are sure that only the main thread will * receive the watchdog signal. */ sigemptyset(&sigset); sigaddset(&sigset, SIGALRM); if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) redisLog(REDIS_WARNING, "Warning: can't mask SIGALRM in bio.c thread: %s", strerror(errno)); while(1) { listNode *ln; /* The loop always starts with the lock hold. */ if (listLength(bio_jobs[type]) == 0) { //已经加锁了,进入等待bio_condvar[type]不为0,并立即解锁,等待。 //等其他线程pthread_cond_signal的时候,会通知这个线程,别等待了,从而再次加锁,返回。 pthread_cond_wait(&bio_condvar[type],&bio_mutex[type]); continue; } /* Pop the job from the queue. */ ln = listFirst(bio_jobs[type]);//获取到了一个任务,处理值。 job = ln->value; /* It is now possible to unlock the background system as we know have * a stand alone job structure to process.*/ pthread_mutex_unlock(&bio_mutex[type]); //根据不同的任务类型,做响应的清理。 /* Process the job accordingly to its type. */ if (type == REDIS_BIO_CLOSE_FILE) { close((long)job->arg1);//关闭文件。 } else if (type == REDIS_BIO_AOF_FSYNC) { aof_fsync((long)job->arg1);//同步刷新数据。 } else { redisPanic("Wrong job type in bioProcessBackgroundJobs()."); } zfree(job); /* Lock again before reiterating the loop, if there are no longer * jobs to process we'll block again in pthread_cond_wait(). */ pthread_mutex_lock(&bio_mutex[type]); listDelNode(bio_jobs[type],ln); bio_pending[type]--;//递减这种类型的挂起计数。 } }
void decrRefCount(void *obj) { robj *o = obj; if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0"); /* Object is in memory, or in the process of being swapped out. * * If the object is being swapped out, abort the operation on * decrRefCount even if the refcount does not drop to 0: the object * is referenced at least two times, as value of the key AND as * job->val in the iojob. So if we don't invalidate the iojob, when it is * done but the relevant key was removed in the meantime, the * complete jobs handler will not find the key about the job and the * assert will fail. */ if (o->refcount == 1) { switch(o->type) { case REDIS_STRING: freeStringObject(o); break; default: redisPanic("Unknown object type"); break; } zfree(o); } else { o->refcount--; } }
/* Initialize an iterator at the specified index. */ listTypeIterator *listTypeInitIterator(robj *subject, long index, unsigned char direction) { listTypeIterator *li = zmalloc(sizeof(listTypeIterator)); li->subject = subject; li->encoding = subject->encoding; li->direction = direction; if (li->encoding == REDIS_ENCODING_ZIPLIST) { li->zi = ziplistIndex(subject->ptr,index); } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) { li->ln = listIndex(subject->ptr,index); } else { redisPanic("Unknown list encoding"); } return li; }
/* Move to the next entry in the set. Returns the object at the current * position. * * Since set elements can be internally be stored as redis objects or * simple arrays of integers, setTypeNext returns the encoding of the * set object you are iterating, and will populate the appropriate pointer * (objele) or (llele) accordingly. * * Note that both the objele and llele pointers should be passed and cannot * be NULL since the function will try to defensively populate the non * used field with values which are easy to trap if misused. * * When there are no longer elements -1 is returned. * Returned objects ref count is not incremented, so this function is * copy on write friendly. */ int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) { if (si->encoding == REDIS_ENCODING_HT) { dictEntry *de = dictNext(si->di); if (de == NULL) return -1; *objele = dictGetKey(de); *llele = -123456789; /* Not needed. Defensive. */ } else if (si->encoding == REDIS_ENCODING_INTSET) { if (!intsetGet(si->subject->ptr,si->ii++,llele)) return -1; *objele = NULL; /* Not needed. Defensive. */ } else { redisPanic("Wrong set encoding in setTypeNext"); } return si->encoding; }
void freeZsetObject(robj *o) { zset *zs; switch (o->encoding) { case REDIS_ENCODING_SKIPLIST: zs = o->ptr; dictRelease(zs->dict); zslFree(zs->zsl); zfree(zs); break; case REDIS_ENCODING_ZIPLIST: zfree(o->ptr); break; default: redisPanic("Unknown sorted set encoding"); } }
inline static void zunionInterAggregate(double *target, double val, int aggregate) { if (aggregate == REDIS_AGGR_SUM) { *target = *target + val; /* The result of adding two doubles is NaN when one variable * is +inf and the other is -inf. When these numbers are added, * we maintain the convention of the result being 0.0. */ if (isnan(*target)) *target = 0.0; } else if (aggregate == REDIS_AGGR_MIN) { *target = val < *target ? val : *target; } else if (aggregate == REDIS_AGGR_MAX) { *target = val > *target ? val : *target; } else { /* safety net */ redisPanic("Unknown ZUNION/INTER aggregate type"); } }
/* Unblock a client calling the right function depending on the kind * of operation the client is blocking for. * 不阻塞客户端 * 取消给定的客户端的阻塞状态*/ void unblockClient(redisClient *c) { if (c->btype == REDIS_BLOCKED_LIST) { unblockClientWaitingData(c); } else if (c->btype == REDIS_BLOCKED_WAIT) { unblockClientWaitingReplicas(c); } else { redisPanic("Unknown btype in unblockClient()."); } /* Clear the flags, and put the client in the unblocked list so that * we'll process new commands in its query buffer ASAP. */ c->flags &= ~REDIS_BLOCKED; c->flags |= REDIS_UNBLOCKED; c->btype = REDIS_BLOCKED_NONE; server.bpop_blocked_clients--; listAddNodeTail(server.unblocked_clients,c); }
/* Get a decoded version of an encoded object (returned as a new object). * If the object is already raw-encoded just increment the ref count. */ robj *getDecodedObject(robj *o) { robj *dec; if (sdsEncodedObject(o)) { incrRefCount(o); return o; } if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) { char buf[32]; ll2string(buf,32,(long)o->ptr); dec = createStringObject(buf,strlen(buf)); return dec; } else { redisPanic("Unknown encoding type"); } }
/* The not copy on write friendly version but easy to use version * of setTypeNext() is setTypeNextObject(), returning new objects * or incrementing the ref count of returned objects. So if you don't * retain a pointer to this object you should call decrRefCount() against it. * * This function is the way to go for write operations where COW is not * an issue as the result will be anyway of incrementing the ref count. */ robj *setTypeNextObject(setTypeIterator *si) { int64_t intele; robj *objele; int encoding; encoding = setTypeNext(si,&objele,&intele); switch(encoding) { case -1: return NULL; case REDIS_ENCODING_INTSET: return createStringObjectFromLongLong(intele); case REDIS_ENCODING_HT: incrRefCount(objele); return objele; default: redisPanic("Unsupported encoding"); } return NULL; /* just to suppress warnings */ }
int setTypeRemove(robj *setobj, robj *value) { long long llval; if (setobj->encoding == REDIS_ENCODING_HT) { if (dictDelete(setobj->ptr,value) == DICT_OK) { if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr); return 1; } } else if (setobj->encoding == REDIS_ENCODING_INTSET) { if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) { int success; setobj->ptr = intsetRemove(setobj->ptr,llval,&success); if (success) return 1; } } else { redisPanic("Unknown set encoding"); } return 0; }
/* Emit the commands needed to rebuild a set object. * The function returns 0 on error, 1 on success. */ int rewriteSetObject(rio *r, robj *key, robj *o) { long long count = 0, items = setTypeSize(o); if (o->encoding == REDIS_ENCODING_INTSET) { int ii = 0; int64_t llval; while(intsetGet(o->ptr,ii++,&llval)) { if (count == 0) { int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ? REDIS_AOF_REWRITE_ITEMS_PER_CMD : items; if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0; if (rioWriteBulkString(r,"SADD",4) == 0) return 0; if (rioWriteBulkObject(r,key) == 0) return 0; } if (rioWriteBulkLongLong(r,llval) == 0) return 0; if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0; items--; } } else if (o->encoding == REDIS_ENCODING_HT) { dictIterator *di = dictGetIterator(o->ptr); dictEntry *de; while((de = dictNext(di)) != NULL) { robj *eleobj = dictGetKey(de); if (count == 0) { int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ? REDIS_AOF_REWRITE_ITEMS_PER_CMD : items; if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0; if (rioWriteBulkString(r,"SADD",4) == 0) return 0; if (rioWriteBulkObject(r,key) == 0) return 0; } if (rioWriteBulkObject(r,eleobj) == 0) return 0; if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0; items--; } dictReleaseIterator(di); } else { redisPanic("Unknown set encoding"); } return 1; }
void lsetCommand(redisClient *c) { int slotnum = keyHashSlot(c->argv[1]->ptr, sdslen(c->argv[1]->ptr)); robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr,slotnum); if (o == NULL || checkType(c,o,REDIS_LIST)) return; long index; robj *value = (c->argv[3] = tryObjectEncoding(c->argv[3])); if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != REDIS_OK)) return; listTypeTryConversion(o,value); if (o->encoding == REDIS_ENCODING_ZIPLIST) { unsigned char *p, *zl = o->ptr; p = ziplistIndex(zl,index); if (p == NULL) { addReply(c,shared.outofrangeerr); } else { o->ptr = ziplistDelete(o->ptr,&p); value = getDecodedObject(value); o->ptr = ziplistInsert(o->ptr,p,value->ptr,sdslen(value->ptr)); decrRefCount(value); addReply(c,shared.ok); signalModifiedKey(c->db,c->argv[1],slotnum); notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"lset",c->argv[1],c->db->id); server.dirty++; } } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { listNode *ln = listIndex(o->ptr,index); if (ln == NULL) { addReply(c,shared.outofrangeerr); } else { decrRefCount((robj*)listNodeValue(ln)); listNodeValue(ln) = value; incrRefCount(value); addReply(c,shared.ok); signalModifiedKey(c->db,c->argv[1],slotnum); notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"lset",c->argv[1],c->db->id); server.dirty++; } } else { redisPanic("Unknown list encoding"); } }
void *bioProcessBackgroundJobs(void *arg) { struct bio_job *job; #ifdef _WIN32 size_t type = (size_t) arg; #else unsigned long type = (unsigned long) arg; #endif pthread_detach(pthread_self()); pthread_mutex_lock(&bio_mutex[type]); while(1) { listNode *ln; /* The loop always starts with the lock hold. */ if (listLength(bio_jobs[type]) == 0) { pthread_cond_wait(&bio_condvar[type],&bio_mutex[type]); continue; } /* Pop the job from the queue. */ ln = listFirst(bio_jobs[type]); job = ln->value; /* It is now possible to unlock the background system as we know have * a stand alone job structure to process.*/ pthread_mutex_unlock(&bio_mutex[type]); /* Process the job accordingly to its type. */ if (type == REDIS_BIO_CLOSE_FILE) { close((long)(size_t)job->arg1); } else if (type == REDIS_BIO_AOF_FSYNC) { aof_fsync((long)(size_t)job->arg1); } else { redisPanic("Wrong job type in bioProcessBackgroundJobs()."); } zfree(job); /* Lock again before reiterating the loop, if there are no longer * jobs to process we'll block again in pthread_cond_wait(). */ pthread_mutex_lock(&bio_mutex[type]); listDelNode(bio_jobs[type],ln); bio_pending[type]--; } }
/* Duplicate a string object, with the guarantee that the returned object * has the same encoding as the original one. * * This function also guarantees that duplicating a small integere object * (or a string object that contains a representation of a small integer) * will always result in a fresh object that is unshared (refcount == 1). * * The resulting object always has refcount set to 1. */ robj *dupStringObject(robj *o) { robj *d; redisAssert(o->type == REDIS_STRING); switch(o->encoding) { case REDIS_ENCODING_RAW: return createRawStringObject(o->ptr,sdslen(o->ptr)); case REDIS_ENCODING_EMBSTR: return createEmbeddedStringObject(o->ptr,sdslen(o->ptr)); case REDIS_ENCODING_INT: d = createObject(REDIS_STRING, NULL); d->encoding = REDIS_ENCODING_INT; d->ptr = o->ptr; return d; default: redisPanic("Wrong encoding."); break; } }
/* Unblock a client calling the right function depending on the kind * of operation the client is blocking for. */ void unblockClient(redisClient *c) { if (c->btype == REDIS_BLOCKED_LIST) { unblockClientWaitingData(c); } else if (c->btype == REDIS_BLOCKED_WAIT) { unblockClientWaitingReplicas(c); } else { redisPanic("Unknown btype in unblockClient()."); } /* Clear the flags, and put the client in the unblocked list so that * we'll process new commands in its query buffer ASAP. */ c->flags &= ~REDIS_BLOCKED; c->btype = REDIS_BLOCKED_NONE; server.bpop_blocked_clients--; /* The client may already be into the unblocked list because of a previous * blocking operation, don't add back it into the list multiple times. */ if (!(c->flags & REDIS_UNBLOCKED)) { c->flags |= REDIS_UNBLOCKED; listAddNodeTail(server.unblocked_clients,c); } }
void listTypeConvert(robj *subject, int enc) { listTypeIterator *li; listTypeEntry entry; redisAssertWithInfo(NULL,subject,subject->type == REDIS_LIST); if (enc == REDIS_ENCODING_LINKEDLIST) { list *l = listCreate(); listSetFreeMethod(l,decrRefCount); /* 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_LINKEDLIST; zfree(subject->ptr); subject->ptr = l; } else { redisPanic("Unsupported list conversion"); } }
int getDoubleFromObject(robj *o, double *target) { double value; char *eptr; if (o == NULL) { value = 0; } else { redisAssert(o->type == REDIS_STRING); if (o->encoding == REDIS_ENCODING_RAW) { value = strtod(o->ptr, &eptr); if (eptr[0] != '\0' || isnan(value)) return REDIS_ERR; } else if (o->encoding == REDIS_ENCODING_INT) { value = (long)o->ptr; } else { redisPanic("Unknown string encoding"); } } *target = value; return REDIS_OK; }
void lindexCommand(redisClient *c) { robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk); if (o == NULL || checkType(c,o,REDIS_LIST)) return; long index; robj *value = NULL; if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != REDIS_OK)) return; if (o->encoding == REDIS_ENCODING_ZIPLIST) { unsigned char *p; unsigned char *vstr; unsigned int vlen; long long vlong; p = ziplistIndex(o->ptr,index); if (ziplistGet(p,&vstr,&vlen,&vlong)) { if (vstr) { value = createStringObject((char*)vstr,vlen); } else { value = createStringObjectFromLongLong(vlong); } addReplyBulk(c,value); decrRefCount(value); } else { addReply(c,shared.nullbulk); } } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { listNode *ln = listIndex(o->ptr,index); if (ln != NULL) { value = listNodeValue(ln); addReplyBulk(c,value); } else { addReply(c,shared.nullbulk); } } else { redisPanic("Unknown list encoding"); } }
/* Delete the element pointed to. */ void listTypeDelete(listTypeEntry *entry) { listTypeIterator *li = entry->li; if (li->encoding == REDIS_ENCODING_ZIPLIST) { unsigned char *p = entry->zi; li->subject->ptr = ziplistDelete(li->subject->ptr,&p); /* Update position of the iterator depending on the direction */ if (li->direction == REDIS_TAIL) li->zi = p; else li->zi = ziplistPrev(li->subject->ptr,p); } else if (entry->li->encoding == REDIS_ENCODING_LINKEDLIST) { listNode *next; if (li->direction == REDIS_TAIL) next = entry->ln->next; else next = entry->ln->prev; listDelNode(li->subject->ptr,entry->ln); li->ln = next; } else { redisPanic("Unknown list encoding"); } }
void listTypePush(robj *subject, robj *value, int where) { /* Check if we need to convert the ziplist */ listTypeTryConversion(subject,value); if (subject->encoding == REDIS_ENCODING_ZIPLIST && ziplistLen(subject->ptr) >= server.list_max_ziplist_entries) listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST); if (subject->encoding == REDIS_ENCODING_ZIPLIST) { int pos = (where == REDIS_HEAD) ? ZIPLIST_HEAD : ZIPLIST_TAIL; value = getDecodedObject(value); subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),pos); decrRefCount(value); } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) { if (where == REDIS_HEAD) { listAddNodeHead(subject->ptr,value); } else { listAddNodeTail(subject->ptr,value); } incrRefCount(value); } else { redisPanic("Unknown list encoding"); } }