c印记(八): ring buffer实现

一、写在前面的话

之所以自己要自己实现一个ring buffer,和前面的ini file解析的原因差不多,都是在深度定制轮子。
我从事是多媒体播放或者录影方面工作,且是在嵌入式平台或移动端上运行,内存,cpu运行主频等资源都相对受限,所以模组之间的数据交互,就不能简单重复的使用malloc/free的方式,这样会产生内存碎片,而且有的模组(比如硬件解码或编码)的输入输出的buffer都需要物理地址的,所以就更加无法使用malloc/free了,当然也不能使用memory copy,因为这对cpu,总线等资源的消耗也是不小的,比如解码 1080p的视频,输出的数据每一帧都相当大,使用memory copy,在一般的嵌入式平台就不太可能实现流 畅播放了。

另一方面,在视频解码的时候,往往每次都需要将一个完整帧的数据送入解码器,而且,这段数据的内存地址必须是连续的,一般的ring buffer,是可能出现异常的, 比如在ring buffer的尾端,还剩下8 KB的剩余空间,但是这一帧数据却有10KB,如果是一般的ring buffer,可能就是在尾端填 8KB的数据,然后再将写指针移到头部再填2KB数据,如此一来,就无法保证这一帧数据的地址是连续的了, 这里需要做的是,跳过这8KB空闲空间,将写指针移到头部,然后再尝试申请10KB的数据, 同时,当读指针读到尾端的时候,一样要跳过这8KB空闲空间,不能将其视为有效数据。 综合这些原因和其他一些因素,也就决定了必须得重新实现一个满足需求的ring buffer。

二、ring buffer实现

在ring buffer的实现中同样有基础数据类型的定义,错误号的定义等,由于和ini file解析是差不多的,因为在这里就不在重复叙述,直接上代码。

其头文件如下:

#ifndef __TINY_RING_BUFFER_H__
#define __TINY_RING_BUFFER_H__

