说到缓存区,是一个可复制可简单的问题。有的缓存区,自带文件持久化,日志,多线程和线程重入 ,智能扩容/缩容。
缓冲区在下读过linux kernel、muduo、llibevent的设计。这三种是比较专业一些缓存区。
剥离外在,缓存区的主要用于有两方面:作为消息载体,在内存进行消息的缓存和传递;利用内存外存的速度差距,作为提高磁盘io性能的一个组件。
余庆写的代码有一种风格:朴实无华,轻抽象、重组合。 Fastdfs的缓冲区设计上面没有前面三种那么花哨。但是也是具备了缓冲区最重要的两个功能。
万物之始,大道至简,衍化至繁。作为程序员来说,你有多久没有返璞归真了,整天一开口就要故意或者被动把各种简单的东西,往复杂化的方向去搞。
按照我们的传统,直接上源码,自己拉下去读。(已经经过详细的注释)
#ifndef __FAST_BUFFER_H__
#define __FAST_BUFFER_H__
#include <stdint.h>
#include "common_define.h"
typedef struct fast_buffer {
char *data; // data[alloc_size],以及'\0'结尾
int alloc_size;
int length; // data指向内存区域已使用字节
} FastBuffer;
#ifdef __cplusplus
extern "C" {
#endif
static inline int fast_buffer_length(FastBuffer *buffer)
{
return buffer->length;
}
static inline char *fast_buffer_data(FastBuffer *buffer)
{
return buffer->data;
}
int fast_buffer_init_ex(FastBuffer *buffer, const int init_capacity);
static inline int fast_buffer_init(FastBuffer *buffer)
{
return fast_buffer_init_ex(buffer, 0);
}
#define fast_buffer_clear(buffer) fast_buffer_reset(buffer)
static inline void fast_buffer_reset(FastBuffer *buffer)
{
buffer->length = 0;
*buffer->data = '\0';
}
void fast_buffer_destroy(FastBuffer *buffer);
int fast_buffer_check(FastBuffer *buffer, const int inc_len);
int fast_buffer_append(FastBuffer *buffer, const char *format, ...);
int fast_buffer_append_buff(FastBuffer *buffer, const char *data, const int len);
int fast_buffer_append_int(FastBuffer *buffer, const int n);
int fast_buffer_append_int64(FastBuffer *buffer, const int64_t n);
int fast_buffer_append_file(FastBuffer *buffer, const char *filename);
static inline int fast_buffer_append_string(FastBuffer *buffer, const char *str)
{
return fast_buffer_append_buff(buffer, str, strlen(str));
}
static inline int fast_buffer_append_string2(FastBuffer *buffer, const string_t *add)
{
return fast_buffer_append_buff(buffer, add->str, add->len);
}
static inline int fast_buffer_append_buffer(FastBuffer *buffer, FastBuffer *src)
{
return fast_buffer_append_buff(buffer, src->data, src->length);
}
#ifdef __cplusplus
}
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/stat.h>
#include "logger.h"
#include "shared_func.h"
#include "fast_buffer.h"
// 为buffer申请指定容量的内存空间,用'\0'结尾(尾0不计入数据长度)
int fast_buffer_init_ex(FastBuffer * buffer, const int init_capacity)
{
buffer->length = 0;
if (init_capacity > 0) {
buffer->alloc_size = init_capacity;
}
else {
buffer->alloc_size = 256;
}
buffer->data = (char *)malloc(buffer->alloc_size);
if (buffer->data == NULL) {
logError("file: " __FILE__ ", line: %d, "
"malloc %d bytes fail", __LINE__, buffer->alloc_size);
return ENOMEM;
}
* (buffer->data) = '\0';
return 0;
}
// 释放缓存区域、复位缓存指针、复位数据长度
void fast_buffer_destroy(FastBuffer * buffer)
{
if (buffer->data != NULL) {
free(buffer->data);
buffer->data = NULL;
buffer->length = 0;
}
}
/*
检测bufffer是否需要扩容(再增加inc_len个字节数据)
buffer扩容有两种方法:
第一、用realloc()扩容原来的内存区域,好处是避免内存复制和释放,坏处是有可能失败。
第二、用malloc()申请一片新的内存区域,复制原来的数据到新的内存区域,释放老的内存区域。
*/
int fast_buffer_check(FastBuffer * buffer, const int inc_len)
{
int alloc_size;
char * buff;
if (buffer->alloc_size >= buffer->length + inc_len) {
return 0;
}
alloc_size = buffer->alloc_size * 2;
while (alloc_size <= buffer->length + inc_len) {
alloc_size *= 2;
}
buff = (char *)malloc(alloc_size);
if (buff == NULL) {
logError("file: " __FILE__ ", line: %d, "
"malloc %d bytes fail", __LINE__, alloc_size);
return ENOMEM;
}
if (buffer->length > 0) {
memcpy(buff, buffer->data, buffer->length);
}
free(buffer->data);
buffer->data = buff;
buffer->alloc_size = alloc_size;
return 0;
}
// 向Buffer打印数据,如果由于空间不足没有打印成功,则(根据不足的字节数)扩容后,剩余数据追加打印到扩容后的buffer中
int fast_buffer_append(FastBuffer * buffer, const char * format, ...)
{
va_list ap;
int result;
int len;
if ((result = fast_buffer_check(buffer, 64)) != 0) {
return result;
}
va_start(ap, format);
len = vsnprintf(buffer->data + buffer->length,
buffer->alloc_size - buffer->length, format, ap);
va_end(ap);
if (len < buffer->alloc_size - buffer->length) {
buffer->length += len;
}
else //maybe full, realloc and try again
{
if ((result = fast_buffer_check(buffer, len)) == 0) {
va_start(ap, format);
buffer->length += vsnprintf(buffer->data + buffer->length,
buffer->alloc_size - buffer->length, format, ap);
va_end(ap);
}
else {
* (buffer->data + buffer->length) = '\0'; //restore
}
}
return result;
}
// 扩容buffer(如果空间不足),追加写入buffer
int fast_buffer_append_buff(FastBuffer * buffer, const char * data, const int len)
{
int result;
if (len <= 0) {
return 0;
}
if ((result = fast_buffer_check(buffer, len)) != 0) {
return result;
}
memcpy(buffer->data + buffer->length, data, len);
buffer->length += len;
* (buffer->data + buffer->length) = '\0';
return 0;
}
// 扩容buffer(如果空间不足),追加一个字符格式的int数据
int fast_buffer_append_int(FastBuffer * buffer, const int n)
{
int result;
if ((result = fast_buffer_check(buffer, 16)) != 0) {
return result;
}
buffer->length += sprintf(buffer->data + buffer->length, "%d", n);
return 0;
}
// 扩容buffer(如果空间不足),追加一个字符格式的int64_t数据
int fast_buffer_append_int64(FastBuffer * buffer, const int64_t n)
{
int result;
if ((result = fast_buffer_check(buffer, 32)) != 0) {
return result;
}
buffer->length += sprintf(buffer->data + buffer->length, "%" PRId64, n);
return 0;
}
// 读取filename文件内容到buffer
int fast_buffer_append_file(FastBuffer * buffer, const char * filename)
{
struct stat st;
int result;
int64_t file_size;
if (stat(filename, &st) != 0) {
result = errno != 0 ? errno: ENOENT;
if (result == ENOENT) {
logError("file: " __FILE__ ", line: %d, "
"file %s not exist!", __LINE__,
filename);
}
else {
logError("file: " __FILE__ ", line: %d, "
"stat file %s fail, "
"result: %d, error info: %s", __LINE__,
filename, result, strerror(result));
}
return result;
}
if (!S_ISREG(st.st_mode)) {
logError("file: " __FILE__ ", line: %d, "
"file %s is NOT a regular file!",
__LINE__, filename);
return EINVAL;
}
// (如有必要)扩容buffer空间,使其可以放下整个文件
file_size = st.st_size + 1;
if ((result = fast_buffer_check(buffer, file_size)) != 0) {
return result;
}
// 在另外一个文件里面,是一个简单文件读取的函数
if ((result = getFileContentEx(filename, buffer->data + buffer->length,
0, &file_size)) != 0) {
return result;
}
buffer->length += file_size;
return 0;
}