programing

C에서 순환 버퍼를 구현하려면 어떻게 해야 합니까?

prostudy 2022. 6. 9. 22:07
반응형

C에서 순환 버퍼를 구현하려면 어떻게 해야 합니까?

어떤 유형의 오브젝트도 저장할 수 있는 고정 크기(컴파일 시간이 아닌 런타임에 선택 가능) 원형 버퍼가 필요하며 매우 고성능이어야 합니다.멀티태스킹 임베디드 환경이지만 공동 작업이기 때문에 리소스 경합 문제는 없을 것입니다.

처음에 생각한 것은 타입(단순 열거/정의)과 페이로드에 대한 보이드 포인터를 포함하는 단순한 구조를 버퍼에 저장하는 것이었습니다만, 가능한 한 고속으로 하고 싶기 때문에, 힙 바이패스를 포함한 제안을 받아들일 수 있습니다.

표준 중 만족스러운 되어 것 .코드에서 본 바로는 CPU에 크게 최적화되어 있지 않습니다.C코드를 컴파일 했을 뿐입니다.strcpy()손으로 코딩한 조립품도 없어요

어떤 코드나 아이디어라도 매우 감사할 것입니다.필요한 조작은 다음과 같습니다.

  • 특정 크기의 버퍼를 만듭니다.
  • 꽁무니를 빼다
  • 머리에서부터 꺼내다
  • 카운트를 반환하다
  • 버퍼를 삭제하다

가장 간단한 해결책은 항목 크기와 항목 수를 추적한 다음 적절한 바이트 수의 버퍼를 생성하는 것입니다.

typedef struct circular_buffer
{
    void *buffer;     // data buffer
    void *buffer_end; // end of data buffer
    size_t capacity;  // maximum number of items in the buffer
    size_t count;     // number of items in the buffer
    size_t sz;        // size of each item in the buffer
    void *head;       // pointer to head
    void *tail;       // pointer to tail
} circular_buffer;

void cb_init(circular_buffer *cb, size_t capacity, size_t sz)
{
    cb->buffer = malloc(capacity * sz);
    if(cb->buffer == NULL)
        // handle error
    cb->buffer_end = (char *)cb->buffer + capacity * sz;
    cb->capacity = capacity;
    cb->count = 0;
    cb->sz = sz;
    cb->head = cb->buffer;
    cb->tail = cb->buffer;
}

void cb_free(circular_buffer *cb)
{
    free(cb->buffer);
    // clear out other fields too, just to be safe
}

void cb_push_back(circular_buffer *cb, const void *item)
{
    if(cb->count == cb->capacity){
        // handle error
    }
    memcpy(cb->head, item, cb->sz);
    cb->head = (char*)cb->head + cb->sz;
    if(cb->head == cb->buffer_end)
        cb->head = cb->buffer;
    cb->count++;
}

void cb_pop_front(circular_buffer *cb, void *item)
{
    if(cb->count == 0){
        // handle error
    }
    memcpy(item, cb->tail, cb->sz);
    cb->tail = (char*)cb->tail + cb->sz;
    if(cb->tail == cb->buffer_end)
        cb->tail = cb->buffer;
    cb->count--;
}

버퍼를 코드화할 때 필요한 유형을 열거할 수 있습니까, 아니면 동적 콜을 통해 런타임에 유형을 추가할 수 있습니까?전자의 경우 버퍼를 n개의 구조체의 힙 할당 배열로 만듭니다.여기서 각 구조체는 데이터 유형을 식별하는 열거 태그와 모든 데이터 유형의 결합이라는2개의 요소로 구성됩니다.소규모 요소의 추가 스토리지에서 손실되는 것은 할당/배분 및 그에 따른 메모리 단편화를 처리할 필요가 없다는 점에서 보충할 수 있습니다.그런 다음 버퍼의 선두 및 테일 요소를 정의하는 시작 및 종료 인덱스를 추적하고 인덱스를 증가/감소할 때 mod n을 계산해야 합니다.

