Librdkafka的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu) 1 --- 隊(duì)列

  • Librdkafka用純C寫成,作者在C API基礎(chǔ)上作了C++的簡單封裝;
  • 說到C, 自然里面離不開大量的指針操作, 內(nèi)存操作, 引用計(jì)數(shù)等等, 作者一一為我們作了實(shí)現(xiàn);
  • 基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)里面也說到了很多,比如 鏈表, 各種隊(duì)列, 二叉樹等等, 接下來我們會(huì)一一介紹.

Queue
  • 所在文件: src/queue.h和src/rdsysqueue.h
  • queue.h是個(gè)很古老的實(shí)現(xiàn)了, 作者這里用的是NetBsd里的實(shí)現(xiàn), 還有其他很多版本, 實(shí)現(xiàn)都大同小異, 里面指針的運(yùn)用真是值得我們好好學(xué)習(xí);
  • 網(wǎng)上關(guān)于這個(gè)的講解有很多, 我們這里只是簡單過一下用得最多的Tail queue;
  • 頭指針定義 #define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,):
#define _TAILQ_HEAD(name, type, qual)                   \
struct name {                               \
    qual type *tqh_first;       /* first element */     \
    qual type *qual *tqh_last;  /* addr of last next element */ \
}

兩個(gè)元素:
tqh_first: 指向隊(duì)列的第一個(gè)成員;
tqh_last: 存的是隊(duì)列里的最后一個(gè)元素的 next指針的變量地址, 這個(gè)二級(jí)指針太有用了,我們后邊會(huì)再講到;

  • 隊(duì)列的entry: #define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) 實(shí)際是用最少侵入式的方式實(shí)現(xiàn)了一個(gè)類似于C++的模板的機(jī)制, 定義中的type就是隊(duì)列里元素的類型, 可以是任意struct類型, 這個(gè)_TAILQ_ENTRY(type, qual)放在這個(gè)struct類型的定義里,是其的一個(gè)成員, 然后各個(gè)元素通過這個(gè)TAILQ_ENTRY成員彼此串聯(lián)起來, 松耦合在一起
#define _TAILQ_ENTRY(type, qual)                    \
struct {                                \
    qual type *tqe_next;        /* next element */      \
    qual type *qual *tqe_prev;  /* address of previous next element */\
}
#define TAILQ_ENTRY(type)   _TAILQ_ENTRY(struct type,)

兩個(gè)元素:
tqe_next: 指向隊(duì)列的下一個(gè)成員;
tqe_prev: 存的是前一個(gè)元素的 next指針的變量地址, 這個(gè)二級(jí)指針太有用了,我們后邊會(huì)再講到;

  • 獲隊(duì)列的最后一個(gè)元素#define TAILQ_LAST(head, headname):
(*(((struct headname *)((head)->tqh_last))->tqh_last))

要理解這個(gè)其實(shí)關(guān)鍵一點(diǎn)是上面定義的TAILQ_HEADTAILQ_ENTRY在結(jié)構(gòu)和內(nèi)存布局上是一樣一樣的.
(head)->tqh_last)是最后一個(gè)元素的next指針的地址, 因?yàn)?code>TAILQ_ENTRY(type)這個(gè)定義是沒有類型名的,我們不能直接cast成 TAILQ_ENTRY(type)類型, 所有就只能cast成(struct headname *), 所有((struct headname *)((head)->tqh_last))也就是最后一個(gè)元素的地址,(((struct headname *)((head)->tqh_last))->tqh_last)就是最后一個(gè)元素的tqe_prev值, 這個(gè)tqe_prev指向的是它前一個(gè)元素的next的地址, 解引用后自然就指向隊(duì)列最后一個(gè)元素自己了, 有點(diǎn)繞~~~

  • 獲取當(dāng)前元素的前一個(gè)元素 #define TAILQ_PREV(elm, headname, field)
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))

有了上一個(gè)取最后元素的經(jīng)驗(yàn),這個(gè)理解起來就容易多啦, 這里就不講啦~~~

  • 其他一些操作, 頭插,尾插, 前插,后插, 遍歷, 反向遍歷, 刪除都比較簡單了, 主要也都是指針操作;
rd_list_t
  • 是一個(gè)append-only的輕量級(jí)數(shù)組鏈表, 支持固定大小和按需自動(dòng)增長兩種方式且支持sort;
  • 所在文件: src/rdlist.h 和 src/rdlist.c
  • 定義:
typedef struct rd_list_s {
        int    rl_size;  // 目前l(fā)ist的容易
        int    rl_cnt;  // 當(dāng)前l(fā)ist里已放入的元素個(gè)數(shù)
        void **rl_elems; // 存儲(chǔ)元素的內(nèi)存, 可以看成是一個(gè)void*類型的數(shù)組
        void (*rl_free_cb) (void *); // 存儲(chǔ)的元素的釋放函數(shù)
        int    rl_flags; 
        #define RD_LIST_F_ALLOCATED  0x1  /* The rd_list_t is allocated,
                   * will be free on destroy() */
        #define RD_LIST_F_SORTED     0x2  /* Set by sort(), cleared by any mutations.
                   * When this flag is set bsearch() is used
                   * by find(), otherwise a linear search. */
        #define RD_LIST_F_FIXED_SIZE 0x4  /* Assert on grow */
        #define RD_LIST_F_UNIQUE     0x8  /* Don't allow duplicates:
                                   * ONLY ENFORCED BY CALLER. */
} rd_list_t;
  • 創(chuàng)建list: rd_list_t *rd_list_new (int initial_size, void (*free_cb) (void *))
