string buffer

    前段时间,在工作中需要使用Sqlite3嵌入式数据库,在生成sql语句的过程中,每次都要去预估sql大概的长度,有些时候插入一条记录的时候,sql语句可能会很长,如果是在栈上分配,觉得不是很妥,索性就写了一个可以自动扩容的string buffer,用于存储生成的sql。

    1. 首先是声明string buffer:

typedef struct _string_buffer_t
{
    uint32_t    size;
    uint32_t    len;
    char*       str;
    uint16_t    free:1;  // self free or not
    uint16_t    reverse:15;
}string_buffer_t;

     2. 接下来是实现string buffer对象的初始化与销毁的方法:

int string_buffer_init(string_buffer_t *self, char *p, uint32_t size)
{
    if(self == NULL){
        ERR("null point");
        return ENULL_POINT;
    }

    if(size == 0){
        size = STRING_BUFFER_DEFAULT_SIZE;
    }

    if(p){
        self->str = p;
        self->free = 0;
    }else{
        self->str = (char *)MALLOC(size);
        if(self->str == NULL){
            ERR("MALLOC failed");
            return EMALLOC;
        }
        self->free = 1;
    }

    self->size = size;
    self->len = 0;
    return RET_SUCCESS;
}

void string_buffer_reset(string_buffer_t *self)
{
    if(self){
        if(self->str && self->free){
            free(self->str);
            memset(self, 0, sizeof(*self));
        }
    }
}

    在init方法中,允许用户使用外部分配的内存,如果不使用,则p=NULL.

    3. 接着实现string expand方法:

static int _string_buffer_expand(string_buffer_t *self, size_t len)
{
    if(self->free == 0){
    	DBG("can not expand");
        return EINVAL_ARG;
    }

    size_t t_len = self->len + len + 1;
    if(t_len <= self->size)
        return RET_SUCCESS;

    t_len = self->size + len + 1;
    t_len = ((t_len + STRING_BUFFER_MASK) && ~STRING_BUFFER_MASK);
    char *ptr = (char *)realloc(self->str, t_len);
    if(ptr != NULL){
        self->size = t_len;
        if(ptr != self->str){
            memcpy(ptr, self->str, self->len);
            self->str = ptr;
        }
        return RET_SUCCESS;
    }else{
    	ERR("realloc failed");
    	return EMALLOC;
    }
}

    4. 最后,为了方便,实现比较常用的几个方法:

int string_buffer_snprintf(string_buffer_t *self, const char *fmt, ...)
{
    int ret;
    va_list ap;
    va_start(ap, fmt);

    if(self->len >= self->size){
        if((ret = _string_buffer_expand(self, self->len)) < 0){
            return ret;
        }
    }

_RETRY:
    ret = vsnprintf(self->str + self->len, self->size - self->len, fmt, ap);
    if(ret <= 0){
    	DBG("vsnprintf failed");
    	return -1;
    }

    if((self->len + ret) >= self->size){
    	if(self->str[self->size-1] != '\0'){
    	    if((ret = _string_buffer_expand(self, ret)) < 0){
    	        return ret;
    	    }
    
    	    goto _RETRY;
    	}
    }

    self->len += ret;
    self->str[self->len] = '\0';

    va_end(ap);
    return RET_SUCCESS;
}

int string_buffer_strcat(string_buffer_t *self, const char *str)
{
    size_t len = strlen(str);
    int ret = _string_buffer_expand(self, len);
    if(ret != RET_SUCCESS)
        return ret;

    strcpy(self->str + self->len, str);
    self->len += len; 
    self->str[self->len] = '\0';
    return RET_SUCCESS;
}

int string_buffer_insert(string_buffer_t *self, int index, const char *str)
{
    size_t len = strlen(str);
    size_t start;
    int ret;

    if(index > self->len){
        DBG("overlen: index(%d) > len(%u)", index, self->len);
        return EINVAL_ARG;
    }
    
    if(index >= 0){
        start = index;
    }else{
        if(index + self->len >= 0){
            start = index + self->len;
        }else{
            DBG("overlen: index(%d) < -len(%u)", index, self->len);
            return EINVAL_ARG;
        }
    }

    size_t end = start + len;
    if(end >= self->size){
        if((ret = _string_buffer_expand(self, len+1)) < 0){
            return ret;
        }
    }

    strcpy(self->str+start, str);
    self->len = end;
    self->str[self->len] = '\0';

    return RET_SUCCESS;
}

void string_buffer_debug(string_buffer_t *self)
{
    if(self && self->str){
        printf("str: %s\n", self->str);
    }
}

const char *string_buffer_get_str(string_buffer_t *self)
{
    if(self)
        return self->str;
}

    其中string_buffer_snprintf就像是snprintf,可以直接格式化字符串。string_buffer_strcat跟strcat一样效果,将字符串str连接到原先字符串的后面。string_buffer_insert方法,用于在指定的位置index处,插入字符串str,这里的index可以是负数,如-1表示插入到最后一个字符的位置处。string_buffer_debug方法是为了方便调试,将里面的字符串打印出来。

    5. 最后这里简单的写了一个测试程序

int string_buffer_test(void)
{
    string_buffer_t sql_buf;
    int ret;

    printf("** test sql_buffer:\n");
    ret = sql_buffer_init(&sql_buf, NULL, 0);
    ASSERT(ret == RET_SUCCESS);

    ret = string_buffer_strcat(&sql_buf, \
        "CREATE TABLE test_table(id INT PRIMARY KEY,name CHAR NOT NULL);");
    ASSERT(ret == RET_SUCCESS);
    string_buffer_debug(&sql_buf);

    ret = string_buffer_snprintf(&sql_buf, "INSERT INTO %s(id, name) VALUES(%d,'%s')",
						"test_table", 1, "xxxx");
    ASSERT(ret == RET_SUCCESS);	
    string_buffer_debug(&sql_buf);

    ret = string_buffer_snprintf(&sql_buf, "UPDATE %s SET name='%s' WHERE id=%d,",\
                "test_table", "yyyy", 1);
    ASSERT(ret == RET_SUCCESS);
    ret = string_buffer_insert(&sql_buf, -1, "");
    string_buffer_debug(&sql_buf);

    sql_buffer_reset(&sql_buf);
    printf("** test sql_buffer success\n");
    return 0;
}

int main(int argc, char **argv)
{
    return string_buffer_test();
}


你可能感兴趣的:(sql)