sds:simple dynamic string
    sds是redis自己定义的string类型,让我们来康康redis是如何实现自己的string类型的。

    1. //sds实际是char*的别名,用来隐式的根据前缀来跟踪string的大小
    2. typedef char *sds;
    3. /* Note: sdshdr5 is never used, we just access the flags byte directly.
    4. * However is here to document the layout of type 5 SDS strings. */
    5. struct __attribute__ ((__packed__)) sdshdr5 {
    6. unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    7. char buf[];
    8. };
    9. struct __attribute__ ((__packed__)) sdshdr8 {
    10. uint8_t len; /* used */
    11. uint8_t alloc; /* excluding the header and null terminator */
    12. unsigned char flags; /* 3 lsb of type, 5 unused bits */
    13. char buf[];
    14. };
    15. struct __attribute__ ((__packed__)) sdshdr16 {
    16. uint16_t len; /* used */
    17. uint16_t alloc; /* excluding the header and null terminator */
    18. unsigned char flags; /* 3 lsb of type, 5 unused bits */
    19. char buf[];
    20. };
    21. struct __attribute__ ((__packed__)) sdshdr32 {
    22. uint32_t len; /* used */
    23. uint32_t alloc; /* excluding the header and null terminator */
    24. unsigned char flags; /* 3 lsb of type, 5 unused bits */
    25. char buf[];
    26. };
    27. struct __attribute__ ((__packed__)) sdshdr64 {
    28. uint64_t len; /* used */
    29. uint64_t alloc; /* excluding the header and null terminator */
    30. unsigned char flags; /* 3 lsb of type, 5 unused bits */
    31. char buf[];
    32. };
    1. //根据指针init和长度来创建一个新的sds
    2. sds sdsnewlen(const void *init, size_t initlen) {
    3. return _sdsnewlen(init, initlen, 0);
    4. }
    5. /* Create a new sds string with the content specified by the 'init' pointer
    6. * and 'initlen'.
    7. * If NULL is used for 'init' the string is initialized with zero bytes.
    8. * If SDS_NOINIT is used, the buffer is left uninitialized;
    9. *
    10. * The string is always null-termined (all the sds strings are, always) so
    11. * even if you create an sds string with:
    12. *
    13. * mystring = sdsnewlen("abc",3);
    14. *
    15. * You can print the string with printf() as there is an implicit \0 at the
    16. * end of the string. However the string is binary safe and can contain
    17. * \0 characters in the middle, as the length is stored in the sds header. */
    18. sds _sdsnewlen(const void *init, size_t initlen, int trymalloc) {
    19. void *sh;
    20. sds s;
    21. char type = sdsReqType(initlen);
    22. /* Empty strings are usually created in order to append. Use type 8
    23. * since type 5 is not good at this. */
    24. if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
    25. int hdrlen = sdsHdrSize(type);
    26. unsigned char *fp; /* flags pointer. */
    27. size_t usable;
    28. sh = trymalloc?
    29. s_trymalloc_usable(hdrlen+initlen+1, &usable) :
    30. s_malloc_usable(hdrlen+initlen+1, &usable);
    31. if (sh == NULL) return NULL;
    32. if (init==SDS_NOINIT)
    33. init = NULL;
    34. else if (!init)
    35. memset(sh, 0, hdrlen+initlen+1);
    36. s = (char*)sh+hdrlen;
    37. fp = ((unsigned char*)s)-1;
    38. usable = usable-hdrlen-1;
    39. if (usable > sdsTypeMaxSize(type))
    40. usable = sdsTypeMaxSize(type);
    41. switch(type) {
    42. case SDS_TYPE_5: {
    43. *fp = type | (initlen << SDS_TYPE_BITS);
    44. break;
    45. }
    46. case SDS_TYPE_8: {
    47. SDS_HDR_VAR(8,s);
    48. sh->len = initlen;
    49. sh->alloc = usable;
    50. *fp = type;
    51. break;
    52. }
    53. case SDS_TYPE_16: {
    54. SDS_HDR_VAR(16,s);
    55. sh->len = initlen;
    56. sh->alloc = usable;
    57. *fp = type;
    58. break;
    59. }
    60. case SDS_TYPE_32: {
    61. SDS_HDR_VAR(32,s);
    62. sh->len = initlen;
    63. sh->alloc = usable;
    64. *fp = type;
    65. break;
    66. }
    67. case SDS_TYPE_64: {
    68. SDS_HDR_VAR(64,s);
    69. sh->len = initlen;
    70. sh->alloc = usable;
    71. *fp = type;
    72. break;
    73. }
    74. }
    75. if (initlen && init)
    76. memcpy(s, init, initlen);
    77. s[initlen] = '\0';
    78. return s;
    79. }

    程序中首先会为sdshdr申请一块内存空间。所申请空间的实际大小取决于zmalloc所使用的malloc和CPU的架构,如果zmalloc采用的是libc malloc并且CPU采用的是64位架构,则整个prefix会占用16字节

    It allocates a memory block for sdshdr at first. Actual size depends on a back-end malloc implementation for zmalloc and a CPU architecture. If the back-end is libc malloc and the architecture is 64-bit, it consumes 16 bytes for the whole prefix.

    /* Enlarge the free space at the end of the sds string so that the caller
     * is sure that after calling this function can overwrite up to addlen
     * bytes after the end of the string, plus one more byte for nul term.
     *
     * Note: this does not change the *length* of the sds string as returned
     * by sdslen(), but only the free buffer space we have. */
    sds sdsMakeRoomFor(sds s, size_t addlen) {
        void *sh, *newsh;
        size_t avail = sdsavail(s);
        size_t len, newlen;
        char type, oldtype = s[-1] & SDS_TYPE_MASK;
        int hdrlen;
        size_t usable;
    
        /* Return ASAP if there is enough space left. */
        if (avail >= addlen) return s;
    
        len = sdslen(s);
        sh = (char*)s-sdsHdrSize(oldtype);
        newlen = (len+addlen);
        if (newlen < SDS_MAX_PREALLOC)
            newlen *= 2;
        else
            newlen += SDS_MAX_PREALLOC;
    
        type = sdsReqType(newlen);
    
        /* Don't use type 5: the user is appending to the string and type 5 is
         * not able to remember empty space, so sdsMakeRoomFor() must be called
         * at every appending operation. */
        if (type == SDS_TYPE_5) type = SDS_TYPE_8;
    
        hdrlen = sdsHdrSize(type);
        if (oldtype==type) {
            newsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable);
            if (newsh == NULL) return NULL;
            s = (char*)newsh+hdrlen;
        } else {
            /* Since the header size changes, need to move the string forward,
             * and can't use realloc */
            newsh = s_malloc_usable(hdrlen+newlen+1, &usable);
            if (newsh == NULL) return NULL;
            memcpy((char*)newsh+hdrlen, s, len+1);
            s_free(sh);
            s = (char*)newsh+hdrlen;
            s[-1] = type;
            sdssetlen(s, len);
        }
        usable = usable-hdrlen-1;
        if (usable > sdsTypeMaxSize(type))
            usable = sdsTypeMaxSize(type);
        sdssetalloc(s, usable);
        return s;
    }