1. 程式人生 > 實用技巧 >佇列之ring_buffer優雅實現--附個人程式碼理解

佇列之ring_buffer優雅實現--附個人程式碼理解

1. 下面張貼實現該佇列僅需的兩個標頭檔案:ring_buffer_iterator.h 和fifo_iterator.h

ring_buffer_iterator.h

  1 /*
  2  *
  3  *  This program is free software; you can redistribute it and/or modify it
  4  *  under  the terms of the GNU General  Public License as published by the
  5  *  Free Software Foundation;  either version 2 of the License, or (at your
6 * option) any later version. 7 * 8 * You should have received a copy of the GNU General Public License along 9 * with this program; if not, write to the Free Software Foundation, Inc., 10 * 675 Mass Ave, Cambridge, MA 02139, USA. 11 * 12 */ 13 14 15 #ifndef _RING_BUFFER_ITERATOR_H_
16 #define _RING_BUFFER_ITERATOR_H_ 17 18 #include <unistd.h> 19 #include <signal.h> 20 21 #include <stdlib.h> 22 #include <string.h> 23 #include <errno.h> 24 #include <stdint.h> 25 26 struct ring_buffer_iterator { 27 int head; // 標識下一個待出隊元素被存放的位置
28 int tail; // 標識下一個新入隊元素將被存放的位置 29 30 void *buffer; // 外部已經申請好的一片記憶體 31 int node_size; // 每個節點資訊所佔用的記憶體大小 32 int node_capacity; // 標識允許掛接的節點數量 33 int node_available;// 標識已經掛接的節點數量 34 }; 35 36 // 建立一個佇列容器:佇列所需的記憶體在外部分配,此處所謂的佇列容器僅僅是維護一個記錄相關資訊的資料結構表。 37 static inline struct ring_buffer_iterator *alloc_ring_buffer_iterator( 38 void *buffer, int node_size, int node_capacity) 39 { 40 struct ring_buffer_iterator *iterator; 41 42 if (!buffer) { 43 return NULL; 44 } 45 46 if (node_size <= 0) { 47 return NULL; 48 } 49 50 iterator = malloc( sizeof(struct ring_buffer_iterator) ); 51 if (!iterator) { 52 return NULL; 53 } 54 55 memset(iterator, 0x00, sizeof(struct ring_buffer_iterator) ); 56 57 //從這裡可以看出,佇列所需的記憶體在外部分配。此處所謂的佇列容器僅僅是維護一個記錄相關資訊的資料結構表。 58 iterator->buffer = buffer; 59 iterator->node_size = node_size; 60 iterator->node_capacity = node_capacity; 61 62 return iterator; 63 } 64 65 static inline void free_ring_buffer_iterator(struct ring_buffer_iterator *it) 66 { 67 if (!it) { 68 return ; 69 } 70 71 free(it); 72 } 73 static inline int ring_buffer_get(struct ring_buffer_iterator *it, void *buffer) 74 { 75 void *node; 76 77 if (!it || !buffer) { 78 return -EINVAL; 79 } 80 81 if (!it->node_available) { 82 return -ENODATA; 83 } 84 // 求出待讀取的佇列元素資料的首地址。 85 node = (uint8_t *)it->buffer + it->head * it->node_size; 86 87 // 臨界點處理 : 當head的值為node_capacity-1時,下一個待讀取的元素就是位置下標0處了。 88 if (it->head == it->node_capacity - 1) { 89 /* 90 * case of rolling at the end of buffer 91 */ 92 it->head = 0; 93 94 } else { // 非臨界點處理,head自增即可 95 it->head++; 96 } 97 98 it->node_available--; 99 100 memmove(buffer, node, it->node_size); 101 102 return 0; 103 } 104 105 106 static inline int ring_buffer_peek(struct ring_buffer_iterator *it, void *buffer) 107 { 108 void *node; 109 110 if (!it || !buffer) { 111 return -EINVAL; 112 } 113 114 if (!it->node_capacity) { 115 return -ENODATA; 116 } 117 118 node = (uint8_t *)it->buffer + it->head * it->node_size; 119 120 memmove(buffer, node, it->node_size); 121 122 return 0; 123 } 124 125 126 static inline int ring_buffer_put(struct ring_buffer_iterator *it, void *buffer) 127 { 128 void *node; 129 130 if (!it || !buffer) { 131 return -EINVAL; 132 } 133 134 /* 對下方的問題1的解答: 135 佇列中有資料的情況下,head和tail還會有相等的場景麼。 136 我認為不會進入這種情況,這段程式碼是多餘的。 137 */ 138 if (it->node_available) { 139 140 // 新元素入隊待存放地址下標 什麼時候 等於 待出隊元素的地址下標呢?《== 問題1 141 if (it->tail == it->head) { 142 /* 143 * case of rolling to buffer over lapped 144 */ 145 if (it->node_capacity != 1) { 146 /* 147 * case of not single node capacity buffer 148 */ 149 if (it->tail == it->node_available - 1) { 150 /* 151 * case of rolling to end of buffer 152 */ 153 it->head = 0; 154 } else { 155 it->head = it->tail + 1; 156 } 157 } 158 159 } 160 } /* end of it->node_available */ 161 162 node = (uint8_t *)it->buffer + it->tail *it->node_size; 163 memmove(node, buffer, it->node_size); 164 165 if (it->tail == it->node_capacity - 1) { 166 /* 更新下一個入隊元素將要被存放的地址下標。 167 * rolling to end of buffer 168 */ 169 it->tail = 0; 170 } else { 171 it->tail++; 172 } 173 174 if (it->node_available != it->node_capacity) { 175 it->node_available++; 176 } 177 178 return 0; 179 } 180 181 static inline void ring_buffer_clear(struct ring_buffer_iterator *it) 182 { 183 if (!it) { 184 return; 185 } 186 187 it->head = 0; 188 it->tail = 0; 189 it->node_available = 0; 190 it->node_capacity = 0; 191 } 192 193 194 /* 195 * =1: the ring buffer is full 196 * =0: the ring buffer is not full 197 */ 198 static inline int ring_buffer_full(struct ring_buffer_iterator *it) 199 { 200 if (!it) { 201 return -EINVAL; 202 } 203 204 return it->node_available == it->node_capacity; 205 } 206 207 /* 208 * =1: the ring buffer is empty 209 * =0: the ring buffer is not empty 210 */ 211 static inline int ring_buffer_empty(struct ring_buffer_iterator *it) 212 { 213 if (!it) { 214 printf("it = NULL \n"); 215 return -EINVAL; 216 } 217 218 printf("it->node_available :[ %d ] \n", it->node_available); 219 220 return !(it->node_available); 221 } 222 223 static inline int ring_buffer_capacity(struct ring_buffer_iterator *it) 224 { 225 if (!it) { 226 return -EINVAL; 227 } 228 229 return (it->node_capacity); 230 } 231 232 static inline int ring_buffer_available(struct ring_buffer_iterator *it) 233 { 234 if (!it) { 235 return -EINVAL; 236 } 237 238 return (it->node_available); 239 } 240 241 242 243 #endif /* _RING_BUFFER_ITERATOR_H_ */

