動態字符串SDS的實現 | 自己實現Redis源代碼(1)

通過對《Redis設計與實現》一書的學習,我打算動手自己實現一份“Redis源代碼”作爲自己的學習記錄。

對Redis感興趣的同學可以查看我的另一篇文章 造個輪子 | 自己動手寫一個Redis

本章介紹的是Redis源代碼中的動態字符串SDS的實現。

動態字符串SDS的實現

SDS的API

(1)創建一個包含給定c字符串的sds

sds sdsnew(char *);

(2)爲sds(也就是buf數組)分配指定空間

sds sdsnewlen(sds,int);

(3)創建一個不包含任何內容的空字符串

sds sdsempty(void);

(4)釋放給定的sds

void sdsfree(sds);

(5)創建一個給定sds的副本

sds sdsdup(sds);

(6)清空sds保存的字符串內容

sds sdsclear(sds);

(7)將給定c字符串拼接到另一個sds字符串的末尾

sds sdscat(sds,char *);

(8)將給定sds字符串拼接到另一個sds字符串的末尾

sds sdscatsds(sds,sds);

(9)將給定的c字符串複製到sds裏面,覆蓋原有的字符串

sds sdscpy(sds,char *);

(10)保留sds給定區間內的數據

sds sdsrange(sds,int,int);

(11)從sds中移除所有在c字符串中出現過的字符

sds sdstrim(sds,const char *);

(12)對比兩個sds字符串是否相同

bool sdscmp(sds,sds);



頭文件

#ifndef SDS_H
#define SDS_H
//實現Redis中的動態字符串
//SDS:simple dynamic string


typedef struct sdshdr{
    //記錄buf數組中已使用字節的數量
    //等於SDS所保存字符串的長度,不
    //包括最後的'\0';
    int len;
    //記錄buf數組中未使用字節的數量
    int free;
    //字節數組,用於保存字符串,以
    //'\0'結束
    char* buf;
}*sds;

//返回sds已使用空間的字節數:len
static inline int sdslen(const sds sh){
    return sh->len;
}

//返回sds未使用空間的字節數:free
static inline int sdsavail(const sds sh){
    return sh->free;
}


//創建一個包含給定c字符串的sds
sds sdsnew(char *);

//爲sds(也就是buf數組)分配指定空間/len
sds sdsnewlen(sds,int);

//創建一個不包含任何內容的空字符串
sds sdsempty(void);

//釋放給定的sds
void sdsfree(sds);

//創建一個給定sds的副本
sds sdsdup(sds);

//清空sds保存的字符串內容
sds sdsclear(sds);

//將給定c字符串拼接到另一個sds字符串的末尾
sds sdscat(sds,char *);

//將給定sds字符串拼接到另一個sds字符串的末尾
sds sdscatsds(sds,sds);

//將給定的c字符串複製到sds裏面,覆蓋原有的字符串
sds sdscpy(sds,char *);


//保留sds給定區間內的數據,不在區間內的數據會被覆蓋或清除
//s = sdsnew("Hello World");
//sdsrange(s,1,-1); => "ello World"
sds sdsrange(sds,int,int);

//接受一個sds和一個c字符串作爲參數,從sds中移除所有在c字符串中出現過的字符
//s = sdsnew("AA...AA.a.aa.aHelloWorld     :::");
//s = sdstrim(s,"A. :");
//printf("%s\n", s);
//Output will be just "Hello World".
//大小寫不敏感
sds sdstrim(sds,const char *);

//對比兩個sds字符串是否相同
bool sdscmp(sds,sds);

#endif



SDS API的實現

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sds.h"

//創建一個包含給定c字符串的sds
sds sdsnew(char *init){
    sds sh=(sds)malloc(sizeof(struct sdshdr));
    sh->len=strlen(init);
    sh->free=0;
    sh->buf=(char*)malloc(sizeof(char)*(sh->len+1));
    //將字符串內容進行復制
    int i;
    for(i=0;i<sh->len;i++){
        (sh->buf)[i]=init[i];
    }
    (sh->buf)[i]='\0';
    return sh;
}

//爲sds(也就是buf數組)分配指定空間/len
sds sdsnewlen(sds sh,int len){
    int i;
    sh->free=len-1-sh->len;
    //保存之前的buf內容
    char *str=(char *)malloc(sizeof(char)*(sh->len+1));
    for(i=0; i<(sh->len); i++){
        str[i]=sh->buf[i];    
    }
    str[i]='\0';
    //sh->buf=(char*)realloc(sh->buf,len);
    sh->buf=(char*)malloc(sizeof(char)*len);
    for(i=0; i<(sh->len); i++){
        sh->buf[i]=str[i];
    }
    sh->buf[i]='\0';
    free(str);
    return sh;
}