#ifdef __cplusplus
extern "C"{
#endif


/***************************************************************************
 *
 * macro declaration
 *
 ***************************************************************************/

/** compat the keyword 'inline' */
#if (_MSC_VER > 1200) && !defined (__cplusplus)
#define inline    _inline  /** to compat keyword 'inline' */
#endif

/***************************************************************************
 *
 * data structure declaration
 *
 ***************************************************************************/

/** base data type define */

//general signed integer type
typedef int                GS32;  //32 bits

//general unsigned integer type
typedef unsigned char      GU08; //8  bits
typedef unsigned int       GU32; //32 bits

//boolean type declaration
typedef enum GBOL
{
    GFALSE = 0,
    GTRUE = !GFALSE,
}GBOL;

//general error type define
typedef enum general_error_e
{
    G_OK = 0,

    /** There were insufficient resources to perform the requested operation */
    G_ErrorInsufficientResources = (GS32) 0x80001000,
    /** There was an error, but the cause of the error could not be determined */
    G_ErrorUndefined = (GS32) 0x80001001,
    /** One or more parameters were not valid */
    G_ErrorBadParameter = (GS32)0x80001004,
    G_ErrorInvalidOperation = (GS32)0x8000101C, /** invalid operation */    
    G_ErrorNoMemory = (GS32)0x80001009,/** havn't enough free memory in buffer */

    /** No target with the specified name string was found */
    G_ErrorNotFound = (GS32)0x80001003,
}general_error_t;



/** ring buffer data structure define */
typedef struct tiny_ring_buffer_s
{
    GS32 last_error; /** recording last operation errror code */
    GU32 size;       /** ring buffer total size */
    GU08 *data;      /** ring buffer base address */

    /*
     *@brief reset ring buffer to initial state
     *
     *@param trb  [in] ring buffer API pointer
     *
     *@return none.
     *
     **/
    void (*reset)(struct tiny_ring_buffer_s* trb);

    /*
     *@brief get how may bytes data cached in ring buffer
     *
     *@param trb  [in] ring buffer API pointer
     *
     *@return the size of cached data in ring buffer.
     *
     **/
    GU32 (*getCachedSize)(struct tiny_ring_buffer_s* trb);

    /*
     *@brief get free space in ring buffer
     *
     *@param trb  [in] ring buffer API pointer
     *
     *@return the size of free space in ring buffer.
     *
     **/
    GU32 (*getFreeSpace)(struct tiny_ring_buffer_s* trb);

    /*
     *@brief request/allocate memory
     *
     *@param trb  [in] ring buffer API pointer
     *@param size [in] requested memory size
     *
     *@return success: memory pointer, failed: NULL.
     *
     *@note  when return NULL, user can get error code by
     *       tiny_ring_buffer_s.last_error
     *
     **/
    void* (*alloc)(struct tiny_ring_buffer_s* trb, GU32 size);

    /*
     *@brief reallocate memory
     *
     *@param trb      [in] ring buffer API pointer
     *@param new_size [in] reallocated memory size
     *@param old_ptr  [in] old memory pointer
     *@param old_size [in] the size of old memory
     *
     *@return success: memory pointer, failed: NULL.
     *
     *@note  when return NULL, user can get error code by
     *       tiny_ring_buffer_s.last_error
     *
     **/

     /** 如果需要使用realloc改变memory 大小的话,就只能是对当前最新分配的 buffer,
      * 因为 此ring buffer的memory分配和释放是遵循 FIFO机制的,不能随意是使用realloc
      * 改变非当前最新alloc的memory 
      */
    void* (*realloc)(struct tiny_ring_buffer_s* trb, GU32 new_size, void* old_ptr, GU32 old_size);

    /*
     *@brief  free one segment memory
     *
     *@param trb  [in] ring buffer API pointer
     *@param ptr  [in] memory pointer
     *@param size [in] the size of memory
     *
     *@return none.
     *
     **/
    void  (*free)(struct tiny_ring_buffer_s* trb, void* ptr, GU32 size);

    /*
     *@brief print the information of read ptr, write ptr and so on.
     *
     *@param trb  [in] ring buffer API pointer
     *
     *@return none.
     *
     **/
    void (*dump)(struct tiny_ring_buffer_s* trb);

    /*
     *@brief destroy ring buffer instance.
     *
     *@param trb  [in] ring buffer API pointer
     *
     *@return none.
     *
     **/
    void (*destroy)(struct tiny_ring_buffer_s* trb);
}tiny_ring_buffer_t;

/***************************************************************************
 *
 * API declaration
 *
 ***************************************************************************/

/*
 *@brief create ring buffer instance
 *
 *@param trb  [out] ring buffer API instance pointer
 *@param size [in]  the request ring buffer size
 *@param data [in]  extern supply the memory pointer, if put as NULL, the
 *                  tiny ring buffer will auto allocate memory with input
 *                  'size'
 *
 *@return none.
 *
 **/
GS32 tinyRingBufferCreate(tiny_ring_buffer_t** trb, GU32 size, void* data);


#ifdef __cplusplus
}
#endif


#endif //end of __TINY_RING_BUFFER_H__

具体的实现文件如下:

#define _CRT_SECURE_NO_WARNINGS

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "tiny_ring_buffer.h"


/***************************************************************************
*
* macro define
*
***************************************************************************/
#define LOGI printf
#define  LOGE printf
/***************************************************************************
*
* data structure define
*
***************************************************************************/

typedef struct my_ring_buffer_s
{
    tiny_ring_buffer_t common;

    GBOL is_owns_data; /** GTRUE: ring buffer memory allocated by itself */
    GU32 free_size; /** recording free memory size in ring buffer */
    GU32 cached_size; /** recording cached data size in ring buffer */

    GU08 *end_ptr; /** the end pointer of ring buffer */
    GU08 *write_ptr;
    GU08 *read_ptr;
    GU08 *trail_ptr; /** the end pointer of cached data */
}my_ring_buffer_t;