fifo_iterator.h

/*
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under  the terms of the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the License, or (at your
 *  option) any later version.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#ifndef _FIFO_ITERATOR_H_
#define _FIFO_ITERATOR_H_

#include "ring_buffer_iterator.h"

struct fifo_iterator {
    struct ring_buffer_iterator *ring_it;
};

// 建立一個迭代器,建立一個容器實,並讓該迭代器指向該容器。
static inline struct fifo_iterator *alloc_fifo_iterator(
        void *buffer, int size, int capacity)
{
    // 建立一個迭代器
    struct fifo_iterator *iterator = malloc(sizeof(struct fifo_iterator));

    if (!iterator) {
        return NULL;
    }

    // 建立一個容器,並讓該迭代器指向該容器。   
    iterator->ring_it = alloc_ring_buffer_iterator(buffer, size, capacity);
    if (!iterator->ring_it) {
        return NULL;
    }

    return iterator;
}

static inline void free_fifo_iterator(struct fifo_iterator *it)
{
    if (it && it->ring_it) {
        free_ring_buffer_iterator(it->ring_it);
    }

    if (it) {
        free(it);
    }
}

/*
 * =1: fifo buffer is full
 * =0: fifo buffer is not full
 */
static inline int fifo_full(struct fifo_iterator *it)
{
    return ring_buffer_full(it->ring_it);
}