@Adam Rosenfield의 솔루션은 정확하지만 보다 경량화된 솔루션으로 구현할 수 있습니다.circular_buffer하지 않는 count ★★★★★★★★★★★★★★★★★」capacity.

이 구조에는 다음 4개의 포인터만 저장할 수 있습니다.

  • buffer 내 . 메모리 내 버퍼의 시작을 나타냅니다.
  • buffer_end 내의 을 사용법
  • head된 데이터의 저장된 데이터의 끝을 가리킵니다.
  • tail된 데이터의 저장된 데이터의 시작을 가리킵니다.

는 그 돈을 수 있었다.sz속성을 사용하여 저장 단위를 매개 변수 지정할 수 있습니다.

countcapacity위의 포인터를 사용하여 값을 도출할 수 있어야 합니다.

용량.

capacity직진형입니다.이것은, 거리를 분할하는 것으로 얻을 수 있기 때문입니다.buffer_end 및 ""buffer sz(일부러)

capacity = (buffer_end - buffer) / sz

세어보세요

하지만 계산해보니 일이 좀 더 복잡해져요. 찼는지 할 수 방법은 .head ★★★★★★★★★★★★★★★★★」tail같은 위치를 가리키고 있습니다.

이 문제를 해결하려면 버퍼는 추가 요소에 메모리를 할당해야 합니다.를 들어,의 원하는 이 「」인 , 「」는 「」입니다.10 * sz 그럼 을 요.11 * sz.

용량 공식은 다음과 같습니다(아래의 스니펫은 의사 코드입니다).

capacity_bytes = buffer_end - buffer - sz
capacity = capacity_bytes / sz

이 추가 요소 시맨틱을 통해 버퍼가 비어 있는지 또는 꽉 찼는지를 평가하는 조건을 구성할 수 있습니다.

빈 상태 조건

, 「」를 해 주세요.head 있습니다.tail★★★★★★★★★★★★★★★★★★:

head == tail

위의 값이 true로 평가될 경우 버퍼는 비어 있습니다.

완전 상태 조건

, 「」가 합니다.head는 1보다 뒤에 .tail즉, 따 the에서 한 공간입니다.head:tail로케이션은 다음과 같아야 합니다.1 * sz.

tail 크다head:

tail - head == sz

위의 값이 true로 평가되면 버퍼가 꽉 찬 것입니다.

head 크다tail:

  1. buffer_end - head는 점프할 합니다.head버퍼 끝에 도달합니다.
  2. tail - buffer버퍼 시작에서 '테일'로 점프하기 위해 필요한 공간을 반환합니다.
  3. 를 더하는 것은 에서 점프하는 데 .headtail
  4. 을 넘지 합니다.1 * sz
(buffer_end - head) + (tail - buffer) == sz
=> buffer_end - buffer - head + tail == sz
=> buffer_end - buffer - sz == head - tail
=> head - tail == buffer_end - buffer - sz
=> head - tail == capacity_bytes

위의 값이 true로 평가되면 버퍼가 꽉 찬 것입니다.

실제로

의 @ Rosenfield를 @Adam Rosenfield를합니다.circular_buffer★★★★

#include <string.h>

#define CB_SUCCESS 0        /* CB operation was successful */
#define CB_MEMORY_ERROR 1   /* Failed to allocate memory */
#define CB_OVERFLOW_ERROR 2 /* CB is full. Cannot push more items. */
#define CB_EMPTY_ERROR 3    /* CB is empty. Cannot pop more items. */

typedef struct circular_buffer {
  void *buffer;
  void *buffer_end;
  size_t sz;
  void *head;
  void *tail;
} circular_buffer;

