Reputation:
So i have a circular array of max size 2097152 and i want it to fill up to 524288 and return the address that holds that 524288 indices. Then fills up another 524288 and do the same. and keeps doing that since it's a circular array.
I am getting stream of data via TCP. This data comes in different sizes, but for now I'm just trying to fill up my array with numbers.
I'm not sure how to approach this.
I have this so far:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <stdbool.h>
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)
{
printf("myError: Buffer returned Null");
}
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++;
}
int main()
{
int *arr = malloc (BUFFER_LEN * sizeof *arr);
int i;
for(i = 0; i <= BUFFER_LEN; i++) { arr[i] = i; }
return(0);
}
Upvotes: 0
Views: 371
Reputation: 33631
I've done a few of these circular buffer / ring queues before. Below is one of the versions I've used. The source code is complete and has a demo/diagnostic program. It should be buildable and runnable without [much :-)] change.
The central core of this, embodied in the .h
file has been floating around my code base for 10-20 years, so it's got some mileage on it. Because of this, it has a few things that I've added based on my actual usage and experience with it.
There are a few differences from your version. Not necessarily better or worse--just different. Hopefully, this will give you some ideas for your own code.
For one, there is only one pointer to the start of the queue. The enqueue element [which you named cb_head
] is an index rather than a pointer. Likewise for the dequeue [cb_tail
]. From my experience, this makes the code a bit simpler, is on-par speedwise [and can sometimes be faster].
Although I have versions that use pointers to everything, they have pointers to a specific [struct] type, rather than void *
pointers and a "sizeof" element. But, to do this in C, requires a lot of CPP macro [or metaprogramming] trickery, to get the effect of a C++ template.
One of the other reasons for using indexes vs. pointers is creating multithread/thread safe queues. This hasn't been designed into this particular version, but there is a cursory example with qrngnew_lck
that would use a lock (e.g. pthread_mutex_lock/pthread_mutex_unlock
).
Also, as an alternative to a mutex, it is possible to use primitives from stdatomic.h
(e.g. atomic_compare_exchange*
) to change the enqueue/dequeue values. It is much easier to use this if these values are int
vs void *
When dealing with a lot of data (e.g. from recv
), what's most useful is to know how many cells can be copied in a single chunk (i.e. memcpy
), rather than doing individual pushes and pops [which is slow].
Also, maintaining a separate count of number of elements in queue isn't necessary as this is easily calculated from the enqueue and dequeue values.
Things of interest are:
memcpy
memcpy
Anyway, the code is below. It is three files: qrng.h
, qrng.c
, and qrngdemo.c
.
Sorry about the qrng*
. It's a personal signature style (i.e. "quirk"). It could just as easily have been ring
everywhere [but, because I have several different versions, I use this naming to avoid collisions in C's namespace]. It could also have been [say] cb
everywhere :-)
qrng.h:
// ovrlib/qrng.h -- ring queue control
#ifndef _ovrlib_qrng_h_
#define _ovrlib_qrng_h_
#define QRNGMAGIC 0xDEAFDEAD
#define QRNGINLINE static inline
#define _QRNGOFF(_itm) \
((long) _itm)
#define QRNGOFF(_qrng,_itm) \
_QRNGOFF(_qrng->_itm)
#define QRNG_FMT \
"deq=%ld enq=%ld pend=%ld/%ld avail=%ld/%ld qmax=%ld"
#define QRNG_PRT(_qrng) \
QRNGOFF(_qrng,qrng_deq), \
QRNGOFF(_qrng,qrng_enq), \
_QRNGOFF(qrng_pend_buf(_qrng)), \
_QRNGOFF(qrng_pend_tot(_qrng)), \
_QRNGOFF(qrng_avail_buf(_qrng)), \
_QRNGOFF(qrng_avail_tot(_qrng)), \
QRNGOFF(_qrng,qrng_qmax)
// pointer to queue data item
// NOTES:
// (1) _always_ use void *
// (2) the way this is used, setting this to _anything_ else will _not_ work
typedef void *queitm_p;
typedef const void *queitm_pc;
// queue index
// NOTES:
// (1) _must_ be signed
// (2) for most queues, an int is sufficient
#ifdef QRNG_BIGIDX
typedef long qidx_t;
#else
typedef int qidx_t;
#endif
typedef long qlen_t;
typedef unsigned int u32;
typedef struct quering_struct quering_t;
typedef quering_t *quering_p;
typedef const quering_t *quering_pc;
struct quering_struct {
u32 qrng_magic; // magic number
u32 qrng_stat; // status
int qrng_algn[2]; // align to 64 byte boundary
// WARNING:
// (1) accesses to these via sysxchgl require them in
// _exactly_ this order -- do _not_ reorder these
// (2) for 64b mode (cmpxchg16b), these must be aligned to a 16 byte
// boundary
qidx_t qrng_deq; // dequeue pointer
qidx_t qrng_enq; // enqueue pointer
qidx_t qrng_siz; // size of queitm_t
queitm_p qrng_base; // base address of ring buffer
qidx_t qrng_qmax; // number of queue elements
};
// equates to status
#define QRNGALLOC (1u << 0) // 1=qrng_base is allocated on heap
// qrng_len -- get byte offset/length from index/count
QRNGINLINE qlen_t
qrng_len(quering_p qrng,qidx_t idx)
{
qlen_t len;
len = idx;
len *= qrng->qrng_siz;
return len;
}
// qrng_ptr -- get flat pointer to queue element
QRNGINLINE queitm_p
qrng_ptr(quering_p qrng,qidx_t idx)
{
queitm_p ptr;
ptr = qrng->qrng_base;
ptr += qrng_len(qrng,idx);
return ptr;
}
// qrng_wrap_dec -- wrap queue index after decrement
QRNGINLINE qidx_t
qrng_wrap_dec(quering_p qrng,qidx_t qitm,qidx_t inc)
{
qitm -= inc;
if (qitm < 0)
qitm += qrng->qrng_qmax;
return qitm;
}
// qrng_wrap_inc -- wrap queue index after increment
QRNGINLINE qidx_t
qrng_wrap_inc(quering_p qrng,qidx_t qitm,qidx_t inc)
{
qidx_t dif;
qitm += inc;
dif = qitm - qrng->qrng_qmax;
if (dif >= 0)
qitm = dif;
return qitm;
}
// qrng_reset -- reset queue pointers
QRNGINLINE void
qrng_reset(quering_p qrng)
{
qrng->qrng_enq = 0;
qrng->qrng_deq = 0;
}
// qrng_full -- decide if qrng queue is full
// RETURNS: 1=full
QRNGINLINE int
qrng_full(quering_p qrng)
{
qidx_t qenq;
qenq = qrng_wrap_inc(qrng,qrng->qrng_enq,1);
return (qenq == qrng->qrng_deq);
}
// _qrng_empty -- decide if qrng queue is empty
// RETURNS: 1=empty
QRNGINLINE int
_qrng_empty(quering_p qrng,qidx_t enq)
{
return (qrng->qrng_deq == enq);
}
// qrng_empty -- decide if qrng queue is empty
// RETURNS: 1=empty
QRNGINLINE int
qrng_empty(quering_p qrng)
{
return _qrng_empty(qrng,qrng->qrng_enq);
}
// qrng_avail_buf -- amount that can be added by single memcpy
QRNGINLINE qidx_t
qrng_avail_buf(quering_p qrng)
{
qidx_t len;
len = qrng->qrng_deq - qrng->qrng_enq;
if (len <= 0) {
len = qrng->qrng_qmax - qrng->qrng_enq;
if (qrng->qrng_deq == 0)
--len;
}
else
--len;
return len;
}
// qrng_avail_tot_ptr -- total amount that can be added
QRNGINLINE qidx_t
qrng_avail_tot_ptr(quering_p qrng,qidx_t deq,qidx_t enq)
{
qidx_t len;
len = deq - enq;
if (len <= 0)
len += qrng->qrng_qmax;
--len;
return len;
}
// qrng_avail_tot -- total amount that can be added
QRNGINLINE qidx_t
qrng_avail_tot(quering_p qrng)
{
return qrng_avail_tot_ptr(qrng,qrng->qrng_deq,qrng->qrng_enq);
}
// qrng_pend_buf -- amount that may be dequeued by single memcpy
QRNGINLINE qidx_t
qrng_pend_buf(quering_p qrng)
{
qidx_t len;
len = qrng->qrng_enq - qrng->qrng_deq;
if (len < 0)
len = qrng->qrng_qmax - qrng->qrng_deq;
return len;
}
// qrng_pend_tot -- total amount that may be dequeued
QRNGINLINE qidx_t
qrng_pend_tot(quering_p qrng)
{
qidx_t len;
len = qrng->qrng_enq - qrng->qrng_deq;
if (len < 0)
len += qrng->qrng_qmax;
return len;
}
// qrng_deq_buf -- dequeue buffer from qrng queue
QRNGINLINE void
qrng_deq_buf(quering_p qrng,qidx_t inclen)
// inclen -- amount to increment
{
qrng->qrng_deq = qrng_wrap_inc(qrng,qrng->qrng_deq,inclen);
}
// qrng_enq_buf -- enqueue buffer into qrng queue
QRNGINLINE void
qrng_enq_buf(quering_p qrng,qidx_t inclen)
// inclen -- amount to increment
{
qrng->qrng_enq = qrng_wrap_inc(qrng,qrng->qrng_enq,inclen);
}
// /home/cae/OBJ/ovrgen/ovrlib/qrng.proto -- prototypes
// FILE: /home/cae/preserve/ovrstk/ovrlib/qrng.c
// ovrlib/qrng -- ring queue common control
// _qrngnoalloc -- handle alloc failure
void
_qrngnoalloc(quering_p qrng,int sverr);
// qrng_setup -- passive setup
// RETURNS: 1=initialized
int
qrng_setup(quering_p qrng,queitm_p bp,qidx_t siz,qidx_t cnt);
// qrng_alloc -- allocate ring queue
queitm_p
qrng_alloc(quering_p qrng,qidx_t cnt);
// qrng_free -- free queue
void
qrng_free(quering_p qrng);
// qrng_deq_sgl -- dequeue single element from qrng queue
queitm_p
qrng_deq_sgl(quering_p qrng);
// qrng_enq_sgl -- enqueue single element into qrng queue
queitm_p
qrng_enq_sgl(quering_p qrng,queitm_p qitm);
// qrngnew_lck -- enqueue multiple items into qrng queue (syslock)
// RETURNS: pointer to items to store (or NULL)
queitm_p
qrngnew_lck(quering_p qrng,qidx_t cnt,quering_p rlsdeq);
// _qrngnew_init -- do special type-specific initialization
void
_qrngnew_init(queitm_p qitm);
// _qrngnew_onfull -- decide if capture is stopped because queue is full
queitm_p
_qrngnew_onfull(quering_p qrng,qidx_t rtn);
// qrngcowbrk -- break copy-on-write
void
qrngcowbrk(quering_p qrng);
// qrngfault -- output fault
void
qrngfault(quering_p qrng,const char *fmt,...) __attribute__((__format__(__printf__,2,3)));
#endif
qrng.c:
// ovrlib/qrng -- ring queue common control
#include <qrng.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#if 0
#define zprt(_lvl,_fmt...) fprintf(stderr,_fmt)
#else
#define zprt(_lvl,_fmt...) /**/
#endif
// _qrngnoalloc -- handle alloc failure
void
_qrngnoalloc(quering_p qrng,int sverr)
{
qrngfault(qrng,"_qrngnoalloc: unable to allocate buffer -- %s\n",
strerror(sverr));
}
// qrng_setup -- passive setup
// RETURNS: 1=initialized
int
qrng_setup(quering_p qrng,queitm_p bp,qidx_t siz,qidx_t cnt)
{
int initflg;
#ifdef CPLXCVTLNG
if ((CPLXCVTLNG(qrng) % 16) != 0)
qrngfault("qrngsetup: alignment fault -- qrng=%p\n",qrng);
#endif
do {
initflg = (qrng->qrng_magic != QRNGMAGIC);
if (initflg)
memset(qrng,0,sizeof(quering_t));
qrng->qrng_magic = QRNGMAGIC;
qrng->qrng_siz = siz;
// allocate space for queue
if (bp == NULL)
bp = qrng_alloc(qrng,cnt);
else
qrng_free(qrng);
qrng->qrng_base = bp;
qrng->qrng_qmax = cnt;
// break copy-on-write
qrngcowbrk(qrng);
} while (0);
qrng_reset(qrng);
return initflg;
}
// qrng_alloc -- allocate ring queue
queitm_p
qrng_alloc(quering_p qrng,qidx_t cnt)
{
queitm_p qitm;
int sverr;
do {
qitm = qrng->qrng_base;
// don't realloc if old and new sizes match -- just reset the pointers
if (qitm != NULL) {
if (cnt == qrng->qrng_qmax) {
break;
}
}
// free the old queue
qrng_free(qrng);
// allocate the queue
qitm = calloc(cnt,qrng->qrng_siz);
sverr = errno;
// fault on alloc failure
if (qitm == NULL)
_qrngnoalloc(qrng,sverr);
qrng->qrng_stat |= QRNGALLOC;
} while (0);
qrng_reset(qrng);
return qitm;
}
// qrng_free -- free queue
void
qrng_free(quering_p qrng)
{
queitm_p qitm;
do {
qitm = qrng->qrng_base;
if (qitm == NULL) {
break;
}
if (qrng->qrng_stat & QRNGALLOC) {
free(qitm);
}
} while (0);
qrng->qrng_base = NULL;
qrng->qrng_stat &= ~QRNGALLOC;
}
// qrng_deq_sgl -- dequeue single element from qrng queue
queitm_p
qrng_deq_sgl(quering_p qrng)
{
qidx_t deq;
queitm_p qrtn;
do {
if (qrng_empty(qrng)) {
qrtn = NULL;
break;
}
deq = qrng->qrng_deq;
qrtn = qrng_ptr(qrng,deq);
qrng->qrng_deq = qrng_wrap_inc(qrng,deq,1);
} while (0);
return qrtn;
}
// qrng_enq_sgl -- enqueue single element into qrng queue
queitm_p
qrng_enq_sgl(quering_p qrng,queitm_p qitm)
// qitm -- item to enqueue (if NULL, caller will do copy on return)
{
qidx_t enq;
queitm_p qrtn;
do {
if (qrng_full(qrng)) {
qrtn = NULL;
break;
}
enq = qrng->qrng_enq;
qrtn = qrng_ptr(qrng,enq);
// we give the caller the option of doing the copy manually or letting
// us do it
if (qitm != NULL)
memcpy(qrtn,qitm,qrng->qrng_siz);
qrng->qrng_enq = qrng_wrap_inc(qrng,enq,1);
} while (0);
return qrtn;
}
// qrngnew_lck -- enqueue multiple items into qrng queue (syslock)
// RETURNS: pointer to items to store (or NULL)
queitm_p
qrngnew_lck(quering_p qrng,qidx_t cnt,quering_p rlsdeq)
{
qidx_t nenq;
qidx_t ndeq;
qidx_t odeq;
qidx_t oenq;
int stopflg;
int wflg;
int dflg;
int ovflg;
queitm_p optr;
stopflg = 0;
// lock it
//SYSLOCKQ(&qrng->qrng_lock,0);
do {
// grab the old values
odeq = qrng->qrng_deq;
oenq = qrng->qrng_enq;
do {
// point to one beyond where we wish to store
nenq = qrng_wrap_inc(qrng,oenq,cnt);
// decide if we wrapped the enqueue pointer
wflg = (nenq < oenq);
// decide if dequeue increment is positive (non-negative)
dflg = (nenq >= odeq);
// decide on overflow
// NOTE: there is an elaborate explanation for the overflow
// logic in qrng.m5m
if (oenq >= odeq)
ovflg = wflg && dflg;
else
ovflg = (wflg != dflg);
// [initial] filling of queue:
// (1) enq was higher than deq and it did _not_ wrap
// (2) enq was lower than deq and it did _not_ touch/go over
if (! ovflg) {
ndeq = odeq;
break;
}
// advance the dequeue pointer to make room
ndeq = qrng_wrap_inc(qrng,nenq,1);
} while (0);
// allow caller to "release" the dequeued nodes
if (rlsdeq != NULL) {
rlsdeq->qrng_deq = odeq;
rlsdeq->qrng_enq = ndeq;
}
// lay down the new pointers
qrng->qrng_enq = nenq;
qrng->qrng_deq = ndeq;
} while (0);
// zap the type (ASAP)
// NOTE: there is a slight (virtually non-existent) race condition here
// which only occurs if we get held off too long and a dump begins
do {
if (stopflg) {
optr = _qrngnew_onfull(qrng,oenq);
break;
}
optr = qrng_ptr(qrng,oenq);
_qrngnew_init(optr);
} while (0);
// unlock it
//SYSUNLOCKQ(&qrng->qrng_lock);
return optr;
}
// _qrngnew_init -- do special type-specific initialization
void
_qrngnew_init(queitm_p qitm)
{
//ARGV_USED(qitm);
}
// _qrngnew_onfull -- decide if capture is stopped because queue is full
queitm_p
_qrngnew_onfull(quering_p qrng,qidx_t rtn)
{
queitm_p ptr;
qrngfault(qrng,"qrngnew: stop on full\n");
ptr = NULL;
return ptr;
}
// qrngcowbrk -- break copy-on-write
void
qrngcowbrk(quering_p qrng)
{
qlen_t len;
len = qrng_len(qrng,qrng->qrng_qmax);
if (len > 0)
memset(qrng->qrng_base,0,len);
}
// qrngfault -- output fault
void
qrngfault(quering_p qrng,const char *fmt,...)
{
va_list ap;
va_start(ap,fmt);
vfprintf(stderr,fmt,ap);
va_end(ap);
exit(1);
}
qrngdemo.c:
// qrngdemo/qrngdemo -- test/demo program for qrng
#include <qrng.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int opt_v;
qidx_t opt_M;
int opt_T;
#define dbgprt(_fmt...) \
do { \
if (opt_v) \
printf(_fmt); \
} while (0)
#define fault(_fmt...) \
do { \
printf(_fmt); \
exit(1); \
} while (0)
quering_t sampque;
#ifndef MAXCNT
#if 0
#define MAXCNT 524288
#else
#define MAXCNT 337
#endif
#endif
typedef struct {
unsigned int samp_pos;
unsigned int samp_neg;
} sample_t;
typedef sample_t *sample_p;
unsigned int filloff;
unsigned int cmpoff;
sample_p temp;
// sampsetup -- do setup of sample queue
void
sampsetup(quering_p sampq)
{
if (opt_M < 3)
opt_M = 3;
qrng_setup(sampq,NULL,sizeof(sample_t),opt_M);
temp = calloc(opt_M + 10,sizeof(sample_t));
}
// randval -- get random count
qidx_t
randval(qidx_t max)
{
qidx_t cnt;
cnt = rand() % opt_M;
if (cnt <= 0)
cnt = 1;
if (cnt > max)
cnt = max;
return cnt;
}
// fill -- fill queue
void
fill(quering_p sampq)
{
sample_p samp;
qidx_t addcnt;
qidx_t maxcnt;
qidx_t xcnt;
qidx_t idx;
maxcnt = qrng_avail_tot(sampq);
addcnt = randval(maxcnt);
dbgprt("fill: ENTER maxcnt=%ld addcnt=%ld\n",
_QRNGOFF(maxcnt),_QRNGOFF(addcnt));
// fill linear buffer
for (idx = 0; idx < addcnt; ++idx) {
samp = &temp[idx];
samp->samp_pos = filloff;
samp->samp_neg = ~filloff;
filloff += 1;
}
dbgprt("fill: TEMP %8.8X/%8.8X\n",
temp[0].samp_pos,temp[addcnt - 1].samp_pos);
// copy linear buffer into ring queue
for (idx = 0; addcnt > 0; idx += xcnt, addcnt -= xcnt) {
xcnt = qrng_avail_buf(sampq);
if (xcnt > addcnt)
xcnt = addcnt;
if (xcnt <= 0)
break;
dbgprt("fill: COPY %8.8X/%8.8X -- xcnt=%ld " QRNG_FMT "\n",
temp[idx].samp_pos,temp[idx + xcnt - 1].samp_pos,
_QRNGOFF(xcnt),
QRNG_PRT(sampq));
memcpy(qrng_ptr(sampq,sampq->qrng_enq),&temp[idx],qrng_len(sampq,xcnt));
qrng_enq_buf(sampq,xcnt);
}
dbgprt("fill: EXIT " QRNG_FMT "\n",QRNG_PRT(sampq));
}
// cmp -- compare queue
void
cmp(quering_p sampq)
{
sample_p samp;
qidx_t cmpcnt;
qidx_t maxcnt;
qidx_t xcnt;
qidx_t chkcnt;
qidx_t idx;
maxcnt = qrng_pend_tot(sampq);
cmpcnt = randval(maxcnt);
dbgprt("cmp: ENTER maxcnt=%ld cmpcnt=%ld\n",
_QRNGOFF(maxcnt),_QRNGOFF(cmpcnt));
// copy data from ring queue into linear buffer
chkcnt = 0;
for (idx = 0; cmpcnt > 0; idx += xcnt, cmpcnt -= xcnt) {
xcnt = qrng_pend_buf(sampq);
if (xcnt > cmpcnt)
xcnt = cmpcnt;
if (xcnt <= 0)
break;
chkcnt += xcnt;
memcpy(&temp[idx],qrng_ptr(sampq,sampq->qrng_deq),qrng_len(sampq,xcnt));
dbgprt("cmp: COPY %8.8X/%8.8X -- xcnt=%ld " QRNG_FMT "\n",
temp[idx].samp_pos,temp[idx + xcnt - 1].samp_pos,
_QRNGOFF(xcnt),
QRNG_PRT(sampq));
qrng_deq_buf(sampq,xcnt);
}
if (chkcnt > 0)
dbgprt("cmp: TEMP %8.8X/%8.8X chkcnt=%ld\n",
temp[0].samp_pos,temp[chkcnt - 1].samp_pos,_QRNGOFF(chkcnt));
// check linear buffer
for (idx = 0; idx < chkcnt; ++idx) {
samp = &temp[idx];
if ((samp->samp_pos != cmpoff) || (samp->samp_neg != ~cmpoff))
fault("cmp: failure -- idx=%d samp_pos=%8.8X cmpoff=%8.8X\n",
idx,samp->samp_pos,cmpoff);
cmpoff += 1;
}
dbgprt("cmp: EXIT " QRNG_FMT "\n",QRNG_PRT(sampq));
}
// main -- main program
int
main(int argc,char **argv)
{
char *cp;
--argc;
++argv;
opt_M = MAXCNT;
opt_T = 10000000;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'M':
opt_M = strtol(cp,&cp,10);
break;
case 'T':
opt_T = strtol(cp,&cp,10);
break;
case 'v':
opt_v = 1;
break;
}
}
sampsetup(&sampque);
for (int iter = opt_T; iter >= 0; --iter) {
fill(&sampque);
cmp(&sampque);
}
qrng_free(&sampque);
return 0;
}
Upvotes: 0
Reputation: 4922
You can return the start of your data with the address-of operator (&
) and access this like an array. e.g.
char *fill_cb(circular_buffer *cb, char *buf, size_t sz)
{
/* Assume there is room in the buffer: caller must check before calling this function */
if(cb->tail + sz < cb->buffer_end) {
memcpy(cb->tail, buf, sz);
cb->tail += sz;
cb->count += sz;
return cb->tail - sz;
} else {
size_t tail_room = cb->buffer_end - cb->tail;
memcpy(cb->tail, buf, tail_room);
memcpy(cb->buffer, buf + tail_room, sz - tail_room);
cb->tail = cb->buffer + sz - tail_room;
return cb->buffer_end - tail_room;
}
}
Now you can use this pointer and access the memory in the circular buffer much like an array
e.g.
my_data = fill_cb(cb, buf, 20);
do_stuff(my_data[10]);
However, this will only work when you didn't wrap! The array access expects memory to be contiguous. But we wrapped inside the circular buffer. So returning a pointer to the start of the data inserted isn't useful to the using program. You need to write access methods to get the data out of the circular buffer. In C++ you might overload []
to make it look like an array. In C you need to write functions for these.
Basically, you are hoping that there can be a memory region which is wrapped like your diagram, but memory is flat and if you return the address of the start of the data and access from there continuously then you will read past the end of the circular buffer (and in to undefined behaviour).
Upvotes: 0