diff options
author | antirez <antirez@gmail.com> | 2010-06-22 00:07:48 +0200 |
---|---|---|
committer | antirez <antirez@gmail.com> | 2010-07-01 14:38:51 +0200 |
commit | e2641e09cc0daf44f63f654230f72d22acf3a9af (patch) | |
tree | f0443876d28414f7c80787593e5f35a9f9c87747 /src/object.c | |
parent | c2ff0e90b8ce84d7b966622ffe0178303bb0a625 (diff) | |
download | redis-e2641e09cc0daf44f63f654230f72d22acf3a9af.tar.gz |
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
Diffstat (limited to 'src/object.c')
-rw-r--r-- | src/object.c | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/src/object.c b/src/object.c new file mode 100644 index 000000000..4854909e0 --- /dev/null +++ b/src/object.c @@ -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"; + } +} |