Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Queue in C via array

Tags:

c

queue

I have implemented a queue in C language with usage of an array of structures.

typedef struct{
    req_t     buffer[BUFFER_SIZE];   // buffer
    uint16_t    size;                          // length of the queue
    uint16_t    count;                 // number of elements present in the queue
    req_t     *p_head;                 // pointer to head of the queue (read end)
    req_t     *p_tail;                 // pointer to tail of the queue (write end)
}circular_buffer_t;

void init_cb(circular_buffer_t *p_cb){

    p_cb->p_head = p_cb->buffer;
    p_cb->p_tail = p_cb->buffer;
    p_cb->count = 0;
    p_cb->size = BUFFER_SIZE;

}

The problem is that above given implementation is usable only for storing the instances of req_t structures. Now I need to store instances of another structure and I don't know how to define the queue in more general way so that I will be able to use same queue for instances of different structures. Problem is that I need to know the structure type before buffer definition. Does anybody have any idea how to solve that?

    #ifndef CIRCULAR_BUFFER_H_
#define CIRCULAR_BUFFER_H_

#define BUFFER_SIZE 32

// macro creates variant of the queue for each struct type
#define define_queue(TYPE)                                                       \
                                                                               \
  // queue element definition                                                  \                                                                                
  typedef struct{                                                              \
    TYPE     buffer[BUFFER_SIZE];                                              \
    uint16_t size;                                                             \
    uint16_t count;                                                            \
    TYPE     *p_head;                                                          \
    TYPE     *p_tail;                                                          \
  }circular_buffer_##TYPE##_t                                                  \
                                                                               \
                                                                               \
  // queue init function definition                                            \                                                                               
  void init_cb_##TYPE(circular_buffer_##TYPE##_t *p_cb){                       \
    p_cb->p_head = p_cb->buffer;                                               \
      p_cb->p_tail = p_cb->buffer;                                               \
      p_cb->count = 0;                                                           \
      p_cb->size = BUFFER_SIZE;                                                  \
  }                                                                            \
                                                                               \
  // queue enqueue function definition                                         \                                                                               
  BOOL enqueue_cb_##TYPE(circular_buffer_##TYPE##_t *p_cb, TYPE *p_enq_elem){  \
                                                                               \    
    if(p_cb->count < p_cb->size){                                              \
                                                                               \
         taskENTER_CRITICAL();                                                     \
                                                                               \
            *(p_cb->p_tail) = *p_enq_elem;                                           \
            p_cb->p_tail = ((++(p_cb->p_tail) == (p_cb->buffer + p_cb->size)) ?      \
                      (p_cb->buffer) : (p_cb->p_tail));                        \
            p_cb->count++;                                                           \
                                                                               \
         taskEXIT_CRITICAL();                                                      \
                                                                               \
         return TRUE;                                                              \
                                                                               \
      }else{                                                                     \
                                                                               \
         return FALSE;                                                             \
                                                                               \
      }                                                                          \
                                                                               \
  }                                                                            \
                                                                               \
  // queue dequeue function definition                                         \                                                                               
  BOOL dequeue_cb_##TYPE(circular_buffer_##TYPE##_t *p_cb, TYPE *p_deq_elem){  \
                                                                               \
    if((p_cb->count) != 0){                                                    \
                                                                               \
        taskENTER_CRITICAL();                                                      \
                                                                               \
            *p_deq_elem = *(p_cb->p_head);                                           \
            p_cb->p_head = ((++(p_cb->p_head) == (p_cb->buffer + p_cb->size)) ?      \
                      (p_cb->buffer) : (p_cb->p_head));                        \
            p_cb->count--;                                                           \
                                                                               \
        taskEXIT_CRITICAL();                                                       \
                                                                               \
        return TRUE;                                                               \
                                                                               \
     }else{                                                                      \
                                                                               \
        return FALSE;                                                              \
                                                                               \
     }                                                                           \
                                                                               \
  }                                                                            \


// macros for functions declarations
#define declare_init_cb(TYPE)    void init_cb_##TYPE(circular_buffer_##TYPE##_t *p_cb)
#define declare_enqueue_cb(TYPE) BOOL enqueue_cb_##TYPE(circular_buffer_##TYPE##_t *p_cb, TYPE p_enq_elem);
#define declare_dequeue_cb(TYPE) BOOL dequeue_cb_##TYPE(circular_buffer_##TYPE##_t *p_cb, TYPE p_deq_elem);                                                