/*
 * =1: fifo buffer is empty
 * =0: fifo buffer is not empty
 */
static inline int fifo_empty(struct fifo_iterator *it)
{
    return ring_buffer_empty(it->ring_it);
}

static inline int fifo_enqueue(struct fifo_iterator *it, void *buffer)
{
    if (ring_buffer_full(it->ring_it)) {
        return -EOVERFLOW;
    }

    static int i=0;
    printf("enQuence Success! %d \n", ++i);
    return ring_buffer_put(it->ring_it, buffer);
}

static inline int fifo_dequeue(struct fifo_iterator *it, void *buffer)
{
    return ring_buffer_get(it->ring_it, buffer);
}

static inline int fifo_peek(struct fifo_iterator *it, void *buffer)
{
    return ring_buffer_peek(it->ring_it, buffer);
}

#endif /* _FIFO_ITERATOR_H_ */

2. 測試程式碼展示

main.c

#include <stdio.h>
#include "fifo_iterator.h"

typedef struct _mydatastruct{

    int data1;
    int data2;

}mydatastruct;

mydatastruct data_t_in, data_t_out;

#define  fifo_array   4

int main()
{
    struct fifo_iterator * fifo_it = NULL;
    
    void* pbuff = malloc(sizeof(mydatastruct)*fifo_array);

    /*  alloc_fifo_iterator(void *buffer, int size, int capacity)函式內部 : 
            fifo_it->ring_it->buffer = buffer;
            fifo_it->ring_it->node_size = node_size;
            fifo_it->ring_it->node_capacity = node_capacity;  
    */
    fifo_it = alloc_fifo_iterator(pbuff, sizeof(mydatastruct), fifo_array);

    while(1)
    {

      //  in
      data_t_in.data1 = 88; 
      data_t_in.data2 = 89;
      if(0 != fifo_enqueue(fifo_it, &data_t_in))
        printf("Error\n");

      //  in
      data_t_in.data1 = 98;
      data_t_in.data2 = 99;
      if(0 != fifo_enqueue(fifo_it, &data_t_in))
        printf("Error\n");

       // out  
      fifo_dequeue(fifo_it, &data_t_out);
      printf("  fifo_dequeue :    \
          data_t_out.data1 [%d]   data_t_out.data2 [%d]  \n", \
                      data_t_out.data1, data_t_out.data2);
      //  in
      data_t_in.data1 = 100;
      if(0 != fifo_enqueue(fifo_it, &data_t_in))
        printf("Error\n");

       // out  
      fifo_dequeue(fifo_it, &data_t_out);
      printf("  fifo_dequeue :    \
          data_t_out.data1 [%d]   data_t_out.data2 [%d]  \n", \
                      data_t_out.data1, data_t_out.data2);

       // out  
      fifo_dequeue(fifo_it, &data_t_out);
      printf("  fifo_dequeue :    \
          data_t_out.data1 [%d]  data_t_out.data2 [%d]  \n", \
                      data_t_out.data1, data_t_out.data2);


      printf(" \n\n");
      sleep(1);
    }

    return 0;
}

makefile:

.PHONY: doit

doit:
    #mips-linux-gnu-gcc -g main.c -o  main_app
    gcc -g main.c -o  main_app

.