ringq.c源码解析
一:ring_t类型描述:
下面的描述在uemf.h和ringq.c中都有:
ringq_t类型定义在uemf.h中:

typedef struct {
unsigned char *buf; /* Holding buffer for data */
unsigned char *servp; /* Pointer to start of data */
unsigned char *endp; /* Pointer to end of data */
unsigned char *endbuf; /* Pointer to end of buffer */
int buflen; /* Length of ring queue */
int maxsize; /* Maximum size */
int increment; /* Growth increment */
} ringq_t;
二.ringq_t相关的宏:
在ringq.c文件中包含的对ringq_t操作的宏有如下这些:
1)RINGQ_LEN(rq)
功能:返回ringq_t类型对象rq的有效长度。
定义如下:

#define RINGQ_LEN(rq) \
((rq->servp > rq->endp) ? \
(rq->buflen + (rq->endp - rq->servp)) : \
(rq->endp - rq->servp))
解析:ringq_t在内存中开辟一块循环使用的缓冲区,其成员buf指向该缓冲区的起始地址,endbuf指向紧挨该缓冲区的下一个字节(注意:并不是该缓冲区的最后一个字节)。buflen是该缓冲区的长度,即buflen=endbuf-buf. servp指向缓冲区中有效数据的第一个字节(不知道源码中的un-consumed是否有误),endp指向缓冲区中空闲块的第一个字节。那么在一般情况下,endp>serp,且endp-servp即为缓冲区中有效数据的字节数。
ringq_t的特点即是缓冲区循环使用。当endp或servp到达缓冲区的最后一个字节,如果要继续增长(即要到达endbuf所指的位置)时,缓冲区的填充着要负责将它们回卷(wrap)到缓冲区的起始位置。这样循环利用这块缓冲区使得内存利用率得到提高。
宏RINGQ_LEN的功能就是返回ringq_t类型对象中的有效数据的长度。由于缓冲区被循环使用,使得有两种情况会出现:1)endp>servp,这是一般情况,此时有效数据的长度(所占的字节数)即为endp-servp;2)endp<serp,此种情况的产生是由于endp的增长使其回卷到缓冲区的前端继续增长,那么此时的有效数据的长度是从servp到endbuf和从buf到endp这两段长度的和,即endbuf-servp+endp-buf=endbuf-buf+endp-servp=buflen+endp-servp.
三.相关的函数:
1)static int getBinBlockSize(int size)
功能:如果参数size<2的 B_SHIFT次方(定义在uemf.h中,为4),则返回2的B_SHIFT次方。如果参数size>=2的B_SHIFT次方,则返回大于2的B_SHIFT次方的第一个2的整数次幂。
作用:程序中申请空间的时候,尽量满足2的N次方的申请值,不然会产生很多碎片,或者造成内存中降低申请到大空间的可能性,这在资源紧缺的嵌入式开发中是非常值得关注的。
代码:

/******************************************************************************/
/*
* Find the smallest binary memory size that "size" will fit into. This
* makes the ringq and ringqGrow routines much more efficient. The balloc
* routine likes powers of 2 minus 1.
*/
static int getBinBlockSize(int size)
{
int q;
size = size >> B_SHIFT;
for (q = 0; size; size >>= 1) {
q++;
}
return (1 << (B_SHIFT + q));
}
/******************************************************************************
代码解析:首先我们自己完成这样一个函数,要求该函数返回比输入参数大的第一个2的整数次幂。我们这里把函数原型依然定义为int getBinBlockSize(int size)。
代码如下(在VC下测试过):

#include<iostream.h>
int getBinBlockSize(int size);
void main()
{
int a;
cout<<"please input a positive integer:";
cin>>a;
cout<<getBinBlockSize(a)<<endl;
}
int getBinBlockSize(int size)
{
for(int q=0;size>0;size>>=1)
{
q++;
}
return 1<<q;
}
其核心思想就是统计输入参数占用了多少个二进制位。这个函数理解了,那么上面的函数就好理解了,只是加了一个比较阈值B_SHIFT而已。
2)int ringqOpen(ringq_t *rq, int initSize, int maxsize)
功能:新建一个ringq_t类型的对象(准确的说,这种说法有误,因为ring_t对象通过ringq_t rq即可创建,这个函数的实际作用是初始化ringq_t对象相关的缓冲区)。参数initSize是函数调用者希望分配的缓冲区大小,但实际传递给内存分配函数的请求大小是经过函数int getBinBlockSize(int size)调整过的,如果initSize比2的B_SHIFT(在uemf.h中定义为4)次方小,则用2的B_SHIFT次方去调用实际内存分配函数balloc,如果initSize比2的B_SHIFT次幂大,则用大于initSize的第一个2的整数次幂作为参数去调用实际内存分配函数balloc。balloc的解析目前不表。maxsize的作用是设置缓冲区大小的上界,如果设置为-1,则表示缓冲区无上界。
源码:

/*********************************** Code *************************************/
/*
* Create a new ringq. "increment" is the amount to increase the size of the
* ringq should it need to grow to accomodate data being added. "maxsize" is
* an upper limit (sanity level) beyond which the q must not grow. Set maxsize
* to -1 to imply no upper limit. The buffer for the ringq is always
* dynamically allocated. Set maxsize
*/
int ringqOpen(ringq_t *rq, int initSize, int maxsize)
{
int increment;
a_assert(rq);
a_assert(initSize >= 0);
increment = getBinBlockSize(initSize);
if ((rq->buf = balloc(B_L, (increment))) == NULL) {
return -1;
}
rq->maxsize = maxsize;
rq->buflen = increment;
rq->increment = increment;
rq->endbuf = &rq->buf[rq->buflen];
rq->servp = rq->buf;
rq->endp = rq->buf;
*rq->servp = '\0';
return 0;
}
补充:新建了大小经过算法调整后的缓冲区后,代码还把serp、endp都指向缓冲区的起始位置,且填上\0字符。
3)void ringqFlush(ringq_t *rq)
功能:清空ringq_t类型对象所有的内存缓冲区中的有效数据。使得servp和endp都指向缓冲区中的第一个字节,即buf指针所指处。并将这块内存的内容设置为‘\
代码:

/******************************************************************************/
/*
* Flush all data in a ring q. Reset the pointers.
*/
void ringqFlush(ringq_t *rq)
{
a_assert(rq);
a_assert(rq->servp);
rq->servp = rq->buf;
rq->endp = rq->buf;
if (rq->servp) {
*rq->servp = '\0';
}
}
4)void ringqClose(ringq_t *rq)
功能:删除ring_t对象,并且释放其指向的内存缓冲区。
代码:

/******************************************************************************/
/*
* Delete a ringq and free the ringq buffer.
*/
void ringqClose(ringq_t *rq)
{
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
if (rq == NULL) {
return;
}
ringqFlush(rq);
bfree(B_L, (char*) rq->buf);
rq->buf = NULL;
}
解析:该函数在调用了ringqFlush()清空缓冲区有效数据外,还释放了整个缓冲区的内存。
5)int ringqLen(ringq_t *rq)
功能:返回ringq_t对象的内存缓冲区中数据的长度。与RINGQ_LEN(rq)宏功能同。
代码:

/******************************************************************************/
/*
* Return the length of the data in the ringq. Users must fill the queue to
* a high water mark of at most one less than the queue size.
*/
int ringqLen(ringq_t *rq)
{
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
if (rq->servp > rq->endp) {
return rq->buflen + rq->endp - rq->servp;
} else {
return rq->endp - rq->servp;
}
}
6)int ringqGetc(ringq_t *rq)
功能:返回ringq_t对象的内存缓冲区中的有效数据区得第一个字节处的内容。也就是返回servp所指的内容,并将servp+1,如果servp+1后到达endbuf,则将其回卷(wrap)到缓冲区的开头即buf处。
代码:

/******************************************************************************/
/*
* Get a byte from the queue
*/
int ringqGetc(ringq_t *rq)
{
char_t c;
char_t* cp;
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
if (rq->servp == rq->endp) {
return -1;
}
cp = (char_t*) rq->servp;
c = *cp++;
rq->servp = (unsigned char *) cp;
if (rq->servp >= rq->endbuf) {
rq->servp = rq->buf;
}
/*
* 17 Sep 03 BgP -- using the implicit conversion from signed char to
* signed int in the return below makes this function work incorrectly when
* dealing with UTF-8 encoded text. UTF-8 may include characters that are >
* 127, which a signed char treats as negative. When we return a 'negative'
* value from this function, it gets converted to a negative
* integer, instead of a small positive integer, which is what we want.
* So, we cast to (unsigned char) before returning, and the problem goes
* away...
*/
return (int) ((unsigned char) c);
}
7)int ringqPutBlkMax(ringq_t *rq)
功能:返回缓冲区通过一次拷贝操作可以接受的最大字节数。需要考虑到回卷的情况。
代码:

int ringqPutBlkMax(ringq_t *rq)
{
int space, in_a_line;
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
space = rq->buflen - RINGQ_LEN(rq) - 1;
in_a_line = rq->endbuf - rq->endp;
return min(in_a_line, space);
}
8)int ringqGetBlkMax(ringq_t *rq)
功能:与ringqPutBlkMax(ringq_t *rq函数对应,该函数返回缓冲区通过一次拷贝可以提供的有效数据的字节数。同样要考虑两种情况,一种是没有回卷,即endp>servp,另一种是回卷了的,即servp>endp.
代码:

int ringqGetBlkMax(ringq_t *rq)
{
int len, in_a_line;
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
len = RINGQ_LEN(rq);
in_a_line = rq->endbuf - rq->servp;
return min(in_a_line, len);
}
9)int ringqGetBlk(ringq_t *rq, unsigned char *buf, int size)
功能:从rq的缓冲区的数据区的起始位置起,取出连续的一块数据放到参数buf所指的内存处。参数size是请求的数据块大小,但实际拷贝的大小由size即ringqGetBlkMax()函数共同确定。
副作用:拷贝完成后,循环缓冲区servp将增大实际拷贝的字节数,若起到达endbuf则要回卷。返回值是实际拷贝成功的字节数。
代码:

/******************************************************************************/
/*
* Get a block of data from the ringq. Return the number of bytes returned.
*/
int ringqGetBlk(ringq_t *rq, unsigned char *buf, int size)
{
int this, bytes_read;
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
a_assert(buf);
a_assert(0 <= size && size < rq->buflen);
/*
* Loop getting the maximum bytes we can get in a single straight line copy
*/
bytes_read = 0;
while (size > 0) {
this = ringqGetBlkMax(rq);
this = min(this, size);
if (this <= 0) {
break;
}
memcpy(buf, rq->servp, this);
buf += this;
rq->servp += this;
size -= this;
bytes_read += this;
if (rq->servp >= rq->endbuf) {
rq->servp = rq->buf;
}
}
return bytes_read;
}
10)int ringqPutBlk(ringq_t *rq, unsigned char *buf, int size)
功能:与ringqGetBlk函数对应理解。
代码:

/******************************************************************************/
/*
* Add a block of data to the ringq. Return the number of bytes added.
* Grow the q as required.
*/
int ringqPutBlk(ringq_t *rq, unsigned char *buf, int size)
{
int this, bytes_put;
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
a_assert(buf);
a_assert(0 <= size);
/*
* Loop adding the maximum bytes we can add in a single straight line copy
*/
bytes_put = 0;
while (size > 0) {
this = min(ringqPutBlkMax(rq), size);
if (this <= 0) {
if (! ringqGrow(rq)) {
break;
}
this = min(ringqPutBlkMax(rq), size);
}
memcpy(rq->endp, buf, this);
buf += this;
rq->endp += this;
size -= this;
bytes_put += this;
if (rq->endp >= rq->endbuf) {
rq->endp = rq->buf;
}
}
return bytes_put;
}
11)static int ringqGrow(ringq_t *rq)
功能:扩充rq的循环缓冲区的大小。扩充的大小为ringq_t对象中的increament成员。如果该循环缓冲区能够被扩充,则返回true。需要调用者自己保证扩充后的缓冲区长度不要超过该ringq_t对象的maxsize成员规定的上限值。
代码:

/******************************************************************************/
/*
* Grow the buffer. Return true if the buffer can be grown. Grow using
* the increment size specified when opening the ringq. Don't grow beyond
* the maximum possible size.
*/
static int ringqGrow(ringq_t *rq)
{
unsigned char *newbuf;
int len;
a_assert(rq);
if (rq->maxsize >= 0 && rq->buflen >= rq->maxsize) {
return 0;
}
len = ringqLen(rq);
if ((newbuf = balloc(B_L, rq->buflen + rq->increment)) == NULL) {
return 0;
}
ringqGetBlk(rq, newbuf, ringqLen(rq));
bfree(B_L, (char*) rq->buf);
rq->buflen += rq->increment;
rq->endp = newbuf;
rq->servp = newbuf;
rq->buf = newbuf;
rq->endbuf = &rq->buf[rq->buflen];
ringqPutBlk(rq, newbuf, len);
/*
* Double the increment so the next grow will line up with balloc'ed memory
*/
rq->increment = getBinBlockSize(2 * rq->increment);
return 1;
}
12)int ringqPutc(ringq_t *rq, char_t c)
功能:与ringqGetc函数功能对应,该函数向缓冲区中添加一个字符,使添加后的字符存放于有效数据区的最后一个存储单元。主要代码很简单,重要的是检错,自动调整缓冲区大小与回卷判断部分。
代码:

