SDS, short for Simple Dynamic Strings, is a string type defined by Redis.

typedef char *sds;
Copy the code

Redis is just a string array with Simple Dynamic Strings. Yes, I thought so at that time, but after reading the source code carefully, I found that SDS is not fighting alone, it also has comrades SDSHDR, SDSHDR is a quintuplet, they are SDSHDR5, SDSHDR8, SDSHD16, SDSHDR32, SDSHD64. From small to big.

SDSHDR stands for Simple Dynamic Strings Header

/* The old five (sdshdr5) is never used because they are not the same as others */
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* The lower three bits represent the type, and the higher five bits represent the length of the string */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* The length of the string */
    uint8_t alloc; /* Allocate the length */
    unsigned char flags; /* The lower three bits indicate the type, and the higher five bits are not used */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* The length of the string */
    uint16_t alloc; /* Allocate the length */
    unsigned char flags; /* The lower three bits indicate the type, and the higher five bits are not used */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* The length of the string */
    uint32_t alloc; /* Allocate the length */
    unsigned char flags; /* The lower three bits indicate the type, and the higher five bits are not used */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* The length of the string */
    uint64_t alloc; /* Allocate the length */
    unsigned char flags; /* The lower three bits indicate the type, and the higher five bits are not used */
    char buf[];
};
Copy the code

Knowledge! This is the key! __attribute__ ((__packed__)) you’ll see the following code in a moment: (s)-(sizeof(struct sdshdr##T))), s[-1], (char*) s-sdshdrsize (s[-1]))

This command is used to disable memory alignment at compile time.

So, the structure in memory looks like this:

Look like this, before those coquettish walk position is very clear.

// s minus SDSHDR length = pointer to the SDSHDR structure(s) - (sizeof (struct sdshdr# # T))),// s previous position = flags
s[-1]
// Has the same effect as 1
(char*)s-sdsHdrSize(s[-1])
Copy the code

With that in mind, it’s easy enough to look at the code in SDS.c and SDS.h. Let’s focus on two functions

  1. Create SDS string
sds sdsnewlen(const void *init, size_t initlen) {
    void *sh;
    sds s;
    /* Determines the type of SDS based on the length of the string */
    char type = sdsReqType(initlen);
    /* The fifth is discriminated against */
    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
    /* Calculate the length of sdsHeader */
    int hdrlen = sdsHdrSize(type);
    /* flags */
    unsigned char *fp; 

    /* Open up memory space, +1 is to put a last \0, compatible with traditional C language, when in Rome, do as the Romans do */
    sh = s_malloc(hdrlen+initlen+1);
    if(! init) memset(sh,0, hdrlen+initlen+1);
    if (sh == NULL) return NULL;
    /* This bit points to the beginning of the string */
    s = (char*)sh+hdrlen;
    /* This bit goes to flags */
    fp = ((unsigned char*)s)-1;

    /* Initializes sdsHeader */ depending on the type
    switch(type) {
        case SDS_TYPE_5: {
            *fp = type | (initlen << SDS_TYPE_BITS);
            break;
        }
        case SDS_TYPE_8: {
            SDS_HDR_VAR(8,s);
            sh->len = initlen;
            sh->alloc = initlen;
            *fp = type;
            break;
        }
        case SDS_TYPE_16: {
            SDS_HDR_VAR(16,s);
            sh->len = initlen;
            sh->alloc = initlen;
            *fp = type;
            break;
        }
        case SDS_TYPE_32: {
            SDS_HDR_VAR(32,s);
            sh->len = initlen;
            sh->alloc = initlen;
            *fp = type;
            break;
        }
        case SDS_TYPE_64: {
            SDS_HDR_VAR(64,s);
            sh->len = initlen;
            sh->alloc = initlen;
            *fp = type;
            break; }}/* String assignment */
    if (initlen && init)
        memcpy(s, init, initlen);
    s[initlen] = '\ 0';
    return s;
}
Copy the code
  1. Dynamically expand SDS space
sds sdsMakeRoomFor(sds s, size_t addlen) {
    void *sh, *newsh;
    /* avail = alloc-len */
    size_t avail = sdsavail(s);
    size_t len, newlen;
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    int hdrlen;

    /* If there is enough space left, there is no need to expand */
    if (avail >= addlen) return s;

    len = sdslen(s);
    sh = (char*)s-sdsHdrSize(oldtype);
    newlen = (len+addlen);
    /* Redis believes that once the string has been expanded, it has a high chance of being expanded again, so it will add more space to it to prevent frequent expansion */
    if (newlen < SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC;
    /* recalculate type */
    type = sdsReqType(newlen);

    /* The fifth is discriminated against again */
    if (type == SDS_TYPE_5) type = SDS_TYPE_8;

    hdrlen = sdsHdrSize(type);
    if (oldtype==type) {
        /* When the original type is the same as the new type, the original base is realloc space */
        newsh = s_realloc(sh, hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        s = (char*)newsh+hdrlen;
    } else {
        /* Otherwise you need to re-malloc the entire space and copy */
        newsh = s_malloc(hdrlen+newlen+1);
        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);
    }
    sdssetalloc(s, newlen);
    return s;
}
Copy the code

Concern public number: Java baodian