Comment mettre en œuvre une liste circulaire (ring buffer) en C?

Comment puis-je mettre en œuvre une liste circulaire qui remplace la plus ancienne entrée lorsqu'elle est pleine?

pour un peu de fond, je veux utiliser une liste circulaire à L'intérieur de GWT; donc en utilisant une lib tierce partie est ce que je veux.

29
demandé sur George Stocker 2008-10-19 00:59:26

7 réponses

une implémentation très simple, exprimée en C. implémente une file D'attente circulaire de style buffer FIFO. Pourrait être rendu plus générique en créant une structure contenant la taille de la file d'attente, les données de la file d'attente, et les index de la file d'attente (entrée et sortie), qui seraient passés avec les données à ajouter ou supprimer de la file d'attente. Ces mêmes routines pourraient alors gérer plusieurs files d'attente. Notez également que cela permet des files d'attente de n'importe quelle taille, bien que des raccourcis puissent être utilisés si vous utilisez des puissances de 2 et personnalisez le code plus.

/* Very simple queue
 * These are FIFO queues which discard the new data when full.
 *
 * Queue is empty when in == out.
 * If in != out, then 
 *  - items are placed into in before incrementing in
 *  - items are removed from out before incrementing out
 * Queue is full when in == (out-1 + QUEUE_SIZE) % QUEUE_SIZE;
 *
 * The queue will hold QUEUE_ELEMENTS number of items before the
 * calls to QueuePut fail.
 */

/* Queue structure */
#define QUEUE_ELEMENTS 100
#define QUEUE_SIZE (QUEUE_ELEMENTS + 1)
int Queue[QUEUE_SIZE];
int QueueIn, QueueOut;

void QueueInit(void)
{
    QueueIn = QueueOut = 0;
}

int QueuePut(int new)
{
    if(QueueIn == (( QueueOut - 1 + QUEUE_SIZE) % QUEUE_SIZE))
    {
        return -1; /* Queue Full*/
    }

    Queue[QueueIn] = new;

    QueueIn = (QueueIn + 1) % QUEUE_SIZE;

    return 0; // No errors
}

int QueueGet(int *old)
{
    if(QueueIn == QueueOut)
    {
        return -1; /* Queue Empty - nothing to get*/
    }

    *old = Queue[QueueOut];

    QueueOut = (QueueOut + 1) % QUEUE_SIZE;

    return 0; // No errors
}
40
répondu Adam Davis 2010-10-09 16:06:36

si vous voulez une liste circulaire de longueur fixe. Vous pouvez utiliser un (dynamique) tableau. Utilisez deux variables pour la tenue de la maison. L'un pour la position de l'élément suivant, l'un pour compter le nombre d'éléments.

Mettre: placez l'élément sur la place de libre. déplacer la position (longueur modulo). Ajouter 1 au nombre à moins que le nombre soit égal à la longueur de la liste. Obtenir: seulement si le compte>0. déplacer la position vers la gauche (Longueur modulo). Décrémentez le comte.

2
répondu Toon Krijthe 2008-10-18 21:16:08

Utiliser une liste chaînée. Maintenir des pointeurs pour la tête et la queue. Sortez de la tête de liste, poussez sur la queue. Si vous le souhaitez circulaire, assurez-vous que la nouvelle queue pointe toujours à la tête.

je peux comprendre pourquoi vous voulez implémenter un FIFO en utilisant une liste liée, mais pourquoi en faire une liste circulaire?

1
répondu tvanfosson 2008-10-18 21:12:22

utilisez un tableau et conservez une variable P avec la première position disponible.

Augmentation des P chaque fois que vous ajoutez un nouvel élément.

À savoir l'équivalent de l'indice de P dans votre tableau n' (P % n) où n est la taille de votre tableau.

1
répondu Lucia 2008-10-18 21:12:48

j'utilise pour mon microcontrôleur. Pour simplifier le code, un octet ne sera pas rempli. Alias size - 1 est la pleine capacité en fait.

fifo_t* createFifoToHeap(size_t size)
{
    byte_t* buffer = (byte_t*)malloc(size);

    if (buffer == NULL)
        return NULL;

    fifo_t* fifo = (fifo_t*)malloc(sizeof(fifo_t));

    if (fifo == NULL)
    {
       free(buffer);
       return NULL;
    }

    fifo->buffer = buffer;
    fifo->head = 0;
    fifo->tail = 0;
    fifo->size = size;

    return fifo;
}

