{
  "id": "internals-sds",
  "title": "String internals",
  "url": "https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/internals-sds/",
  "summary": "Guide to the original implementation of Redis strings",
  "tags": [
    "docs",
    "operate",
    "stack",
    "oss"
  ],
  "last_updated": "2026-04-09T10:29:34-04:00",
  "page_type": "content",
  "content_hash": "cfbe18f367f46ea63b0cae2a71013ba912a3549d7fc5e76ab9dc59350e80c89e",
  "sections": [
    {
      "id": "content",
      "title": "Content",
      "role": "content",
      "text": "**Note: this document was written by the creator of Redis, Salvatore Sanfilippo, early in the development of Redis (c. 2010). Virtual Memory has been deprecated since Redis 2.6, so this documentation\nis here only for historical interest.**\n\nThe implementation of Redis strings is contained in `sds.c` (`sds` stands for\nSimple Dynamic Strings). The implementation is available as a standalone library\nat [https://github.com/antirez/sds](https://github.com/antirez/sds).\n\nThe C structure `sdshdr` declared in `sds.h` represents a Redis string:\n\n    struct sdshdr {\n        long len;\n        long free;\n        char buf[];\n    };\n\nThe `buf` character array stores the actual string.\n\nThe `len` field stores the length of `buf`. This makes obtaining the length\nof a Redis string an O(1) operation.\n\nThe `free` field stores the number of additional bytes available for use.\n\nTogether the `len` and `free` field can be thought of as holding the metadata of the `buf` character array.\n\nCreating Redis Strings\n---\n\nA new data type named `sds` is defined in `sds.h` to be a synonym for a character pointer:\n\n    typedef char *sds;\n\n`sdsnewlen` function defined in `sds.c` creates a new Redis String:\n\n    sds sdsnewlen(const void *init, size_t initlen) {\n        struct sdshdr *sh;\n\n        sh = zmalloc(sizeof(struct sdshdr)+initlen+1);\n    #ifdef SDS_ABORT_ON_OOM\n        if (sh == NULL) sdsOomAbort();\n    #else\n        if (sh == NULL) return NULL;\n    #endif\n        sh->len = initlen;\n        sh->free = 0;\n        if (initlen) {\n            if (init) memcpy(sh->buf, init, initlen);\n            else memset(sh->buf,0,initlen);\n        }\n        sh->buf[initlen] = '\\0';\n        return (char*)sh->buf;\n    }\n\nRemember a Redis string is a variable of type `struct sdshdr`. But `sdsnewlen` returns a character pointer!!\n\nThat's a trick and needs some explanation.\n\nSuppose I create a Redis string using `sdsnewlen` like below:\n\n    sdsnewlen(\"redis\", 5);\n\nThis creates a new variable of type `struct sdshdr` allocating memory for `len` and `free`\nfields as well as for the `buf` character array.\n\n    sh = zmalloc(sizeof(struct sdshdr)+initlen+1); // initlen is length of init argument.\n\nAfter `sdsnewlen` successfully creates a Redis string the result is something like:\n\n    -----------\n    |5|0|redis|\n    -----------\n    ^   ^\n    sh  sh->buf\n\n`sdsnewlen` returns `sh->buf` to the caller.\n\nWhat do you do if you need to free the Redis string pointed by `sh`?\n\nYou want the pointer `sh` but you only have the pointer `sh->buf`.\n\nCan you get the pointer `sh` from `sh->buf`?\n\nYes. Pointer arithmetic. Notice from the above ASCII art that if you subtract\nthe size of two longs from `sh->buf` you get the pointer `sh`.\n\nThe `sizeof` two longs happens to be the size of `struct sdshdr`.\n\nLook at `sdslen` function and see this trick at work:\n\n    size_t sdslen(const sds s) {\n        struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));\n        return sh->len;\n    }\n\nKnowing this trick you could easily go through the rest of the functions in `sds.c`.\n\nThe Redis string implementation is hidden behind an interface that accepts only character pointers. The users of Redis strings need not care about how it's implemented and can treat Redis strings as a character pointer."
    }
  ],
  "examples": []
}
