环形缓冲区的实现

最近在做网络数据收发操作时,用到环形缓冲区的数据结构,网上看了很多人的实现方式,总觉得不妥,于是决定自己亲自写一个,适用于自己的项目。

结构定义

先看一张关于环形队列的描述图(如下),它是一种FIFO(先入先出)的队列,其明确队列的容量,存储结构上表现为头尾相连,使用起始位置和结束位置标识队列节点的存储区段。

avatar

更多关于环形队列的描述,这里就不再重复,可至 环形缓冲器(FIFO) 查看。

我给出相关代码的结构定义如下:

/**
 * @struct x_ringbuf_t
 * @brief  环形缓存的结构体描述信息。
 */
typedef struct x_ringbuf_t
{
    x_uint32_t   xut_vpos;  ///< 缓存中有效数据的起始位置
    x_uint32_t   xut_vlen;  ///< 缓存中有效数据的长度
    x_uint32_t   xut_size;  ///< 缓存大小
    x_uchar_t  * xct_dptr;  ///< 缓存地址
} x_ringbuf_t;

从上面的结构体定义中,可以看出,数据区段的结束位置,使用了数据区段的长度来替换了,这样做,可避免 起始位置结束位置 重叠在一起时,表示不清 环形缓存满区段 还是 空区段

操作接口

/** 判断环形缓存是否有效 */
#define RBUF_IS_VALID(xrbt_sptr)   ((X_NULL != (xrbt_sptr)) && \
                                    (X_NULL != (xrbt_sptr)->xct_dptr) && ((xrbt_sptr)->xut_size > 0) && \
                                    ((xrbt_sptr)->xut_vpos >= 0) && ((xrbt_sptr)->xut_vpos < (xrbt_sptr)->xut_size) && \
                                    ((xrbt_sptr)->xut_vlen <= (xrbt_sptr)->xut_size))

/** 判断环形缓存是否为 空 */
#define RBUF_IS_EMPTY(xrbt_sptr)   (0 == (xrbt_sptr)->xut_vlen)

/** 判断环形缓存是否为 满 */
#define RBUF_IS_FULL(xrbt_sptr)    ((xrbt_sptr)->xut_size == (xrbt_sptr)->xut_vlen)

/**********************************************************/
/**
 * @brief 用于 环形缓存的内存申请操作 回调函数类型(内存要释放时,请调用 xfunc_ringbuf_free 指向的接口)。
 * 
 * @param [in ] xht_context : 回调的上下文描述句柄。
 * @param [in ] xut_size    : 提交申请的内存字节数。
 * 
 * @return x_uchar_t *
 *         - 操作成功,返回申请到的内存地址;
 *         - 操作失败,返回 X_NULL。
 */
typedef x_uchar_t * (* xfunc_ringbuf_malloc)(x_handle_t xht_context, x_uint32_t xut_size);

/**********************************************************/
/**
 * @brief 用于 环形缓存的内存释放操作 回调函数类型(所释放的内存,即为 xfunc_ringbuf_malloc 所申请的)。
 *
 * @param [in ] xht_context : 回调的上下文描述句柄。
 * @param [in ] xpvt_ptr    : 要释放的内存地址。
 * 
 */
typedef x_void_t (* xfunc_ringbuf_free)(x_handle_t xht_context, x_void_t * xpvt_ptr);

/**********************************************************/
/**
 * @brief 创建 环形缓存对象(使用 rbuf_destroy() 接口进行对象销毁)。
 * 
 * @param [in ] xut_size    : 环形缓存 的缓存大小。
 * @param [in ] xfunc_mptr  : 内存申请操作的回调函数接口(若为 X_NULL,则使用标准 C 的 malloc() 接口进行分配内存)。
 * @param [in ] xht_context : 回调的上下文描述句柄。
 * 
 * @return x_ringbuf_t *
 *         - 操作成功,返回 环形缓存 对象;
 *         - 操作失败,返回  X_NULL。
 */
x_ringbuf_t * rbuf_create(x_uint32_t xut_size, xfunc_ringbuf_malloc xfunc_mptr, x_handle_t xht_context);

