杰杰帶你解讀【機(jī)智云】環(huán)形緩沖區(qū)源碼

本文轉(zhuǎn)載自:??https://mp.weixin.qq.com/s/iBrIu6RyEx_s-MVVkv1RRQ

前言

大家晚上好,我是杰杰煌妈,上個星期儡羔,研究了一下機(jī)智云的源碼,也不能說是研究吧璧诵,就是看了看汰蜘,人家既然能拿來做商業(yè)用,還是有很厲害的地方的之宿,如果還不知道什么叫環(huán)形緩沖區(qū)(環(huán)形隊列)的同學(xué)族操,請看——STM32進(jìn)階之串口環(huán)形緩沖區(qū)實現(xiàn)

好啦。多余的話不多說比被,看看他們的東西比我寫的好在哪吧色难,原理都是一樣的,但是效率會比我的搞等缀,可能應(yīng)用的地方也不一樣枷莉,所以,先看看吧尺迂。

ringbuffer.h

先看看頭文件:ringbuffer.h笤妙。

主要是用宏實現(xiàn)了一個求最小值的函數(shù)冒掌。

還有就是定義了一個環(huán)形緩沖區(qū)的結(jié)構(gòu)體。

#define min(a, b) (a)<(b)?(a):(b)? ?? ?? ?? ?? ?? ? ///< Calculate the minimum value

typedef struct {

size_t rbCapacity;

uint8_t??*rbHead;

uint8_t??*rbTail;

uint8_t??*rbBuff;

}rb_t;

復(fù)制代碼

看英文就能知道意思了蹲盘,rb是ringbuff的縮寫股毫,意思就是環(huán)形緩沖區(qū)洁墙,

結(jié)構(gòu)體中rbCapacity是緩沖區(qū)的容量伐厌,也就是大小沽瞭。

結(jié)構(gòu)體中rbHead是緩沖區(qū)的頭指針绳匀,

rbTail是緩沖區(qū)的尾指針,

rBuff是緩沖區(qū)的首地址箍铭,在創(chuàng)建的時候就用到汰规。

ringbuffer.c

環(huán)形緩沖區(qū)的創(chuàng)建

下面來看看源文件:

int8_t ICACHE_FLASH_ATTR rbCreate(rb_t* rb)

{

if(NULL == rb)

{

return -1;

}

rb->rbHead = rb->rbBuff;

rb->rbTail = rb->rbBuff;

return 0;

}

復(fù)制代碼

這是個創(chuàng)建環(huán)形緩沖區(qū)的函數(shù)根穷,就是初始化了環(huán)形緩沖區(qū)的頭尾指針醇蝴,這個函數(shù)的通用性很強(qiáng)吩坝,因為很多時候不只創(chuàng)建一個緩沖區(qū)。每個緩沖區(qū)的首地址都保存在了rbBuff哑蔫,這個在后面的通用性會很好用。但是杰杰還是覺得不夠好弧呐,因為我們在結(jié)構(gòu)體中定義了緩沖區(qū)的容量闸迷,但是在這里并沒有給他初始化,我覺得應(yīng)該傳入應(yīng)該參數(shù)俘枫,給緩沖區(qū)的容量進(jìn)行初始化一下腥沽。但是無所謂啦。

環(huán)形緩沖區(qū)的刪除

int8_t ICACHE_FLASH_ATTR rbDelete(rb_t* rb)

{

if(NULL == rb)

{

return -1;

}

rb->rbBuff = NULL;

rb->rbHead = NULL;

rb->rbTail = NULL;

rb->rbCapacity = 0;

return 0;

}

復(fù)制代碼

把這些指針指向NULL鸠蚪,但是環(huán)形緩沖區(qū)本身地址的數(shù)據(jù)是不會被清除的今阳,只是表明了這些地址可以被重復(fù)使用了而已。

int32_t ICACHE_FLASH_ATTR rbCapacity(rb_t *rb)

{

if(NULL == rb)

{

return -1;

}

return rb->rbCapacity;

}

復(fù)制代碼

獲取環(huán)形緩沖區(qū)的容量

int32_t ICACHE_FLASH_ATTR rbCapacity(rb_t *rb)

{

if(NULL == rb)

{

return -1;

}

return rb->rbCapacity;

}

復(fù)制代碼

因為可能有多個環(huán)形緩沖區(qū)茅信,但是容量我們不一定會知道盾舌,所以還是寫一個獲取它容量的函數(shù)比較好。

環(huán)形緩沖區(qū)可讀數(shù)據(jù)大小

int32_t ICACHE_FLASH_ATTR rbCanRead(rb_t *rb)

{

if(NULL == rb)

{

return -1;

}

if (rb->rbHead == rb->rbTail)

{

return 0;

}

if (rb->rbHead < rb->rbTail)

{

return rb->rbTail - rb->rbHead;

}

return rbCapacity(rb) - (rb->rbHead - rb->rbTail);

}

復(fù)制代碼

