一 什么是ringbuff
ringbuff是环形buff,是buff缓存的一种,可以用来存放程序运行过程的暂时数据。一般的buff即是一个数组,而环形buff就是将数组的头和尾链接起来的数组。
老规矩,源码先放这:
码云:https://gitee.com/killerp/mylib.git
1,ringbuff的优点
ringbuff比普通的buff使用更加高效灵活,可以边读数据边写数据。
二 ringbuff的实现
一个ringbuff的实现至少需要以下四个功能:
- 创建ringbuff
- 数据写入ringbuff
- 从ringbuff读取数据
- 删除ringbuff
0,ringbuff结构体
typedef struct RingBuff *RingBuff_handle;
//ringbuff结构体
struct RingBuff
{
unsigned char *source; //ringbuff的头部
unsigned char *tail; //尾部
unsigned char *write; //写指针
unsigned char *read; //读指针
int size; //ringbuff总长度
int writed_size; //写入数据的长度
int remain_size; //剩余空间
};
如下图所示,一般一个ringbuff创建后,size,source和tail指针是固定的。当write移动到tail时,如果有数据需要写入,write会移动到source处继续写入,但是write不会覆盖掉未被read指针读取的数据。当read移动到tail时同样,如果需要继续读取数据,会移动到source继续读取。注意!write移动的长度不能超过remain_size(剩余空间);read移动的长度部能超过write_size(已写入长度);基本的工作原理就是这样。
1,ringbuff的创建
ringbuff的创建实际上是调用malloc();实现动态内存分配,内存的大小由用户输入参数决定,分配的内存的地址指针会赋值给source,同时计算tail的指针,第一次创建也会初始化ringbuff的其他成员。一般的size最好比一次读写的长度大几倍。
/*
* 创建一个ringbuff 用户定义字节大小
*/
RingBuff_handle createRingBuff(int totalsize)
{
RingBuff_handle ringbuff;
ringbuff = malloc(sizeof(struct RingBuff));
ringbuff->source = malloc(totalsize);
if(ringbuff->source !=NULL)
{
LOGI("create ringbuff success!");
}
ringbuff->write = ringbuff->source;
ringbuff->read = ringbuff->source;
ringbuff->writed_size = 0;
ringbuff->tail = ringbuff->source + totalsize;
ringbuff->size = totalsize;
ringbuff->remain_size = totalsize;
return ringbuff;
}
2,计算已写入数据的长度
计算writed_size的大小非常关键。这里先给出伪代码计算公式:
writed_size = (size +(write - read)%size
理解它需要先了解ringbuff的读写指针的两种位置关系:
- 写指针在读指针前面
- 读指针在写指针前面
代码实现:
/*
* 获得已写入数据的字节
*/
int getRingBuffWritedSize(RingBuff_handle handle)
{
handle->writed_size = (handle->size + (handle->write - handle->read)) % handle->size;
LOGI("writed_size = %d",handle->writed_size);
return handle->writed_size;
}
3,数据写入ringbuff
在读写数据的处理上都需要分以上两种情况处理。
/*
* 写入ringbuff handle 数据 数据字节长度
*/
int writeRingBuff(RingBuff_handle handle,unsigned char *data,int size)
{
if(handle->source == NULL ||handle == NULL)
{
LOGE("ringbuff has not been init");
return 0;
}
//检查剩余内存是否足够
if(handle->remain_size > size)
{
//写指针在读指针前
if(handle->write - handle->read >0)
{
int writebytes = handle->tail - handle->write - size;
//写入的数据小于写指针到内存尾部
if(writebytes >= 0)
{
memcpy(handle->write,data,size);
handle->write += size;
}
else //写入的数据大于写指针到内存尾部,需要从头部继续写入
{
//写入数据到尾部
memcpy(handle->write,data,handle->tail - handle->write);
data += handle->tail - handle->write;
writebytes = abs(writebytes);
//从头部继续写入剩余数据
memcpy(handle->source,data,writebytes);
handle->write = handle->source + writebytes;
}
}
else //读指针在写指针前
{
memcpy(handle->write,data,size);
handle->write += size;
}
LOGI("write success");
//更新参数
getRingBuffRemainSize(handle);
return 1;
}
else
{
LOGE("write fail");
return 0;
}
}
4,读取ringbuff数据
读取数据也要区别指针的位置情况。读取与写入逻辑上差别不大。
/*
* 读取ringbuff数据
*/
int readRingBuff(RingBuff_handle handle,unsigned char *data,int size)
{
if(handle->source == NULL ||handle == NULL)
{
LOGE("ringbuff has not been init");
return 0;
}
if(data == NULL)
{
malloc(sizeof(unsigned char));
}
//要读取的数据少于已经写入的数据
if(handle->writed_size >= size)
{
//写指针在读指针前
if(handle->write - handle->read >0)
{
memcpy(data,handle->read,size);
handle->read += size;
}
else //读指针在写指针前
{
int readbytes = handle->tail - handle->read - size;
//读取,不用从头读
if(readbytes > 0)
{
memcpy(data,handle->read,size);
handle->read += size;
}
else //需要从头读
{
memcpy(data,handle->read,handle->tail - handle->read);
readbytes = abs(readbytes);
data += handle->tail - handle->read;
memcpy(data,handle->source,readbytes);
handle->read = handle->source + readbytes;
}
}
LOGI("read success");
//更新参数
getRingBuffRemainSize(handle);
return 1;
}
else
{
LOGE("read fail");
return 0;
}
}
5,删除ringbuff
删除函数其实是调用free();函数释放由malloc();分配的内存。需要检查指针是否空。
/*
* 删除ringbuff
*/
int deleteRingBuff(RingBuff_handle handle)
{
//需要判断指针是否空,否则free()会出错
if(handle == NULL ||handle->source == NULL)
{
LOGE("free fail");
return 0;
}
free(handle->source);
free(handle);
LOGI("free ringbuff success");
return 1;
}