/***************************************************************************
*
* inner API define
*
***************************************************************************/

//检测ring buffer是否有足够的剩余空间满足请求的memory的大小
static GS32 TRBCheckRequestSize(my_ring_buffer_t* mrb, GU32 size, GBOL* is_need_turnback_ptr)
{
    GS32 ret = G_ErrorNoMemory;
    *is_need_turnback_ptr = GFALSE;

   if (size <= mrb->free_size)
   {
       if (mrb->read_ptr <= mrb->write_ptr)
       {
          GU32 trail_size = (mrb->end_ptr - mrb->write_ptr);

           if (trail_size >= size)
           {
               ret = G_OK;
           }
           else
           {
               /** not enough memory for request in tailer of buffer,
                * so need turnback and check memory size from the header of buffer
                */
               if ((mrb->free_size - trail_size) >= size)
               {
                   *is_need_turnback_ptr = GTRUE;
                   ret = G_OK;
               }
           }

       }
       else
       {
           /** in this case free size == mrb->read_ptr - mrb->write_ptr ,
            *  so needn't compute tail_size,
            */
           ret = G_OK;
       }
   }

   return ret;
}

/** 翻转写指针 到ring buffer头部 */
static inline void TRBTurnbackWritePtr(my_ring_buffer_t* mrb)
{
    mrb->free_size -= (mrb->end_ptr - mrb->write_ptr);
    mrb->trail_ptr = mrb->write_ptr;
    mrb->write_ptr = mrb->common.data;
}

/** 更新写指针 */
static inline void TRBUpdateWritePtr(my_ring_buffer_t* mrb, GS32 size)
{
    mrb->write_ptr += size;
    mrb->free_size -= size;
    mrb->cached_size += size;
}

/** 翻转读指针 到ring buffer 头部 */
static inline void TRBTurnbackReadPtr(my_ring_buffer_t* mrb)
{
    mrb->free_size += (mrb->end_ptr - mrb->read_ptr);
    mrb->read_ptr   = mrb->common.data;
    mrb->trail_ptr = NULL;
}

/** 更新读指针 */
static inline void TRBUpdateReadPtr(my_ring_buffer_t* mrb, GS32 size)
{
    mrb->read_ptr  += size;
    mrb->free_size += size;
    mrb->cached_size -= size;
}

/** 获取从读指针开始的连续有效数据长度 */
static inline GU32 TRBGetValidDataSize(my_ring_buffer_t* mrb)
{
    return (mrb->trail_ptr) ? (mrb->trail_ptr - mrb->read_ptr) : (mrb->write_ptr - mrb->read_ptr);
}

///
static void TRBReset(struct tiny_ring_buffer_s* trb)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;

    mrb->read_ptr  = trb->data;
    mrb->write_ptr = trb->data;
    mrb->end_ptr   = trb->data + trb->size;
    mrb->trail_ptr = NULL;

    mrb->free_size = trb->size;
    mrb->cached_size = 0;
}

static GU32 TRBGetCachedSize(struct tiny_ring_buffer_s* trb)
{
    return ((my_ring_buffer_t*)trb)->cached_size;
}

static GU32 TRBGetFreeSpace(struct tiny_ring_buffer_s* trb)
{
    return ((my_ring_buffer_t*)trb)->free_size;
}

static void* TRBAlloc(struct tiny_ring_buffer_s* trb, GU32 size)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;
    GU08 *req_buf = NULL;
    GS32 ret = G_ErrorBadParameter;

   if (size > 0)
   {
       if (mrb->free_size > 0)/** have free size */
       {
           GBOL is_need_turnback = GFALSE;

            /** 检测请求size */
           ret = TRBCheckRequestSize(mrb, size, &is_need_turnback);

           if (G_OK == ret)
           {
               if (is_need_turnback)
               {/** 如果需要翻转写指针,就调用相关函数 */
                   TRBTurnbackWritePtr(mrb);
               }

               /** check condition ok, so allocate memory */
               req_buf = mrb->write_ptr;
                /** 获取到需要的memory之后,更新写指针 */
               /** update write ptr */
               TRBUpdateWritePtr(mrb, size);
           }
       }
       else
       {
           ret = G_ErrorNoMemory;
       }
   }

   trb->last_error = ret; /** recording error code */

   return req_buf;
}