//創建一個不包含任何內容的空字符串
sds sdsempty(void){
    sds sh=(sds)malloc(sizeof(struct sdshdr));
    sh->len=0;
    sh->free=0;
    sh->buf=(char*)malloc(sizeof(char));
    sh->buf[0]='\0';
    return sh;
}

//釋放給定的sds
void sdsfree(sds *sh){
    (*sh)->free=0;
    (*sh)->len=0;
    free((*sh)->buf);
    free(*sh);
}

//創建一個給定sds的副本
sds sdsdup(sds sh01){
    sds sh02=(sds)malloc(sizeof(struct sdshdr));
    sh02->free=sh01->free;
    sh02->len=sh01->len;
    sh02->buf=(char*)malloc(sizeof(char)*(sh02->free+sh02->len+1));
    int i;
    for(i=0;i<sh01->len;i++){
        sh02->buf[i]=sh01->buf[i];
    }
    sh02->buf[i]='\0';
    return sh02;
}

//清空sds保存的字符串內容
sds sdsclear(sds sh){
    int total=sh->len+sh->free+1;
    sh->len=0;
    sh->free=total-1;
    sh->buf[0]='\0';
    return sh;
}

//將給定c字符串拼接到另一個sds字符串的末尾
//先檢查sds的空間是否滿足修改所需的要求,如
//果不滿足則自動將sds空間擴展至執行修改所需
//要的大小,然後在執行實際的修改操作——防止
//緩衝區溢出
//擴展空間的原則:拼接後的字符串是n個字節,則
//再給其分配n個字節的未使用空間,buf數組的實際長度爲n+n+1
//當n超過1MB的時候,則爲其分配1MB的未使用空間
//兩個字符串cat,中間使用空格隔開
sds sdscat(sds sh,char *str){
    int newlen=strlen(str);
    int newfree;
    //剩餘的空間不夠cat操作
    if(sh->free<=newlen){
        //超出部分的空間
        newfree=newlen-sh->free;
        if(newfree<1024){
            newfree=newfree+newfree+1+sh->len+sh->free;
            sh=sdsnewlen(sh,newfree);
        }else{
            newfree=newfree+1024+1+sh->len+sh->free;
            sh=sdsnewlen(sh,newfree);
        }
    }
    int i;
    //執行cat操作
    sh->buf[sh->len]=' ';
    for(i=0;i<newlen;i++){
        sh->buf[sh->len+i+1]=str[i];
    }
    sh->buf[sh->len+i+1]='\0';
    sh->len+=(newlen+1);
    sh->free-=newlen;
    return sh;
}

//將給定sds字符串拼接到另一個sds字符串的末尾
sds sdscatsds(sds sh,sds str){
    int newlen=str->len;
    int newfree;
    //剩餘的空間不夠cat操作
    if(sh->free<=newlen){
        //超出部分的空間
        newfree=newlen-sh->free;
        if(newfree<1024){
            newfree=newfree+newfree+1+sh->len+sh->free;
            sh=sdsnewlen(sh,newfree);
        }else{
            newfree=newfree+1024+1+sh->len+sh->free;
            sh=sdsnewlen(sh,newfree);
        }
    }
    int i;
    //執行cat操作
    sh->buf[sh->len]=' ';
    for(i=0;i<newlen;i++){
        sh->buf[sh->len+i+1]=str->buf[i];
    }
    sh->buf[sh->len+i+1]='\0';
    sh->len+=(newlen+1);
    sh->free-=newlen;
    return sh;
}

//將給定的c字符串複製到sds裏面,覆蓋原有的字符串
//需要先檢查
sds sdscpy(sds sh,char *str){
    //新來的長度
    int len=strlen(str);
    //需要使用到的新空間長度
    int newlen=len-sh->len;
    int total;
    //剩餘的空間不夠了需要重新分配,在copy
    if(newlen>=sh->free){
        //新空間長度大於1M,就只多分配newlen+1M+1
        //總的空間是len+newlen+1M+1
        if(newlen>=1024){
            total=len+newlen+1024+1;
            //copy後使用到的len,就是新字符串的長度
            sh->len=len;
            //空閒的空間長度
            //sh->free=total-len-1;
            //sh->buf=(char*)realloc(sh->buf,total);
            sh=sdsnewlen(sh,total);
        //分配newlen+newlen+1
        }else{
            total=len+newlen+newlen+1;
            sh->len=len;
            //sh->free=total-len-1;
            //sh->buf=(char*)realloc(sh->buf,total);
            sh=sdsnewlen(sh,total);
        }
        if(sh->buf==NULL){
            printf("PIG Redis ERROR : Realloc failed.\n");
        }
    }else{
        //剩餘的空間夠,不需要分配
        //原來擁有的總空間
        total=sh->len+sh->free;
        sh->len=len;
        sh->free=total-sh->len;
    }
    //開始copy
    int i;
    for(i=0;i<len;i++){
        (sh->buf)[i]=str[i];
    }
    sh->buf[i]='\0';
    return sh;
}