/**********************************************************/
/**
 * @brief 销毁 环形缓存对象(由 rbuf_create() 接口创建的对象)。
 * 
 * @param [in ] xrbt_sptr   : 环形缓存对象。
 * @param [in ] xfunc_fptr  : 内存释放操作的回调函数接口(若为 X_NULL,则使用标准 C 的 free() 接口进行释放内存)。
 * @param [in ] xht_context : 回调的上下文描述句柄。
 * 
 */
x_void_t rbuf_destroy(x_ringbuf_t * xrbt_sptr, xfunc_ringbuf_free xfunc_fptr, x_handle_t xht_context);

/**********************************************************/
/**
 * @brief 读取环形缓存对象中的有效数据至指定的缓存中;
 *        该操作后,会修改环形缓存中的有效数据相关字段。
 * 
 * @param [in ] xrbt_sptr : 环形缓存对象。
 * @param [out] xct_rptr  : 指定的数据接收缓存。
 * @param [in ] xut_size  : 数据接收缓存大小。
 * 
 * @return x_uint32_t
 *         - 返回读取到的有效数据量(字节数)。
 */
x_uint32_t rbuf_read(x_ringbuf_t * xrbt_sptr, x_uchar_t * xct_rptr, x_uint32_t xut_size);

/**********************************************************/
/**
 * @brief 向环形缓存对象写入数据;
 *        环形缓存剩余的空间量必须足够待写入的数据量,否则直接返回 0,忽略后续操作;
 *        该操作后,会修改环形缓存中的有效数据相关字段。
 * 
 * @param [out] xrbt_sptr : 环形缓存对象。
 * @param [in ] xct_wptr  : 待写入数据的缓存。
 * @param [in ] xut_size  : 待写入数据的缓存大小。
 * 
 * @return x_uint32_t
 *         - 返回写入的有效数据量(字节数)。
 */
x_uint32_t rbuf_write(x_ringbuf_t * xrbt_sptr, const x_uchar_t * xct_wptr, x_uint32_t xut_size);

/**********************************************************/
/**
 * @brief 抹除环形缓存中的头部数据。
 *
 * @param [out] xrbt_sptr  : 环形缓存对象。
 * @param [in ] xut_count  : 请求抹除的字节数。
 *
 * @return x_uint32_t
 *         - 返回抹除的字节数。
 */
x_uint32_t rbuf_erase_head(x_ringbuf_t * xrbt_sptr, x_uint32_t xut_count);

/**********************************************************/
/**
 * @brief 抹除环形缓存中的尾部数据。
 *
 * @param [out] xrbt_sptr  : 环形缓存对象。
 * @param [in ] xut_count  : 请求抹除的字节数。
 *
 * @return x_uint32_t
 *         - 返回抹除的字节数。
 */
x_uint32_t rbuf_erase_tail(x_ringbuf_t * xrbt_sptr, x_uint32_t xut_count);

对于 rbuf_create()rbuf_destroy() 中加入了 内存 申请/释放 的回调操作函数 的参数,这样方便外部介入内存管理操作(使用内存池进行内存的分配与释放操作)。

实现细节

/**********************************************************/
/**
 * @brief 创建 环形缓存对象(使用 rbuf_destroy() 接口进行对象销毁)。
 *
 * @param [in ] xut_size    : 环形缓存 的缓存大小。
 * @param [in ] xfunc_mptr  : 内存申请操作的回调函数接口(若为 X_NULL,则使用标准 C 的 malloc() 接口进行分配内存)。
 * @param [in ] xht_context : 回调的上下文描述句柄。
 *
 * @return x_ringbuf_t *
 *         - 操作成功,返回 环形缓存 对象;
 *         - 操作失败,返回  X_NULL。
 */
