文章目录
前言
在C语言中,我们经常需要处理数据的缓冲区。循环缓冲区是一种特殊类型的缓冲区,它允许我们在固定大小的缓冲区中持续地读取和写入数据,就像在一个环形轨道上一样。当数据到达缓冲区的末尾时,它会从缓冲区的开始处继续写入。这种设计非常高效地使用了内存空间。
在本文中,我们将讨论如何设计一个C语言的循环缓冲区。我们将创建一个数据结构来保存缓冲区的状态,并定义几个函数来读取、写入和清除缓冲区。。
一、缓冲区信息结构体定义
首先,我们需要定义一个结构体来保存缓冲区的信息。这个结构体将包含以下字段:
- head:指向缓冲区中第一个有效数据的指针。
- tail:指向缓冲区中最后一个有效数据的指针。
- validstart:指向缓冲区中有效数据开始位置的指针。
- validend:指向缓冲区中有效数据结束位置的指针。
- buffersize:缓冲区的大小。
- validsize:当前缓冲区中有效数据的长度。
代码如下:
typedef struct ring_buffer_info_t{
unsigned char *head;
unsigned char *tail;
unsigned char *validstart;
unsigned char *validend;
unsigned int buffersize;
unsigned int validsize;
}ring_buffer_info;
二、缓冲区结构体定义
接下来,我们需要定义一个结构体来表示缓冲区本身。这个结构体包括以下几个成员变量:
- info:上面定义的缓冲区信息结构体。
- Write:用于向缓冲区写入数据的函数指针。
- Read:用于从缓冲区读取数据的函数指针。
- Clear:用于清空缓冲区的函数指针。
- Free:用于释放缓冲区内存的函数指针。
代码如下:
//缓冲区
typedef struct ring_buffer_t{
ring_buffer_info info;
int (*Write)(struct ring_buffer_t *ptdev,unsigned char *buf,unsigned int length);
int (*Read)(struct ring_buffer_t *ptdev,unsigned char *buf,unsigned int length);
int (*Clear)(struct ring_buffer_t *ptdev);
int (*Free)(struct ring_buffer_t *ptdev);
}ring_buffer;
三、函数定义
最后,我们需要实现上面定义的函数指针。这些函数将负责在缓冲区中写入、读取和清除数据。下面是一些基本的函数定义:
- Write 函数:将数据写入缓冲区。如果成功,返回0;否则返回-1。
- Read 函数:从缓冲区中读取数据。如果成功,返回0;否则返回-1。
- Clear 函数:清除缓冲区中的所有数据。如果成功,返回0;否则返回-1。
- Free 函数:释放缓冲区的内存空间。如果成功,返回0;否则返回-1。
这些函数的实现将在后面给出。首先,我们需要创建一个新的循环缓冲区。下面是一个创建新循环缓冲区的函数:
1.RingBufferNew 函数
这个函数将创建一个新的循环缓冲区,并初始化它的信息结构体。它接受一个参数,表示缓冲区的长度。如果成功,它将返回一个指向新缓冲区的指针;否则,它将返回NULL。
ring_buffer *RingBufferNew(unsigned int length)
{
if( (length < 0) || (length > 1024) ) //最多开辟1024字节空间
{
return NULL;
}
ring_buffer *newbuffer = NULL;
newbuffer = (ring_buffer *)malloc(sizeof(ring_buffer)); //申请一个环形缓冲区数据结构
if(NULL == newbuffer) //没有足够的内存申请 失败
{
return NULL;
}
newbuffer->info.head = (unsigned char *)malloc(sizeof(unsigned char) * length); //为环形缓冲区申请数据存储部分
if(NULL == newbuffer->info.head) //没有足够的内存申请 失败
{
free(newbuffer);
return NULL;
}
//都申请成功后 对缓冲区信息进行赋值
newbuffer->Write = RingBufferWrite;
newbuffer->Read = RingBufferRead;
newbuffer->Clear = RingBufferClear;
newbuffer->Free = RingBufferFree;
newbuffer->info.tail = newbuffer->info.head + length;
newbuffer->info.validstart = newbuffer->info.head;
newbuffer->info.validend = newbuffer->info.head;
newbuffer->info.buffersize = length;
newbuffer->info.validsize = 0;
return newbuffer;
}
2.RingBufferWrite函数
这个函数用于将数据从源缓冲区src写入到RingBuff类型的缓冲区ptBuff中。如果成功,返回0;否则返回负值。
参数
- ptBuff:指向RingBuff类型的缓冲区的指针,用于存储写入的数据。
- buf:指向源缓冲区的指针,该缓冲区包含要写入的数据。
- length:表示要写入的数据的长度。
函数功能
首先,该函数会对传入的参数进行检查,如果出错返回负值。
其次,该函数会将源缓冲区buf中的数据复制到有效数据后面。如果要存储的数据超过缓冲区剩余空间,则覆盖前面的旧数据。
最后,更新有效数据的大小,并返回0表示写入成功。
static int RingBufferWrite(struct ring_buffer_t *ptdev,unsigned char *buf,unsigned int length)
{
//参数检查
if(NULL == ptdev)
{
return -EINVAL;
}
if(NULL == buf)
{
return -EINVAL;
}
if( (length < 0) || (length > 1024) )
{
return -EINVAL;
}
unsigned int len = length + ptdev->info.validsize;
unsigned int len1 = 0,len2 = 0;
len1 = (unsigned int)(ptdev->info.tail - ptdev->info.validend);
if(len > ptdev->info.buffersize) //超过缓冲区 新数据覆盖旧数据
{
if(length > len1) //要写的数据超过缓冲区尾部
{
len2 = length - len1;
memcpy(ptdev->info.validend,buf,len1);
memcpy(ptdev->info.head,buf+len1,len2);
ptdev->info.validend = ptdev->info.head + len2;
}else
{
memcpy(ptdev->info.validend,buf,len1);
ptdev->info.validend = ptdev->info.validend + len1;
}
ptdev->info.validsize = ptdev->info.buffersize;
ptdev->info.validstart = ptdev->info.validend;
}else
{
if(length > len1)
{
len2 = length - len1;
memcpy(ptdev->info.validend,buf,len1);
memcpy(ptdev->info.head,buf+len1,len2);
ptdev->info.validend = ptdev->info.head + len2;
}else
{
memcpy(ptdev->info.validend,buf,length);
ptdev->info.validend = ptdev->info.validend + length;
}
ptdev->info.validsize = ptdev->info.validsize + length;
}
return ESUCCESS;
}
2.RingBufferWrite函数
这个函数用于从RingBuff类型的缓冲区ptBuff中读取数据到目标缓冲区buf中。如果成功,返回0;否则返回负值。
参数
- ptBuff:指向RingBuff类型的缓冲区的指针,用于存储读出的数据。
- buf:指向目的缓冲区的指针,该缓冲区存储要读出的数据。
- length:表示要读出的数据的长度。
函数功能
首先,该函数会检查ptBuff中的有效数据大小和要读取的数据长度,以确保有足够的数据可供读取。如果缓冲区中没有数据可读,函数将返回负值。
其次,该函数会将ptBuff中的有效数据复制到目标缓冲区dst中。
最后,更新有效数据的大小,并返回0表示读取成功。
static int RingBufferRead(struct ring_buffer_t *ptdev,unsigned char *buf,unsigned int length)
{
//参数检查
if(NULL == ptdev)
{
return -EINVAL;
}
if(NULL == buf)
{
return -EINVAL;
}
if( (length < 0) || (length > 1024) )
{
return -EINVAL;
}
if(ptdev->info.validsize == 0) //缓冲区没有数据直接返回错误
{
return -EINVAL;
}
if(length > ptdev->info.validsize) //需要读的数据超过有效数据 只读有效个数据
length = ptdev->info.validsize;
unsigned int len1 = 0,len2 = 0;
len1 = (unsigned int)(ptdev->info.tail - ptdev->info.validstart);
if(length > len1) //要读的数据超过缓冲区尾部
{
len2 = length - len1;
memcpy(buf,ptdev->info.validstart,len1);
memcpy(buf+len1,ptdev->info.head,len2);
ptdev->info.validstart = ptdev->info.head + len2;
}else
{
memcpy(buf,ptdev->info.validstart,length);
ptdev->info.validstart = ptdev->info.validstart + length;
}
ptdev->info.validsize = ptdev->info.validsize - length;
return ESUCCESS;
}
3.RingBufferClear函数
这个函数用于清除RingBuff类型的缓冲区ptBuff中的所有数据。如果成功,返回0;否则返回负值。
参数
- ptBuff:指向RingBuff类型的缓冲区的指针。
函数功能
该函数会重置有效数据的起始位置和结束位置,并将有效数据的大小设置为0,以清除缓冲区中的所有数据。
最后,返回0表示清除成功。
static int RingBufferClear(struct ring_buffer_t *ptdev)
{
//参数检查
if(NULL == ptdev)
{
return -EINVAL;
}
memset(ptdev->info.head,0,ptdev->info.buffersize);
ptdev->info.validstart = ptdev->info.head;
ptdev->info.validend = ptdev->info.head;
ptdev->info.validsize = 0;
return ESUCCESS;
}
4.RingBufferFree函数
这个函数用于释放RingBuff类型的缓冲区ptBuff的内存空间。如果成功,返回0;否则返回负值。
参数
- ptBuff:指向RingBuff类型的缓冲区的指针。
函数功能
该函数会释放分配给缓冲区的内存空间,包括头部指针、尾部指针、有效数据的起始位置、有效数据的结束位置以及有效数据的大小等。
static int RingBufferFree(struct ring_buffer_t *ptdev)
{
//参数检查
if(NULL == ptdev)
{
return -EINVAL;
}
memset(ptdev->info.head,0,ptdev->info.buffersize);
free(ptdev->info.head);
ptdev->info.validstart = NULL;
ptdev->info.validend = NULL;
ptdev->info.buffersize = 0;
ptdev->info.validsize = 0;
ptdev->info.tail = NULL;
ptdev->info.head = NULL;
memset(ptdev,0,sizeof(ring_buffer));
free(ptdev);
ptdev->Write = NULL;
ptdev->Read = NULL;
ptdev->Clear = NULL;
ptdev->Free = NULL;
return ESUCCESS;
}
总结
循环缓冲区适用于需要高效处理大量数据的场景,如网络通信、音频处理、图像处理等。通过使用循环缓冲区,可以避免频繁的内存分配和释放操作,提高数据处理的速度和效率。同时,循环缓冲区还可以实现数据的零拷贝传输,减少CPU的干预,提高系统的吞吐量和响应速度。
总之,C语言循环缓冲区设计是一种高效的数据结构,适用于需要处理大量数据的场景。通过合理地管理读写指针和循环利用缓冲区空间,可以实现高吞吐量和低延迟的数据处理。在实际应用中,需要根据具体的需求选择合适的循环缓冲区实现方法,并注意处理好数据的读写操作和状态维护。