2016-08-11 9 views
0

Итак, у меня есть круглый массив с максимальным размером 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

Что представляет собой круговой массив? Такой структуры данных нет. Вы имеете в виду ringbuffer? У вас есть ** конкретный ** вопрос, изменяющий ваш код? Это не консалтинговая услуга, см. [Ask]. – Olaf

+0

@Olaf Я спрашивал, как вернуть адрес части моего буфера. Кроме того, кольцевой буфер имеет разные имена, его также называют круговой матрицей, круговым буфером, ... – 2222

+0

Хорошо. Каков твой вопрос? – immibis

ответ

0

Вы можете вернуть начало данных с адресом-оператора (&) и получить доступ к этому, как массив. например

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

Теперь вы можете использовать этот указатель и доступ к памяти в кольцевом буфере так же, как массив

например

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

Однако, это будет работать только тогда, когда вы не обернете! Доступ к массиву ожидает, что память будет смежной. Но мы завернули в круглый буфер. Поэтому возврат указателя на начало вставленных данных не является полезным для используемой программы. Вам нужно написать методы доступа, чтобы получить данные из кругового буфера. В C++ вы можете перегрузить [], чтобы он выглядел как массив. В C вам нужно написать для них функции.

В принципе, вы надеетесь, что может быть область памяти, которая обернута, как ваша диаграмма, но память плоская, и если вы возвращаете адрес данных и доступ оттуда непрерывно, то вы будете читать мимо конца кругового буфера (и до неопределенного поведения).

0

Я уже делал несколько из этих кольцевых буферов/кольцевых очередей. Ниже приведена одна из версий, которые я использовал. Исходный код завершен и имеет программу демонстрации/диагностики. Он должен быть работоспособным и управляемым без изменения [much :-)].

Центральное ядро ​​этого, воплощенное в файле .h, плавает вокруг моей базы кода в течение 10-20 лет, поэтому на нем есть пробег. Из-за этого у него есть несколько вещей, которые я добавил, основываясь на моем фактическом использовании и опыте с ним.

Есть несколько отличий от вашей версии. Не обязательно лучше или хуже - просто разные. Надеюсь, это даст вам некоторые идеи для вашего собственного кода.

Во-первых, есть только один указатель на начало очереди. Элемент enqueue [который вы назвали cb_head] является указателем, а не указателем. Аналогично для dequeue [cb_tail]. По моему опыту, это делает код немного проще, он на скорости быстрее [и иногда может быть быстрее].

Хотя у меня есть версии, которые используют указатели на все, что они имеют указатели на определенный [STRUCT] тип , а не void * указателей и «SizeOf» элемент. Но для этого в C требуется много макросов CPP [или метапрограммирования], чтобы получить эффект от шаблона C++.

Одной из других причин использования индексов против указателей является создание многопоточных/поточных безопасных очередей. Это не было разработано в этой конкретной версии, но есть беглый пример с qrngnew_lck, который будет использовать блокировку (например,pthread_mutex_lock/pthread_mutex_unlock).

Кроме того, в качестве альтернативы мьютексу можно использовать примитивы от stdatomic.h (например, atomic_compare_exchange*), чтобы изменить значения очереди/декомпрессии. Это гораздо проще использовать, если эти значения int против void *


При работе с большим количеством данных (например, из recv), что наиболее полезно будет знать, сколько ячеек можно скопировать в один блок (т. е. memcpy), вместо того, чтобы делать отдельные нажатия и выступы [которые медленны].

Кроме того, не требуется отдельное подсчет количества элементов в очереди, так как это легко рассчитывается из значений очереди и деактивации.

Вещи интерес:

  1. общее количество свободных ячеек доступны епдиеие ДАННЫЕ
  2. число смежных свободных ячеек, доступных для одного memcpy
  3. общее число заполненных ячеек, ожидающих быть удалена из очереди
  4. количество смежных заполненных ячеек в ожидании, которые могут быть извлечены одним memcpy

В любом случае, код ниже. Это три файла: qrng.h, qrng.c и qrngdemo.c.

Извините за qrng*. Это персональный стиль подписи (т. Е. «Quirk»). Это было так же легко, как и везде ring [но, поскольку у меня есть несколько разных версий, я использую это имя, чтобы избежать столкновений в пространстве имен C]. Он также мог бы быть [говорят] 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; 
}