How do you implement a circular buffer in C?

后端 未结 8 1959
滥情空心
滥情空心 2020-11-28 00:53

I have a need for a fixed-size (selectable at run-time when creating it, not compile-time) circular buffer which can hold objects of any type and it needs to be very

相关标签:
8条回答
  • 2020-11-28 01:15

    A simple implementation could consist of:

    • A buffer, implemented as an array of size n, of whatever type you need
    • A read pointer or index (whichever is more efficient for your processor)
    • A write pointer or index
    • A counter indicating how much data is in the buffer (derivable from the read and write pointers, but faster to track it separately)

    Every time you write data, you advance the write pointer and increment the counter. When you read data, you increase the read pointer and decrement the counter. If either pointer reaches n, set it to zero.

    You can't write if counter = n. You can't read if counter = 0.

    0 讨论(0)
  • 2020-11-28 01:16

    Extending adam-rosenfield's solution, i think the following will work for multithreaded single producer - single consumer scenario.

    int cb_push_back(circular_buffer *cb, const void *item)
    {
      void *new_head = (char *)cb->head + cb->sz;
      if (new_head == cb>buffer_end) {
          new_head = cb->buffer;
      }
      if (new_head == cb->tail) {
        return 1;
      }
      memcpy(cb->head, item, cb->sz);
      cb->head = new_head;
      return 0;
    }
    
    int cb_pop_front(circular_buffer *cb, void *item)
    {
      void *new_tail = cb->tail + cb->sz;
      if (cb->head == cb->tail) {
        return 1;
      }
      memcpy(item, cb->tail, cb->sz);
      if (new_tail == cb->buffer_end) {
        new_tail = cb->buffer;
      }
      cb->tail = new_tail;
      return 0;
    }
    
    0 讨论(0)
  • 2020-11-28 01:17
    // Note power of two buffer size
    #define kNumPointsInMyBuffer 1024 
    
    typedef struct _ringBuffer {
        UInt32 currentIndex;
        UInt32 sizeOfBuffer;
        double data[kNumPointsInMyBuffer];
    } ringBuffer;
    
    // Initialize the ring buffer
    ringBuffer *myRingBuffer = (ringBuffer *)calloc(1, sizeof(ringBuffer));
    myRingBuffer->sizeOfBuffer = kNumPointsInMyBuffer;
    myRingBuffer->currentIndex = 0;
    
    // A little function to write into the buffer
    // N.B. First argument of writeIntoBuffer() just happens to have the
    // same as the one calloc'ed above. It will only point to the same
    // space in memory if the calloc'ed pointer is passed to
    // writeIntoBuffer() as an arg when the function is called. Consider
    // using another name for clarity
    void writeIntoBuffer(ringBuffer *myRingBuffer, double *myData, int numsamples) {
        // -1 for our binary modulo in a moment
        int buffLen = myRingBuffer->sizeOfBuffer - 1;
        int lastWrittenSample = myRingBuffer->currentIndex;
    
        int idx;
        for (int i=0; i < numsamples; ++i) {
            // modulo will automagically wrap around our index
            idx = (i + lastWrittenSample) & buffLen; 
            myRingBuffer->data[idx] = myData[i];
        }
    
        // Update the current index of our ring buffer.
        myRingBuffer->currentIndex += numsamples;
        myRingBuffer->currentIndex &= myRingBuffer->sizeOfBuffer - 1;
    }
    

    As long as your ring buffer's length is a power of two, the incredibly fast binary "&" operation will wrap around your index for you. For my application, I'm displaying a segment of audio to the user from a ring buffer of audio acquired from a microphone.

    I always make sure that the maximum amount of audio that can be displayed on screen is much less than the size of the ring buffer. Otherwise you might be reading and writing from the same chunk. This would likely give you weird display artifacts.

    0 讨论(0)
  • 2020-11-28 01:19

    Can you enumerate the types needed at the time you code up the buffer, or do you need to be able to add types at run time via dynamic calls? If the former, then I would create the buffer as a heap-allocated array of n structs, where each struct consists of two elements: an enum tag identifying the data type, and a union of all the data types. What you lose in terms of extra storage for small elements, you make up in terms of not having to deal with allocation/deallocation and the resulting memory fragmentation. Then you just need to keep track of the start and end indices that define the head and tail elements of the buffer, and make sure to compute mod n when incrementing/decrementing the indices.

    0 讨论(0)
  • 2020-11-28 01:27

    C style, simple ring buffer for integers. First use init than use put and get. If buffer does not contain any data it returns "0" zero.

    //=====================================
    // ring buffer address based
    //=====================================
    #define cRingBufCount   512
    int     sRingBuf[cRingBufCount];    // Ring Buffer
    int     sRingBufPut;                // Input index address
    int     sRingBufGet;                // Output index address
    Bool    sRingOverWrite;
    
    void    GetRingBufCount(void)
    {
    int     r;
    `       r= sRingBufPut - sRingBufGet;
            if ( r < cRingBufCount ) r+= cRingBufCount;
            return r; 
    }
    
    void    InitRingBuffer(void)
    {
            sRingBufPut= 0;
            sRingBufGet= 0;
    }       
    
    void    PutRingBuffer(int d)
    {
            sRingBuffer[sRingBufPut]= d;
            if (sRingBufPut==sRingBufGet)// both address are like ziro
            {
                sRingBufPut= IncRingBufferPointer(sRingBufPut);
                sRingBufGet= IncRingBufferPointer(sRingBufGet);
            }
            else //Put over write a data
            {
                sRingBufPut= IncRingBufferPointer(sRingBufPut);
                if (sRingBufPut==sRingBufGet)
                {
                    sRingOverWrite= Ture;
                    sRingBufGet= IncRingBufferPointer(sRingBufGet);
                }
            }
    }
    
    int     GetRingBuffer(void)
    {
    int     r;
            if (sRingBufGet==sRingBufPut) return 0;
            r= sRingBuf[sRingBufGet];
            sRingBufGet= IncRingBufferPointer(sRingBufGet);
            sRingOverWrite=False;
            return r;
    }
    
    int     IncRingBufferPointer(int a)
    {
            a+= 1;
            if (a>= cRingBufCount) a= 0;
            return a;
    }
    
    0 讨论(0)
  • 2020-11-28 01:33

    Here is a simple solution in C. Assume interrupts are turned off for each function. No polymorphism & stuff, just common sense.


    #define BUFSIZE 128
    char buf[BUFSIZE];
    char *pIn, *pOut, *pEnd;
    char full;
    
    // init
    void buf_init()
    {
        pIn = pOut = buf;       // init to any slot in buffer
        pEnd = &buf[BUFSIZE];   // past last valid slot in buffer
        full = 0;               // buffer is empty
    }
    
    // add char 'c' to buffer
    int buf_put(char c)
    {
        if (pIn == pOut  &&  full)
            return 0;           // buffer overrun
    
        *pIn++ = c;             // insert c into buffer
        if (pIn >= pEnd)        // end of circular buffer?
            pIn = buf;          // wrap around
    
        if (pIn == pOut)        // did we run into the output ptr?
            full = 1;           // can't add any more data into buffer
        return 1;               // all OK
    }
    
    // get a char from circular buffer
    int buf_get(char *pc)
    {
        if (pIn == pOut  &&  !full)
            return 0;           // buffer empty  FAIL
    
        *pc = *pOut++;              // pick up next char to be returned
        if (pOut >= pEnd)       // end of circular buffer?
            pOut = buf;         // wrap around
    
        full = 0;               // there is at least 1 slot
        return 1;               // *pc has the data to be returned
    }
    
    0 讨论(0)
提交回复
热议问题