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 在我的上一 ...
随机推荐
- JAVA使用JNI调用C++动态链接库
JAVA使用JNI调用C++动态链接库 使用JNI连接DLL动态链接库,并调用其中的函数 首先 C++中写好相关函数,文件名为test.cpp,使用g++编译为DLL文件,指令如下: g++ -sha ...
- request.getRequestDispatcher()和response.sendRedirect()
request.getRequestDispatcher("/homeMainAction_mainUI.do").forward(getRequest(), getRespons ...
- HDU 1080 Human Gene Functions
最长公共子序列的变形 题目大意:给出两个基因序列,求这两个序列的最大相似度. 题目中的表格给出了两两脱氧核苷酸的相似度. 状态转移方程为: dp[i][j] = max(dp[i-1][j]+Simi ...
- 菜鸟学习笔记4——jquery事件
方法 描述 bind() 向匹配元素附加一个或更多事件处理器 blur() 触发.或将函数绑定到指定元素的 blur 事件 change() 触发.或将函数绑定到指定元素的 change 事件 cli ...
- toB的产品经理和toc产品经理区别
腾讯产品经理现身说法 曾经在UC做过2年to c的app,现在在腾讯做to b的产品. 做to c产品的时候,我很瞧不起做to b产品的同学,认为他们不过是做支撑的. 后来,我参与了一个to b平台级 ...
- ecshop显示所有分类树栏目
1.找到 category.php 和goods.php 两个文件修改: $smarty->assign('categories', get_categories_tree(0)); // 分类 ...
- RAC 之 RMAN 备份
这篇主要介绍的是RAC 环境下的RMAN 备份. 关于Oracle 备份与恢复的一些理论知识参考我的Blog: Oracle 备份 与 恢复 概述 http://blog.csdn.net ...
- Java常用类:String
一.介绍 String:不可变的Unicode字符序列 例如:"Java" 就是4个Unicode字符J,a,v,a组成的 Java没有内置的字符串类型,而是在标准的J ...
- 64 位系统遇到未在本地计算机上注册 Microsoft.Jet.OLEDB.4.0
本人写的.net程序部署在windowsserver 2003的操作系统上正常,部署到windows server 2008上时候报错“未在本地计算机上注册 Microsoft.Jet.OLEDB.4 ...
- pdf Factory的设置
将文件自动保存到其它位置的设置方法(以Excel文件打印为例):