From b38292cd695ebc51a6925f21afeb89eeff1b0208 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 29 May 2019 20:53:21 +0200 Subject: [PATCH] Add generic circular buffer Add a circular buffer implementation, to factorize multiple specific queues implementation. --- app/meson.build | 3 ++ app/src/cbuf.h | 50 +++++++++++++++++++++++++++++ app/tests/test_cbuf.c | 73 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 app/src/cbuf.h create mode 100644 app/tests/test_cbuf.c diff --git a/app/meson.build b/app/meson.build index d22cb63f..73cf55ca 100644 --- a/app/meson.build +++ b/app/meson.build @@ -154,6 +154,9 @@ executable('scrcpy', src, ### TESTS tests = [ + ['test_cbuf', [ + 'tests/test_cbuf.c', + ]], ['test_control_event_queue', [ 'tests/test_control_event_queue.c', 'src/control_event.c' diff --git a/app/src/cbuf.h b/app/src/cbuf.h new file mode 100644 index 00000000..5d9fe4ae --- /dev/null +++ b/app/src/cbuf.h @@ -0,0 +1,50 @@ +// generic circular buffer (bounded queue) implementation +#ifndef CBUF_H +#define CBUF_H + +#include +#include + +// To define a circular buffer type of 20 ints: +// typedef CBUF(int, 20) my_cbuf_t; +// +// data has length CAP + 1 to distinguish empty vs full. +#define CBUF(TYPE, CAP) { \ + TYPE data[(CAP) + 1]; \ + size_t head; \ + size_t tail; \ +} + +#define cbuf_size_(PCBUF) \ + (sizeof((PCBUF)->data) / sizeof(*(PCBUF)->data)) + +#define cbuf_is_empty(PCBUF) \ + ((PCBUF)->head == (PCBUF)->tail) + +#define cbuf_is_full(PCBUF) \ + (((PCBUF)->head + 1) % cbuf_size_(PCBUF) == (PCBUF)->tail) + +#define cbuf_init(PCBUF) \ + (void) ((PCBUF)->head = (PCBUF)->tail = 0) + +#define cbuf_push(PCBUF, ITEM) \ + ({ \ + bool ok = !cbuf_is_full(PCBUF); \ + if (ok) { \ + (PCBUF)->data[(PCBUF)->head] = (ITEM); \ + (PCBUF)->head = ((PCBUF)->head + 1) % cbuf_size_(PCBUF); \ + } \ + ok; \ + }) \ + +#define cbuf_take(PCBUF, PITEM) \ + ({ \ + bool ok = !cbuf_is_empty(PCBUF); \ + if (ok) { \ + *(PITEM) = (PCBUF)->data[(PCBUF)->tail]; \ + (PCBUF)->tail = ((PCBUF)->tail + 1) % cbuf_size_(PCBUF); \ + } \ + ok; \ + }) + +#endif diff --git a/app/tests/test_cbuf.c b/app/tests/test_cbuf.c new file mode 100644 index 00000000..9d5fdc27 --- /dev/null +++ b/app/tests/test_cbuf.c @@ -0,0 +1,73 @@ +#include +#include + +#include "cbuf.h" + +struct int_queue CBUF(int, 32); + +static void test_cbuf_empty(void) { + struct int_queue queue; + cbuf_init(&queue); + + assert(cbuf_is_empty(&queue)); + + bool push_ok = cbuf_push(&queue, 42); + assert(push_ok); + assert(!cbuf_is_empty(&queue)); + + int item; + bool take_ok = cbuf_take(&queue, &item); + assert(take_ok); + assert(cbuf_is_empty(&queue)); + + bool take_empty_ok = cbuf_take(&queue, &item); + assert(!take_empty_ok); // the queue is empty +} + +static void test_cbuf_full(void) { + struct int_queue queue; + cbuf_init(&queue); + + assert(!cbuf_is_full(&queue)); + + // fill the queue + for (int i = 0; i < 32; ++i) { + bool ok = cbuf_push(&queue, i); + assert(ok); + } + bool ok = cbuf_push(&queue, 42); + assert(!ok); // the queue if full + + int item; + bool take_ok = cbuf_take(&queue, &item); + assert(take_ok); + assert(!cbuf_is_full(&queue)); +} + +static void test_cbuf_push_take(void) { + struct int_queue queue; + cbuf_init(&queue); + + bool push1_ok = cbuf_push(&queue, 42); + assert(push1_ok); + + bool push2_ok = cbuf_push(&queue, 35); + assert(push2_ok); + + int item; + + bool take1_ok = cbuf_take(&queue, &item); + assert(take1_ok); + assert(item == 42); + + bool take2_ok = cbuf_take(&queue, &item); + assert(take2_ok); + assert(item == 35); +} + +int main(void) { + test_cbuf_empty(); + test_cbuf_full(); + test_cbuf_push_take(); + return 0; +}