int cb_init(circular_buffer *cb, size_t capacity, size_t sz) {
  const int incremented_capacity = capacity + 1; // Add extra element to evaluate count
  cb->buffer = malloc(incremented_capacity * sz);
  if (cb->buffer == NULL)
    return CB_MEMORY_ERROR;
  cb->buffer_end = (char *)cb->buffer + incremented_capacity * sz;
  cb->sz = sz;
  cb->head = cb->buffer;
  cb->tail = cb->buffer;
  return CB_SUCCESS;
}

int cb_free(circular_buffer *cb) {
  free(cb->buffer);
  return CB_SUCCESS;
}

const int _cb_length(circular_buffer *cb) {
  return (char *)cb->buffer_end - (char *)cb->buffer;
}

int cb_push_back(circular_buffer *cb, const void *item) {
  const int buffer_length = _cb_length(cb);
  const int capacity_length = buffer_length - cb->sz;

  if ((char *)cb->tail - (char *)cb->head == cb->sz ||
      (char *)cb->head - (char *)cb->tail == capacity_length)
    return CB_OVERFLOW_ERROR;

  memcpy(cb->head, item, cb->sz);

  cb->head = (char*)cb->head + cb->sz;
  if(cb->head == cb->buffer_end)
    cb->head = cb->buffer;

  return CB_SUCCESS;
}

int cb_pop_front(circular_buffer *cb, void *item) {
  if (cb->head == cb->tail)
    return CB_EMPTY_ERROR;

  memcpy(item, cb->tail, cb->sz);

  cb->tail = (char*)cb->tail + cb->sz;
  if(cb->tail == cb->buffer_end)
    cb->tail = cb->buffer;

  return CB_SUCCESS;
}

// 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;
}

링 버퍼의 길이가 2의 거듭제곱인 한 엄청나게 빠른 바이너리 "&" 연산이 인덱스를 감싸줍니다.제 어플리케이션에서는 마이크에서 취득한 오디오의 링 버퍼에서 사용자에게 오디오 세그먼트를 표시하고 있습니다.

화면에 표시할 수 있는 최대 오디오 양이 링 버퍼 크기보다 훨씬 적은지 항상 확인합니다.그렇지 않으면 동일한 청크에서 읽고 쓸 수 있습니다.이로 인해 이상한 디스플레이 아티팩트가 발생할 수 있습니다.

여기 C의 간단한 솔루션이 있습니다.각 기능에 대해 인터럽트가 꺼졌다고 가정합니다.다형성이나 그런 거 말고 그냥 상식적인 거.


#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
}

첫 번째 제목입니다.bit int를 사용하여 head와 tail의 "pointers"를 고정하고 사이즈를 조정하여 완전히 동기화하면 버퍼를 랩하기 위한 모듈로 연산이 필요하지 않습니다.IE: 12비트 부호 없는 int에 4096을 채우면 그 자체로 0이 됩니다.어떤 식으로든 문제가 발생하지 않습니다.2의 거듭제곱이라도 모듈로 연산을 제거하면 속도가 거의 정확하게 두 배로 증가합니다.

3세대 i7 Dell XPS 8500에서는 기본 인라인 기능을 탑재한 Visual Studio 2010의 C++ 컴파일러를 사용하여 모든 유형의 데이터 요소 4096 버퍼를 1,000만 번 반복하고 데이터를 처리하는 데 52초가 걸립니다.또, 그 1/8192분의 1이 걸립니다.

테스트 루프를 main()으로 고쳐 쓰는 것으로써, 플로우를 제어하지 않게 됩니다.이것은 버퍼가 가득 찼거나 비어 있는 것을 나타내는 반환값과 어텐던트가 break; 스테이트먼트에 의해 제어되어야 합니다.IE: 필러와 드레이너는 파손이나 불안정성 없이 서로 부딪힐 수 있어야 합니다.언젠가 이 코드를 멀티 스레드화하고 싶기 때문에 이 동작은 매우 중요합니다.