rd_list_t *rd_list_new (int initial_size, void (*free_cb) (void *)) {
    rd_list_t *rl = malloc(sizeof(*rl));
    rd_list_init(rl, initial_size, free_cb);
    rl->rl_flags |= RD_LIST_F_ALLOCATED;
    return rl;
}
  • 容量自增: void rd_list_grow (rd_list_t *rl, size_t size)
    主要是調(diào)用rd_realloc(realloc)
void rd_list_grow (rd_list_t *rl, size_t size) {
        rd_assert(!(rl->rl_flags & RD_LIST_F_FIXED_SIZE)); // 不適用于固定大小類型的list
        rl->rl_size += (int)size;
        if (unlikely(rl->rl_size == 0))
                return; /* avoid zero allocations */
        rl->rl_elems = rd_realloc(rl->rl_elems,
                                  sizeof(*rl->rl_elems) * rl->rl_size);
}
  • 固定容量的list的創(chuàng)建: void rd_list_prealloc_elems (rd_list_t *rl, size_t elemsize, size_t size)
    實(shí)際上連每個(gè)元素的大小也是固定的
oid rd_list_prealloc_elems (rd_list_t *rl, size_t elemsize, size_t size) {
    size_t allocsize;
    char *p;
    size_t i;

    rd_assert(!rl->rl_elems);

    /* Allocation layout:
     *   void *ptrs[cnt];
     *   elems[elemsize][cnt];
     */

    allocsize = (sizeof(void *) * size) + (elemsize * size); //rl_elems數(shù)組本身的大小(元素類型是void*) + 所有元素的大小
    rl->rl_elems = rd_malloc(allocsize);

    /* p points to first element's memory. */
    p = (char *)&rl->rl_elems[size];

    /* Pointer -> elem mapping */
    for (i = 0 ; i < size ; i++, p += elemsize)
        rl->rl_elems[i] = p; //數(shù)組元素賦值

    rl->rl_size = (int)size;
    rl->rl_cnt = 0;
    rl->rl_flags |= RD_LIST_F_FIXED_SIZE; // 標(biāo)識(shí)為固定大小的list
}
  • 添加新元素 void *rd_list_add (rd_list_t *rl, void *elem):
void *rd_list_add (rd_list_t *rl, void *elem) {
        if (rl->rl_cnt == rl->rl_size)
                rd_list_grow(rl, rl->rl_size ? rl->rl_size * 2 : 16); //容量不夠先自增
    rl->rl_flags &= ~RD_LIST_F_SORTED;  //新增后原有的排序失效
    if (elem)
        rl->rl_elems[rl->rl_cnt] = elem;
    return rl->rl_elems[rl->rl_cnt++];  //當(dāng)前已有元素?cái)?shù) + 1
}
  • 按索引(相當(dāng)于數(shù)據(jù)的下標(biāo))移除元素:
static void rd_list_remove0 (rd_list_t *rl, int idx) {
        rd_assert(idx < rl->rl_cnt);

        if (idx + 1 < rl->rl_cnt) //如果idx指向的是最后一個(gè)元素,不用mm了, rl->rl_cnt--就好
                memmove(&rl->rl_elems[idx],
                        &rl->rl_elems[idx+1],
                        sizeof(*rl->rl_elems) * (rl->rl_cnt - (idx+1))); // 實(shí)際就是用memmove作內(nèi)存移動(dòng)
        rl->rl_cnt--;
}
  • 排序用的qsort快排
void rd_list_sort (rd_list_t *rl, int (*cmp) (const void *, const void *)) {
    rd_list_cmp_curr = cmp;
        qsort(rl->rl_elems, rl->rl_cnt, sizeof(*rl->rl_elems),
          rd_list_cmp_trampoline);
    rl->rl_flags |= RD_LIST_F_SORTED;
}
  • 查找:
void *rd_list_find (const rd_list_t *rl, const void *match,
                    int (*cmp) (const void *, const void *)) {
        int i;
        const void *elem;

       //如果排過序就用二分查找
    if (rl->rl_flags & RD_LIST_F_SORTED) {
        void **r;
        rd_list_cmp_curr = cmp;
        r = bsearch(&match/*ptrptr to match elems*/,
                rl->rl_elems, rl->rl_cnt,
                sizeof(*rl->rl_elems), rd_list_cmp_trampoline);
        return r ? *r : NULL;
    }

       沒排過就編歷, 是不是可以先qsort一下呢?
        RD_LIST_FOREACH(elem, rl, i) {
                if (!cmp(match, elem))
                        return (void *)elem;
        }

        return NULL;
}

Librdkafka源碼分析-Content Table

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容