看到一篇写的非常详细的帖子,为防止楼主删帖后找不到,果断转载过来

RingBuffer源代码分析 出处:http://bbs.ickey.cn/community/forum.php?mod=viewthread&tid=43202
(出处: ICKEY BBS)

大家都知道,环形缓冲区是比较常用的数据结构,正好机智云“微信宠物屋源代码v2.3”中也用到了。

下面给大家分析一下。

首先是数据结构:

“RingBuffer.h”

注意是head指向了读区域,tail指向了写区域!

注意是head指向了读区域,tail指向了写区域!

注意是head指向了读区域,tail指向了写区域!

typedef struct {
size_t rb_capacity; //缓冲区容量
char *rb_head; //用于读出的指针
char *rb_tail; //用于写入的指针
char rb_buff[256]; //缓冲区实体
}RingBuffer;

下面分析他的几个函数:

“RingBuffer.c”

//用来比较最小值的宏
#define min(a, b) (a)<(b)?(a)

b)

//新建RingBuffer,给成员赋值
//MAX_RINGBUFFER_LEN 这个宏,被定义为"P0数据最大长度"的2倍
//head/tail 两个指针,都指向缓冲区实体(数组rb_buff)的首地址
void rb_new(RingBuffer* rb)
{
rb->rb_capacity = MAX_RINGBUFFER_LEN; //capacity;
rb->rb_head = rb->rb_buff;
rb->rb_tail = rb->rb_buff;
};

获得缓冲区总容量Capacity:

size_t     rb_capacity(RingBuffer *rb)
{
return rb->rb_capacity;
}

获得缓冲区可读区域,返回可读区域大小:

三种情况:

1、head与tail都指向同一个地方时,可读区域大小为0【这种情况只会在缓冲区还未使用时出现,

开始使用之后,不会出现head/tail重合的现象,即tail永远不会等于head,否则head指向的数据还未读走就被覆盖了!】

2、head < tail  ,说明tail没有写到缓冲区末尾,从缓冲区开头重新开始。可读的区域自然为(tail - head)

3、head > tail  ,说明tail已经从缓冲区末尾写完,并从开头处重新准备写了。

插入图片给大家看看:

rb_buff是数组名,因此可以作为缓冲实体首地址的指针。

size_t     rb_can_read(RingBuffer *rb)
{
if (rb->rb_head == rb->rb_tail) return 0;
if (rb->rb_head < rb->rb_tail) return rb->rb_tail - rb->rb_head;
return rb_capacity(rb) - (rb->rb_head - rb->rb_tail);
}

获得可写区域大小,就可以用总容量 减去 可读区域大小来计算了:

size_t     rb_can_write(RingBuffer *rb)
{
return rb_capacity(rb) - rb_can_read(rb);
}

读数据,从head指向的地址开始,读到data指向的地址处,读count个数据。返回读的个数

三种情况:

1、head < tail  ,此时要从count 和"可读区域大小"中选一个较小的值,作为读操作的次数。避免了count 大于“可读区域”的错误。

2、head > tail  且 count 的个数 小于“从head到缓冲区末尾的数据个数”图中蓝色。直接复制内存,再修改head 指针即可。

3、head > tail  且 count 的个数 大于“从head到缓冲区末尾的数据个数”。

此时,先把从head到缓冲区末尾的值蓝色复制到data处,再把剩余的绿色复制过去。注意两个值:copy_sz 和*(data + copy_sz)如图

这种情况下,问题来了,要是绿色的区域超过了tail 怎么办?:)

所以,应该加了一个判断,这个在写操作中做了,但这里没做。即要读的个数count 要小于可读区域的大小。

不然会出现head > tail 但head 指向的数据以及head 后边的数据又不是有效数据,这个问题。

代码:

size_t     rb_read(RingBuffer *rb, void *data, size_t count)
{
if (rb->rb_head < rb->rb_tail)
{
int copy_sz = min(count, rb_can_read(rb));
memcpy(data, rb->rb_head, copy_sz);
rb->rb_head += copy_sz;
return copy_sz;
}
else
{
if (count < rb_capacity(rb)-(rb->rb_head - rb->rb_buff))
{
int copy_sz = count;
memcpy(data, rb->rb_head, copy_sz);
rb->rb_head += copy_sz;
return copy_sz;
}
else
{
int copy_sz = rb_capacity(rb) - (rb->rb_head - rb->rb_buff);
memcpy(data, rb->rb_head, copy_sz);
rb->rb_head = rb->rb_buff;
copy_sz += rb_read(rb, (char*)data+copy_sz, count-copy_sz);
return copy_sz;
}
}
}

 

 

写数据,把数据从data指向的地址,写到tail 指向的地址,写count个。返回写的个数。

这里进来直接判断,要写入的内容大小 要小于可写区域大小,防止造成数据覆盖。写入合法。

下面写入分了三种情况:

1、2 需要计算tail_avail_sz,这个值为tail 到缓冲区末尾的数据区域大小。

