mirror of
https://github.com/fluencelabs/redis
synced 2025-06-15 18:21:21 +00:00
redis.c split into many different C files.
networking related stuff moved into networking.c moved more code more work on layout of source code SDS instantaneuos memory saving. By Pieter and Salvatore at VMware ;) cleanly compiling again after the first split, now splitting it in more C files moving more things around... work in progress split replication code splitting more Sets split Hash split replication split even more splitting more splitting minor change
This commit is contained in:
405
src/object.c
Normal file
405
src/object.c
Normal file
@ -0,0 +1,405 @@
|
||||
#include "redis.h"
|
||||
#include <pthread.h>
|
||||
|
||||
robj *createObject(int type, void *ptr) {
|
||||
robj *o;
|
||||
|
||||
if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
|
||||
if (listLength(server.objfreelist)) {
|
||||
listNode *head = listFirst(server.objfreelist);
|
||||
o = listNodeValue(head);
|
||||
listDelNode(server.objfreelist,head);
|
||||
if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
|
||||
} else {
|
||||
if (server.vm_enabled)
|
||||
pthread_mutex_unlock(&server.obj_freelist_mutex);
|
||||
o = zmalloc(sizeof(*o));
|
||||
}
|
||||
o->type = type;
|
||||
o->encoding = REDIS_ENCODING_RAW;
|
||||
o->ptr = ptr;
|
||||
o->refcount = 1;
|
||||
if (server.vm_enabled) {
|
||||
/* 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;
|
||||
o->storage = REDIS_VM_MEMORY;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
robj *createStringObject(char *ptr, size_t len) {
|
||||
return createObject(REDIS_STRING,sdsnewlen(ptr,len));
|
||||
}
|
||||
|
||||
robj *createStringObjectFromLongLong(long long value) {
|
||||
robj *o;
|
||||
if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
|
||||
incrRefCount(shared.integers[value]);
|
||||
o = shared.integers[value];
|
||||
} else {
|
||||
if (value >= LONG_MIN && value <= LONG_MAX) {
|
||||
o = createObject(REDIS_STRING, NULL);
|
||||
o->encoding = REDIS_ENCODING_INT;
|
||||
o->ptr = (void*)((long)value);
|
||||
} else {
|
||||
o = createObject(REDIS_STRING,sdsfromlonglong(value));
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
robj *dupStringObject(robj *o) {
|
||||
redisAssert(o->encoding == REDIS_ENCODING_RAW);
|
||||
return createStringObject(o->ptr,sdslen(o->ptr));
|
||||
}
|
||||
|
||||
robj *createListObject(void) {
|
||||
list *l = listCreate();
|
||||
robj *o = createObject(REDIS_LIST,l);
|
||||
listSetFreeMethod(l,decrRefCount);
|
||||
o->encoding = REDIS_ENCODING_LINKEDLIST;
|
||||
return o;
|
||||
}
|
||||
|
||||
robj *createZiplistObject(void) {
|
||||
unsigned char *zl = ziplistNew();
|
||||
robj *o = createObject(REDIS_LIST,zl);
|
||||
o->encoding = REDIS_ENCODING_ZIPLIST;
|
||||
return o;
|
||||
}
|
||||
|
||||
robj *createSetObject(void) {
|
||||
dict *d = dictCreate(&setDictType,NULL);
|
||||
return createObject(REDIS_SET,d);
|
||||
}
|
||||
|
||||
robj *createHashObject(void) {
|
||||
/* All the Hashes start as zipmaps. Will be automatically converted
|
||||
* into hash tables if there are enough elements or big elements
|
||||
* inside. */
|
||||
unsigned char *zm = zipmapNew();
|
||||
robj *o = createObject(REDIS_HASH,zm);
|
||||
o->encoding = REDIS_ENCODING_ZIPMAP;
|
||||
return o;
|
||||
}
|
||||
|
||||
robj *createZsetObject(void) {
|
||||
zset *zs = zmalloc(sizeof(*zs));
|
||||
|
||||
zs->dict = dictCreate(&zsetDictType,NULL);
|
||||
zs->zsl = zslCreate();
|
||||
return createObject(REDIS_ZSET,zs);
|
||||
}
|
||||
|
||||
void freeStringObject(robj *o) {
|
||||
if (o->encoding == REDIS_ENCODING_RAW) {
|
||||
sdsfree(o->ptr);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
void freeSetObject(robj *o) {
|
||||
dictRelease((dict*) o->ptr);
|
||||
}
|
||||
|
||||
void freeZsetObject(robj *o) {
|
||||
zset *zs = o->ptr;
|
||||
|
||||
dictRelease(zs->dict);
|
||||
zslFree(zs->zsl);
|
||||
zfree(zs);
|
||||
}
|
||||
|
||||
void freeHashObject(robj *o) {
|
||||
switch (o->encoding) {
|
||||
case REDIS_ENCODING_HT:
|
||||
dictRelease((dict*) o->ptr);
|
||||
break;
|
||||
case REDIS_ENCODING_ZIPMAP:
|
||||
zfree(o->ptr);
|
||||
break;
|
||||
default:
|
||||
redisPanic("Unknown hash encoding type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void incrRefCount(robj *o) {
|
||||
o->refcount++;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
|
||||
if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX ||
|
||||
!listAddNodeHead(server.objfreelist,o))
|
||||
zfree(o);
|
||||
if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
int checkType(redisClient *c, robj *o, int type) {
|
||||
if (o->type != type) {
|
||||
addReply(c,shared.wrongtypeerr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to encode a string object in order to save space */
|
||||
robj *tryObjectEncoding(robj *o) {
|
||||
long value;
|
||||
sds s = o->ptr;
|
||||
|
||||
if (o->encoding != REDIS_ENCODING_RAW)
|
||||
return o; /* Already encoded */
|
||||
|
||||
/* It's not safe to encode shared objects: shared objects can be shared
|
||||
* everywhere in the "object space" of Redis. Encoded objects can only
|
||||
* appear as "values" (and not, for instance, as keys) */
|
||||
if (o->refcount > 1) return o;
|
||||
|
||||
/* Currently we try to encode only strings */
|
||||
redisAssert(o->type == REDIS_STRING);
|
||||
|
||||
/* Check if we can represent this string as a long integer */
|
||||
if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o;
|
||||
|
||||
/* Ok, this object can be encoded */
|
||||
if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
|
||||
decrRefCount(o);
|
||||
incrRefCount(shared.integers[value]);
|
||||
return shared.integers[value];
|
||||
} else {
|
||||
o->encoding = REDIS_ENCODING_INT;
|
||||
sdsfree(o->ptr);
|
||||
o->ptr = (void*) value;
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 (o->encoding == REDIS_ENCODING_RAW) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare two string objects via strcmp() or alike.
|
||||
* Note that the objects may be integer-encoded. In such a case we
|
||||
* use ll2string() to get a string representation of the numbers on the stack
|
||||
* and compare the strings, it's much faster than calling getDecodedObject().
|
||||
*
|
||||
* Important note: if objects are not integer encoded, but binary-safe strings,
|
||||
* sdscmp() from sds.c will apply memcmp() so this function ca be considered
|
||||
* binary safe. */
|
||||
int compareStringObjects(robj *a, robj *b) {
|
||||
redisAssert(a->type == REDIS_STRING && b->type == REDIS_STRING);
|
||||
char bufa[128], bufb[128], *astr, *bstr;
|
||||
int bothsds = 1;
|
||||
|
||||
if (a == b) return 0;
|
||||
if (a->encoding != REDIS_ENCODING_RAW) {
|
||||
ll2string(bufa,sizeof(bufa),(long) a->ptr);
|
||||
astr = bufa;
|
||||
bothsds = 0;
|
||||
} else {
|
||||
astr = a->ptr;
|
||||
}
|
||||
if (b->encoding != REDIS_ENCODING_RAW) {
|
||||
ll2string(bufb,sizeof(bufb),(long) b->ptr);
|
||||
bstr = bufb;
|
||||
bothsds = 0;
|
||||
} else {
|
||||
bstr = b->ptr;
|
||||
}
|
||||
return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
|
||||
}
|
||||
|
||||
/* Equal string objects return 1 if the two objects are the same from the
|
||||
* point of view of a string comparison, otherwise 0 is returned. Note that
|
||||
* this function is faster then checking for (compareStringObject(a,b) == 0)
|
||||
* because it can perform some more optimization. */
|
||||
int equalStringObjects(robj *a, robj *b) {
|
||||
if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
|
||||
return a->ptr == b->ptr;
|
||||
} else {
|
||||
return compareStringObjects(a,b) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t stringObjectLen(robj *o) {
|
||||
redisAssert(o->type == REDIS_STRING);
|
||||
if (o->encoding == REDIS_ENCODING_RAW) {
|
||||
return sdslen(o->ptr);
|
||||
} else {
|
||||
char buf[32];
|
||||
|
||||
return ll2string(buf,32,(long)o->ptr);
|
||||
}
|
||||
}
|
||||
|
||||
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') return REDIS_ERR;
|
||||
} else if (o->encoding == REDIS_ENCODING_INT) {
|
||||
value = (long)o->ptr;
|
||||
} else {
|
||||
redisPanic("Unknown string encoding");
|
||||
}
|
||||
}
|
||||
|
||||
*target = value;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
|
||||
double value;
|
||||
if (getDoubleFromObject(o, &value) != REDIS_OK) {
|
||||
if (msg != NULL) {
|
||||
addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
|
||||
} else {
|
||||
addReplySds(c, sdsnew("-ERR value is not a double\r\n"));
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
*target = value;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int getLongLongFromObject(robj *o, long long *target) {
|
||||
long long value;
|
||||
char *eptr;
|
||||
|
||||
if (o == NULL) {
|
||||
value = 0;
|
||||
} else {
|
||||
redisAssert(o->type == REDIS_STRING);
|
||||
if (o->encoding == REDIS_ENCODING_RAW) {
|
||||
value = strtoll(o->ptr, &eptr, 10);
|
||||
if (eptr[0] != '\0') return REDIS_ERR;
|
||||
} else if (o->encoding == REDIS_ENCODING_INT) {
|
||||
value = (long)o->ptr;
|
||||
} else {
|
||||
redisPanic("Unknown string encoding");
|
||||
}
|
||||
}
|
||||
|
||||
*target = value;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
|
||||
long long value;
|
||||
if (getLongLongFromObject(o, &value) != REDIS_OK) {
|
||||
if (msg != NULL) {
|
||||
addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
|
||||
} else {
|
||||
addReplySds(c, sdsnew("-ERR value is not an integer\r\n"));
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
*target = value;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
|
||||
long long value;
|
||||
|
||||
if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
|
||||
if (value < LONG_MIN || value > LONG_MAX) {
|
||||
if (msg != NULL) {
|
||||
addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
|
||||
} else {
|
||||
addReplySds(c, sdsnew("-ERR value is out of range\r\n"));
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
*target = value;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
char *strEncoding(int encoding) {
|
||||
switch(encoding) {
|
||||
case REDIS_ENCODING_RAW: return "raw";
|
||||
case REDIS_ENCODING_INT: return "int";
|
||||
case REDIS_ENCODING_HT: return "hashtable";
|
||||
case REDIS_ENCODING_ZIPMAP: return "zipmap";
|
||||
case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
|
||||
case REDIS_ENCODING_ZIPLIST: return "ziplist";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user