/******************************************************************************/
/*
* Add a char to the queue. Note if being used to store wide strings
* this does not add a trailing '\0'. Grow the q as required.
*/
int ringqPutc(ringq_t *rq, char_t c)
{
char_t *cp;
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
if ((ringqPutBlkMax(rq) < (int) sizeof(char_t)) && !ringqGrow(rq)) {
return -1;
}
cp = (char_t*) rq->endp;
*cp++ = (char_t) c;
rq->endp = (unsigned char *) cp;
if (rq->endp >= rq->endbuf) {
rq->endp = rq->buf;
}
return 0;
}
13)int ringqInsertc(ringq_t *rq, char_t c)
功能:与ringq_Putc相反,前者是往缓冲区数据部分的尾部添加数据,而这个函数是是使得添加后的字符是数据缓冲区的第一个字符。同样重要的部分在于检错机制和回卷的判断以及能否根据现有数据缓冲区的大小自动调整整个缓冲区大小。
代码:

/******************************************************************************/
/*
* Insert a wide character at the front of the queue
*/
int ringqInsertc(ringq_t *rq, char_t c)
{
char_t *cp;
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
if (ringqPutBlkMax(rq) < (int) sizeof(char_t) && !ringqGrow(rq)) {
return -1;
}
if (rq->servp <= rq->buf) {
rq->servp = rq->endbuf;
}
cp = (char_t*) rq->servp;
*--cp = (char_t) c;
rq->servp = (unsigned char *) cp;
return 0;
}
14)int ringqPutStr(ringq_t *rq, char_t *str)
功能:向ringq_t玄幻缓冲区的数据部分的尾部添加一个字符串。并将endp所指的内存单元置为/0结束符。
代码:

int ringqPutStr(ringq_t *rq, char_t *str)
{
int rc;
a_assert(rq);
a_assert(str);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
rc = ringqPutBlk(rq, (unsigned char*) str, gstrlen(str) * sizeof(char_t));
*((char_t*) rq->endp) = (char_t) '\0';
return rc;
}
15)void ringqAddNull(ringq_t *rq)
功能:把ringq_t缓冲区的endp指向的位置设置为\0结束符。
代码:

void ringqAddNull(ringq_t *rq)
{
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
*((char_t*) rq->endp) = (char_t) '\0';
}
16)int ringqGetcA(ringq_t *rq)
int ringqPutcA(ringq_t *rq, char c)
int ringqInsertcA(ringq_t *rq, char c)
int ringqPutStrA(ringq_t *rq, char *str)
功能:上述四个函数是在定义了unicode格式时实现与ringqGetc、ringqPutc、ringqInsertc、ringqPutstr对应的功能。
17)void ringqPutBlkAdj(ringq_t *rq, int size)
功能:当用户往缓冲区中传递数据了后,调整缓冲区中endp指针的位置。但是因为有了ringqPutBlk函数,感觉这个函数有些多余,以后用到了再体会。
代码:

void ringqPutBlkAdj(ringq_t *rq, int size)
{
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
a_assert(0 <= size && size < rq->buflen);
rq->endp += size;
if (rq->endp >= rq->endbuf) {
rq->endp -= rq->buflen;
}
/*
* Flush the queue if the endp pointer is corrupted via a bad size
*/
if (rq->endp >= rq->endbuf) {
error(E_L, E_LOG, T("Bad end pointer"));
ringqFlush(rq);
}
}
18)void ringqGetBlkAdj(ringq_t *rq, int size)
功能:当用户从缓冲区中取走数据后,调整数据缓冲区的起始指针servp。同上因为有函数ringqGetBlk的存在,感觉这个函数有些多余。
代码:

/******************************************************************************/
/*
* Adjust the servp pointer after the user has copied data from the queue.
*/
void ringqGetBlkAdj(ringq_t *rq, int size)
{
a_assert(rq);
a_assert(rq->buflen == (rq->endbuf - rq->buf));
a_assert(0 < size && size < rq->buflen);
rq->servp += size;
if (rq->servp >= rq->endbuf) {
rq->servp -= rq->buflen;
}
/*
* Flush the queue if the servp pointer is corrupted via a bad size
*/
if (rq->servp >= rq->endbuf) {
error(E_L, E_LOG, T("Bad serv pointer"));
ringqFlush(rq);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)