[技术干货]
LiteOS_Lab仓库组件杂谈---ring_buffer.c
摘要:本贴讲解ring_buffer.c文件,该文件中的函数用于在uart_at.c文件中管理串口接收缓存内存,是我们AT框架的底层,对于深度理解AT框架底层数据的运作起到了至关重要的作用。
tag_ring_buffer_t结构体
我们在使用ring_buffer.c文件中的函数之前,都需要通过该结构体创建一个其对应的结构体变量,并调用初始化函数,让我们需要管理的内存和这个结构体中的成员进行关联
/* * dataoff * ring buffer map: | ----datalen------- * | / \ * |/ \ * ---------------------------------------------------------- * | empty space |//////valid data///////| | * ---------------------------------------------------------- * | \ / * | \ / * | \ / * | ---------------buflen-------------------------- * buf */ typedef struct { unsigned char *buf; ///< which means the buffer int buflen; ///< which means the buffer limit int datalen; ///< which means how many data in the buffer int dataoff; ///< which means the valid data offset in the buffer }tag_ring_buffer_t;
读到这里,大家有没有想过这样做的好处是什么?我们通过特定函数对ring_buf清除或者把数据读走,实际上并没有真正的清除了其中的数据只是修改了dataoff变量和datalen变量,下面我们先来看看初始化函数。
tag_ring_buffer_t结构体初始化并和被管理的内存绑定
首先,我们需要申请一段内存,并创建一个tag_ring_buffer_t结构体类型的变量,通过调用ring_buffer_init函数,让tag_ring_buffer_t结构体类型的变量和我们申请的这段内存产生管理,从而能上面说的这种机制来管理这段内存。
先来看一个uart_at.c文件中的例子
uart_at.c文件中创建一个g_atio_cb结构体变量,成员包括了tag_ring_buffer_t创建的结构体变量rcvring,这个变量用于管理rcvringmem这段内存空间,通过ring_buffer_init函数将它们关联到一起。
int ring_buffer_init(tag_ring_buffer_t *ring,unsigned char *buf, int buflen,int offset,int datalen) { int ret = -1; if((NULL == ring)) { return ret; } ring->buf = buf; //将传入内存地址指针添加到结构体中 ring->buflen = buflen; //设置内存大小 ring->datalen = datalen; //设置数据长度,初始值为0,因为还没有数据,在读写函数中会去修改该变量 ring->dataoff = offset; //设置有效数据起始位置,初始值为0,因为还没有数据,在读写函数中会去修改该变量 ret = 0; return ret; }
向ring_buffer中写数据
/* * ring结构体:我们想写入数据的ring_buffer,buf指针:指向要写入的数据的起始地址,len:要写入数据的长度 * 返回值:实际写入数据的个数 */ int ring_buffer_write(tag_ring_buffer_t *ring,unsigned char *buf, int len) { int ret = -1; int cpylen; //the current time we should move int lenleft; //and how many data still left to move int offset; unsigned char *src; unsigned char *dst; if((NULL == ring)||(NULL == buf)||(0 == len)) //传入的参数不对 { return ret;//which means parameters error } /* 如果ring结构体中的数据长度等于内存长度,那说明满了,写不下了 */ if((ring->datalen == ring->buflen)|| (ring->buflen <= 0)) { ret = 0; return ret;//which means you could copy nothing here } /* 如果我们要写的数据长度大于剩余空间的长度,那么只能写一部分进去(剩余空间的大小),反之,可以全部写进去 */ ret = len > (ring->buflen-ring->datalen)?(ring->buflen-ring->datalen):len; /* 这里的意思是我们写数据, 从empty space2的起始位置开始写,如果数据长度大于empty space2的大小,就需要回到empty space1的位置接着写 ,是不是有点ring的味道了? ---------------------------------------------------------------------------------------------- || empty space1 ||//////valid data///////|| empty space2 || ---------------------------------------------------------------------------------------------- */ //now let us think the method to fill the data,take care of the roll back lenleft = ret; //需要写的数据大小 src = buf; //需要写的数据的起始位置 /* 如果有效数据起始位置加上有效数据长度大于内存长度,说明这段内存中包含了一份roll back的数据,长这样 */ /* ---------------------------------------------------------------------------------------------- || valid data2 ||//////empty space///////|| valid data1 || ---------------------------------------------------------------------------------------------- 其中valid data1和valid data2是同一帧数据,该数据由valid data1开始到valid data2结束,所以我们只能从valid data2结束位置开始写新数据 */ if((ring->dataoff+ring->datalen)>ring->buflen) //which means the data has roll back { offset = (ring->dataoff+ring->datalen)%ring->buflen; //we could move it one time 计算一下内存开始位置的数据大小 cpylen = lenleft; //需要写的数据的大小 dst = ring->buf + offset; //设置dst指针指向valid data2结束位置 if(cpylen > 0) { (void) memcpy(dst,src,cpylen); //开始写数据 ring->datalen += cpylen; //更新datalen计数,增加上我们写入数据的大小 lenleft -= cpylen; //计算还有多少数据没写 } } /* 如果效数据起始位置加上需要写的数据长度大于内存大小,我们就需要写到内存末尾转回到内存起始位置接 着写,假设old valid data是之前已经存在的数据,valid data是我们写的一帧数据,那么写完之后就和下图一样 ---------------------------------------------------------------------------------------------- || valid data ||//////empty space/////// || old valid data || valid data || ---------------------------------------------------------------------------------------------- */ else if((ring->dataoff+ring->datalen + lenleft)>ring->buflen) //which means the data will be roll back { //which means roll back,we should copy some here to the tail offset = ring->dataoff + ring->datalen; //计算老的数据的结束位置 cpylen = ring->buflen - offset; //计算新的数据有多少可以写到尾部 dst = ring->buf + offset; //将老的数据的结束位置地址赋值给dst (void) memcpy(dst,src,cpylen); //开始写尾部的数据 src += cpylen; //移动指针,指向剩余的数据 ring->datalen += cpylen; //更新数据长度 lenleft -= cpylen; //计算还有多少数据没写 } //here means we could move it by one time if(lenleft > 0) //如果有数据还没写完 { offset = (ring->dataoff+ring->datalen)%ring->buflen; //we could move it one time 计算下次写的位置 cpylen = lenleft; //将剩余为写入的数据个数写到cpylen中 dst = ring->buf + offset; //设置下次写的位置 (void) memcpy(dst,src,cpylen); //开始写 ring->datalen += cpylen; //更新数据长度计数 } return ret; }
向ring_buffer中读数据
/* * ring结构体:我们从ring_buffer中读取数据,buf:传入一块内存的地址,将读到的数据存到这块内存中,len:这块内存的大小 * 返回值:返回实际读到的内存大小 */ int ring_buffer_read(tag_ring_buffer_t *ring,unsigned char *buf, int len) { int ret = -1; int cpylen; //the current time we should move int lenleft; //and how many data still left to move int offset; unsigned char *src; unsigned char *dst; if((NULL == ring)||(NULL == buf)||(0 == len)) { return ret;//which means parameters error } /* 如果ring_buffer为空 */ if((ring->datalen == 0) || (ring->buflen <= 0)) { ret = 0; return ret;//which means you could copy nothing here } /* 计算实际读到的数据大小 */ ret = len > ring->datalen?ring->datalen:len; //now let us think the method to fill the data,take care of the roll back lenleft = ret; dst = buf; /* 如果数据的偏移位置大于等于内存大小减去需要读取的数据大小,情况如下图,从old valid data开始读, 读到vaild data2结束 */ /* ---------------------------------------------------------------------------------------------- || valid data2 ||//////empty space/////// || old valid data || valid data1 || ---------------------------------------------------------------------------------------------- */ if(ring->dataoff >= (ring->buflen - lenleft)) //which means the data has roll back { offset =ring->dataoff; //we cpy part 从offset位置开始读 cpylen = ring->buflen - ring->dataoff; //先读到内存末尾 src = ring->buf + offset; //设置开始读的地址 if(cpylen > 0) { (void) memcpy(dst,src,cpylen); ring->dataoff = (ring->dataoff + cpylen)%ring->buflen; //将dataoff移动到内存末尾 ring->datalen -= cpylen; //ring中的数据计数器减去已经读了的数据 lenleft -= cpylen; //计算剩余需要读取的数据 dst += cpylen; //移动dst指针指向用于存储数据的内存的为空的位置 } } //here means we could move it by one time /* 从头部开始读 */ if(lenleft > 0) { offset =ring->dataoff; //we cpy part cpylen = lenleft; src = ring->buf + offset; (void) memcpy(dst,src,cpylen); ring->dataoff = ring->dataoff + cpylen; ring->datalen -= cpylen; } return ret; }
剩余函数
剩余函数比较简单,可以略过
/* 获取ring_buffrt中数据的长度 * */ int ring_buffer_datalen(tag_ring_buffer_t *ring) { int ret = -1; if(NULL != ring) { ret = ring->datalen; } return ret; } /* 获取ring_buffrt中剩余空间 * */ int ring_buffer_freespace(tag_ring_buffer_t *ring) { int ret = -1; if(NULL != ring) { ret = ring->buflen-ring->datalen; } return ret; } /* 重置ring_buffer,本质就是修改两个计数器即可 * */ int ring_buffer_reset(tag_ring_buffer_t *ring) { int ret = -1; if(NULL != ring) { ring->datalen = 0; ring->dataoff = 0; ret = 0; } return ret; } /* ring_buffrt反初始化 * */ int ring_buffer_deinit(tag_ring_buffer_t *ring) { int ret = -1; if(NULL != ring) { (void) memset(ring,0,sizeof(tag_ring_buffer_t)); ret = 0; } return ret; }