x_ringbuf_t * rbuf_create(x_uint32_t xut_size, xfunc_ringbuf_malloc xfunc_mptr, x_handle_t xht_context)
{
    x_ringbuf_t * xrbt_sptr = X_NULL;
    if (xut_size <= 0)
    {
        return X_NULL;
    }

    if (X_NULL == xfunc_mptr)
    {
        xrbt_sptr = (x_ringbuf_t *)malloc(sizeof(x_ringbuf_t) + xut_size);
    }
    else
    {
        xrbt_sptr = (x_ringbuf_t *)xfunc_mptr(xht_context, sizeof(x_ringbuf_t) + xut_size);
    }

    if (X_NULL != xrbt_sptr)
    {
        xrbt_sptr->xut_vpos = 0;
        xrbt_sptr->xut_vlen = 0;
        xrbt_sptr->xut_size = xut_size;
        xrbt_sptr->xct_dptr = ((x_uchar_t *)xrbt_sptr) + sizeof(x_ringbuf_t);
    }

    return xrbt_sptr;
}

/**********************************************************/
/**
 * @brief 销毁 环形缓存对象(由 rbuf_create() 接口创建的对象)。
 *
 * @param [in ] xrbt_sptr   : 环形缓存对象。
 * @param [in ] xfunc_fptr  : 内存释放操作的回调函数接口(若为 X_NULL,则使用标准 C 的 free() 接口进行分配内存)。
 * @param [in ] xht_context : 回调的上下文描述句柄。
 *
 */
x_void_t rbuf_destroy(x_ringbuf_t * xrbt_sptr, xfunc_ringbuf_free xfunc_fptr, x_handle_t xht_context)
{
    if (X_NULL != xrbt_sptr)
    {
        if (X_NULL == xfunc_fptr)
        {
            free(xrbt_sptr);
        }
        else
        {
            xfunc_fptr(xht_context, (x_void_t *)xrbt_sptr);
        }
    }
}

/**********************************************************/
/**
 * @brief 读取环形缓存对象中的有效数据至指定的缓存中;
 *        该操作后,会修改环形缓存中的有效数据相关字段。
 *
 * @param [in ] xrbt_sptr : 环形缓存对象。
 * @param [out] xct_rptr  : 指定的数据接收缓存。
 * @param [in ] xut_size  : 数据接收缓存大小。
 *
 * @return x_uint32_t
 *         - 返回读取到的有效数据量(字节数)。
 */
x_uint32_t rbuf_read(x_ringbuf_t * xrbt_sptr, x_uchar_t * xct_rptr, x_uint32_t xut_size)
{
    x_uint32_t xut_rbytes = 0;
    x_uint32_t xut_cbytes = 0;

    XASSERT(RBUF_IS_VALID(xrbt_sptr));
    XASSERT((0 == xut_size) || ((xut_size > 0) && (X_NULL != xct_dptr)));

    if ((xut_size <= 0) || (xrbt_sptr->xut_vlen <= 0))
    {
        return 0;
    }

    xut_rbytes = (xut_size >= xrbt_sptr->xut_vlen) ? xrbt_sptr->xut_vlen : xut_size;

    if ((xrbt_sptr->xut_vpos + xut_rbytes) <= xrbt_sptr->xut_size)
    {
        memcpy(xct_rptr, xrbt_sptr->xct_dptr + xrbt_sptr->xut_vpos, xut_rbytes);
    }
    else
    {
        xut_cbytes = xrbt_sptr->xut_size - xrbt_sptr->xut_vpos;
        memcpy(xct_rptr, xrbt_sptr->xct_dptr + xrbt_sptr->xut_vpos, xut_cbytes);
        memcpy(xct_rptr + xut_cbytes, xrbt_sptr->xct_dptr, xut_rbytes - xut_cbytes);
    }

    xrbt_sptr->xut_vlen -= xut_rbytes;
    if (0 == xrbt_sptr->xut_vlen)
        xrbt_sptr->xut_vpos = 0;
    else
        xrbt_sptr->xut_vpos = (xrbt_sptr->xut_vpos + xut_rbytes) % xrbt_sptr->xut_size;

    return xut_rbytes;
}

/**********************************************************/
/**
 * @brief 向环形缓存对象写入数据;
 *        环形缓存剩余的空间量必须足够待写入的数据量,否则直接返回 0,忽略后续操作;
 *        该操作后,会修改环形缓存中的有效数据相关字段。
 *
 * @param [out] xrbt_sptr : 环形缓存对象。
 * @param [in ] xct_wptr  : 待写入数据的缓存。
 * @param [in ] xut_size  : 待写入数据的缓存大小。
 *
 * @return x_uint32_t
 *         - 返回写入的有效数据量(字节数)。
 */