QUUE_DESC(큐 기술자) 및 초기화 함수는 이 코드 내의 모든 버퍼를 강제로 2의 거듭제곱합니다.그렇지 않으면 위의 계획은 작동하지 않습니다.QUUE_DESC는 하드코드가 아닌 매니페스트 상수(#define BITS_EL_KNT)를 사용하여 구축합니다.(여기서는 2의 거듭제곱으로 충분한 유연성이 있다고 가정합니다.)

버퍼 크기를 선택할 수 있도록 하기 위해 다른 접근법(여기에는 표시되지 않음)을 시도하여 FIFO 버퍼를 관리할 수 있는 Head, Tail, EleKnt에 USHRT를 사용하기로 결정했습니다.모듈로 연산을 피하기 위해 Head, Tail과 & &로 마스크를 작성했는데, 그 마스크는 (EleKnt -1)이 되었습니다.비트 입력 대신 USHRTS를 사용하면 조용한 기계에서 성능이 최대 15% 향상됩니다.인텔의 CPU 코어는 항상 버스보다 고속이기 때문에 사용량이 많은 공유 머신에서는 데이터 구조를 패킹하면 다른 경쟁 스레드보다 먼저 로드 및 실행할 수 있습니다.트레이드오프

버퍼의 실제 스토리지는 calloc()를 사용하여 힙에 할당되며 포인터는 구조체의 하단에 있으므로 구조체와 포인터의 주소는 정확히 동일합니다.IE. 레지스터를 연결하기 위해 구조 주소에 오프셋을 추가할 필요가 없습니다.

같은 맥락에서 버퍼에 서비스를 제공하는 모든 변수는 물리적으로 버퍼에 인접하여 동일한 구조로 바인드되므로 컴파일러는 아름다운 어셈블리 언어를 만들 수 있다.어셈블리를 보려면 인라인 최적화를 중지해야 합니다. 그렇지 않으면 어셈블리는 망각 상태에 빠지기 때문입니다.

모든 데이터 유형의 다형을 지원하기 위해 할당 대신 memcpy()를 사용했습니다.컴파일마다 하나의 랜덤 변수 유형을 지원하는 유연성이 필요한 경우 이 코드는 완벽하게 작동합니다.

다형성의 경우 유형과 스토리지 요건만 알면 됩니다.디스크립터의 DATA_DESC 배열을 사용하면 QUEUE_DESC.pBuffer에 저장된 각 데이터를 추적하여 올바르게 검색할 수 있습니다.가장 큰 데이터 유형의 모든 요소를 저장할 수 있는 충분한 pBuffer 메모리를 할당하기만 하면 됩니다. 단, DATA_DESC.dBytes에서 특정 데이터가 실제로 사용하고 있는 스토리지의 양을 추적합니다.대체 방법은 힙 매니저를 재설계하는 것입니다.

즉, QUUE_DESC의 UCHAR *pBuffer에는 데이터 유형과 크기를 추적하기 위한 병렬 지원 배열이 있지만 pBuffer에 있는 데이터 저장 위치는 현재 그대로 유지됩니다.새로운 멤버는 DATA_DESC *pDataDesc 또는 DATA_DESC DataDesc [ 2 ^B ]와 같습니다.ITS_EL_KNT]를 참조해 컴파일러를 전송하기 위한 방법을 찾을 수 있는 경우.Calloc()는 이러한 상황에서 항상 더 유연합니다.

Q_Put(), Q_Get에서는 memcpy()를 사용할 수 있지만 실제로 복사된 바이트 수는 QUE_DESC가 아닌 DATA_DESC.dBytes에 의해 결정됩니다.EleBytes.요소는 특정 입력 또는 취득에 대해 잠재적으로 모든 다른 유형/사이즈입니다.

이 코드는 속도와 버퍼 크기 요건을 충족하며, 6가지 데이터 타입의 요건을 충족시킬 수 있다고 생각합니다.코드가 올바르게 동작하고 있는지 아닌지에 대해 만족하실 수 있도록, 많은 테스트 픽스처를 printf() 스테이트먼트 형식으로 남겨두었습니다.난수 생성기는 코드가 임의의 헤드/테일 콤보에서 작동함을 나타냅니다.

