/* Add a new node to the list, to head, contaning the specified 'value' * pointer as value. * * On error, NULL is returned and no operation is performed (i.e. the * list remains unaltered). * On success the 'list' pointer you pass to the function is returned. */ list *listAddNodeHead(list *list, void *value) { listNode *node;
/* This is our hash table structure. Every dictionary has two of this as we * implement incremental rehashing, for the old to the new table. */ typedefstructdictht { dictEntry **table; unsignedlong size; unsignedlong sizemask; unsignedlong used; } dictht;
/* If safe is set to 1 this is a safe iteartor, that means, you can call * dictAdd, dictFind, and other functions against the dictionary even while * iterating. Otherwise it is a non safe iterator, and only dictNext() * should be called while iterating. */ typedefstructdictIterator { dict *d; int table, index, safe; dictEntry *entry, *nextEntry; } dictIterator;
/* Identity hash function for integer keys */ unsignedintdictIdentityHashFunction(unsignedint key) { return key; }
/* Generic hash function (a popular one from Bernstein). * I tested a few and this was the best. */ unsignedintdictGenHashFunction(constunsignedchar *buf, int len){ unsignedint hash = 5381;
typedefstructredisObject { unsigned type:4; unsigned storage:2; /* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */ unsigned encoding:4; unsigned lru:22; /* lru time (relative to server.lruclock) */ int refcount; void *ptr; /* VM fields are only allocated if VM is active, otherwise the * object allocation function will just allocate * sizeof(redisObjct) minus sizeof(redisObjectVM), so using * Redis without VM active will not have any overhead. */ } robj;
/* Objects encoding. Some kind of objects like Strings and Hashes can be * internally represented in multiple ways. The 'encoding' field of the object * is set to one of this fields for this object. */ #define REDIS_ENCODING_RAW 0 /* Raw representation */ #define REDIS_ENCODING_INT 1 /* Encoded as integer */ #define REDIS_ENCODING_HT 2 /* Encoded as hash table */ #define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ #define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */ #define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ #define REDIS_ENCODING_INTSET 6 /* Encoded as intset */ #define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
/* Set the LRU to the current lruclock (minutes resolution). * We do this regardless of the fact VM is active as LRU is also * used for the maxmemory directive when Redis is used as cache. * * Note that this code may run in the context of an I/O thread * and accessing server.lruclock in theory is an error * (no locks). But in practice this is safe, and even if we read * garbage Redis will not fail. */ o->lru = server.lruclock; /* The following is only needed if VM is active, but since the conditional * is probably more costly than initializing the field it's better to * have every field properly initialized anyway. */ o->storage = REDIS_VM_MEMORY; return o; }
voidsetGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire){ int retval; long seconds = 0; /* initialized to avoid an harmness warning */
if (expire) { if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK) return; if (seconds <= 0) { addReplyError(c,"invalid expire time in SETEX"); return; } }
lookupKeyWrite(c->db,key); /* Force expire of old key if needed */ retval = dbAdd(c->db,key,val); if (retval == REDIS_ERR) { if (!nx) { dbReplace(c->db,key,val); incrRefCount(val); } else { addReply(c,shared.czero); return; } } else { incrRefCount(val); } touchWatchedKey(c->db,key); server.dirty++; removeExpire(c->db,key); if (expire) setExpire(c->db,key,time(NULL)+seconds); addReply(c, nx ? shared.cone : shared.ok); }
voidlistTypePush(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); } elseif (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"); } }
robj *listTypePop(robj *subject, int where){ robj *value = NULL; if (subject->encoding == REDIS_ENCODING_ZIPLIST) { unsignedchar *p; unsignedchar *vstr; unsignedint vlen; longlong vlong; int pos = (where == REDIS_HEAD) ? 0 : -1; p = ziplistIndex(subject->ptr,pos); if (ziplistGet(p,&vstr,&vlen,&vlong)) { if (vstr) { value = createStringObject((char*)vstr,vlen); } else { value = createStringObjectFromLongLong(vlong); } /* We only need to delete an element when it exists */ subject->ptr = ziplistDelete(subject->ptr,&p); } } elseif (subject->encoding == REDIS_ENCODING_LINKEDLIST) { list *list = subject->ptr; listNode *ln; if (where == REDIS_HEAD) { ln = listFirst(list); } else { ln = listLast(list); } if (ln != NULL) { value = listNodeValue(ln); incrRefCount(value); listDelNode(list,ln); } } else { redisPanic("Unknown list encoding"); } return value; }
intsetTypeAdd(robj *subject, robj *value){ longlong llval; if (subject->encoding == REDIS_ENCODING_HT) { if (dictAdd(subject->ptr,value,NULL) == DICT_OK) { incrRefCount(value); return1; } } elseif (subject->encoding == REDIS_ENCODING_INTSET) { if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) { uint8_t success = 0; subject->ptr = intsetAdd(subject->ptr,llval,&success); if (success) { /* Convert to regular set when the intset contains * too many entries. */ if (intsetLen(subject->ptr) > server.set_max_intset_entries) setTypeConvert(subject,REDIS_ENCODING_HT); return1; } } else { /* Failed to get integer from object, convert to regular set. */ setTypeConvert(subject,REDIS_ENCODING_HT);
/* The set *was* an intset and this value is not integer * encodable, so dictAdd should always work. */ redisAssert(dictAdd(subject->ptr,value,NULL) == DICT_OK); incrRefCount(value); return1; } } else { redisPanic("Unknown set encoding"); } return0; }
/* Since both ZADD and ZINCRBY are implemented here, we need to increment * the score first by the current score if ZINCRBY is called. */ if (incr) { /* Read the old score. If the element was not present starts from 0 */ dictEntry *de = dictFind(zs->dict,ele); if (de != NULL) score += *(double*)dictGetEntryVal(de);
if (isnan(score)) { addReplyError(c,"resulting score is not a number (NaN)"); /* Note that we don't need to check if the zset may be empty and * should be removed here, as we can only obtain Nan as score if * there was already an element in the sorted set. */ return; } }
/* We need to remove and re-insert the element when it was already present * in the dictionary, to update the skiplist. Note that we delay adding a * pointer to the score because we want to reference the score in the * skiplist node. */ if (dictAdd(zs->dict,ele,NULL) == DICT_OK) { dictEntry *de;
/* New element */ incrRefCount(ele); /* added to hash */ znode = zslInsert(zs->zsl,score,ele); incrRefCount(ele); /* added to skiplist */
/* Update the score in the dict entry */ de = dictFind(zs->dict,ele); redisAssert(de != NULL); dictGetEntryVal(de) = &znode->score; touchWatchedKey(c->db,c->argv[1]); server.dirty++; if (incr) addReplyDouble(c,score); else addReply(c,shared.cone); } else { dictEntry *de; robj *curobj; double *curscore; int deleted;
/* When the score is updated, reuse the existing string object to * prevent extra alloc/dealloc of strings on ZINCRBY. */ if (score != *curscore) { deleted = zslDelete(zs->zsl,*curscore,curobj); redisAssert(deleted != 0); znode = zslInsert(zs->zsl,score,curobj); incrRefCount(curobj);
/* Update the score in the current dict entry */ dictGetEntryVal(de) = &znode->score; touchWatchedKey(c->db,c->argv[1]); server.dirty++; } if (incr) addReplyDouble(c,score); else addReply(c,shared.czero); } }
Copyright Declaration: This station is mainly used to sort out incomprehensible knowledge. I have not fully mastered most of the content. Please refer carefully.