static void* TRBRealloc(struct tiny_ring_buffer_s* trb, GU32 new_size, void* old_ptr, GU32 old_size)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;
    GS32 ret = G_ErrorBadParameter;
    void* new_ptr = NULL;

    if (new_size > 0)
    {
        if (old_ptr && (old_size > 0))
        {
            if (new_size <= old_size)
            {
                GS32 revert_Size = old_size - new_size;

                TRBUpdateWritePtr(mrb, -revert_Size);
                new_ptr = old_ptr;
                ret = G_OK;
            }
            else
            {
                GBOL is_need_turnback_write_ptr = GFALSE;
                ret = TRBCheckRequestSize(mrb, new_size, &is_need_turnback_write_ptr);

                if (G_OK == ret)
                {
                    if (is_need_turnback_write_ptr == GTRUE)
                    {
                        //turnback wirte pointer
                        TRBTurnbackWritePtr(mrb);
                        //copy old data
                        memcpy(mrb->write_ptr, old_ptr, old_size);
                    }

                    new_ptr = mrb->write_ptr;

                    /** update write ptr */
                    TRBUpdateWritePtr(mrb, new_size);
                }
            }
        }
        else
        {
            new_ptr = TRBAlloc(trb, new_size);
            ret = trb->last_error;
        }
    }

    trb->last_error = ret;

    return new_ptr;
}

static void  TRBFree(struct tiny_ring_buffer_s* trb, void* ptr, GU32 size)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;
    GS32 ret = G_ErrorBadParameter;
    GU08* buf = (GU08*)ptr;

    if (buf && (size > 0))
    {
        if ((trb->data <= buf) && (buf < mrb->end_ptr))
        {
            if (mrb->read_ptr == mrb->trail_ptr)
            {
                TRBTurnbackReadPtr(mrb);
            }

            if (ptr == mrb->read_ptr)
            {
                GU32 valid_data_size = TRBGetValidDataSize(mrb); /** valid trail data size */

                if (size <= valid_data_size)
                {
                    TRBUpdateReadPtr(mrb, size);
                    ret = G_OK;
                }
                else
                {
                    LOGE("request free size(%d) > valid data size(%d)\n", size, valid_data_size);
                }
            }
            else
            {
                LOGE("free memory need follow allcate order(like FIFO)\n");
            }
        }
        else
        {
            LOGE("request free buffer(%p, size:%d) out of range(%p - %p)\n", 
                 ptr, size, trb->data, mrb->end_ptr);
        }
    }

    trb->last_error = ret;
}

static void TRBDump(struct tiny_ring_buffer_s* trb)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;
    LOGI("mHead(%p), mEnd(%p), write_ptr(%p) ,read_ptr(%p) \n",
        trb->data, mrb->end_ptr, mrb->write_ptr, mrb->read_ptr);
}

static void TRBDestroy(struct tiny_ring_buffer_s* trb)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;

    if ((mrb->is_owns_data == GTRUE) && (trb->data))
    {
        free(trb->data);
    }

    free(mrb);
}

/***************************************************************************
*
* API define
*
***************************************************************************/