enter code here
// Queue_Small.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include <time.h>
#include <limits.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <math.h>

#define UCHAR unsigned char
#define ULONG unsigned long
#define USHRT unsigned short
#define dbl   double
/* Queue structure */
#define QUEUE_FULL_FLAG 1
#define QUEUE_EMPTY_FLAG -1
#define QUEUE_OK 0
//  
#define BITS_ELE_KNT    12  //12 bits will create 4.096 elements numbered 0-4095
//
//typedef struct    {
//  USHRT dBytes:8;     //amount of QUEUE_DESC.EleBytes storage used by datatype
//  USHRT dType :3; //supports 8 possible data types (0-7)
//  USHRT dFoo  :5; //unused bits of the unsigned short host's storage
// }    DATA_DESC;
//  This descriptor gives a home to all the housekeeping variables
typedef struct  {
    UCHAR   *pBuffer;   //  pointer to storage, 16 to 4096 elements
    ULONG Tail  :BITS_ELE_KNT;  //  # elements, with range of 0-4095
    ULONG Head  :BITS_ELE_KNT;  //  # elements, with range of 0-4095
    ULONG EleBytes  :8;     //  sizeof(elements) with range of 0-256 bytes
    // some unused bits will be left over if BITS_ELE_KNT < 12
    USHRT EleKnt    :BITS_ELE_KNT +1;// 1 extra bit for # elements (1-4096)
    //USHRT Flags   :(8*sizeof(USHRT) - BITS_ELE_KNT +1);   //  flags you can use
    USHRT   IsFull  :1;     // queue is full
    USHRT   IsEmpty :1;     // queue is empty
    USHRT   Unused  :1;     // 16th bit of USHRT
}   QUEUE_DESC;

