diff --git a/.clang-format b/.clang-format index f98fb9c..38abf14 100644 --- a/.clang-format +++ b/.clang-format @@ -51,7 +51,7 @@ AllowShortEnumsOnASingleLine: true AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: All -AllowShortLoopsOnASingleLine: true +AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: All AlwaysBreakAfterReturnType: AllDefinitions AlwaysBreakBeforeMultilineStrings: false diff --git a/.gitignore b/.gitignore index f25f439..32aec77 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,3 @@ *.a build driver -*.elf -*.json -.cache diff --git a/Makefile b/Makefile index 50d4220..10c26a0 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,19 @@ # SPDX-License-Identifier: MIT CC = gcc -CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter -CFLAGS += -Wno-unused-variable -Wno-unused-function +CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter +CFLAGS += -Wno-unused-variable -Wno-unused-function CFLAGS += -Wno-unused-but-set-variable -Wno-unused-value -Wno-unused-label CFLAGS += -Wno-unused-result -Wno-unused-const-variable -CFLAGS += -I. - -ifneq ($(RELEASE), 1) -CFLAGS += -g -O0 -std=c99 -march=native -mtune=native -CFLAGS += -DDEBUG -else +ifneq ($(DEBUG), 1) CFLAGS += -fno-exceptions -fno-asynchronous-unwind-tables -fno-ident CFLAGS += -fno-unwind-tables -fno-stack-protector -fno-plt -fno-pic CFLAGS += -O3 -std=c99 -march=native -mtune=native -fomit-frame-pointer CFLAGS += -fshort-enums +else +CFLAGS += -g -O0 -std=c99 -march=native -mtune=native +CFLAGS += -DDEBUG endif # DEBUG LDFLAGS = -lm @@ -27,9 +25,6 @@ ASMS = $(C_SOURCES:.c=.s) all: $(OBJECTS) -test: test/test.elf - ./test/test.elf - %.o: %.c $(C_HEADERS) @echo "CC $<" @$(CC) $(CFLAGS) -c $< -o $@ @@ -44,9 +39,6 @@ driver: $(OBJECTS) run: driver @./driver -test/test.elf: test/test_ringbuf.o ringbuf.o - @$(CC) $(CFLAGS) $^ -o $@ -lpthread -lcmocka - lib: $(OBJECTS) @ar rcs librbuf.a ringbuf.o @@ -58,7 +50,7 @@ install: @cp ringbuf.h /usr/local/include clean: - rm -f $(OBJECTS) $(ASMS) driver librbuf.a librbuf.so **/*.o **/*.elf + rm -f $(OBJECTS) $(ASMS) driver librbuf.a librbuf.so asm: $(ASMS) $(OBJECTS) wc -l $(ASMS) @@ -70,4 +62,4 @@ tidy: format: clang-format -i $(C_SOURCES) $(C_HEADERS) -.PHONY: all clean format asm test +.PHONY: all clean format asm diff --git a/README.md b/README.md deleted file mode 100644 index 1626f2c..0000000 --- a/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# RingBuf - -RingBuf is an allocator-agnostic, non-overwriting circular/ring buffer -implementation in C99. -See: [Circular Buffer (Wikipedia)](https://en.wikipedia.org/wiki/Circular_buffer) - -## Features -- Space Efficiency -The code is designed to be portable and flexible. The inspiration initially -came to me when designing a network driver. When operating in memory -constrained environments, every byte is of upmost importance. Traditional -metaprogramming such as templating in C++ and template metaprogramming in C, -although generic has the side effect of expanding into discrete machine code -specific for the specialization applied, essentially scaling (spatially) linear -with the amount of different datatypes we specialize on. This implementation -circumvents this by treating all data as a void pointer with a length. This -implies **no deep copies**. - -- Allocator Agnostic -Another common characteristic of memory constrained environments are -custom malloc implementations and/or variations. - - Signatures - - Arena - -## Design considerations -- Holding a reference to malloc internally would make a tidier interface -- Passing a user-defined function for deep-copies would enable certain applications - -## Usage -In essence: -```c -int value = 42; -struct RingBuf rb; -rb_init(&rb, 10, malloc, sizeof(int)); -rb_push_back(&rb, (void *)&data, memcpy); -rb_pop_front(&rb, &value, memcpy); -``` -Most of these functions return Enum result types. See: -[ringbuf.h](./ringbuf.h). - -## Future Improvements -- Trim the code -- Reduce boilerplate in tests -- Reduce the number of tests in exchange for better test fit diff --git a/driver.c b/driver.c index 19d0e74..58045ed 100644 --- a/driver.c +++ b/driver.c @@ -4,16 +4,27 @@ #include #include -#ifndef DEBUG -#define DEBUG -#endif - -// #define rb_size_t size_t +#define rb_size_t size_t // #define rb_size_t int #include "ringbuf.h" typedef int DATATYPE; +/** + * @brief Debug print and empty the ringbuf + */ +void +rb_debug_empty(struct RingBuf *rb) +{ + int d; + if(rb->count == 0) + return; + printf("Debug Data: ["); + while(rb_pop_front(rb, &d, memcpy) == ReadOk) + printf("%d,", d); + printf("\b]\n"); +} + int main(void) { diff --git a/ringbuf.c b/ringbuf.c index 59626f7..d73affe 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -4,7 +4,6 @@ // #include #include "ringbuf.h" -#include #include #ifdef DEBUG @@ -30,18 +29,7 @@ rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T malloc_fn, void rb_destroy(struct RingBuf *rb, FREE_T free_fn) { - if(rb->buffer) { // Prevent double-free - free_fn(rb->buffer); - rb->buffer = NULL; - rb->buffer_end = NULL; - - rb->struct_size = 0; - rb->capacity = 0; - rb->count = 0; - - rb->write_head = NULL; - rb->read_head = NULL; - } + free_fn(rb->buffer); } void @@ -58,8 +46,6 @@ rb_push_back(struct RingBuf *rb, const void *item, MEMCPY_T memcpy_fn) if(rb->count == rb->capacity) return Full; - assert(rb->buffer != NULL); - memcpy_fn(rb->write_head, item, rb->struct_size); // Advance the write head @@ -131,21 +117,6 @@ rb_pop_front(struct RingBuf *rb, void *item, MEMCPY_T memcpy_fn) return ReadOk; } -#ifdef DEBUG -#include - -void -rb_debug_empty(struct RingBuf *rb) -{ - int d; - if(rb->count == 0) - return; - printf("Debug Data: ["); - while(rb_pop_front(rb, &d, memcpy) == ReadOk) - printf("%d,", d); - printf("\b]\n"); -} - void rb_debug_print(struct RingBuf *rb) { @@ -162,5 +133,3 @@ rb_debug_print(struct RingBuf *rb) DEBUG_PRINT("============%s\n", ""); } - -#endif diff --git a/ringbuf.h b/ringbuf.h index 1844437..46db542 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -111,25 +111,12 @@ enum ReadResult rb_pop_front(struct RingBuf *rb, void *item, /** * @brief Free the ring buffer - * - * @details This function is idempotent, consecutive calls will not result in a - * double free. - * * @param rb The ring buffer * @param free The free function */ void rb_destroy(struct RingBuf *rb, void(free)()); -#ifdef DEBUG - /** * @brief Debug print */ void rb_debug_print(struct RingBuf *rb); - -/** - * @brief Debug print and empty the ringbuf - */ -void rb_debug_empty(struct RingBuf *rb); - -#endif diff --git a/test/test_ringbuf.c b/test/test_ringbuf.c deleted file mode 100644 index 44756e9..0000000 --- a/test/test_ringbuf.c +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "ringbuf.h" - -/** Tests initialization */ -static void -test_rb_init_empty(void **state) { - struct RingBuf rb; - rb_size_t capacity = 10; - rb_size_t struct_size = sizeof(int); - - rb_init(&rb, capacity, malloc, struct_size); - - assert_int_equal(rb.capacity, capacity); - assert_int_equal(rb.count, 0); -} - -/** Tests push_back */ -static void -test_rb_push_back(void **state) { - struct RingBuf rb; - rb_size_t capacity = 10; - rb_size_t struct_size = sizeof(int); - - rb_init(&rb, capacity, malloc, struct_size); - - int data = 10; - - enum WriteResult wr = rb_push_back(&rb, (void *)&data, memcpy); - assert_int_equal(wr, WriteOk); - - assert_int_equal(rb.capacity, capacity); - assert_int_equal(rb.count, 1); -} - -/** Tests the destroy function */ -static void -test_rb_init_destroy(void **state) { - struct RingBuf rb = { 0, 0, 0, NULL, NULL, NULL, NULL }; - rb_size_t capacity = 10; - rb_size_t struct_size = sizeof(int); - - rb_init(&rb, capacity, malloc, struct_size); - - int data = 10; - - rb_push_back(&rb, (void *)&data, memcpy); - - assert_int_equal(rb.capacity, capacity); - assert_int_equal(rb.count, 1); - - rb_destroy(&rb, free); - - assert_null(rb.buffer); -} - -/** Tests fill, but not overflow/wrap-around */ -static void -test_rb_push_back_fill(void **state) { - struct RingBuf rb = { 0, 0, 0, NULL, NULL, NULL, NULL }; - rb_size_t capacity = 10; - rb_size_t struct_size = sizeof(int); - - rb_init(&rb, capacity, malloc, struct_size); - - const int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - - // Insert them all sequentially - for(int i = 0; rb_push_back(&rb, &arr[i], memcpy) == WriteOk; i++); - assert_int_equal(rb.count, 10); - assert_int_equal(rb.capacity, capacity); - - // Read them out and assert expected value - for(int i = 0, d = __INT_MAX__; rb_pop_front(&rb, &d, memcpy) == ReadOk; - i++) { - assert_int_equal(d, arr[i]); - } - - assert_int_equal(rb.capacity, capacity); - assert_int_equal(rb.count, 0); - - rb_destroy(&rb, free); - - assert_null(rb.buffer); -} - -int -main(void) { - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_rb_init_empty), - cmocka_unit_test(test_rb_push_back), - cmocka_unit_test(test_rb_init_destroy), - cmocka_unit_test(test_rb_push_back_fill), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -}