Hacking Strings and redishacking for Redis code reading
Hacking Strings
The implementation of Redis strings is contained in sds. c (sds stands for Simple Dynamic Strings ).
The C structureSdshdrDeclared inSds. hRepresents a Redis string:
struct sdshdr { long len; long free; char buf[];};
TheBufCharacter array stores the actual string.
TheLenField stores the lengthBuf. This makes obtaining the length of a Redis string an O (1) operation.
TheFreeField stores the number of additional bytes available for use.
TogetherLenAndFreeField can be thought of as holding the metadata ofBufCharacter array.
Creating Redis Strings
A new data type namedsds
Is defined inSds. hTo be a synonymn for a character pointer:
typedef char *sds;
sdsnewlen
Function defined inSds. cCreates a new Redis String:
sds sdsnewlen(const void *init, size_t initlen) { struct sdshdr *sh; sh = zmalloc(sizeof(struct sdshdr)+initlen+1);#ifdef SDS_ABORT_ON_OOM if (sh == NULL) sdsOomAbort();#else if (sh == NULL) return NULL;#endif sh->len = initlen; sh->free = 0; if (initlen) { if (init) memcpy(sh->buf, init, initlen); else memset(sh->buf,0,initlen); } sh->buf[initlen] = '\0'; return (char*)sh->buf;}
Remember a Redis string is a variable of typestruct sdshdr
.sdsnewlen
Returns a character pointer !!
That's a trick and needs some explanation.
Suppose I create a Redis string usingsdsnewlen
Like below:
sdsnewlen("redis", 5);
This creates a new variable of typestruct sdshdr
Allocating memoryLenAndFreeFields as well as forBufCharacter array.
sh = zmalloc(sizeof(struct sdshdr)+initlen+1); // initlen is length of init argument.
Aftersdsnewlen
Succesfully creates a Redis string the result is something like:
-----------|5|0|redis|-----------^ ^sh sh->buf
sdsnewlen
Returns sh-> buf to the caller.
What do you do if you need to free the Redis string pointedsh
?
You want the pointersh
But you only have the pointersh->buf
.
Can you get the pointersh
Fromsh->buf
?
Yes. Pointer arithmetic. Notice from the above ASCII art that if you subtract the size of two longs fromsh->buf
You get the pointersh
.
The sizeof two longs happens to be the sizestruct sdshdr
.
Looksdslen
Function and see this trick at work:
size_t sdslen(const sds s) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); return sh->len;}
Knowing this trick you cocould easily go through the rest of the functions inSds. c.
The Redis string implementation is hidden behind an interface that accepts only character pointers. The users of Redis strings need not care about how its implemented and treat Redis strings as a character pointer.
C Struct array member without specific length