diff options
Diffstat (limited to 'aclk')
32 files changed, 6378 insertions, 8 deletions
diff --git a/aclk/aclk.c b/aclk/aclk.c index e95d7d6ab7..6d583a76a6 100644 --- a/aclk/aclk.c +++ b/aclk/aclk.c @@ -4,7 +4,7 @@ #ifdef ENABLE_ACLK #include "aclk_stats.h" -#include "mqtt_wss_client.h" +#include "mqtt_websockets/mqtt_wss_client.h" #include "aclk_otp.h" #include "aclk_tx_msgs.h" #include "aclk_query.h" diff --git a/aclk/aclk_otp.c b/aclk/aclk_otp.c index 207ca08cf0..e03f8b2121 100644 --- a/aclk/aclk_otp.c +++ b/aclk/aclk_otp.c @@ -7,7 +7,7 @@ #include "daemon/common.h" -#include "mqtt_websockets/c-rbuf/include/ringbuffer.h" +#include "mqtt_websockets/c-rbuf/cringbuffer.h" static int aclk_https_request(https_req_t *request, https_req_response_t *response) { int rc; diff --git a/aclk/aclk_query.h b/aclk/aclk_query.h index dbe6f9e5e6..371b8d0a24 100644 --- a/aclk/aclk_query.h +++ b/aclk/aclk_query.h @@ -5,7 +5,7 @@ #include "libnetdata/libnetdata.h" -#include "mqtt_wss_client.h" +#include "mqtt_websockets/mqtt_wss_client.h" #include "aclk_query_queue.h" diff --git a/aclk/aclk_stats.h b/aclk/aclk_stats.h index 002ebcfa6b..758347ae26 100644 --- a/aclk/aclk_stats.h +++ b/aclk/aclk_stats.h @@ -6,7 +6,7 @@ #include "daemon/common.h" #include "libnetdata/libnetdata.h" #include "aclk_query_queue.h" -#include "mqtt_wss_client.h" +#include "mqtt_websockets/mqtt_wss_client.h" extern netdata_mutex_t aclk_stats_mutex; diff --git a/aclk/aclk_tx_msgs.h b/aclk/aclk_tx_msgs.h index 9e7d890772..86ed20c381 100644 --- a/aclk/aclk_tx_msgs.h +++ b/aclk/aclk_tx_msgs.h @@ -5,7 +5,7 @@ #include <json-c/json.h> #include "libnetdata/libnetdata.h" #include "daemon/common.h" -#include "mqtt_wss_client.h" +#include "mqtt_websockets/mqtt_wss_client.h" #include "schema-wrappers/schema_wrappers.h" #include "aclk_util.h" diff --git a/aclk/aclk_util.h b/aclk/aclk_util.h index 38ef5b0bcb..6c0239cc31 100644 --- a/aclk/aclk_util.h +++ b/aclk/aclk_util.h @@ -5,7 +5,7 @@ #include "libnetdata/libnetdata.h" #ifdef ENABLE_ACLK -#include "mqtt_wss_client.h" +#include "mqtt_websockets/mqtt_wss_client.h" #define CLOUD_EC_MALFORMED_NODE_ID 1 #define CLOUD_EMSG_MALFORMED_NODE_ID "URL requests node_id but there is not enough chars following (for it to be valid uuid)." diff --git a/aclk/https_client.h b/aclk/https_client.h index c87b3b69fd..79d0a42b75 100644 --- a/aclk/https_client.h +++ b/aclk/https_client.h @@ -5,8 +5,8 @@ #include "libnetdata/libnetdata.h" -#include "mqtt_websockets/c-rbuf/include/ringbuffer.h" -#include "mqtt_websockets/c_rhash/include/c_rhash.h" +#include "mqtt_websockets/c-rbuf/cringbuffer.h" +#include "mqtt_websockets/c_rhash/c_rhash.h" typedef enum http_req_type { HTTP_REQ_GET = 0, diff --git a/aclk/mqtt_websockets/.github/workflows/run-tests.yaml b/aclk/mqtt_websockets/.github/workflows/run-tests.yaml new file mode 100644 index 0000000000..da5dde821b --- /dev/null +++ b/aclk/mqtt_websockets/.github/workflows/run-tests.yaml @@ -0,0 +1,14 @@ +name: run-tests +on: + push: + schedule: + - cron: '5 3 * * 0' + pull_request: +jobs: + run-tests: + runs-on: ubuntu-latest + steps: + - name: Install ruby and deps + run: sudo apt-get install ruby ruby-dev mosquitto + - name: Checkout + uses: actions/checkout@v2 diff --git a/aclk/mqtt_websockets/.gitignore b/aclk/mqtt_websockets/.gitignore new file mode 100644 index 0000000000..9f1a0d89a3 --- /dev/null +++ b/aclk/mqtt_websockets/.gitignore @@ -0,0 +1,10 @@ +build/* +!build/.keep +test +.vscode +mqtt/mqtt.c +mqtt/include/mqtt.h +libmqttwebsockets.* +*.o +.dirstamp +.deps diff --git a/aclk/mqtt_websockets/README.md b/aclk/mqtt_websockets/README.md new file mode 100644 index 0000000000..b159686dfb --- /dev/null +++ b/aclk/mqtt_websockets/README.md @@ -0,0 +1,7 @@ +# mqtt_websockets + +Library to connect MQTT client over Websockets Secure (WSS). + +## License + +The Project is released under GPL v3 license. See [License](LICENSE) diff --git a/aclk/mqtt_websockets/c-rbuf/cringbuffer.c b/aclk/mqtt_websockets/c-rbuf/cringbuffer.c new file mode 100644 index 0000000000..165980166e --- /dev/null +++ b/aclk/mqtt_websockets/c-rbuf/cringbuffer.c @@ -0,0 +1,203 @@ +// Copyright: SPDX-License-Identifier: GPL-3.0-only + +#include "cringbuffer.h" +#include "cringbuffer_internal.h" + +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +// this allows user to use their own +// custom memory allocation functions +#ifdef RBUF_CUSTOM_MALLOC +#include "ringbuffer_pal.h" +#else +#define crbuf_malloc(...) malloc(__VA_ARGS__) +#define crbuf_free(...) free(__VA_ARGS__) +#endif + +rbuf_t rbuf_create(size_t size) +{ + rbuf_t buffer = crbuf_malloc(sizeof(struct rbuf_t) + size); + if (!buffer) + return NULL; + + memset(buffer, 0, sizeof(struct rbuf_t)); + + buffer->data = ((char*)buffer) + sizeof(struct rbuf_t); + + buffer->head = buffer->data; + buffer->tail = buffer->data; + buffer->size = size; + buffer->end = buffer->data + size; + + return buffer; +} + +void rbuf_free(rbuf_t buffer) +{ + crbuf_free(buffer); +} + +void rbuf_flush(rbuf_t buffer) +{ + buffer->head = buffer->data; + buffer->tail = buffer->data; + buffer->size_data = 0; +} + +char *rbuf_get_linear_insert_range(rbuf_t buffer, size_t *bytes) +{ + *bytes = 0; + if (buffer->head == buffer->tail && buffer->size_data) + return NULL; + + *bytes = ((buffer->head >= buffer->tail) ? buffer->end : buffer->tail) - buffer->head; + return buffer->head; +} + +char *rbuf_get_linear_read_range(rbuf_t buffer, size_t *bytes) +{ + *bytes = 0; + if(buffer->head == buffer->tail && !buffer->size_data) + return NULL; + + *bytes = ((buffer->tail >= buffer->head) ? buffer->end : buffer->head) - buffer->tail; + + return buffer->tail; +} + +int rbuf_bump_head(rbuf_t buffer, size_t bytes) +{ + size_t free_bytes = rbuf_bytes_free(buffer); + if (bytes > free_bytes) + return 0; + int i = buffer->head - buffer->data; + buffer->head = &buffer->data[(i + bytes) % buffer->size]; + buffer->size_data += bytes; + return 1; +} + +int rbuf_bump_tail(rbuf_t buffer, size_t bytes) +{ + if(!rbuf_bump_tail_noopt(buffer, bytes)) + return 0; + + // if tail catched up with head + // start writing buffer from beggining + // this is not necessary (rbuf must work well without it) + // but helps to optimize big writes as rbuf_get_linear_insert_range + // will return bigger continuous region + if(buffer->tail == buffer->head) { + assert(buffer->size_data == 0); + rbuf_flush(buffer); + } + + return 1; +} + +size_t rbuf_get_capacity(rbuf_t buffer) +{ + return buffer->size; +} + +size_t rbuf_bytes_available(rbuf_t buffer) +{ + return buffer->size_data; +} + +size_t rbuf_bytes_free(rbuf_t buffer) +{ + return buffer->size - buffer->size_data; +} + +size_t rbuf_push(rbuf_t buffer, const char *data, size_t len) +{ + size_t to_cpy; + char *w_ptr = rbuf_get_linear_insert_range(buffer, &to_cpy); + if(!to_cpy) + return to_cpy; + + to_cpy = MIN(to_cpy, len); + memcpy(w_ptr, data, to_cpy); + rbuf_bump_head(buffer, to_cpy); + if(to_cpy < len) + to_cpy += rbuf_push(buffer, &data[to_cpy], len - to_cpy); + return to_cpy; +} + +size_t rbuf_pop(rbuf_t buffer, char *data, size_t len) +{ + size_t to_cpy; + const char *r_ptr = rbuf_get_linear_read_range(buffer, &to_cpy); + if(!to_cpy) + return to_cpy; + + to_cpy = MIN(to_cpy, len); + memcpy(data, r_ptr, to_cpy); + rbuf_bump_tail(buffer, to_cpy); + if(to_cpy < len) + to_cpy += rbuf_pop(buffer, &data[to_cpy], len - to_cpy); + return to_cpy; +} + +static inline void rbuf_ptr_inc(rbuf_t buffer, const char **ptr) +{ + (*ptr)++; + if(*ptr >= buffer->end) + *ptr = buffer->data; +} + +int rbuf_memcmp(rbuf_t buffer, const char *haystack, const char *needle, size_t needle_bytes) +{ + const char *end = needle + needle_bytes; + + // as head==tail can mean 2 things here + if (haystack == buffer->head && buffer->size_data) { + if (*haystack != *needle) + return (*haystack - *needle); + rbuf_ptr_inc(buffer, &haystack); + needle++; + } + + while (haystack != buffer->head && needle != end) { + if (*haystack != *needle) + return (*haystack - *needle); + rbuf_ptr_inc(buffer, &haystack); + needle++; + } + return 0; +} + +int rbuf_memcmp_n(rbuf_t buffer, const char *to_cmp, size_t to_cmp_bytes) +{ + return rbuf_memcmp(buffer, buffer->tail, to_cmp, to_cmp_bytes); +} + +char *rbuf_find_bytes(rbuf_t buffer, const char *needle, size_t needle_bytes, int *found_idx) +{ + const char *ptr = buffer->tail; + *found_idx = 0; + + if (!rbuf_bytes_available(buffer)) + return NULL; + + if (buffer->head == buffer->tail && buffer->size_data) { + if(!rbuf_memcmp(buffer, ptr, needle, needle_bytes)) + return (char *)ptr; + rbuf_ptr_inc(buffer, &ptr); + (*found_idx)++; + } + + while (ptr != buffer->head) + { + if(!rbuf_memcmp(buffer, ptr, needle, needle_bytes)) + return (char *)ptr; + rbuf_ptr_inc(buffer, &ptr); + (*found_idx)++; + } + return NULL; +} diff --git a/aclk/mqtt_websockets/c-rbuf/cringbuffer.h b/aclk/mqtt_websockets/c-rbuf/cringbuffer.h new file mode 100644 index 0000000000..eb98035a96 --- /dev/null +++ b/aclk/mqtt_websockets/c-rbuf/cringbuffer.h @@ -0,0 +1,47 @@ +// Copyright: SPDX-License-Identifier: GPL-3.0-only + +#ifndef CRINGBUFFER_H +#define CRINGBUFFER_H + +#include <stddef.h> + +typedef struct rbuf_t *rbuf_t; + +rbuf_t rbuf_create(size_t size); +void rbuf_free(rbuf_t buffer); +void rbuf_flush(rbuf_t buffer); + +/* /param bytes how much bytes can be copied into pointer returned + * /return pointer where data can be copied to or NULL if buffer full + */ +char *rbuf_get_linear_insert_range(rbuf_t buffer, size_t *bytes); +char *rbuf_get_linear_read_range(rbuf_t buffer, size_t *bytes); + +int rbuf_bump_head(rbuf_t buffer, size_t bytes); +int rbuf_bump_tail(rbuf_t buffer, size_t bytes); + +/* @param buffer related buffer instance + * @returns total capacity of buffer in bytes (not free/used) + */ +size_t rbuf_get_capacity(rbuf_t buffer); + +/* @param buffer related buffer instance + * @returns count of bytes stored in the buffer + */ +size_t rbuf_bytes_available(rbuf_t buffer); + +/* @param buffer related buffer instance + * @returns count of bytes available/free in the buffer (how many more bytes you can store in this buffer) + */ +size_t rbuf_bytes_free(rbuf_t buffer); + +/* writes as many bytes from `data` into the `buffer` as possible + * but maximum `len` bytes + */ +size_t rbuf_push(rbuf_t buffer, const char *data, size_t len); +size_t rbuf_pop(rbuf_t buffer, char *data, size_t len); + +char *rbuf_find_bytes(rbuf_t buffer, const char *needle, size_t needle_bytes, int *found_idx); +int rbuf_memcmp_n(rbuf_t buffer, const char *to_cmp, size_t to_cmp_bytes); + +#endif diff --git a/aclk/mqtt_websockets/c-rbuf/cringbuffer_internal.h b/aclk/mqtt_websockets/c-rbuf/cringbuffer_internal.h new file mode 100644 index 0000000000..d32de187ce --- /dev/null +++ b/aclk/mqtt_websockets/c-rbuf/cringbuffer_internal.h @@ -0,0 +1,37 @@ +// Copyright: SPDX-License-Identifier: GPL-3.0-only + +#ifndef CRINGBUFFER_INTERNAL_H +#define CRINGBUFFER_INTERNAL_H + +struct rbuf_t { + char *data; + + // points to next byte where we can write + char *head; + // points to oldest (next to be poped) readable byte + char *tail; + + // to avoid calculating data + size + // all the time + char *end; + + size_t size; + size_t size_data; +}; + +/* this exists so that it can be tested by unit tests + * without optimization that resets head and tail to + * beginning if buffer empty + */ +inline static int rbuf_bump_tail_noopt(rbuf_t buffer, size_t bytes) +{ + if (bytes > buffer->size_data) + return 0; + int i = buffer->tail - buffer->data; + buffer->tail = &buffer->data[(i + bytes) % buffer->size]; + buffer->size_data -= bytes; + + return 1; +} + +#endif diff --git a/aclk/mqtt_websockets/c-rbuf/ringbuffer_test.c b/aclk/mqtt_websockets/c-rbuf/ringbuffer_test.c new file mode 100644 index 0000000000..6a17c99567 --- /dev/null +++ b/aclk/mqtt_websockets/c-rbuf/ringbuffer_test.c @@ -0,0 +1,485 @@ +// Copyright: SPDX-License-Identifier: GPL-3.0-only + +#include "ringbuffer.h" + +// to be able to access internals +// never do this from app +#include "../src/ringbuffer_internal.h" + +#include <stdio.h> +#include <string.h> + +#define KNRM "\x1B[0m" +#define KRED "\x1B[31m" +#define KGRN "\x1B[32m" +#define KYEL "\x1B[33m" +#define KBLU "\x1B[34m" +#define KMAG "\x1B[35m" +#define KCYN "\x1B[36m" +#define KWHT "\x1B[37m" + +#define UNUSED(x) (void)(x) + +int total_fails = 0; +int total_tests = 0; +int total_checks = 0; + +#define CHECK_EQ_RESULT(x, y) \ + while (s_len--) \ + putchar('.'); \ + printf("%s%s " KNRM "\n", (((x) == (y)) ? KGRN : KRED), (((x) == (y)) ? " PASS " : " FAIL ")); \ + if ((x) != (y)) \ + total_fails++; \ + total_checks++; + +#define CHECK_EQ_PREFIX(x, y, prefix, subtest_name, ...) \ + { \ + int s_len = \ + 100 - \ + printf(("Checking: " KWHT "%s %s%2d " subtest_name " " KNRM), __func__, prefix, subtest_no, ##__VA_ARGS__); \ + CHECK_EQ_RESULT(x, y) \ + } + +#define CHECK_EQ(x, y, subtest_name, ...) \ + { \ + int s_len = \ + 100 - printf(("Checking: " KWHT "%s %2d " subtest_name " " KNRM), __func__, subtest_no, ##__VA_ARGS__); \ + CHECK_EQ_RESULT(x, y) \ + } + +#define TEST_DECL() \ + int subtest_no = 0; \ + printf(KYEL "TEST SUITE: %s\n" KNRM, __func__); \ + total_tests++; + +static void test_rbuf_get_linear_insert_range() +{ + TEST_DECL(); + + // check empty buffer behaviour + rbuf_t buff = rbuf_create(5); + char *to_write; + size_t ret; + to_write = rbuf_get_linear_insert_range(buff, &ret); + CHECK_EQ(ret, 5, "empty size"); + CHECK_EQ(to_write, buff->head, "empty write ptr"); + rbuf_free(buff); + + // check full buffer behaviour + subtest_no++; + buff = rbuf_create(5); + ret = rbuf_bump_head(buff, 5); + CHECK_EQ(ret, 1, "ret"); + to_write = rbuf_get_linear_insert_range(buff, &ret); + CHECK_EQ(to_write, NULL, "writable NULL"); + CHECK_EQ(ret, 0, "writable count = 0"); + + // check buffer flush + subtest_no++; + rbuf_flush(buff); + CHECK_EQ(rbuf_bytes_free(buff), 5, "size_free"); + CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail"); + CHECK_EQ(buff->head, buff->data, "head_ptr"); + CHECK_EQ(buff->tail, buff->data, "tail_ptr"); + + // check behaviour head > tail + subtest_no++; + rbuf_flush(buff); + rbuf_bump_head(buff, 3); + to_write = rbuf_get_linear_insert_range(buff, &ret); + CHECK_EQ(to_write, buff->head, "write location"); + CHECK_EQ(ret, 2, "availible to linear write"); + + // check behaviour tail > head + subtest_no++; + rbuf_flush(buff); + rbuf_bump_head(buff, 5); + rbuf_bump_tail(buff, 3); + CHECK_EQ(buff->head, buff->data, "head_ptr"); + CHECK_EQ(buff->tail, buff->data + 3, "tail_ptr"); + to_write = rbuf_get_linear_insert_range(buff, &ret); + CHECK_EQ(to_write, buff->head, "write location"); + CHECK_EQ(ret, 3, "availible to linear write"); + +/* // check behaviour tail and head at last element + subtest_no++; + rbuf_flush(buff); + rbuf_bump_head(buff, 4); + rbuf_bump_tail(buff, 4); + CHECK_EQ(buff->head, buff->end - 1, "head_ptr"); + CHECK_EQ(buff->tail, buff->end - 1, "tail_ptr"); + to_write = rbuf_get_linear_insert_range(buff, &ret); + CHECK_EQ(to_write, buff->head, "write location"); + CHECK_EQ(ret, 1, "availible to linear write");*/ + + // check behaviour tail and head at last element + // after rbuf_bump_tail optimisation that restarts buffer + // in case tail catches up with head + subtest_no++; + rbuf_flush(buff); + rbuf_bump_head(buff, 4); + rbuf_bump_tail(buff, 4); + CHECK_EQ(buff->head, buff->data, "head_ptr"); + CHECK_EQ(buff->tail, buff->data, "tail_ptr"); + to_write = rbuf_get_linear_insert_range(buff, &ret); + CHECK_EQ(to_write, buff->head, "write location"); + CHECK_EQ(ret, 5, "availible to linear write"); +} + +#define _CHECK_EQ(x, y, subtest_name, ...) CHECK_EQ_PREFIX(x, y, prefix, subtest_name, ##__VA_ARGS__) +#define _PREFX "(size = %5zu) " +static void test_rbuf_bump_head_bsize(size_t size) +{ + char prefix[16]; + snprintf(prefix, 16, _PREFX, size); + int subtest_no = 0; + rbuf_t buff = rbuf_create(size); + _CHECK_EQ(rbuf_bytes_free(buff), size, "size_free"); + + subtest_no++; + int ret = rbuf_bump_head(buff, size); + _CHECK_EQ(buff->data, buff->head, "loc"); + _CHECK_EQ(ret, 1, "ret"); + _CHECK_EQ(buff->size_data, buff->size, "size"); + _CHECK_EQ(rbuf_bytes_free(buff), 0, "size_free"); + + subtest_no++; + ret = rbuf_bump_head(buff, 1); + _CHECK_EQ(buff->data, buff->head, "loc no move"); + _CHECK_EQ(ret, 0, "ret error"); + _CHECK_EQ(buff->size_data, buff->size, "size"); + _CHECK_EQ(rbuf_bytes_free(buff), 0, "size_free"); + rbuf_free(buff); + + subtest_no++; + buff = rbuf_create(size); + ret = rbuf_bump_head(buff, size - 1); + _CHECK_EQ(buff->head, buff->end-1, "loc end"); + rbuf_free(buff); +} +#undef _CHECK_EQ + +static void test_rbuf_bump_head() +{ + TEST_DECL(); + UNUSED(subtest_no); + + size_t test_sizes[] = { 1, 2, 3, 5, 6, 7, 8, 100, 99999, 0 }; + for (int i = 0; test_sizes[i]; i++) + test_rbuf_bump_head_bsize(test_sizes[i]); +} + +static void test_rbuf_bump_tail_noopt(int subtest_no) +{ + rbuf_t buff = rbuf_create(10); + CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free"); + CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail"); + + subtest_no++; + int ret = rbuf_bump_head(buff, 5); + CHECK_EQ(ret, 1, "ret"); + CHECK_EQ(rbuf_bytes_free(buff), 5, "size_free"); + CHECK_EQ(rbuf_bytes_available(buff), 5, "size_avail"); + CHECK_EQ(buff->head, buff->data + 5, "head_ptr"); + CHECK_EQ(buff->tail, buff->data, "tail_ptr"); + + subtest_no++; + ret = rbuf_bump_tail_noopt(buff, 2); + CHECK_EQ(ret, 1, "ret"); + CHECK_EQ(rbuf_bytes_available(buff), 3, "size_avail"); + CHECK_EQ(rbuf_bytes_free(buff), 7, "size_free"); + CHECK_EQ(buff->head, buff->data + 5, "head_ptr"); + CHECK_EQ(buff->tail, buff->data + 2, "tail_ptr"); + + subtest_no++; + ret = rbuf_bump_tail_noopt(buff, 3); + CHECK_EQ(ret, 1, "ret"); + CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail"); + CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free"); + CHECK_EQ(buff->head, buff->data + 5, "head_ptr"); + CHECK_EQ(buff->tail, buff->data + 5, "tail_ptr"); + + subtest_no++; + ret = rbuf_bump_tail_noopt(buff, 1); + CHECK_EQ(ret, 0, "ret"); + CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail"); + CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free"); + CHECK_EQ(buff->head, buff->data + 5, "head_ptr"); + CHECK_EQ(buff->tail, buff->data + 5, "tail_ptr"); + + subtest_no++; + ret = rbuf_bump_head(buff, 7); + CHECK_EQ(ret, 1, "ret"); + CHECK_EQ(rbuf_bytes_available(buff), 7, "size_avail"); + CHECK_EQ(rbuf_bytes_free(buff), 3, "size_free"); + CHECK_EQ(buff->head, buff->data + 2, "head_ptr"); + CHECK_EQ(buff->tail, buff->data + 5, "tail_ptr"); + + subtest_no++; + ret = rbuf_bump_tail_noopt(buff, 5); + CHECK_EQ(ret, 1, "ret"); + CHECK_EQ(rbuf_bytes_available(buff), 2, "size_avail"); + CHECK_EQ(rbuf_bytes_free(buff), 8, "size_free"); + CHECK_EQ(buff->head, buff->data + 2, "head_ptr"); + CHECK_EQ(buff->tail, buff->data, "tail_ptr"); + + // check tail can't overrun head + subtest_no++; + ret = rbuf_bump_tail_noopt(buff, 3); + CHECK_EQ(ret, 0, "ret"); + CHECK_EQ(rbuf_bytes_available(buff), 2, "size_avail"); + CHECK_EQ(rbuf_bytes_free(buff), 8, "size_free"); + CHECK_EQ(buff->head, buff->data + 2, "head_ptr"); + CHECK_EQ(buff->tail, buff->data, "tail_ptr"); + + // check head can't overrun tail + subtest_no++; + ret = rbuf_bump_head(buff, 9); + CHECK_EQ(ret, 0, "ret"); + CHECK_EQ(rbuf_bytes_available(buff), 2, "size_avail"); + CHECK_EQ(rbuf_bytes_free(buff), 8, "size_free"); + CHECK_EQ(buff->head, buff->data + 2, "head_ptr"); + CHECK_EQ(buff->tail, buff->data, "tail_ptr"); + + // check head can fill the buffer + subtest_no++; + ret = rbuf_bump_head(buff, 8); + CHECK_EQ(ret, 1, "ret"); + CHECK_EQ(rbuf_bytes_available(buff), 10, "size_avail"); + CHECK_EQ(rbuf_bytes_free(buff), 0, "size_free"); + CHECK_EQ(buff->head, buff->data, "head_ptr"); + CHECK_EQ(buff->tail, buff->data, "tail_ptr"); |