C语言循环缓冲区设计


前言

在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语言循环缓冲区设计是一种高效的数据结构,适用于需要处理大量数据的场景。通过合理地管理读写指针和循环利用缓冲区空间,可以实现高吞吐量和低延迟的数据处理。在实际应用中,需要根据具体的需求选择合适的循环缓冲区实现方法,并注意处理好数据的读写操作和状态维护。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

葛霸霸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值