GS32 tinyRingBufferCreate(tiny_ring_buffer_t** trb, GU32 size, void* data)
{
    GS32 ret = G_ErrorBadParameter;

    if (trb && (size > 0))
    {
        my_ring_buffer_t* mrb = (my_ring_buffer_t*)malloc(sizeof(my_ring_buffer_t));

        if (mrb)
        {
            GU08* base_ptr = data;
            memset(mrb, 0, sizeof(*mrb));

            if (data == NULL)
            {
                base_ptr = (GU08*)malloc(size);
                mrb->is_owns_data = GTRUE;
            }

            if (base_ptr)
            {
                tiny_ring_buffer_t* temp = (tiny_ring_buffer_t*)mrb;

                temp->data = base_ptr;
                temp->size = size;
                temp->last_error = G_OK;

                temp->alloc = TRBAlloc;
                temp->destroy = TRBDestroy;
                temp->dump = TRBDump;
                temp->free = TRBFree;
                temp->getCachedSize = TRBGetCachedSize;
                temp->getFreeSpace = TRBGetFreeSpace;
                temp->realloc = TRBRealloc;
                temp->reset = TRBReset;

                TRBReset(temp);

                *trb = temp;

                ret = G_OK;
            }
            else
            {
                free(mrb);
            }
        }
        else
        {
            LOGE("allocate ring buffer intance failed\n");
            ret = G_ErrorInsufficientResources;
        }
    }

    return ret;
}

三、使用例子

#include <stdio.h>
#include "tiny_ring_buffer.h"

///
#define RING_BUF_SIZE (16 * 1024)
#define REQ_BUF_SIZE  (1024)
#define ALIGN_SIZE    (1024)

#define LOGD printf
#define LOGE printf


static GU08 g_buf[RING_BUF_SIZE] = {0};


///

int main(int argc, char *argv[])
{
    GU32 i        = 0;
    GS32 ret      = G_OK;
    GU32 do_flag   = 0;
    GU32 test_num  = 0;
    GU08 *temp_buf = NULL;
    GU08 *last_buf = NULL;
    tiny_ring_buffer_t* trb = NULL;
    //create a ring buffer  instance.

    ret = tinyRingBufferCreate(&trb, RING_BUF_SIZE, NULL);


    LOGD("strat test tiny ring buffer...\n");

    LOGD("test(%d), supply memory by ring buffer self(malloc)...\n", test_num);
    do
    {
        do_flag = 0;

        if (!trb)
        {
            LOGE("Error: create a ring buffer instance failed... \n");
            ret = G_ErrorInsufficientResources;
            break;
        }

        LOGD("<<======================================>> \n");
        ret = trb->size;
        LOGD("create size(%d), reallysize(%d)\n", RING_BUF_SIZE, ret);
        if (ret != RING_BUF_SIZE)
        {
            LOGE("ring buffer size invalid(%d), request(%d)\n", trb->size, RING_BUF_SIZE);
            ret = G_ErrorUndefined;
            break;
        }
        else
        {
           ret = G_OK;
        }

        for (i = 0; i < RING_BUF_SIZE / REQ_BUF_SIZE; i++)
        {
            temp_buf = trb->alloc(trb, REQ_BUF_SIZE);
            LOGD("alloc buffer(%p), size(%d)\n", temp_buf, REQ_BUF_SIZE);
            if (temp_buf == NULL)
            {
                LOGE("alloc buffer failed(0x%08x)\n", trb->last_error);
                break;
            }

            if ((i % 2))
            {
                trb->free(trb, last_buf, REQ_BUF_SIZE * 2);
                LOGD("free buffer(%p), size(%d)\n", last_buf, REQ_BUF_SIZE * 2);
                if (trb->last_error != G_OK)
                {
                    LOGE("free buffer failed(0x%08x)\n", trb->last_error);
                    break;
                }

                last_buf = NULL;
            }
            else
            {
                last_buf = temp_buf;
            }
        }

        //todo test

        LOGD("<<======================================>> \n");
        test_num++;

        if (test_num < 2)
        {
            trb->destroy(trb);
            trb = NULL;

            //test external provide buffer
            ret = tinyRingBufferCreate(& trb, RING_BUF_SIZE, g_buf);
            LOGD("test(%d), supply memory by external buffer...\n", test_num);
            do_flag = 1;
            continue;
        }

    } while (do_flag);

    if (trb)
    {
        trb->destroy(trb);
    }

    LOGD("end of test tiny ring buffer...\n");

    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值