C/C++实现环形缓冲

在开发过程中经常会遇到需要使用环形缓冲的地方,比如在流媒体方面,对于接收到的音视频数据的存储、以及音频解码后PCM数据的存储等问题上,最好使用一个环形缓冲,接收到,或者解码后压入该缓冲区中,在需要解码,或者需要塞入声卡时,再从该缓冲区中读取,这样压入和取出同一个缓冲区,既方便快捷,又安全有效。源码如下:

typedef char s8;
typedef unsigned char u8;
typedef short s16;
typedef unsigned short u16;
typedef int s32;
typedef unsigned int u32;
typedef unsigned   uint32_t;

typedef void *FIFOBUFFERHANDLE;    //定义一个指针,方便外部引用

#ifndef _max
#define _max(a, b) a > b ? a : b
#endif

#ifndef _min
#define _min(a, b) a > b ? b : a
#endif

//定义一个结构体,用于记录该缓冲区的开始、读取、写入、接收的内存地址
typedef struct _TFIFOBUFFER
{
    u8 *pu8Buffer, *pu8Read, *pu8Write, *pu8End;
}TFIFOBUFFER, *PTFIFOBUFFER;

//创建一个指定大小的缓冲区
FIFOBUFFERHANDLE FifoBufferCreate(s32 s32Size)
{
    PTFIFOBUFFER ptFifoBuffer = NULL;
    do
    {
        ptFifoBuffer = (PTFIFOBUFFER)malloc(sizeof(TFIFOBUFFER));
        if(NULL == ptFifoBuffer)
        {
            break;
        }
        memset(ptFifoBuffer, 0, sizeof(TFIFOBUFFER));
        ptFifoBuffer->pu8Buffer = (u8 *)malloc(s32Size);
        if(NULL == ptFifoBuffer->pu8Buffer)
        {
            break;
        }
        memset(ptFifoBuffer->pu8Buffer, 0, s32Size);
        ptFifoBuffer->pu8Write = ptFifoBuffer->pu8Read = ptFifoBuffer->pu8Buffer;
        ptFifoBuffer->pu8End = ptFifoBuffer->pu8Buffer + s32Size;
        if(NULL == ptFifoBuffer->pu8Buffer)
        {
            break;
        }
        return (FIFOBUFFERHANDLE)ptFifoBuffer;
    }while(false);
    if(NULL != ptFifoBuffer)
    {
        if(NULL != ptFifoBuffer->pu8Buffer)
        {
            free(ptFifoBuffer->pu8Buffer);
            ptFifoBuffer->pu8Buffer = NULL;
        }
        free(ptFifoBuffer);
        ptFifoBuffer = NULL;
    }
    return NULL;
}

//释放一个缓冲区
void FifoBufferDestroy(FIFOBUFFERHANDLE pHandle)
{
    PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle;
    if(NULL != ptFifoBuffer)
    {
        if(NULL != ptFifoBuffer->pu8Buffer)
        {
            free(ptFifoBuffer->pu8Buffer);
            ptFifoBuffer->pu8Buffer = NULL;
        }
        free(ptFifoBuffer);
        ptFifoBuffer = NULL;
    }
}

//重置缓冲区中读写指针
void FifoBufferReset(FIFOBUFFERHANDLE pHandle)
{
    PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle;
    if(NULL != ptFifoBuffer)
    {
        ptFifoBuffer->pu8Write = ptFifoBuffer->pu8Read = ptFifoBuffer->pu8Buffer;
    }
}

//获取大小
int FifoBufferSize(FIFOBUFFERHANDLE pHandle)
{
    s32 s32Size = 0;
    PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle;
    if(NULL != ptFifoBuffer)
    {
        s32Size = ptFifoBuffer->pu8Write - ptFifoBuffer->pu8Read;
        if(s32Size < 0)
        {
            s32Size += ptFifoBuffer->pu8End - ptFifoBuffer->pu8Buffer;
        }
    }
    return s32Size;
}