1、head < tail  ,count < tail_avail_sz  。直接复制内容。假如tail 到了缓冲区末尾,让tail 回到缓冲区首地址。

2、head < tail  ,count > tail_avail_sz  。先写入 tail_avail_sz 个数据,tail 回到缓冲区首地址,再写入剩余的部分。

3、head > tail  ,这种情况最简单,由于已经做了写入合法判断,所以直接复制内容,修改tail 即可。

代码:

size_t     rb_write(RingBuffer *rb, const void *data, size_t count)
{
if (count >= rb_can_write(rb))
return -1; if (rb->rb_head <= rb->rb_tail)
{
int tail_avail_sz = rb_capacity(rb) - (rb->rb_tail - rb->rb_buff);
if (count <= tail_avail_sz)
{
memcpy(rb->rb_tail, data, count);
rb->rb_tail += count;
if (rb->rb_tail == rb->rb_buff+rb_capacity(rb))
rb->rb_tail = rb->rb_buff;
return count;
}
else
{
memcpy(rb->rb_tail, data, tail_avail_sz);
rb->rb_tail = rb->rb_buff; return tail_avail_sz + rb_write(rb, (char*)data+tail_avail_sz, count-tail_avail_sz);
}
}
else
{
memcpy(rb->rb_tail, data, count);
rb->rb_tail += count;
return count;
}
}

对于源程序中的,指针不为NULL判断,其实是必须要加上的,不知道为什么,我下载的代码,这些部分都被注释掉了。

RingBuffer源代码分析的更多相关文章

  1. android-plugmgr源代码分析

    android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...

  2. Twitter Storm源代码分析之ZooKeeper中的目录结构

    徐明明博客:Twitter Storm源代码分析之ZooKeeper中的目录结构 我们知道Twitter Storm的所有的状态信息都是保存在Zookeeper里面,nimbus通过在zookeepe ...

  3. 转:SDL2源代码分析

    1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...

  4. 转:RTMPDump源代码分析

    0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...

  5. 转:ffdshow 源代码分析

    ffdshow神奇的功能:视频播放时显示运动矢量和QP FFDShow可以称得上是全能的解码.编码器.最初FFDShow只是mpeg视频解码器,不过现在他能做到的远不止于此.它能够解码的视频格式已经远 ...

  6. UiAutomator源代码分析之UiAutomatorBridge框架

    上一篇文章<UIAutomator源代码分析之启动和执行>我们描写叙述了uitautomator从命令行执行到载入測试用例执行測试的整个流程.过程中我们也描写叙述了UiAutomatorB ...

  7. MyBatis架构设计及源代码分析系列(一):MyBatis架构

    如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...

  8. hostapd源代码分析(三):管理帧的收发和处理

    hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...

  9. hostapd源代码分析(二):hostapd的工作机制

    [转]hostapd源代码分析(二):hostapd的工作机制 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004433 在我的上一 ...

随机推荐

  1. java连接mysql的一个小例子

    想要用java 连接数据库,需要在classpath中加上jdbc的jar包路径 在eclipse中,Project的properties里面的java build path里面添加引用 连接成功的一 ...

  2. UVa 1587 Box

    题意:给出6个矩形的长和宽,问是否能够构成一个长方体 先假设一个例子 2 3 3 4 2 3 3 4 4 2 4 2 排序后 2 3 2 3 3 4 3 4 4 2 4 2 如果要构成一个长方体的话, ...

  3. linux的命令(1)

    系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...

  4. 【转】iOS 开发之协议protocal-代理传值delegate

    原文网址:http://www.cnblogs.com/wzrong/p/3201938.html 刚开始做iOS开发的时候,对 protocol.delegate 的理解一直都是晕晕乎乎一知半解的状 ...

  5. ANDROID5.0触摸屏校准

    1.校准原理: 1)首先生成校准用的参数,可以适用tslib生成校准参数,也可以使用校准app生成:使用校准app进行校准对使用者要求比较低,使用者可以不用学习复杂的命令:本文使用app方式 2)生成 ...

  6. Android RecyclerView使用详解(三)

    在上一篇(RecyclerView使用详解(二))文章中介绍了RecyclerView的多Item布局实现,接下来要来讲讲RecyclerView的Cursor实现,相较于之前的实现,Cursor有更 ...

  7. java-No exception of type ConfigurationException can be thrown; an exception type must be a subclass of Throwable

    功能:读配置文件 java菜鸟:导入工程在报名处就开始报错,第一次遇到 import org.apache.commons.lang3.StringUtils; import org.apache.c ...

  8. ASIFormDataRequest 登录

    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL: [NSURL URLWithString: @"http: ...

  9. 嵌入式 VFS: Cannot open root device "mtdblock2" or unknown-block(2,0)

    系统启动后,虽然nand驱动表现正常,但是最后挂载rootfs时候出错: Kernel command line: root=/dev/mtdblock2 rw init=/linuxrc conso ...

  10. 序列化、反序列化和transient关键字的作用

    引言 将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口, ...