-
简单图解
缓冲区内部:容量为 capacity 的 内存块 可以看作 长度为 capacity 的 字节数组
读/写指针的变化:版本二中的读写操作函数使用 memcpy 函数复制内存,当写指针在读指针右边时,读取操作最多调用 1 次 memcpy 函数,而写入操作最多调用 2 次 memcpy 函数。当写指针在读指针左边时,读取操作最多调用两次 memcpy 函数,而写入操作最多调用 1 次 memcpy 函数。
-
(读写操作,版本一:函数内部 每次读/写一个字节,操作大量数据时效率很低)
利用 取余运算 使得读/写指针在长度为 capacity 的连续内存空间上循环进行读/写操作 -
(读写操作,版本二:使用 memcpy 函数复制内存 ,效率高)
-
(读写操作,版本三:线程安全版本,可以使用互斥量并委托使用版本二的读写函数作简单实现)
目录
- 头文件 buffer.h
- 源文件 buffer.c
- 简单测试 test.c
1. 头文件 buffer.h
/* buffer.h */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
struct buffer {
/* 缓冲区容量,用于 malloc 申请内存空间 */
size_t capacity;
/* 累计所有写入操作实际写入的字节总数,其对 capacity 取余即为‘下一个写入位置’ */
size_t byte_writed_count;
/* 累计所有读取操作实际读取的字节总数,其对 capacity 取余即为‘下一个读取位置’ */
size_t byte_readed_count;
/* 指向实际缓冲区 */
char *data;
};
/* 初始化缓冲区 buf,使用指定容量 capacity 申请内存空间 */
void buffer_init(struct buffer *buf, size_t capacity);
/* 版本一:读写函数,内部每次读写一个字节,
将指针 chars 指向的长度为 len 的数据写入缓冲区 buf,
返回值:实际写入的字节数,可能小于 len,当缓冲区可写空间不足时。 */
size_t buffer_write(struct buffer *buf, char *chars, size_t len);
/* 从缓冲区 buf 中读取长度为 len 的数据到指针 chars 指向的空间,
返回值:实际读取的字节数,可能小于 len,当缓冲区没有足够的数据时。 */
size_t buffer_read(struct buffer *buf, char *chars, size_t len);
/* 版本二:读/写函数,内部使用 memcpy 函数进行内存复制,效率高 */
size_t buffer_write_by_memcpy(struct buffer *buf, char *chars, size_t len);
size_t buffer_read_by_memcpy(struct buffer *buf, char *chars, size_t len);
/* (待实现)版本三:线程安全版本
(待实现)线程安全写,应与线程安全读 buffer_pread 配合使用 */
// size_t buffer_pwrite(struct buffer *buf, char *chars, size_t len);
/* (待实现)线程安全读,应与线程安全写 buffer_pwrite 配合使用 */
// size_t buffer_pread(struct buffer *buf, char *chars, size_t len);
/* 返回值:缓冲区中已缓存的(可读取)字节数 */
size_t buffer_available_read(struct buffer *buf);
/* 返回值:缓冲区可写入的字节数 */
size_t buffer_available_write(struct buffer *buf);
/* 返回值:缓冲区容量 capacity */
size_t buffer_capacity(struct buffer *buf);
/* 清空缓冲区,实际上仅仅重置读/写指针,缓冲区容量不变 */
void buffer_clear(struct buffer *buf);
/* 销毁/释放缓冲区,free 内存资源 */
void buffer_destroy(struct buffer *buf);
/* 简单错误日志输出至 stderr */
void errlog(const char *msg);
/* 简单日志输出至 stdout */
void infolog(const char *msg);
2. 源文件 buffer.c
/* buffer.c */
#include "buffer.h"
// 初始化缓冲区 buf,使用指定容量 capacity 申请内存空间
void buffer_init(struct buffer *buf, size_t capacity){
if(buf == NULL || capacity <= 0){
errlog("Invalid argument.");
// error
errno = EINVAL; // // Invalid argument
exit(EXIT_FAILURE);
}
// 实际分配内存
buf -> data = malloc(capacity);
if(buf -> data == NULL){
errlog("Cannot allocate memory: unknown error.");
// error
errno = ENOMEM;
exit(EXIT_FAILURE);
}
buf -> capacity = capacity;
buf -> byte_readed_count = 0;
buf -> byte_writed_count = 0;
}
// 版本一,内部每次读/写一个字节,效率低
// 将指针 chars 指向的长度为 len 的数据写入缓冲区 buf,
// 返回值:实际写入的字节数,可能小于 len,当缓冲区可写空间不足时。
size_t buffer_write(struct buffer *buf, char *chars, size_t len){
if(buffer_available_write(buf) == 0){
return 0;
}
if(buf != NULL && chars != NULL && len > 0){
int i = 0;
for(;i < len && buffer_available_write(buf) > 0;i++, buf -> byte_writed_count ++){
// 每次写入一个字节
(buf -> data)[buf -> byte_writed_count % buf -> capacity] = *(chars + i);
}
return i;
}else{
errlog("Invalid argument");
// error
errno = EINVAL;
exit(EXIT_FAILURE);
}
}
// 版本一,内部每次读/写一个字节,效率低
// 从缓冲区 buf 中读取长度为 len 的数据到指针 chars 指向的空间,
// 返回值:实际读取的字节数,可能小于 len,当缓冲区没有足够的数据时。
size_t buffer_read(struct buffer *buf, char *chars, size_t len){
if(buffer_available_read(buf) == 0){
return 0;
}
if(buf != NULL && chars != NULL && len > 0){
int i = 0;
for(;i < len && buffer_available_read(buf) > 0;i++, buf -> byte_readed_count ++){
// 每次读取一个字节
*(chars + i) = (buf -> data)[buf -> byte_readed_count % buf -> capacity];
}
return i;
}else{
errlog("Invalid argument");
// error
errno = EINVAL;
exit(EXIT_FAILURE);
}
}
// 版本二,使用 memcpy 函数复制内存,效率高
size_t buffer_write_by_memcpy(struct buffer *buf, char *chars, size_t len){
if(buffer_available_write(buf) == 0){
return 0;
}
if(buf != NULL && chars != NULL && len > 0){
//
int avail_write_count = buffer_available_write(buf);
int next_write_index = (buf -> byte_writed_count) % (buf -> capacity);
int first_count = (buf -> capacity) - next_write_index;
if(avail_write_count <= first_count){
// ‘可写区域’分布在写指针右侧,即:memcpy 函数最多可执行 1 次
int write_count;
if(avail_write_count <= len){
write_count = avail_write_count;
}else{
write_count = len;
}
memcpy(&(buf -> data)[next_write_index], chars, write_count);
buf -> byte_writed_count += write_count;
return write_count;
}else{
// avail_write_count > first_count
// ‘可写区域’分布在写指针两侧,即:memcpy 函数最多可执行 2 次
if(len <= first_count){
memcpy(&(buf -> data)[next_write_index], chars, len);
buf -> byte_writed_count += len;
return len;
}else{
// first write
memcpy(&(buf->data)[next_write_index], chars, first_count);
buf->byte_writed_count += first_count;
int write_count;
if (avail_write_count >= len){
write_count = len - first_count;
}else{
// avail_write_count < len
write_count = avail_write_count - first_count;
}
// second write
memcpy(&(buf->data)[0], &chars[first_count], write_count);
buf->byte_writed_count += write_count;
return first_count + write_count;
}
}
}else{
errlog("Invalid argument");
// error
errno = EINVAL;
exit(EXIT_FAILURE);
}
}
// 版本二,使用 memcpy 函数复制内存,效率高
size_t buffer_read_by_memcpy(struct buffer *buf, char *chars, size_t len){
if(buffer_available_read(buf) == 0){
return 0;
}
if(buf != NULL && chars != NULL && len > 0){
//
int avail_read_count = buffer_available_read(buf);
int next_read_index = (buf -> byte_readed_count) % (buf -> capacity);
int first_count = (buf -> capacity) - next_read_index;
if(avail_read_count <= first_count){
// ‘可读区域’分布在读指针右侧,即:memcpy 函数最多可执行 1 次
int read_count;
if(avail_read_count <= len){
read_count = avail_read_count;
}else{
read_count = len;
}
memcpy(chars, &(buf -> data)[next_read_index], read_count);
buf -> byte_readed_count += read_count;
return read_count;
}else{
// avail_read_count > first_count
// ‘可读区域’分布在读指针两侧,即:memcpy 函数最多可执行 2 次
if(len <= first_count){
memcpy(chars, &(buf -> data)[next_read_index], len);
buf -> byte_readed_count += len;
return len;
}else{
// first write
memcpy(chars, &(buf -> data)[next_read_index], first_count);
buf->byte_readed_count += first_count;
int read_count;
if (avail_read_count >= len){
read_count = len - first_count;
}else{
// avail_read_count < len
read_count = avail_read_count - first_count;
}
// second write
memcpy(&chars[first_count], &(buf->data)[0], read_count);
buf->byte_readed_count += read_count;
return first_count + read_count;
}
}
}else{
errlog("Invalid argument");
// error
errno = EINVAL;
exit(EXIT_FAILURE);
}
}
// 返回值:缓冲区中已缓存的(可读取)字节数
size_t buffer_available_read(struct buffer *buf){
if(buf != NULL){
return (buf -> byte_writed_count) - (buf -> byte_readed_count);
}else{
return 0;
}
}
// 返回值:缓冲区可写入的字节数
size_t buffer_available_write(struct buffer *buf){
if(buf != NULL){
return (buf -> capacity) - ((buf -> byte_writed_count) - (buf -> byte_readed_count));
}else{
return 0;
}
}
// 返回值:缓冲区容量 capacity
size_t buffer_capacity(struct buffer *buf){
if(buf != NULL){
return buf -> capacity;
}else{
return 0;
}
}
// 清空缓冲区,实际上仅仅重置读/写指针,缓冲区容量不变
void buffer_clear(struct buffer *buf){
if(buf != NULL){
buf -> byte_readed_count = 0;
buf -> byte_writed_count = 0;
}
}
// 销毁/释放缓冲区,free 内存资源
void buffer_destroy(struct buffer *buf){
if(buf != NULL){
buf -> capacity = 0;
free(buf -> data);
}
}
// 简单错误日志输出
void errlog(const char *msg){
fprintf(stderr, "error: %s\n", msg);
}
// 简单日志输出
void infolog(const char *msg){
fprintf(stdout, "info: %s\n", msg);
}
3. 简单测试
/* test.c */
#include "buffer.h"
/* 简单测试 */
void main(){
// 1
// test func: errlog
// errlog("invalid arguments");
// infolog("this is hello");
// 2
// test func buffer_init
struct buffer buf;
char str[1024];
long int res;
buffer_init(&buf, 10);
printf("buffer capacity is %ld bytes\n", buf.capacity);
printf("writed %ld\n", buffer_write(&buf, "abc", 3));
printf("1 - buffer_available_read is %ld bytes\n", buffer_available_read(&buf));
printf("1 - buffer_available_write is %ld bytes\n", buffer_available_write(&buf));
res = buffer_read(&buf, str, res = buffer_available_read(&buf));
str[res] = '\0';
printf("readed str: %s\n", str);
printf("2 - buffer_available_read is %ld bytes\n", buffer_available_read(&buf));
printf("2 - buffer_available_write is %ld bytes\n", buffer_available_write(&buf));
//
printf("writed %ld\n", buffer_write(&buf, "abc123456789xyz", 10));
printf("writed %ld\n", buffer_write(&buf, "abc123456789xyz", 10));
printf("3 - buffer_available_read is %ld bytes\n", buffer_available_read(&buf));
printf("3 - buffer_available_write is %ld bytes\n", buffer_available_write(&buf));
res = buffer_read(&buf, str, res = buffer_available_read(&buf));
str[res] = '\0';
printf("readed str: %s\n", str);
printf("4 - buffer_available_read is %ld bytes\n", buffer_available_read(&buf));
printf("4 - buffer_available_write is %ld bytes\n", buffer_available_write(&buf));
buffer_clear(&buf);
buffer_destroy(&buf);
}
运行测试,执行结果如下:
ubuntu@cuname:~/dev/beginning-linux-programming/test$ gcc -o test test.c buffer.c
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test
buffer capacity is 10 bytes
writed 3
1 - buffer_available_read is 3 bytes
1 - buffer_available_write is 7 bytes
readed str: abc
2 - buffer_available_read is 0 bytes
2 - buffer_available_write is 10 bytes
writed 10
writed 0
3 - buffer_available_read is 10 bytes
3 - buffer_available_write is 0 bytes
readed str: abc1234567
4 - buffer_available_read is 0 bytes
4 - buffer_available_write is 10 bytes