/* Helper function for the redisAsyncCommand* family of functions. Writes a * formatted command to the output buffer and registers the provided callback * function with the context. */ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, char *cmd, size_t len) { redisContext *c = &(ac->c); redisCallback cb; int pvariant, hasnext; char *cstr, *astr; size_t clen, alen; char *p; sds sname; /* Don't accept new commands when the connection is about to be closed. */ if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR; /* Setup callback */ cb.fn = fn; cb.privdata = privdata; /* Find out which command will be appended. */ p = nextArgument(cmd,&cstr,&clen); assert(p != NULL); hasnext = (p[0] == '$'); pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0; cstr += pvariant; clen -= pvariant; if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) { c->flags |= REDIS_SUBSCRIBED; /* Add every channel/pattern to the list of subscription callbacks. */ while ((p = nextArgument(p,&astr,&alen)) != NULL) { sname = sdsnewlen(astr,alen); if (pvariant) dictReplace(ac->sub.patterns,sname,&cb); else dictReplace(ac->sub.channels,sname,&cb); } } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) { /* It is only useful to call (P)UNSUBSCRIBE when the context is * subscribed to one or more channels or patterns. */ if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR; /* (P)UNSUBSCRIBE does not have its own response: every channel or * pattern that is unsubscribed will receive a message. This means we * should not append a callback function for this command. */ } else { if (c->flags & REDIS_SUBSCRIBED) /* This will likely result in an error reply, but it needs to be * received and passed to the callback. */ __redisPushCallback(&ac->sub.invalid,&cb); else __redisPushCallback(&ac->replies,&cb); } __redisAppendCommand(c,cmd,len); /* Always schedule a write when the write buffer is non-empty */ if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data); return REDIS_OK; }
int dbSet(const sds setName, const set *s) { set *prevSet = NULL; if (NULL == setName || 0 == strlen(setName) || NULL == s) return -1; lockWrite(sets); if (NULL != (prevSet = (set *) dictFetchValue(sets, setName))) { lockWrite(prevSet); if (!prevSet->registered) setDestroy(prevSet); unregisterSyncObject(prevSet); } if (0 != registerSyncObject(s) && 1 != syncObjectIsRegistered(s)) { unlockWrite(sets); return -1; } dictReplace(sets, setName, (void *) s); unlockWrite(sets); return 0; }
/* Overwrite an existing key with a new value. Incrementing the reference * count of the new value is up to the caller. * This function does not modify the expire time of the existing key. * * The program is aborted if the key was not already present. */ void dbOverwrite(memoryDb *db, sds *key, value_t *val) { struct dictEntry *de = dictFind(db->dict, key); redisAssertWithInfo(NULL, key, de != NULL); value_t *oldval = dictGetVal(de); setValueVersion(val, oldval->ver+1); dictReplace(db->dict, key, val); }
void setExpire(redisDb *db, robj *key, time_t when) { dictEntry *de; /* Reuse the sds from the main dict in the expire dict */ de = dictFind(db->dict,key->ptr); redisAssert(de != NULL); dictReplace(db->expires,dictGetEntryKey(de),(void*)when); }
/* scriptNameCommand() has compound sub-arguments, so it looks slightly more * convoluted than it actually is. Just read each if/else branch as * if it were an individual command. */ void scriptNameCommand(redisClient *c) { char *req = c->argv[1]->ptr; sds script_name = c->argv[2]->ptr; if (c->argc == 4 && !strcasecmp(req, "set")) { sds target_sha = c->argv[3]->ptr; if (sdslen(target_sha) != 40 || dictFind(server.lua_scripts,target_sha) == NULL) { addReply(c, g.err.nosha); return; } /* If name doesn't exist, dictReplace == dictAdd */ dictReplace(g.names, script_name, target_sha); addReplyBulkCBuffer(c, script_name, sdslen(script_name)); } else if (c->argc == 3 && !strcasecmp(req, "get")) { sds found; if ((found = dictFetchValue(g.names, script_name))) { addReplyBulkCBuffer(c, found, sdslen(found)); } else { addReply(c, g.err.noname); } } else if (c->argc == 2 && !strcasecmp(req, "getall")) { dictIterator *di; dictEntry *de; unsigned long sz = dictSize(g.names); if (!sz) { addReply(c, shared.emptymultibulk); return; } /* Multiply by 2 because the size of the dict is the number of keys. * We are returning keys *and* values, so length is dictSize * 2 */ addReplyMultiBulkLen(c, sz * 2); di = dictGetIterator(g.names); while ((de = dictNext(di))) { addReplyBulkCString(c, dictGetKey(de)); addReplyBulkCString(c, dictGetVal(de)); } dictReleaseIterator(di); } else if (c->argc == 3 && !strcasecmp(req, "del")) { sds deleted; if ((deleted = dictFetchValue(g.names, script_name))) { dictDelete(g.names, script_name); addReplyBulkCBuffer(c, deleted, sdslen(deleted)); } else { addReply(c, g.err.noname); } } else { addReplyError(c, "Unknown scriptName subcommand or arg count"); } }
/* If the key does not exist, this is just like dbAdd(). Otherwise * the value associated to the key is replaced with the new one. * * On update (key already existed) 0 is returned. Otherwise 1. */ int dbReplace(redisDb *db, robj *key, robj *val) { if (dictFind(db->dict,key->ptr) == NULL) { sds copy = sdsdup(key->ptr); dictAdd(db->dict, copy, val); return 1; } else { dictReplace(db->dict, key->ptr, val); return 0; } }
/* set "OFFSET var" for next cursor iteration */ void incrOffsetVar(redisClient *c, cswc_t *w, long incr) { robj *ovar = createStringObject(w->ovar, sdslen(w->ovar)); if (w->lim > incr) { deleteKey(c->db, ovar); } else { lolo value = (w->ofst == -1) ? (lolo)incr : (lolo)w->ofst + (lolo)incr; robj *val = createStringObjectFromLongLong(value); int ret = dictAdd(c->db->dict, ovar, val); if (ret == DICT_ERR) dictReplace(c->db->dict, ovar, val); } server.dirty++; }
/* Add an element, discard the old if the key already exists. * Return 0 on insert and 1 on update. * This function will take care of incrementing the reference count of the * retained fields and value objects. */ int hashTypeSet(robj *o, robj *field, robj *value) { int update = 0; if (o->encoding == REDIS_ENCODING_ZIPLIST) { unsigned char *zl, *fptr, *vptr; field = getDecodedObject(field); value = getDecodedObject(value); zl = o->ptr; fptr = ziplistIndex(zl, ZIPLIST_HEAD); if (fptr != NULL) { fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1); if (fptr != NULL) { /* Grab pointer to the value (fptr points to the field) */ vptr = ziplistNext(zl, fptr); logicErrorExpr(vptr != NULL, "Never happend"); update = 1; /* Delete value */ zl = ziplistDelete(zl, &vptr); /* Insert new value */ zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr)); } } if (!update) { /* Push new field/value pair onto the tail of the ziplist */ zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL); zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL); } o->ptr = zl; decrRefCount(field); decrRefCount(value); /* Check if the ziplist needs to be converted to a hash table */ if (hashTypeLength(o) > server.hash_max_ziplist_entries) hashTypeConvert(o, REDIS_ENCODING_HT); } else if (o->encoding == REDIS_ENCODING_HT) { if (dictReplace(o->ptr, field, value)) { /* Insert */ incrRefCount(field); } else { /* Update */ update = 1; } incrRefCount(value); } else { logicError("Unknown hash encoding"); } return update; }
/* If the key does not exist, this is just like dbAdd(). Otherwise * the value associated to the key is replaced with the new one. * * On update (key already existed) 0 is returned. Otherwise 1. */ int dbReplace(redisDb *db, robj *key, robj *val) { robj *oldval; int retval; if ((oldval = dictFetchValue(db->dict,key->ptr)) == NULL) { sds copy = sdsdup(key->ptr); dictAdd(db->dict, copy, val); retval = 1; } else { dictReplace(db->dict, key->ptr, val); retval = 0; } if (server.ds_enabled) cacheSetKeyMayExist(db,key); return retval; }
int cache_set(qqCache* qc,void* key, void* val){ //first lookup void* lookup; void* die_key; void* wrappedVal; size_t datasize; lookup = dictFetchValue(qc->dict,key); if(lookup==NULL){ // not hit datasize = qc->type->valueSize(val)+qc->type->keySize(key); //reserve value wrappedVal=qc->type->wrapValue(key,STORE(qc->type->valDup,val),datasize); dictAdd(qc->dict,key,wrappedVal); qc->type->put(wrappedVal); qc->usedsize+=datasize; while(qc->usedsize > qc->maxsize){ //dieout will release the rawval space first, //then release the other space regarding Replace Algorithm //and update the usedsize die_key=qc->type->dieout(qc->type->valDestructor,&qc->usedsize); dictDelete(qc->dict,die_key); //key has been destructed } }else{ //hit datasize = qc->type->valueSize(val)+qc->type->keySize(key); //update dict wrappedVal=qc->type->wrapValue(key,STORE(qc->type->valDup,val),datasize); dictReplace(qc->dict,key,wrappedVal); //replace and adjust Replace Structure, release the old wrappedVal //lookup:oldnode wrappedVal:newnode access(lookup,wrappedVal,&qc->usedsize,qc->type->valDestructor); while(qc->usedsize > qc->maxsize){ //dieout will release the rawval space first, //then release the other space regarding Replace Algorithm //and update the usedsize die_key=qc->type->dieout(qc->type->valDestructor,&qc->usedsize); dictDelete(qc->dict,die_key); } } }
/* Overwrite an existing key with a new value. Incrementing the reference * count of the new value is up to the caller. * This function does not modify the expire time of the existing key. * * The program is aborted if the key was not already present. */ void dbOverwrite(redisDb *db, robj *key, robj *val) { struct dictEntry *de = dictFind(db->dict,key->ptr); redisAssertWithInfo(NULL,key,de != NULL); dictReplace(db->dict, key->ptr, val); }
void params_map_add(params_map *p_map, param_entry *param) { dictReplace(p_map, param->name, param); }
/* This generic command implements both ZADD and ZINCRBY. * scoreval is the score if the operation is a ZADD (doincrement == 0) or * the increment if the operation is a ZINCRBY (doincrement == 1). */ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, int doincrement) { robj *zsetobj; zset *zs; double *score; if (isnan(scoreval)) { addReplySds(c,sdsnew("-ERR provide score is Not A Number (nan)\r\n")); return; } zsetobj = lookupKeyWrite(c->db,key); if (zsetobj == NULL) { zsetobj = createZsetObject(); dbAdd(c->db,key,zsetobj); } else { if (zsetobj->type != REDIS_ZSET) { addReply(c,shared.wrongtypeerr); return; } } zs = zsetobj->ptr; /* Ok now since we implement both ZADD and ZINCRBY here the code * needs to handle the two different conditions. It's all about setting * '*score', that is, the new score to set, to the right value. */ score = zmalloc(sizeof(double)); if (doincrement) { dictEntry *de; /* Read the old score. If the element was not present starts from 0 */ de = dictFind(zs->dict,ele); if (de) { double *oldscore = dictGetEntryVal(de); *score = *oldscore + scoreval; } else { *score = scoreval; } if (isnan(*score)) { addReplySds(c, sdsnew("-ERR resulting score is Not A Number (nan)\r\n")); zfree(score); /* 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; } } else { *score = scoreval; } /* What follows is a simple remove and re-insert operation that is common * to both ZADD and ZINCRBY... */ if (dictAdd(zs->dict,ele,score) == DICT_OK) { /* case 1: New element */ incrRefCount(ele); /* added to hash */ zslInsert(zs->zsl,*score,ele); incrRefCount(ele); /* added to skiplist */ touchWatchedKey(c->db,c->argv[1]); server.dirty++; if (doincrement) addReplyDouble(c,*score); else addReply(c,shared.cone); } else { dictEntry *de; double *oldscore; /* case 2: Score update operation */ de = dictFind(zs->dict,ele); redisAssert(de != NULL); oldscore = dictGetEntryVal(de); if (*score != *oldscore) { int deleted; /* Remove and insert the element in the skip list with new score */ deleted = zslDelete(zs->zsl,*oldscore,ele); redisAssert(deleted != 0); zslInsert(zs->zsl,*score,ele); incrRefCount(ele); /* Update the score in the hash table */ dictReplace(zs->dict,ele,score); touchWatchedKey(c->db,c->argv[1]); server.dirty++; } else { zfree(score); } if (doincrement) addReplyDouble(c,*score); else addReply(c,shared.czero); } }