//  ---------------------------------------------------------------------------
//  Function prototypes
QUEUE_DESC *Q_Init(QUEUE_DESC *Q, int BitsForEleKnt, int DataTypeSz);
int Q_Put(QUEUE_DESC *Q, UCHAR *pNew);
int Q_Get(UCHAR *pOld, QUEUE_DESC *Q);
//  ---------------------------------------------------------------------------
QUEUE_DESC *Q_Init(QUEUE_DESC *Q, int BitsForEleKnt, int DataTypeSz)    {
    memset((void *)Q, 0, sizeof(QUEUE_DESC));//init flags and bit integers to zero
    //select buffer size from powers of 2 to receive modulo 
    //                arithmetic benefit of bit uints overflowing
    Q->EleKnt   =   (USHRT)pow(2.0, BitsForEleKnt);
    Q->EleBytes =   DataTypeSz; // how much storage for each element?
    //  Randomly generated head, tail a test fixture only. 
    //      Demonstrates that the queue can be entered at a random point 
    //      and still perform properly. Normally zero
    srand(unsigned(time(NULL)));    // seed random number generator with current time
    Q->Head = Q->Tail = rand(); // supposed to be set to zero here, or by memset
    Q->Head = Q->Tail = 0;
    //  allocate queue's storage
    if(NULL == (Q->pBuffer = (UCHAR *)calloc(Q->EleKnt, Q->EleBytes)))  {
        return NULL;
    }   else    {
        return Q;
    }
}
//  ---------------------------------------------------------------------------
int Q_Put(QUEUE_DESC *Q, UCHAR *pNew)   
{
    memcpy(Q->pBuffer + (Q->Tail * Q->EleBytes), pNew, Q->EleBytes);
    if(Q->Tail == (Q->Head + Q->EleKnt)) {
        //  Q->IsFull = 1;
        Q->Tail += 1;   
        return QUEUE_FULL_FLAG; //  queue is full
    }
    Q->Tail += 1;   //  the unsigned bit int MUST wrap around, just like modulo
    return QUEUE_OK; // No errors
}
//  ---------------------------------------------------------------------------
int Q_Get(UCHAR *pOld, QUEUE_DESC *Q)   
{
    memcpy(pOld, Q->pBuffer + (Q->Head * Q->EleBytes), Q->EleBytes);
    Q->Head += 1;   //  the bit int MUST wrap around, just like modulo

    if(Q->Head == Q->Tail)      {
        //  Q->IsEmpty = 1;
        return QUEUE_EMPTY_FLAG; // queue Empty - nothing to get
    }
    return QUEUE_OK; // No errors
}
//
//  ---------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])    {
//  constrain buffer size to some power of 2 to force faux modulo arithmetic
    int LoopKnt = 1000000;  //  for benchmarking purposes only
    int k, i=0, Qview=0;
    time_t start;
    QUEUE_DESC Queue, *Q;
    if(NULL == (Q = Q_Init(&Queue, BITS_ELE_KNT, sizeof(int)))) {
        printf("\nProgram failed to initialize. Aborting.\n\n");
        return 0;
    }

    start = clock();
    for(k=0; k<LoopKnt; k++)    {
        //printf("\n\n Fill'er up please...\n");
        //Q->Head = Q->Tail = rand();
        for(i=1; i<= Q->EleKnt; i++)    {
            Qview = i*i;
            if(QUEUE_FULL_FLAG == Q_Put(Q, (UCHAR *)&Qview))    {
                //printf("\nQueue is full at %i \n", i);
                //printf("\nQueue value of %i should be %i squared", Qview, i);
                break;
            }
            //printf("\nQueue value of %i should be %i squared", Qview, i);
        }
        //  Get data from queue until completely drained (empty)
        //
        //printf("\n\n Step into the lab, and see what's on the slab... \n");
        Qview = 0;
        for(i=1; i; i++)    {
            if(QUEUE_EMPTY_FLAG == Q_Get((UCHAR *)&Qview, Q))   {
                //printf("\nQueue value of %i should be %i squared", Qview, i);
                //printf("\nQueue is empty at %i", i);
                break;
            }
            //printf("\nQueue value of %i should be %i squared", Qview, i);
        }
        //printf("\nQueue head value is %i, tail is %i\n", Q->Head, Q->Tail);
    }
    printf("\nQueue time was %5.3f to fill & drain %i element queue  %i times \n", 
                     (dbl)(clock()-start)/(dbl)CLOCKS_PER_SEC,Q->EleKnt, LoopKnt);
    printf("\nQueue head value is %i, tail is %i\n", Q->Head, Q->Tail);
    getchar();
    return 0;
}

심플한 실장은 다음과 같이 구성됩니다.

  • 필요한 모든 유형의 n사이즈의 배열로 구현되는 버퍼
  • 읽기 포인터 또는 인덱스 (어느쪽이 프로세서에 적합한지)
  • 쓰기 포인터 또는 인덱스
  • 버퍼에 있는 데이터의 양을 나타내는 카운터(읽기 및 쓰기 포인터에서 파생할 수 있지만 개별적으로 추적하는 것이 빠름)

데이터를 쓸 때마다 쓰기 포인터를 전진시키고 카운터를 증가시킵니다.데이터를 읽을 때는 읽기 포인터를 늘리고 카운터를 줄입니다.포인터 중 하나가 n에 도달하면 0으로 설정합니다.

카운터 = n이면 쓸 수 없습니다.카운터 = 0이면 읽을 수 없습니다.

C 스타일, 정수를 위한 단순한 링 버퍼.put and get보다 init을 먼저 사용하세요.버퍼에 데이터가 포함되지 않으면 "0" 0을 반환합니다.

//=====================================
// 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;
}

Adam-rosenfield의 솔루션을 확장하면, 멀티스레드 싱글 생산자-싱글 컨슈머 시나리오에 다음과 같은 것이 효과적이라고 생각합니다.

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;
}

언급URL : https://stackoverflow.com/questions/827691/how-do-you-implement-a-circular-buffer-in-c

반응형