#define CHECK_FIFO_NULL(fifo) MAC_FUNC(if (fifo == NULL) return 0;)

size_t fifoPushByte(fifo_t* fifo, byte_t byte)
{
    CHECK_FIFO_NULL(fifo);

    if (fifoIsFull(fifo) == true)
       return 0;

    fifo->buffer[fifo->head] = byte;

    fifo->head++;
    if (fifo->head == fifo->size)
       fifo->head = 0;

    return 1;
}

size_t fifoPushBytes(fifo_t* fifo, byte_t* bytes, size_t count)
{
    CHECK_FIFO_NULL(fifo);

    for (uint32_t i = 0; i < count; i++)
    {
        if (fifoPushByte(fifo, bytes[i]) == 0)
            return i;
    }

    return count;
}

size_t fifoPopByte(fifo_t* fifo, byte_t* byte)
{
    CHECK_FIFO_NULL(fifo);

    if (fifoIsEmpty(fifo) == true)
        return 0;

    *byte = fifo->buffer[fifo->tail];

    fifo->tail++;
    if (fifo->tail == fifo->size)
        fifo->tail = 0;

    return 1;
}

size_t fifoPopBytes(fifo_t* fifo, byte_t* bytes, size_t count)
{
    CHECK_FIFO_NULL(fifo);

    for (uint32_t i = 0; i < count; i++)
    {
        if (fifoPopByte(fifo, bytes + i) == 0)
            return i;
    }

    return count;
}

bool fifoIsFull(fifo_t* fifo)
{
    if ((fifo->head == (fifo->size - 1) && fifo->tail == 0) || (fifo->head == (fifo->tail - 1)))
        return true;
    else
        return false;
}

bool fifoIsEmpty(fifo_t* fifo)
{
    if (fifo->head == fifo->tail)
        return true;
    else
        return false;
}

size_t fifoBytesFilled(fifo_t* fifo)
{
    if (fifo->head == fifo->tail)
        return 0;
    else if ((fifo->head == (fifo->size - 1) && fifo->tail == 0) || (fifo->head == (fifo->tail - 1)))
        return fifo->size;
    else if (fifo->head < fifo->tail)
        return (fifo->head) + (fifo->size - fifo->tail);
    else
        return fifo->head - fifo->tail; 
}
1
répondu arapEST 2013-10-31 11:04:50

je ne pense pas que la file d'attente est la meilleure façon de faire un cache. Vous voulez être votre cache, à très vite! Et faire un balayage linéaire de votre file d'attente n'est pas la solution sauf si vous voulez que votre cache soit vraiment petit ou que votre mémoire soit vraiment limitée.

en supposant que vous ne voulez pas d'un très petit cache ou d'un cache lent, l'utilisation d'une liste liée avec une carte de hachage de valeur au noeud dans la liste liée est une bonne façon de procéder. Vous pouvez toujours expulser la tête, et chaque fois qu'un élément est accessible, vous pouvez le retirer et le mettre dans la tête de la liste. Pour y accéder, vous pouvez l'obtenir directement ou vérifier si elle est dans le cache dans O(1). L'éviction d'un élément est aussi O (1) et donc la mise à jour de la liste.

pour un exemple, regardez LinkedHashMap en java.

http://docs.oracle.com/javase/6/docs/api/java/util/LinkedHashMap.html

0
répondu Neo M Hacker 2013-11-04 04:24:19

Voici une façon élégante de créer dynamiquement augmenter/de diminuer la file d'attente circulaire en utilisant java.

j'ai commenté la plus grande partie du code pour une compréhension facile et rapide. Espérons que cela aide :)

    public class CircularQueueDemo {
    public static void main(String[] args) throws Exception {

        CircularQueue queue = new CircularQueue(2);
        /* dynamically increasing/decreasing circular queue */
        System.out.println("--dynamic circular queue--");
        queue.enQueue(1);
        queue.display();
        queue.enQueue(2);
        queue.display();
        queue.enQueue(3);
        queue.display();
        queue.enQueue(4);
        queue.display();
        queue.deQueue();
        queue.deQueue();
        queue.enQueue(5);
        queue.deQueue();    
        queue.display();

    }
}