//保留sds給定區間內的數據,不在區間內的數據會被覆蓋或清除
//s = sdsnew("Hello World");
//sdsrange(s,1,-1); => "ello World"
sds sdsrange(sds sh,int start,int end){
    int newlen=end-start+1;
    char *str=(char*)malloc(sizeof(char)*(sh->len+1));
    //sh1->free=sh->len-sh1->len;
    int i,j;
    for(i=start,j=0;i<=end;i++,j++){
        str[j]=sh->buf[i];
    }
    str[j]='\0';
    sh->buf=(char*)malloc(sizeof(char)*(sh->len+1));
    sh->free=sh->len-newlen;
    sh->len=newlen;
    for(i=0;i<strlen(str);i++){
        sh->buf[i]=str[i];
    }
    sh->buf[i]='\0';
    free(str);
    return sh;
}

//接受一個sds和一個c字符串作爲參數,從sds中移除所有在c字符串中出現過的字符
//s = sdsnew("AA...AA.a.aa.aHelloWorld     :::");
//s = sdstrim(s,"A. :");
//printf("%s\n", s);
//Output will be just "Hello World".
//截斷操作需要通過內存重分配來釋放字符串中不再使用的空間,否則會造成內存泄漏
//大小寫不敏感
//使用惰性空間釋放優化字符串的縮短操作,執行縮短操作的時候,不立即使用內存重分
//配來回收縮短後多出來的字節,而是使用free屬性記錄這些字節,等待將來使用
sds sdstrim(sds s,const char *chstr);

//對比兩個sds字符串是否相同
bool sdscmp(sds sh1,sds sh2){
    if(sh1->len!=sh2->len){
        return false;
    }
    for(int i=0;i<sh1->len;i++){
        if(sh1->buf[i]!=sh2->buf[i]){
            return false;
        }
    }
    return true;
}

int main(){
    printf("sdsnew('sss')\n");
    sds sh=sdsnew("sss");
    printf("%s\n",sh->buf);
    printf("%d\n",sh->len);
    printf("%d\n",sh->free);

    printf("sdscat(sh,'www')\n");
    sh=sdscat(sh,"www");
    printf("%s\n",sh->buf);
    /*for(int i=0;i<sh->len;i++){
        printf("%c",sh->buf[i]);
    }*/
    printf("%d\n",sh->len);
    printf("%d\n",sh->free);

    sds sh1=sdsnew("qqqq");
    sh=sdscatsds(sh,sh1);
    printf("%s\n",sh->buf);
    printf("%d\n",sh->len);
    printf("%d\n",sh->free);

    sh=sdsrange(sh,1,5);
    printf("%s\n",sh->buf);
    printf("%d\n",sh->len);
    printf("%d\n",sh->free);

    sds sh3=sdsnew("qqqq");
    sds sh4=sdsnew("qqqq");
    
    if(sdscmp(sh3,sh4)){
        printf("same\n");
    }else{
        printf("no same\n");
    }

/*    printf("sdscpy(sh,'wwww')\n");
    sh=sdscpy(sh,"wwww");
    printf("%s\n",sh->buf);
    printf("%d\n",sh->len);
    printf("%d\n",sh->free);

    printf("sdsnewlen(sh,12)\n");
    sh=sdsnewlen(sh,12);
    printf("%s\n",sh->buf);
    printf("%d\n",sh->len);
    printf("%d\n",sh->free);
    
    printf("sdsdup(sh)\n");
    sds sh1=sdsdup(sh);
    printf("%s\n",sh1->buf);
    printf("%d\n",sh1->len);
    printf("%d\n",sh1->free);

    printf("sdsclear(sh1)\n");
    sh1=sdsclear(sh1);
    printf("%s\n",sh1->buf);
    printf("%d\n",sh1->len);
    printf("%d\n",sh1->free);
*/
    sdsfree(&sh);
    sdsfree(&sh1);
    //sdsfree(&sh2);
    sdsfree(&sh3);
    sdsfree(&sh4);
    system("pause");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章