返回圆形数组的一部分的地址

时间:2016-08-11 01:24:17

标签: c arrays linux malloc circular-buffer

所以我有一个最大大小为2097152的圆形数组,我希望它填满524288并返回包含524288索引的地址。然后填补另一个524288并做同样的事情。并继续这样做,因为它是一个圆形数组。

我通过TCP获取数据流。这些数据有不同的大小,但是现在我只是想用数字填充我的数组 我不知道如何处理这个问题。 到目前为止我有这个:

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

2 个答案:

答案 0 :(得分:0)

您可以使用address-of运算符(&)返回数据的开头,并像数组一样访问它。 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;
    }
}

现在你可以使用这个指针并像循环数据一样访问循环缓冲区中的内存

e.g。

my_data = fill_cb(cb, buf, 20);
do_stuff(my_data[10]);

然而,只有当你没有包装时,这才有效!数组访问期望内存是连续的。但我们包裹在循环缓冲区内。因此,返回指向所插入数据开头的指针对于使用程序并不有用。您需要编写访问方法以从循环缓冲区中获取数据。在C ++中,您可能会重载[]以使其看起来像一个数组。在C中,您需要为这些函数编写函数。

基本上,你希望有一个像图表一样包装的内存区域,但是内存是平的,如果你返回数据的 start 的地址并且从那里连续访问那么你将读取循环缓冲区的末尾(以及未定义的行为)。

答案 1 :(得分:0)

之前我已经完成了一些循环缓冲区/环路队列。以下是我使用过的版本之一。源代码已完成,并具有演示/诊断程序。它应该是可构建的,并且可以在没有[更多:-)]的情况下运行。

这个体现在.h文件中的核心内容已经在我的代码库中浮动了10到20年,所以它有一些里程。因此,我根据自己的实际使用情况和使用经验添加了一些内容。

与您的版本存在一些差异。不一定更好或更糟 - 只是不同。希望这能为您提供一些自己代码的想法。

首先,只有一个指向队列开头的指针。 enqueue元素[您命名为cb_head]是索引而不是指针。同样对于出列[cb_tail]。根据我的经验,这使得代码更简单,速度更快[并且有时可以更快]。

虽然我有使用指针指向所有内容的版本,但它们指向特定的 [struct]类型,而不是void *指针和&#34; sizeof&#34;元件。但是,要在C中执行此操作,需要大量的CPP宏[或元编程]技巧才能获得C ++模板的效果。

使用索引与指针的其他原因之一是创建多线程/线程安全队列。这个版本还没有被设计到这个特定版本中,但有一个使用锁定的qrngnew_lck的粗略示例(例如pthread_mutex_lock/pthread_mutex_unlock)。

此外,作为互斥锁的替代方法,可以使用stdatomic.h中的基元(例如atomic_compare_exchange*)来更改入队/出队值。如果这些值为int vs void *

,则使用此功能会更容易

当处理大量数据时(例如来自recv),最有用的是知道可以在一个块中复制多少个单元(即memcpy),而不是而不是单独推动和弹出[这很慢]。

此外,不需要保持队列中元素数量的单独计数,因为这很容易根据入队和出队值计算。

感兴趣的事情是:

  1. 可用于排队数据的空闲单元总数
  2. 单个memcpy
  3. 可用的连续空闲单元数
  4. 待处理的已填充单元格总数
  5. 可以使用单个memcpy
  6. 提取的待连续填充单元格的数量

    无论如何,代码如下。这是三个文件:qrng.hqrng.cqrngdemo.c

    qrng*抱歉。它是一种个人签名风格(即#34; quirk&#34;)。它可以很容易地到处ring [但是,因为我有几个不同的版本,我使用这个命名来避免C&#39的命名空间中的冲突]。到处都可以[比较] cb: - )

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