class CircularQueue {
    private int[] queue;
    public int front;
    public int rear;
    private int capacity;

    public CircularQueue(int cap) {
        front = -1;
        rear = -1;
        capacity = cap;
        queue = new int[capacity];
    }

    public boolean isEmpty() {
        return (rear == -1);
    }

    public boolean isFull() {
        if ((front == 0 && rear == capacity - 1) || (front == rear + 1))
            return true;
        else
            return false;
    }

    public void enQueue(int data) { 
        if (isFull()) {            //if queue is full then expand it dynamically   
            reSize();                    
            enQueue(data);
        } else {                                 //else add the data to the queue
            if (rear == -1)                      //if queue is empty
                rear = front = 0;
            else if (rear == capacity)          //else if rear reached the end of array then place rear to start (circular array)
                rear = 0;
            else
                rear++;                         //else just incement the rear 
            queue[rear] = data;                 //add the data to rear position
        }
    }

    public void reSize() {
        int new_capacity = 2 * capacity;                  //create new array of double the prev size
        int[] new_array = new int[new_capacity];          

        int prev_size = getSize();                        //get prev no of elements present
        int i = 0;                                        //place index to starting of new array

        while (prev_size >= 0) {                          //while elements are present in prev queue
            if (i == 0) {                                 //if i==0 place the first element to the array
                new_array[i] = queue[front++];
            } else if (front == capacity) {               //else if front reached the end of array then place rear to start (circular array) 
                front = 0;
                new_array[i] = queue[front++];
            } else                                        //else just increment the array
                new_array[i] = queue[front++];
            prev_size--;                                  //keep decreasing no of element as you add the elements to the new array
            i++;                                          //increase the index of new array
        }
        front = 0;                                        //assign front to 0
        rear = i-1;                                       //assign rear to the last index of added element
        capacity=new_capacity;                            //assign the new capacity
        queue=new_array;                                  //now queue will point to new array (bigger circular array)
    }

    public int getSize() {
        return (capacity - front + rear) % capacity;                  //formula to get no of elements present in circular queue
    }

    public int deQueue() throws Exception {
        if (isEmpty())                                       //if queue is empty
            throw new Exception("Queue is empty");
        else {
            int item = queue[front];                        //get item from front
            if (front == rear)                              //if only one element
                front = rear = -1;
            else if (front == capacity)                     //front reached the end of array then place rear to start (circular array)
                front = 0;
            else
                front++;                                    //increment front by one
            decreaseSize();                                 //check if size of the queue can be reduced to half
            return item;                                    //return item from front
        }

    }

    public void decreaseSize(){                           //function to decrement size of circular array dynamically
        int prev_size = getSize();
        if(prev_size<capacity/2){                         //if size is less than half of the capacity
            int[] new_array=new int[capacity/2];          //create new array of half of its size
            int index=front;                              //get front index
            int i=0;                                      //place an index to starting of new array (half the size)
            while(prev_size>=0){                          //while no of elements are present in the queue
                if(i==0)                                  //if index==0 place the first element
                    new_array[i]=queue[front++];
                else if(front==capacity){                 //front reached the end of array then place rear to start (circular array)      
                    front=0;
                    new_array[i]=queue[front++];
                }
                else
                    new_array[i]=queue[front++];         //else just add the element present in index of front
                prev_size--;                             //decrease the no of elements after putting to new array 
                i++;                                     //increase the index of i
            }
            front=0;                                     //assign front to 0
            rear=i-1;                                    //assign rear to index of last element present in new array(queue)
            capacity=capacity/2;                         //assign new capacity (half the size of prev)
            queue=new_array;                             //now queue will point to new array (or new queue)
        }
    }

    public void display() {                           //function to display queue
        int size = getSize();
        int index = front;

        while (size >= 0) {
            if (isEmpty())
                System.out.println("Empty queue");
            else if (index == capacity)
                index = 0;
            System.out.print(queue[index++] + "=>");
            size--;
        }
        System.out.println("  Capacity: "+capacity);

    }

}

Sortie:

--dynamique de la file d'attente circulaire--

1 = > Capacité: 2

1=>2=> Capacité: 2

1=>2=>3=> Capacité: 4

1=>2=>3=>4=> Capacité d'accueil: 4

4=>5 = > Capacité: 2

0
répondu Prateek Joshi 2016-01-06 01:12:37