#endif

Structures I am going to use with the queue

    typedef struct{
    uint32_t addr;          // address of the alarm signal
    BOOL     critical;      // alarm is critical (=TRUE), alarm is non critical (=FALSE)
    BOOL     set;           // alarm was set     (=TRUE)
    BOOL     cleared;       // alarm was cleared (=TRUE)
    BOOL     communicated;  // alarm is communicated to Main Controller (=TRUE)
    uint8_t  code;          // alarm code   (0 - 255) - permanently 180
    uint8_t  no;            // alarm number (0 - 255)
    uint8_t  no_flashes;    // number of LED flashes if the alarm is active
}alarm_t;

and

 typedef struct{
    msg_e req_type;                              // request type
    uint8_t         blk_no;                      // block number
    uint8_t         no_records;                  // number of influenced records
    uint8_t         data_id[MAX_NO_RECORDS];     // data id, max. number of records in one block
    uint16_t        value[MAX_NO_RECORDS];       // written value, max. number of records in one block
    uint8_t         cleared_alarm_no;            // number of the alarm which should be cleared
    uint8_t         flash_load;                  // 0 = Go into flash load mode
    uint8_t         mode[6];                     // 000000 - Normal, BOOTBL - Boot block
    uint8_t         data_block[BLOCK_SIZE];      // one block in flash memory
    uint8_t         flash_page_number;           // page number in flash memory (starting at 1)
    uint8_t         flash_block_number;          // block number in flash memory (starting at 1)
}req_t;
like image 628
Steve Avatar asked Dec 10 '22 10:12

Steve


2 Answers

If you want to store any type of struct in your queue, you have to use void * type and store in the queue only the pointers to any structs.

typedef struct{
    void      *buffer[BUFFER_SIZE];   // buffer
    uint16_t  size;                          // length of the queue
    uint16_t  count;                 // number of elements present in the queue
    void      *p_head;                 // pointer to head of the queue (read end)
    void      *p_tail;                 // pointer to tail of the queue (write end)
}circular_buffer_t;

Then, you have just to put any pointer in your queue like this:

circular_buffer_t p_cb;
my_struct_t       *my_struct = malloc(sizeof(my_struct_t));

// set
p_cb.buffer[0] = (void*)my_struct;

// get
(my_struct_t*)p_cb.buffer[0];
like image 120
Thomas Arbona Avatar answered Dec 28 '22 01:12

Thomas Arbona


1. Storing structs by value

If you specify the struct size when creating the queue, you can use it to store actual structs (copied by value) into the buffer.

typedef struct {
    u32 capacity;
    u32 element_size;
    u8 * head;    // next free slot 
    u8 * tail;    // oldest enqueued item
    u8 * buffer;
    u8 * buffer_end;
} circular_buffer_t;

void circbuff_init(circular_buffer_t *p_cb, u8 *buffer, u32 element_size, u32 capacity)
{
    p_cb->capacity = capacity;
    p_cb->element_size = element_size;
    p_cb->buffer = buffer;
    p_cb->buffer_end = buffer + (capacity * element_size);
    p_cb->head = buffer;
    p_cb->tail = buffer;
}

Note that .count is redundant, you can calculate it at any time, and removing it makes reader/writer syncronization easier (in case that you read and write from different interrupts).

You need to take care to pass the correct buffer size and element_size:

circbuff_init(p_cb, buffer, sizeof(SomeStruct), sizeof(buffer) / sizeof(SomeStruct));

And then you just copy each element:

bool circbuff_dequeue(circular_buffer_t *hnd, void *dst)
{
    // if empty, do nothing
    if (circbuff_isEmpty(hnd))
        return false;

    memcpy(dst, hnd->tail, hnd->element_size);
    hnd->tail = modulo_increment(hnd, hnd->tail);
    return true;
}

2. Storing pointers to structs

This is already mentioned in some other answer.

3. Using a macro to create a typed buffer for each struct type

This is similar to how klib works. You would have to call certain macros to define each concrete type of circular buffer (for each struct), but then you would have compile time type safety.

like image 25
Groo Avatar answered Dec 27 '22 23:12

Groo