//向缓冲区中写入数据
s32 FifoBufferWrite(FIFOBUFFERHANDLE pHandle, u8 *pu8Buffer, s32 s32Size)
{
    s32 s32Length = 0;
    PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle;
    if(NULL == ptFifoBuffer)
    {
        return 0;
    }
    do
    {
        s32Length = _min(ptFifoBuffer->pu8End - ptFifoBuffer->pu8Write, s32Size);
        memcpy(ptFifoBuffer->pu8Write, pu8Buffer, s32Length);
        pu8Buffer = pu8Buffer + s32Length;
        ptFifoBuffer->pu8Write += s32Length;  //向后偏移写指针
        
        //如果缓冲区写指针到达了缓冲区尾部,则将写指针移动到缓冲区开始地址,实现真正的环         //形缓冲
        if(ptFifoBuffer->pu8Write >= ptFifoBuffer->pu8End)
        {
            ptFifoBuffer->pu8Write = ptFifoBuffer->pu8Buffer;
        }
        s32Size -= s32Length;
    }while(s32Size > 0);
    return 1;
}

//读数据
s32 FifoBufferRead(FIFOBUFFERHANDLE pHandle, u8 *pu8Buffer, s32 *ps32Size)
{
    s32 s32Length = 0, pTempSize = (*ps32Size);
    PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle;
    if(NULL == ptFifoBuffer || NULL == pu8Buffer || 0 > pTempSize)
    {
        return 0;
    }
    if(0 == pTempSize)
    {
        (*ps32Size) = 0;
        return 1;
    }
    (*ps32Size) = 0;
    do
    {
        s32Length = _min(FifoBufferSize(pHandle), pTempSize);
        s32Length = _min(ptFifoBuffer->pu8End - ptFifoBuffer->pu8Read, s32Length);
        if(0 == s32Length)
        {
            break;
        }
        memcpy(pu8Buffer, ptFifoBuffer->pu8Read, s32Length);
        pu8Buffer = pu8Buffer + s32Length;
        ptFifoBuffer->pu8Read = ptFifoBuffer->pu8Read + s32Length;//向后偏移读指针
        
        //如果缓冲区读指针到达了缓冲区尾部,则将读指针移动到缓冲区开始地址,实现真正的环         //形缓冲
        if(ptFifoBuffer->pu8Read >= ptFifoBuffer->pu8End)
        {
            ptFifoBuffer->pu8Read = ptFifoBuffer->pu8Buffer;
        }
        pTempSize -= s32Length;
        (*ps32Size) += s32Length;
    }while(pTempSize > 0);
    return 1;
}

s32 FifoBufferShade(FIFOBUFFERHANDLE pHandle, s32 s32Offset)
{
    PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle;
    if(NULL != ptFifoBuffer)
    {
        if((ptFifoBuffer->pu8Read + s32Offset) > ptFifoBuffer->pu8End)
        {
            return *(ptFifoBuffer->pu8Buffer + (s32Offset - (ptFifoBuffer->pu8End - ptFifoBuffer->pu8Read)));
        }
        else
        {
            return *(ptFifoBuffer->pu8Read + s32Offset);
        }
    }
    return 0;
}

使用该缓冲区时,初始化一块大小合适的内存,并在外部进行加锁,同时需要读写操作保持基本一致,即可;如果用户设置的缓冲区过小,或者读写速率差距较大,则会造成用户数据的丢失。

以上代码使用纯C写的,当然也可以使用C++进行编写,写一个类,借助string类能开发出一套更简洁的缓冲缓冲代码,只要注意逻辑实现不出问题即可。

时间: 2024-09-28 22:55:44

C/C++实现环形缓冲的相关文章

四轴飞行器1.7 NRF24L01P无线通讯和改进型环形缓冲

原创文章,欢迎转载,转载请注明出处 这次花了10多天了才再次写blog,一是中秋优点小活动,二是这次完成了不少东西.. 终于接近完成了,这次完成了NRF的通讯,并且用了改进的环形缓冲和简单的通讯协议规划 看着做的东西挺少,实际工作量不小..哈..我们先要在遥控上写个简易的UI,显示一些数据,然后读取手柄数据,通过NRF传给飞控,看看数据是否正确传输,而且还要解决数据传输中的各种问题,虽然以前用过NRF905,但是NRF24L01是没有用过的,不过大同小异. 1:NRF24L01P模块的使用 2:

SQL Server 环形缓冲区(Ring Buffer) -- 环形缓冲在AlwaysOn的应用

SQL Server 环形缓冲区(Ring Buffer) -- 环形缓冲在AlwaysOn的应用 可以从SQL Server环形缓冲区得到一些诊断AlwaysOn的信息,或从sys.dm_os_ring_buffers动态管理视图.环形缓冲在SQL Server启动的时候创建,在SQL Server系统内记录告警用于内部诊断.它们不被支持,但你仍能从中获取有用的信息.下面的查询能从AlwaysON环形缓冲获取所有的事件记录. SELECT * FROM sys.dm_os_ring_buffe

瑞萨S5D9实现UART环形缓冲

队列的常见两种形式,普通队列和环形队列: 普通队列 环形队列: 在计算机中,每个信息都是存储在存储单元中的,比喻一下吧,上图的一些小正方形格子就是一个个存储单元,你可以理解为常见的数组,存放我们一个个的信息. 当有大量数据的时候,我们不能存储所有的数据,那么计算机处理数据的时候,只能先处理先来的,那么处理完后呢,就会把数据释放掉,再处理下一个.那么,已经处理的数据的内存就会被浪费掉.因为后来的数据只能往后排队,如过要将剩余的数据都往前移动一次,那么效率就会低下了,肯定不现实,所以,环形队列就出现

golang数据结构和算法之CircularBuffer环形缓冲队列

慢慢练语法和思路, 想说的都在代码及注释里. CircularBuffer package CircularBuffer const arraySize = 10 type CircularBuffer struct { data [arraySize]int pointer int } //只实现了CircularBuffer环形缓冲队列的基本方法 func (b *CircularBuffer) InsertValue(i int) { if b.pointer == len(b.data)

linux设备驱动第五篇:驱动中的并发与竟态

综述 在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争. 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时.并行被执行.而并发的执行单元对共享资源(硬件资源和软件上的全局.静态变量)的访问则容易导致竞态(race conditions).可能导致并发和竟态的情况有: SMP(Symmetric Multi-Processing),对称多处理结构.SMP是一种紧耦合.共享存储的系统模型,它的特点是多个CPU使用共

SOCKET 封包和拆包

对于基于TCP开发的通讯程序,有个很重要的问题需要解决,就是封包和拆包.自从我从事网络通讯编程工作以来(大概有三年的时间了),我一直在思索和改进封包和拆包的方法.下面就针对这个问题谈谈我的想法,抛砖引玉.若有不对,不妥之处,恳求大家指正.在此先谢过大家了. 一.为什么基于TCP的通讯程序需要进行封包和拆包. TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河里的流水,是连成一片的,其间是没有分界线的.但一般通讯程序开发是需要定义一个个相互独立的数据包的,比如用于登陆的

【转】【C#】C# 迭代器

迭代器模式是设计模式中行为模式(behavioral pattern)的一个例子,他是一种简化对象间通讯的模式,也是一种非常容易理解和使用的模式.简单来说,迭代器模式使得你能够获取到序列中的所有元素而不用关心是其类型是array,list,linked list或者是其他什么序列结构.这一点使得能够非常高效的构建数据处理通道(data pipeline)--即数据能够进入处理通道,进行一系列的变换,或者过滤,然后得到结果.事实上,这正是LINQ的核心模式. 在.NET中,迭代器模式被IEnume

Smart210学习记录------linux串口驱动

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有3个核心数据结构,它们都定义在<#include linux/serial_core.h> 1.uart_driver uart_driver包含了串口设备名.串口驱动名.主次设备号.串口控制台(可选)等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver). struct

STM32 ADC基础与多通道采样

12位ADC是一种逐次逼近型模拟数字数字转换器.它有多达18个通道,可测量16个外部和2个内部信号源.ADC的输入时钟不得超过14MHZ,它是由PCLK2经分频产生.如果被ADC转换的模拟电压低于低阀值或高于高阀值,AWD模拟看门狗状态位被设置. ADC通常要与DMA一起使用 这里只是简单的用库配置ADC 不断扫描来实现ADC的应用. 配置DMA: void DMA_Config(void) { DMA_InitTypeDef DMA_InitStructure;//定义DMA初始化结构体 DM