如果緩沖區(qū)是沒有被創(chuàng)建的蘸鲸,那么返回-1妖谴,表示非法,如果環(huán)形緩沖區(qū)的首尾都在一個位置酌摇,那么表面環(huán)形緩沖區(qū)沒有數(shù)據(jù)膝舅,那么是不可讀的,否則就返回正常的數(shù)據(jù)窑多,rb->rbTail - rb->rbHead / rbCapacity(rb) - (rb->rbHead - rb->rbTail)仍稀,請用數(shù)學(xué)的方法理解這段代碼。

獲取環(huán)形緩沖區(qū)可寫數(shù)據(jù)大小

同理獲取可寫數(shù)據(jù)也是一樣的

int32_t ICACHE_FLASH_ATTR rbCanWrite(rb_t *rb)

{

if(NULL == rb)

{

return -1;

}

return rbCapacity(rb) - rbCanRead(rb);

}

復(fù)制代碼

環(huán)形緩沖區(qū)讀數(shù)據(jù)

int32_t ICACHE_FLASH_ATTR rbRead(rb_t *rb, void *data, size_t count)

{

int32_t copySz = 0;

if(NULL == rb)

{

return -1;

}

if(NULL == data)

{

return -1;

}

if (rb->rbHead < rb->rbTail)

{

copySz = min(count, rbCanRead(rb));

memcpy(data, rb->rbHead, copySz);

rb->rbHead += copySz;

return copySz;

}

else

{

if (count < rbCapacity(rb)-(rb->rbHead - rb->rbBuff))

{

copySz = count;

memcpy(data, rb->rbHead, copySz);

rb->rbHead += copySz;

return copySz;

}

else

{

copySz = rbCapacity(rb) - (rb->rbHead - rb->rbBuff);

memcpy(data, rb->rbHead, copySz);

rb->rbHead = rb->rbBuff;

copySz += rbRead(rb, (char*)data+copySz, count-copySz);

return copySz;

}

}

}

復(fù)制代碼

如果是緩沖區(qū)沒被創(chuàng)建或者是讀數(shù)據(jù)地址非法(NULL)都將返回錯誤埂息。如果rb->rbHead < rb->rbTail技潘,就是可讀數(shù)據(jù)的地址是遞增的遥巴,那么可以直接讀數(shù)據(jù),讀取的最大數(shù)據(jù)不能超過緩沖區(qū)可讀最大數(shù)據(jù)崭篡,所以要用copySz = min(count, rbCanRead(rb));限制一下讀取數(shù)據(jù)的大小挪哄,因為是直接拷貝數(shù)據(jù),所以琉闪,在較多數(shù)據(jù)面前的話迹炼,這種做法很好,比如像網(wǎng)絡(luò)上的數(shù)據(jù)颠毙,更是適合用這種方法斯入。讀完之后把rbHead 頭指針重新更新,rb->rbHead += copySz;因為環(huán)形緩沖區(qū)在數(shù)據(jù)存儲(軟件地址上)是環(huán)形的蛀蜜,所以刻两,假如數(shù)據(jù)地址不是遞增的,那么無法直接拷貝滴某,需要分段拷貝磅摹,count < rbCapacity(rb)-(rb->rbHead - rb->rbBuff)如果要讀取的數(shù)據(jù)小于從環(huán)形緩沖區(qū)的首地址開始到環(huán)形緩沖區(qū)大小的地址,那么這段地址還是遞增的霎奢,所以可以直接拷貝過去户誓,并且把頭指針更新一下。

copySz = count;

memcpy(data, rb->rbHead, copySz);

rb->rbHead += copySz;

復(fù)制代碼

最后一種情況就是幕侠,需要分段讀取了帝美,先把頭指針到緩沖區(qū)最后一個地址的這部分讀取了,再加上從緩沖區(qū)首地址開始讀取count-copySz那么長數(shù)據(jù)的數(shù)據(jù)晤硕,就ok了悼潭。然后把兩端數(shù)據(jù)拼接起來。數(shù)據(jù)保存在data中舞箍。

copySz = rbCapacity(rb) - (rb->rbHead - rb->rbBuff);

memcpy(data, rb->rbHead, copySz);

rb->rbHead = rb->rbBuff;

copySz += rbRead(rb, (char*)data+copySz, count-copySz);

復(fù)制代碼

環(huán)形緩沖區(qū)寫數(shù)據(jù)

int32_t ICACHE_FLASH_ATTR rbWrite(rb_t *rb, const void *data, size_t count)

{

int32_t tailAvailSz = 0;

if((NULL == rb)||(NULL == data))

{

return -1;

}

if (count >= rbCanWrite(rb))

{

return -2;

}

if (rb->rbHead <= rb->rbTail)

{

tailAvailSz = rbCapacity(rb) - (rb->rbTail - rb->rbBuff);

if (count <= tailAvailSz)

{

memcpy(rb->rbTail, data, count);

rb->rbTail += count;

if (rb->rbTail == rb->rbBuff+rbCapacity(rb))

{

rb->rbTail = rb->rbBuff;

}

return count;

}

else

{

memcpy(rb->rbTail, data, tailAvailSz);

rb->rbTail = rb->rbBuff;

return tailAvailSz + rbWrite(rb, (char*)data+tailAvailSz, count-tailAvailSz);

}

}

else

{

memcpy(rb->rbTail, data, count);

rb->rbTail += count;

return count;

}

}

