RingBuffer源代码分析
看到一篇写的非常详细的帖子,为防止楼主删帖后找不到,果断转载过来
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源代码分析的更多相关文章
- android-plugmgr源代码分析
android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...
- Twitter Storm源代码分析之ZooKeeper中的目录结构
徐明明博客:Twitter Storm源代码分析之ZooKeeper中的目录结构 我们知道Twitter Storm的所有的状态信息都是保存在Zookeeper里面,nimbus通过在zookeepe ...
- 转:SDL2源代码分析
1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...
- 转:RTMPDump源代码分析
0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...
- 转:ffdshow 源代码分析
ffdshow神奇的功能:视频播放时显示运动矢量和QP FFDShow可以称得上是全能的解码.编码器.最初FFDShow只是mpeg视频解码器,不过现在他能做到的远不止于此.它能够解码的视频格式已经远 ...
- UiAutomator源代码分析之UiAutomatorBridge框架
上一篇文章<UIAutomator源代码分析之启动和执行>我们描写叙述了uitautomator从命令行执行到载入測试用例执行測试的整个流程.过程中我们也描写叙述了UiAutomatorB ...
- MyBatis架构设计及源代码分析系列(一):MyBatis架构
如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...
- hostapd源代码分析(三):管理帧的收发和处理
hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...
- hostapd源代码分析(二):hostapd的工作机制
[转]hostapd源代码分析(二):hostapd的工作机制 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004433 在我的上一 ...
随机推荐
- android 安装 出现Android Native Development Tools不能安装
Software being installed: Android Native Development Tools 20.0.0.v201206242043-391819 (com.android. ...
- 51nod1437 迈克步
傻叉单调栈 #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> ...
- [Swift 语法点滴]—— Struct Vs Class
摘自:stackoverflow.com/questions/24232799/why-choose-struct-over-class Structure instances are always ...
- iso中第三方框架SDWebImage的使用步骤
一.SDWebImage的使用 1.依赖的框架 * ImageIO.framework * MapKit.framework 2.UIImageView下载图片需要的头文件:UIImageView+W ...
- 迁移应用数据库到MySQL Database on Azure
by Rong Yu 有用户问怎么把他们应用的数据库迁移到MySQL Database on Azure上,有哪些方式,有没有需要注意的地方.今天我们来概括介绍一下迁移应用数据库到MySQL Data ...
- Activiti 多个并发子流程的应用
多个部门发起资金计划,最后统一到财务部审批,每个部门发起资金计划是一个子流程,财务部审批是多个部门的计划同时审批,审批完成后,再提交上级领导审批. 流程如下: 要解决以上问题,需要实现多个子流程并行处 ...
- 【Python】控制流语句、函数、模块、数据结构
1.三种控制流语句:if\for\while 2.每句后都要加冒号 3.有elif语句=else后加一个if 注意使用变量名! 注意缩进! 注意控制流语句后面要加冒号! 4.for i in rang ...
- git常用知识整理
分布式和集中版本控制的区别 分布式版本控制系统与集中式版本控制系统有何不同呢?首先,分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因 ...
- JQUERY EASYUI 验证框(VALIDATEBOX)用法
Query EasyUI 验证框(ValidateBox)在表单的验证方面给我们提供了很方便的方法,下面来介绍一下验证框(ValidateBox)的详细用法(查看演示):HTML 代码 <inp ...
- delphi 712 Word 2
//导出Wordprocedure TFrm_Computing.ExportWord(aFileName: string; aFileType: string);var wordApp, WordD ...