x_uint32_t rbuf_write(x_ringbuf_t * xrbt_sptr, const x_uchar_t * xct_wptr, x_uint32_t xut_size)
{
    x_uint32_t xut_wpos = 0;
    x_uint32_t xut_wlen = 0;

    XASSERT(RBUF_IS_VALID(xrbt_sptr));
    XASSERT((0 == xut_size) || ((xut_size > 0) && (X_NULL != xct_wptr)));

    if ((xut_size <= 0) || (xut_size > (xrbt_sptr->xut_size - xrbt_sptr->xut_vlen)))
    {
        return 0;
    }

    xut_wpos = (xrbt_sptr->xut_vpos + xrbt_sptr->xut_vlen) % xrbt_sptr->xut_size;

    if ((xut_wpos + xut_size) <= xrbt_sptr->xut_size)
    {
        memcpy(xrbt_sptr->xct_dptr + xut_wpos, xct_wptr, xut_size);
    }
    else
    {
        xut_wlen = xrbt_sptr->xut_size - xut_wpos;
        memcpy(xrbt_sptr->xct_dptr + xut_wpos, xct_wptr, xut_wlen);
        memcpy(xrbt_sptr->xct_dptr, xct_wptr + xut_wlen, xut_size - xut_wlen);
    }

    xrbt_sptr->xut_vlen += xut_size;

    return xut_size;
}

/**********************************************************/
/**
 * @brief 抹除环形缓存中的头部数据。
 *
 * @param [out] xrbt_sptr  : 环形缓存对象。
 * @param [in ] xut_count  : 请求抹除的字节数。
 *
 * @return x_uint32_t
 *         - 返回抹除的字节数。
 */
x_uint32_t rbuf_erase_head(x_ringbuf_t * xrbt_sptr, x_uint32_t xut_count)
{
    x_uint32_t xut_bytes = 0;

    XASSERT(RBUF_IS_VALID(xrbt_sptr));

    if (xut_count <= 0)
    {
        return 0;
    }

    if (xut_count >= xrbt_sptr->xut_vlen)
    {
        xut_bytes = xrbt_sptr->xut_vlen;

        xrbt_sptr->xut_vpos = 0;
        xrbt_sptr->xut_vlen = 0;
    }
    else
    {
        xut_bytes = xut_count;

        xrbt_sptr->xut_vpos = (xrbt_sptr->xut_vpos + xut_count) % xrbt_sptr->xut_size;
        xrbt_sptr->xut_vlen -= xut_count;
    }

    return xut_bytes;
}

/**********************************************************/
/**
 * @brief 抹除环形缓存中的尾部数据。
 *
 * @param [out] xrbt_sptr  : 环形缓存对象。
 * @param [in ] xut_count  : 请求抹除的字节数。
 *
 * @return x_uint32_t
 *         - 返回抹除的字节数。
 */
x_uint32_t rbuf_erase_tail(x_ringbuf_t * xrbt_sptr, x_uint32_t xut_count)
{
    x_uint32_t xut_bytes = 0;

    XASSERT(RBUF_IS_VALID(xrbt_sptr));

    if (xut_count <= 0)
    {
        return 0;
    }

    if (xut_count >= xrbt_sptr->xut_vlen)
    {
        xut_bytes = xrbt_sptr->xut_vlen;

        xrbt_sptr->xut_vpos = 0;
        xrbt_sptr->xut_vlen = 0;
    }
    else
    {
        xut_bytes = xut_count;

        xrbt_sptr->xut_vlen -= xut_count;
    }

    return xut_bytes;
}

源码下载

ring_buffer: https://github.com/Gaaagaa/ring_buffer

扩展阅读

无锁环形队列的一种高效实现:https://www.cnblogs.com/dodng/p/4367791.html

posted @ 2018-11-24 17:42  VxGaaagaa  阅读(423)  评论(0编辑  收藏  举报