Fastdfs源碼分析4----緩存區設計

說到緩存區,是一個可複製可簡單的問題。有的緩存區,自帶文件持久化,日誌,多線程和線程重入 ,智能擴容/縮容。
緩衝區在下讀過linux kernel、muduo、llibevent的設計。這三種是比較專業一些緩存區。
剝離外在,緩存區的主要用於有兩方面:作爲消息載體,在內存進行消息的緩存和傳遞;利用內存外存的速度差距,作爲提高磁盤io性能的一個組件。
餘慶寫的代碼有一種風格:樸實無華,輕抽象、重組合。 Fastdfs的緩衝區設計上面沒有前面三種那麼花哨。但是也是具備了緩衝區最重要的兩個功能。

萬物之始,大道至簡,衍化至繁。作爲程序員來說,你有多久沒有返璞歸真了,整天一開口就要故意或者被動把各種簡單的東西,往複雜化的方向去搞。
Tangshuncai\color{#222514}{-}\color{#222514}{-}\color{#222514}{-}\color{#222514}{-}\color{#4285f4}{T}\color{#ea4335}{a}\color{#fbbc05}{n}\color{#4285f4}{g}\color{#34a853}{s}\color{#ea4335}{h}\color{#4285f4}{u}\color{#fbbc05}{n}\color{#34a853}{c}\color{#ea4335}{a}\color{#fbbc05}{i}

按照我們的傳統,直接上源碼,自己拉下去讀。(已經經過詳細的註釋)

#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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章