復(fù)制代碼

與讀書同理的舰褪,將一定長度的數(shù)據(jù)從某段地(data)址寫入環(huán)形緩沖區(qū)。如果數(shù)據(jù)地址非法或者是可寫數(shù)據(jù)長度不夠疏橄,那么就會返回錯誤代碼抵知。先看后面的

else

{

memcpy(rb->rbTail, data, count);

rb->rbTail += count;

return count;

? ? }

復(fù)制代碼

如果寫數(shù)據(jù)的地址是地址的話,那么是可以直接寫的软族,注意的是刷喜,寫數(shù)據(jù)的地址并非讀數(shù)據(jù)的地址,剛好相反的立砸,可讀數(shù)據(jù)的地址是絕對不允許寫的掖疮。同理,假如寫書的地址不是遞增的話颗祝,那么浊闪,也是分成兩段恼布,假如寫入數(shù)據(jù)的長度小于從尾指針到環(huán)形緩沖區(qū)最后一個地址的長度,那么搁宾,寫入的這段數(shù)據(jù)其實其地址也是遞增的折汞,同樣是可以直接寫的,然后更新一下尾指針盖腿。

memcpy(rb->rbTail, data, count);

rb->rbTail += count;

if (rb->rbTail == rb->rbBuff+rbCapacity(rb))

{

rb->rbTail = rb->rbBuff;

}

復(fù)制代碼

否則爽待,也需要分段寫入,先寫入從尾指針到環(huán)形緩沖區(qū)最后一個地址的長度翩腐,然后從環(huán)形緩沖區(qū)的首地址開始再寫入剩下的數(shù)據(jù)長度count-tailAvailSz鸟款,

memcpy(rb->rbTail, data, tailAvailSz);

rb->rbTail = rb->rbBuff;

return tailAvailSz + rbWrite(rb, (char*)data+tailAvailSz, count-tailAvailSz);

復(fù)制代碼

好了,至此茂卦,源碼基本分析完畢何什,現(xiàn)在說說為什么比我的源碼寫得好,

第一點(diǎn)等龙,代碼的效率处渣,我寫的源碼是一個個數(shù)據(jù)的寫入,而機(jī)智云是一系列數(shù)據(jù)的寫入蛛砰。讀數(shù)據(jù)也是一樣霍比,一系列數(shù)據(jù)讀出,而我的源碼則是一個個數(shù)據(jù)讀出暴备,并且使用了求模的運(yùn)算防止指針越界,這在運(yùn)算中效率是不夠高的们豌。

第二代碼的健壯性涯捻,還是機(jī)智云的好,我的代碼是沒有檢查是否真正有有效的數(shù)據(jù)寫入望迎。同樣的代碼讀出也是檢查了讀出數(shù)據(jù)的地址是否真正有效障癌,防止數(shù)據(jù)非法丟失”缱穑總的來說涛浙,需要不斷成長,還是要研究研究別人商業(yè)上用的源碼摄欲,雖然說很多原理我們都知道轿亮,但是親自寫的話,不一定能寫得出來胸墙,

還有就是我注,重用現(xiàn)有源碼比創(chuàng)新的效率更高,因為并不是所有人都能另走捷徑迟隅,做開拓者的但骨,我們用已有的好東西足以励七。

END

需要源碼的同學(xué)可以在公眾號回復(fù)“機(jī)智云源碼”晚安!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奔缠,一起剝皮案震驚了整個濱河市掠抬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌校哎,老刑警劉巖两波,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贬蛙,居然都是意外死亡雨女,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門阳准,熙熙樓的掌柜王于貴愁眉苦臉地迎上來氛堕,“玉大人,你說我怎么就攤上這事野蝇∷现桑” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵绕沈,是天一觀的道長锐想。 經(jīng)常有香客問我,道長乍狐,這世上最難降的妖魔是什么赠摇? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮浅蚪,結(jié)果婚禮上藕帜,老公的妹妹穿的比我還像新娘。我一直安慰自己惜傲,他們只是感情好洽故,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盗誊,像睡著了一般时甚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哈踱,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天荒适,我揣著相機(jī)與錄音,去河邊找鬼开镣。 笑死吻贿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的哑子。 我是一名探鬼主播舅列,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肌割,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了帐要?” 一聲冷哼從身側(cè)響起把敞,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎榨惠,沒想到半個月后奋早,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赠橙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年耽装,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片期揪。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡掉奄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凤薛,到底是詐尸還是另有隱情姓建,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布缤苫,位于F島的核電站速兔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏活玲。R本人自食惡果不足惜涣狗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舒憾。 院中可真熱鬧镀钓,春花似錦、人聲如沸珍剑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽招拙。三九已至,卻和暖如春措译,著一層夾襖步出監(jiān)牢的瞬間别凤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工领虹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留规哪,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓塌衰,卻偏偏與公主長得像诉稍,于是被迫代替她去往敵國和親蝠嘉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容