From 4e1254fde19aef7bc7aa5326003e83cd93204db6 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 30 Jun 2024 21:11:30 +0200 Subject: [PATCH 01/46] Default to release build --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d27a91c..e1cb59a 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ 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 -ifeq ($(RELEASE), 1) +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 From 9c746a10cf8604d9d1317db8c40b1d513ee1515e Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 30 Jun 2024 21:11:49 +0200 Subject: [PATCH 02/46] Formatting --- ringbuf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ringbuf.c b/ringbuf.c index bd9c26e..5c004ba 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -24,10 +24,12 @@ rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T malloc_fn, rb->write_head = rb->buffer; rb->read_head = rb->buffer; - // Read from buffer at max position to force a segfault if theres an issue DEBUG_PRINT("Reading from buffer at position %d\n", rb->capacity * rb->struct_size); + + // Read from buffer at max position to force a segfault if theres an issue void *top = rb->buffer + (rb->capacity * rb->struct_size); + DEBUG_PRINT("Buffer top successfully read at virtual address: %p\n", &top); DEBUG_PRINT( From 07bb60c8ae72da1a833347446b1b2cc1f50d63e7 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 30 Jun 2024 23:57:08 +0200 Subject: [PATCH 03/46] Better docstrings and example driver --- driver.c | 13 ++++++++----- ringbuf.h | 54 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/driver.c b/driver.c index 2350cc4..8718d9f 100644 --- a/driver.c +++ b/driver.c @@ -13,14 +13,17 @@ main(void) struct RingBuf rb; rb_init(&rb, 10, malloc, sizeof(int)); - int data = 5; + int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - rb_push_back(&rb, &data, memcpy); + int ok = WriteOk; // Assume we can write + for(int i = 0; i < 10 && ok == WriteOk; i++) { + ok = rb_push_back(&rb, &arr[i], memcpy); + } int d; - rb_pop_front(&rb, &d); - - printf("Data: %d\n", d); + while(rb_pop_front(&rb, &d) == ReadOk) { + printf("Data: %d\n", d); + } rb_destroy(&rb, free); diff --git a/ringbuf.h b/ringbuf.h index bffc0f2..a017a7f 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -6,38 +6,60 @@ #define rb_size_t int #endif -/** Signatures of generic functions */ +/** Signatures of allocators */ typedef void *(*ALLOC_T)(rb_size_t); + +/** Signature of memcpy */ typedef void *(*MEMCPY_T)(void *, const void *, rb_size_t); /** - * Ring buffer, also known as circular buffer. + * @brief Ring buffer, also known as circular buffer. */ struct RingBuf { - rb_size_t struct_size; /* Size of the struct */ - rb_size_t capacity; /* The physical capacity of the entire ringbuf */ - rb_size_t count; /* The number of elements in the buffer */ - void *write_head; /* Address of the write head */ - void *read_head; /* Address of the read head */ - void *buffer; /* The actual data */ - void *buffer_end; /* The end of the buffer */ + rb_size_t struct_size; /** Size of the struct */ + rb_size_t capacity; /** The physical capacity of the entire ringbuf */ + rb_size_t count; /** The number of elements in the buffer */ + void *write_head; /** Address of the write head */ + void *read_head; /** Address of the read head */ + void *buffer; /** The actual data */ + void *buffer_end; /** The end of the buffer */ } __attribute__((packed)); -// TODO: Perhaps unify these to RBResult? - enum WriteResult { Full, WriteOk }; /** Result of a write */ enum ReadResult { Empty, ReadOk }; /** Result of a read */ -/** Initialize the ring buffer */ -void rb_init(struct RingBuf *rb, rb_size_t capacity, void *(*alloc)(rb_size_t), +/** + * @brief Initialize the ring buffer + * @param rb The ring buffer to initialize + * @param capacity The capacity of the ring buffer + * @param alloc The allocator function + * @param struct_size The size of the struct + * @return void + */ +void rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T alloc, rb_size_t struct_size); -/** Insert data to the ring buffer */ +/** + * @brief Insert data to the ring buffer + * @param rb The ring buffer + * @param item The item to insert + * @param memcpy_fn The memcpy function + * @return WriteResult + */ enum WriteResult rb_push_back(struct RingBuf *rb, const void *item, MEMCPY_T memcpy_fn); -/** Read data from the ring buffer */ +/** + * @brief Read data from the ring buffer + * @param rb The ring buffer + * @param item The item to read into + * @return ReadResult + */ enum ReadResult rb_pop_front(struct RingBuf *rb, void *item); -/** Free the ring buffer */ +/** + * @brief Free the ring buffer + * @param rb The ring buffer + * @param free The free function + */ void rb_destroy(struct RingBuf *rb, void(free)()); From df9cd367a30b46a7a7e59159addabdf1b6aeb51b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 05:39:08 +0200 Subject: [PATCH 04/46] .so and .a target --- .gitignore | 2 ++ Makefile | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 46ca31e..32aec77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ *.o *.s +*.so +*.a build driver diff --git a/Makefile b/Makefile index e1cb59a..49f09d0 100644 --- a/Makefile +++ b/Makefile @@ -39,8 +39,18 @@ driver: $(OBJECTS) run: driver @./driver +lib: $(OBJECTS) + @ar rcs librbuf.a ringbuf.o + +dylib: $(OBJECTS) + @$(CC) $(CFLAGS) -fPIC -shared -o librbuf.so ringbuf.o + +install: + @cp librbuf.a /usr/local/lib + @cp ringbuf.h /usr/local/include + clean: - rm -f $(OBJECTS) $(ASMS) driver + rm -f $(OBJECTS) $(ASMS) driver librbuf.a librbuf.so asm: $(ASMS) $(OBJECTS) wc -l $(ASMS) From c4f1ede6b01c5a66d211e94ed295a4e34c05373b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 05:39:21 +0200 Subject: [PATCH 05/46] rb_push_many first draft --- ringbuf.c | 41 +++++++++++++++++++++++++++++++++++++++++ ringbuf.h | 22 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/ringbuf.c b/ringbuf.c index 5c004ba..d0b4621 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -63,6 +63,47 @@ rb_push_back(struct RingBuf *rb, const void *item, MEMCPY_T memcpy_fn) return WriteOk; } +enum WriteResult +rb_push_many(struct RingBuf *rb, const void *items, MEMCPY_T memcpy_fn, + rb_size_t n) +{ + if(rb->count + n > rb->capacity) + return Full; // Perhaps rename to InsufficientSpace + + // If the write head will move past the end of the buffer + // we need to to the write in two steps. + void *end = (char *)rb->write_head + rb->struct_size * n; + + if(end > rb->buffer_end) { + + // Calculate the number of items that can be written in the first chunk + rb_size_t first_chunk = (char *)rb->buffer_end - (char *)rb->write_head; + + DEBUG_PRINT("Multi-chunk write. First chunk: %d\n", first_chunk); + + // Write the first chunk + memcpy_fn(rb->write_head, items, rb->struct_size * first_chunk); + + // Set the write head to the beginning of the buffer + rb->write_head = rb->buffer; + rb->count += first_chunk; + n -= first_chunk; + } else { + DEBUG_PRINT("Single-chunk write. No need to wrap around.%s\n", ""); + } + + DEBUG_PRINT("Writing %d items\n", n); + memcpy_fn(rb->write_head, items, rb->struct_size * n); + if(rb->write_head == rb->buffer_end) + rb->write_head = rb->buffer; + + // Advance the write head + rb->write_head = (char *)rb->write_head + rb->struct_size * n; + rb->count+=n; + + return WriteOk; +} + enum ReadResult rb_pop_front(struct RingBuf *rb, void *item) { diff --git a/ringbuf.h b/ringbuf.h index a017a7f..f374bac 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -49,6 +49,28 @@ void rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T alloc, enum WriteResult rb_push_back(struct RingBuf *rb, const void *item, MEMCPY_T memcpy_fn); +/** + * @brief Insert multiple data to the ring buffer + * + * @details This function is more efficient than calling rb_push_back multiple + * times. It only advances the write head once, and attempts to write all the + * memory in one go. + * + * If n is greater than the capacity, it will return Full. + * If the full write will overflow, it will wrap around. + * + * If the buffer is full, it will return Full and not write + * anything. + * + * @param rb The ring buffer + * @param items The items to insert + * @param memcpy_fn The memcpy function + * @param n The number of items to insert + * @return WriteResult + */ +enum WriteResult rb_push_many(struct RingBuf *rb, const void *items, + MEMCPY_T memcpy_fn, rb_size_t n); + /** * @brief Read data from the ring buffer * @param rb The ring buffer From db38111b3cc68a00514fc7e6601ad41d1e2b34d0 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 05:42:44 +0200 Subject: [PATCH 06/46] Interface fix where MEMCPY_T parameter was forgotten in rb_pop_front --- ringbuf.c | 4 ++-- ringbuf.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ringbuf.c b/ringbuf.c index d0b4621..81c87c0 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -105,12 +105,12 @@ rb_push_many(struct RingBuf *rb, const void *items, MEMCPY_T memcpy_fn, } enum ReadResult -rb_pop_front(struct RingBuf *rb, void *item) +rb_pop_front(struct RingBuf *rb, void *item, MEMCPY_T memcpy_fn) { if(rb->count == 0) return Empty; - memcpy(item, rb->read_head, rb->struct_size); + memcpy_fn(item, rb->read_head, rb->struct_size); // Advance the read head rb->read_head = (char *)rb->read_head + rb->struct_size; diff --git a/ringbuf.h b/ringbuf.h index f374bac..bd1626b 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -77,7 +77,7 @@ enum WriteResult rb_push_many(struct RingBuf *rb, const void *items, * @param item The item to read into * @return ReadResult */ -enum ReadResult rb_pop_front(struct RingBuf *rb, void *item); +enum ReadResult rb_pop_front(struct RingBuf *rb, void *item, MEMCPY_T memcpy_fn); /** * @brief Free the ring buffer From d19917bba7dd7cef3b7bd6af54de10d7cfc479dd Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 05:50:47 +0200 Subject: [PATCH 07/46] Formatting --- ringbuf.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ringbuf.h b/ringbuf.h index bd1626b..9938c8f 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -39,6 +39,7 @@ enum ReadResult { Empty, ReadOk }; /** Result of a read */ void rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T alloc, rb_size_t struct_size); + /** * @brief Insert data to the ring buffer * @param rb The ring buffer @@ -51,17 +52,17 @@ enum WriteResult rb_push_back(struct RingBuf *rb, const void *item, /** * @brief Insert multiple data to the ring buffer - * + * * @details This function is more efficient than calling rb_push_back multiple * times. It only advances the write head once, and attempts to write all the - * memory in one go. - * + * memory in one go. + * * If n is greater than the capacity, it will return Full. * If the full write will overflow, it will wrap around. - * + * * If the buffer is full, it will return Full and not write * anything. - * + * * @param rb The ring buffer * @param items The items to insert * @param memcpy_fn The memcpy function @@ -77,7 +78,8 @@ enum WriteResult rb_push_many(struct RingBuf *rb, const void *items, * @param item The item to read into * @return ReadResult */ -enum ReadResult rb_pop_front(struct RingBuf *rb, void *item, MEMCPY_T memcpy_fn); +enum ReadResult rb_pop_front(struct RingBuf *rb, void *item, + MEMCPY_T memcpy_fn); /** * @brief Free the ring buffer From 41f817fd5b7d3f9adbb9ce47c511d95fd1f7b7b8 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 05:50:59 +0200 Subject: [PATCH 08/46] rb_clear initial --- ringbuf.c | 8 ++++++++ ringbuf.h | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/ringbuf.c b/ringbuf.c index 81c87c0..e3c7067 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -45,6 +45,14 @@ rb_destroy(struct RingBuf *rb, void(free)()) free(rb->buffer); } +void +rb_clear(struct RingBuf *rb) +{ + rb->count = 0; + rb->write_head = rb->buffer; + rb->read_head = rb->buffer; +} + enum WriteResult rb_push_back(struct RingBuf *rb, const void *item, MEMCPY_T memcpy_fn) { diff --git a/ringbuf.h b/ringbuf.h index 9938c8f..0117887 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -39,6 +39,13 @@ enum ReadResult { Empty, ReadOk }; /** Result of a read */ void rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T alloc, rb_size_t struct_size); +/** + * @brief Clear the ring buffer + * @details This function will reset the read and write heads to the beginning + * of the buffer, and set the count to 0. It will not free the buffer. + * @param rb The ring buffer + */ +void rb_clear(struct RingBuf *rb); /** * @brief Insert data to the ring buffer From 8cbca387269e5c0371cda8d4e2e801399ea743cb Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 06:26:17 +0200 Subject: [PATCH 09/46] Bugfix incrementing count by correct amount in rb_push_many --- ringbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ringbuf.c b/ringbuf.c index e3c7067..23adb94 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -107,7 +107,7 @@ rb_push_many(struct RingBuf *rb, const void *items, MEMCPY_T memcpy_fn, // Advance the write head rb->write_head = (char *)rb->write_head + rb->struct_size * n; - rb->count+=n; + rb->count += n; return WriteOk; } From 8e77f810ec3c5dce234e16df06f753f838712796 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 06:27:05 +0200 Subject: [PATCH 10/46] Better enum docs, added errors related to *_many functions --- ringbuf.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ringbuf.h b/ringbuf.h index 0117887..efffe50 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -25,8 +25,24 @@ struct RingBuf { void *buffer_end; /** The end of the buffer */ } __attribute__((packed)); -enum WriteResult { Full, WriteOk }; /** Result of a write */ -enum ReadResult { Empty, ReadOk }; /** Result of a read */ +/* + * Considerations: One hot encoding for the enum values + * (8 bit each with -fshort-enums) + */ + +/** Result of a write */ +enum WriteResult { + WriteOk, /** The write was successful */ + Full, /** The buffer is full */ + InsufficientSpace /** There is not enough space to write */ +}; + +/** Result of a read */ +enum ReadResult { + Empty, /** The buffer is empty */ + ReadOk, /** The read was successful */ + InsufficientData /** There is not enough data to read */ +}; /** * @brief Initialize the ring buffer From 68af34c42869248860238a5edfed51879ffe7d21 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 06:27:22 +0200 Subject: [PATCH 11/46] -fshort-enums in release mode --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 49f09d0..760129d 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ 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 From c06bb51cdcb39b5b5c220639897ca17c69bc1d1f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 06:27:39 +0200 Subject: [PATCH 12/46] Driver code updated --- driver.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/driver.c b/driver.c index 8718d9f..0c4be14 100644 --- a/driver.c +++ b/driver.c @@ -16,16 +16,31 @@ main(void) int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int ok = WriteOk; // Assume we can write - for(int i = 0; i < 10 && ok == WriteOk; i++) { - ok = rb_push_back(&rb, &arr[i], memcpy); - } + ok = rb_push_many(&rb, arr, memcpy, 8); int d; - while(rb_pop_front(&rb, &d) == ReadOk) { + while(rb_pop_front(&rb, &d, memcpy) == ReadOk) { printf("Data: %d\n", d); } + // Test wrap around + rb_push_many(&rb, arr, memcpy, 10); + while(rb_pop_front(&rb, &d, memcpy) == ReadOk) { + printf("Data: %d\n", d); + } + + // Test clear + rb_clear(&rb); + if(rb_pop_front(&rb, &d, memcpy) != Empty) { + printf("Buffer is not empty after clear...\n"); + } + rb_destroy(&rb, free); + enum WriteResult wr = WriteOk; + enum ReadResult rr = ReadOk; + printf("Size of wr: %lu bytes.\n", sizeof(wr)); + printf("Size of rr: %lu bytes.\n", sizeof(rr)); + return 0; } From eb4b11dda7c6099b93c49554169cdc622e942f48 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 06:31:52 +0200 Subject: [PATCH 13/46] Formatting --- ringbuf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ringbuf.h b/ringbuf.h index efffe50..4e3cbb8 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -26,7 +26,7 @@ struct RingBuf { } __attribute__((packed)); /* - * Considerations: One hot encoding for the enum values + * Considerations: One hot encoding for the enum values * (8 bit each with -fshort-enums) */ From ea100383d7bba52e6c23c2657bfa424b7d6a0f33 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 06:32:22 +0200 Subject: [PATCH 14/46] Typedef for free function signature --- ringbuf.c | 4 ++-- ringbuf.h | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ringbuf.c b/ringbuf.c index 23adb94..9ea1a6d 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -40,9 +40,9 @@ rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T malloc_fn, } void -rb_destroy(struct RingBuf *rb, void(free)()) +rb_destroy(struct RingBuf *rb, FREE_T free_fn) { - free(rb->buffer); + free_fn(rb->buffer); } void diff --git a/ringbuf.h b/ringbuf.h index 4e3cbb8..6d6d01f 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -12,6 +12,9 @@ typedef void *(*ALLOC_T)(rb_size_t); /** Signature of memcpy */ typedef void *(*MEMCPY_T)(void *, const void *, rb_size_t); +/** Signature of free */ +typedef void (*FREE_T)(void *); + /** * @brief Ring buffer, also known as circular buffer. */ From 6022bdee9245200bcad638298a4f14483bce7fa4 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 06:33:00 +0200 Subject: [PATCH 15/46] tidy makefile target to run clang-tidy --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 760129d..6b1324e 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,9 @@ asm: $(ASMS) $(OBJECTS) wc -l $(ASMS) size $(OBJECTS) +tidy: + @clang-tidy $(C_SOURCES) -- $(CFLAGS) + format: clang-format -i $(C_SOURCES) $(C_HEADERS) From de2465de8318365a4c5ca88ed42ce476271af93e Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 06:37:58 +0200 Subject: [PATCH 16/46] Check return in driver program --- driver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/driver.c b/driver.c index 0c4be14..cd59397 100644 --- a/driver.c +++ b/driver.c @@ -16,7 +16,9 @@ main(void) int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int ok = WriteOk; // Assume we can write - ok = rb_push_many(&rb, arr, memcpy, 8); + if(rb_push_many(&rb, arr, memcpy, 8) != WriteOk) { + printf("Failed to write data to buffer...\n"); + } int d; while(rb_pop_front(&rb, &d, memcpy) == ReadOk) { From 06b949bed46dd5a596b10b5de5beb62770e7f258 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 06:38:01 +0200 Subject: [PATCH 17/46] Remove redundant debug code/printing --- ringbuf.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/ringbuf.c b/ringbuf.c index 9ea1a6d..0293f36 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -23,20 +23,6 @@ rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T malloc_fn, rb->count = 0; rb->write_head = rb->buffer; rb->read_head = rb->buffer; - - DEBUG_PRINT("Reading from buffer at position %d\n", - rb->capacity * rb->struct_size); - - // Read from buffer at max position to force a segfault if theres an issue - void *top = rb->buffer + (rb->capacity * rb->struct_size); - - DEBUG_PRINT("Buffer top successfully read at virtual address: %p\n", &top); - - DEBUG_PRINT( - "Initialized ring buffer. Capacit: %d, struct_size: %d, total: %d\n", - rb->capacity, rb->struct_size, rb->capacity * rb->struct_size); - - DEBUG_PRINT("Size of RB: %lu\n", sizeof(struct RingBuf)); } void From 85071d3130d4dc0aace71628ccdf1edabbc2d171 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 06:49:49 +0200 Subject: [PATCH 18/46] Driver code simplifying --- driver.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/driver.c b/driver.c index cd59397..36f7bb5 100644 --- a/driver.c +++ b/driver.c @@ -21,15 +21,18 @@ main(void) } int d; - while(rb_pop_front(&rb, &d, memcpy) == ReadOk) { - printf("Data: %d\n", d); - } + printf("Data: ["); + while(rb_pop_front(&rb, &d, memcpy) == ReadOk) + printf("%d,", d); + printf("\b]\n"); // Test wrap around rb_push_many(&rb, arr, memcpy, 10); - while(rb_pop_front(&rb, &d, memcpy) == ReadOk) { - printf("Data: %d\n", d); - } + + printf("Data: ["); + while(rb_pop_front(&rb, &d, memcpy) == ReadOk) + printf("%d,", d); + printf("\b]\n"); // Test clear rb_clear(&rb); From a5c5ecd64b66220692f058c1ae22579f08d88c48 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 08:39:31 +0200 Subject: [PATCH 19/46] Bug................................... --- driver.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/driver.c b/driver.c index 36f7bb5..3d90144 100644 --- a/driver.c +++ b/driver.c @@ -11,16 +11,52 @@ int main(void) { struct RingBuf rb; + int d; rb_init(&rb, 10, malloc, sizeof(int)); + const int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + // Single writes + printf("\n=== Single writes ===\n\n"); + + int idx = 0; + while (idx < 5) { + if(rb_push_back(&rb, &arr[idx], memcpy) != WriteOk) { + printf("Failed to write data to buffer...\n"); + } + idx++; + } + + // Pop the last n elements + for(int a = 0; a < 1; a++) { + if(rb_pop_front(&rb, &d, memcpy) != ReadOk) { + printf("Failed to read data from buffer...\n"); + } + printf("Data: %d\n", d); + } + + printf("idx: %d\n", idx); + + // Push the rest + while (rb_push_back(&rb, &arr[idx], memcpy) == WriteOk) { + printf("Wrote: %d\n", arr[idx]); + idx++; + } + + printf("Data: ["); + while(rb_pop_front(&rb, &d, memcpy) == ReadOk) + printf("%d,", d); + printf("\b]\n"); + + // Multiple writes + printf("\n=== Multiple writes ===\n\n"); + + rb_clear(&rb); int ok = WriteOk; // Assume we can write if(rb_push_many(&rb, arr, memcpy, 8) != WriteOk) { printf("Failed to write data to buffer...\n"); } - int d; printf("Data: ["); while(rb_pop_front(&rb, &d, memcpy) == ReadOk) printf("%d,", d); From 2886a81574fce8cddba8792e3659fa18815f544c Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 2 Jul 2024 10:31:52 +0200 Subject: [PATCH 20/46] Linker flags and makefile cleanup --- Makefile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 6b1324e..10c26a0 100644 --- a/Makefile +++ b/Makefile @@ -14,10 +14,9 @@ CFLAGS += -fshort-enums else CFLAGS += -g -O0 -std=c99 -march=native -mtune=native CFLAGS += -DDEBUG -endif +endif # DEBUG -# Include debug flags -CFLAGS += -g -O0 -std=c99 -march=native -mtune=native +LDFLAGS = -lm C_SOURCES = $(wildcard *.c) C_HEADERS = $(wildcard *.h) @@ -35,7 +34,7 @@ all: $(OBJECTS) @$(CC) $(CFLAGS) -S -masm=intel $< driver: $(OBJECTS) - @$(CC) $(CFLAGS) $^ -o $@ + @$(CC) $(LDFLAGS) $^ -o $@ run: driver @./driver @@ -44,7 +43,7 @@ lib: $(OBJECTS) @ar rcs librbuf.a ringbuf.o dylib: $(OBJECTS) - @$(CC) $(CFLAGS) -fPIC -shared -o librbuf.so ringbuf.o + @$(CC) $(LDLAGS) -fPIC -shared -o librbuf.so ringbuf.o install: @cp librbuf.a /usr/local/lib From e5006ed262f195032bd2d5c3869977b42756d50c Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Jul 2024 13:31:57 +0200 Subject: [PATCH 21/46] Formatting --- driver.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/driver.c b/driver.c index 3d90144..bdb4f51 100644 --- a/driver.c +++ b/driver.c @@ -17,9 +17,9 @@ main(void) // Single writes printf("\n=== Single writes ===\n\n"); - + int idx = 0; - while (idx < 5) { + while(idx < 5) { if(rb_push_back(&rb, &arr[idx], memcpy) != WriteOk) { printf("Failed to write data to buffer...\n"); } @@ -37,7 +37,7 @@ main(void) printf("idx: %d\n", idx); // Push the rest - while (rb_push_back(&rb, &arr[idx], memcpy) == WriteOk) { + while(rb_push_back(&rb, &arr[idx], memcpy) == WriteOk) { printf("Wrote: %d\n", arr[idx]); idx++; } From 7833bf73a7c275c21e1bc773e66a046d61a110b7 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 14 Jul 2024 01:11:56 +0200 Subject: [PATCH 22/46] Remove unused headers --- ringbuf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ringbuf.c b/ringbuf.c index 0293f36..c272b01 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: MIT */ -#include -#include -#include +// #include +// #include #include "ringbuf.h" +#include #ifdef DEBUG +#include #define DEBUG_PRINT(fmt, ...) printf(fmt, __VA_ARGS__) #else #define DEBUG_PRINT(fmt, ...) From d6a3c95951175f9eaa85c8220f554981ecb19267 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 14 Jul 2024 21:32:40 +0200 Subject: [PATCH 23/46] Testing --- driver.c | 61 +++++++++++++++++++++++++++++++++++++++---------------- ringbuf.c | 22 ++++++++++++++++++-- ringbuf.h | 9 +++++++- 3 files changed, 72 insertions(+), 20 deletions(-) diff --git a/driver.c b/driver.c index bdb4f51..58045ed 100644 --- a/driver.c +++ b/driver.c @@ -5,19 +5,44 @@ #include #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) { struct RingBuf rb; - int d; - rb_init(&rb, 10, malloc, sizeof(int)); - const int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + DATATYPE d; + rb_init(&rb, 10, malloc, sizeof(DATATYPE)); + + rb_debug_print(&rb); + + const DATATYPE arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int arrlen = (rb_size_t)(sizeof(arr) / sizeof(DATATYPE)); // Single writes printf("\n=== Single writes ===\n\n"); + rb_debug_print(&rb); + int idx = 0; while(idx < 5) { if(rb_push_back(&rb, &arr[idx], memcpy) != WriteOk) { @@ -26,22 +51,29 @@ main(void) idx++; } + rb_debug_print(&rb); + // Pop the last n elements - for(int a = 0; a < 1; a++) { + for(int a = 0; a < 10; a++) { if(rb_pop_front(&rb, &d, memcpy) != ReadOk) { printf("Failed to read data from buffer...\n"); + break; } printf("Data: %d\n", d); } + rb_debug_print(&rb); + if(rb.read_head == rb.write_head) + printf("OK\n"); printf("idx: %d\n", idx); // Push the rest - while(rb_push_back(&rb, &arr[idx], memcpy) == WriteOk) { - printf("Wrote: %d\n", arr[idx]); + while(idx < arrlen && rb_push_back(&rb, &arr[idx], memcpy) == WriteOk) { idx++; } + rb_debug_print(&rb); + printf("Data: ["); while(rb_pop_front(&rb, &d, memcpy) == ReadOk) printf("%d,", d); @@ -50,25 +82,20 @@ main(void) // Multiple writes printf("\n=== Multiple writes ===\n\n"); - rb_clear(&rb); + rb_clear(&rb); // Make sure + rb_debug_print(&rb); int ok = WriteOk; // Assume we can write if(rb_push_many(&rb, arr, memcpy, 8) != WriteOk) { printf("Failed to write data to buffer...\n"); } - - printf("Data: ["); - while(rb_pop_front(&rb, &d, memcpy) == ReadOk) - printf("%d,", d); - printf("\b]\n"); + rb_debug_print(&rb); + rb_debug_empty(&rb); // Test wrap around rb_push_many(&rb, arr, memcpy, 10); - - printf("Data: ["); - while(rb_pop_front(&rb, &d, memcpy) == ReadOk) - printf("%d,", d); - printf("\b]\n"); + rb_debug_print(&rb); + rb_debug_empty(&rb); // Test clear rb_clear(&rb); diff --git a/ringbuf.c b/ringbuf.c index c272b01..bb94a01 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -6,6 +6,7 @@ #include "ringbuf.h" #include +#include #ifdef DEBUG #include #define DEBUG_PRINT(fmt, ...) printf(fmt, __VA_ARGS__) @@ -74,7 +75,7 @@ rb_push_many(struct RingBuf *rb, const void *items, MEMCPY_T memcpy_fn, // Calculate the number of items that can be written in the first chunk rb_size_t first_chunk = (char *)rb->buffer_end - (char *)rb->write_head; - DEBUG_PRINT("Multi-chunk write. First chunk: %d\n", first_chunk); + DEBUG_PRINT("Multi-chunk write. First chunk: %ld\n", first_chunk); // Write the first chunk memcpy_fn(rb->write_head, items, rb->struct_size * first_chunk); @@ -87,7 +88,7 @@ rb_push_many(struct RingBuf *rb, const void *items, MEMCPY_T memcpy_fn, DEBUG_PRINT("Single-chunk write. No need to wrap around.%s\n", ""); } - DEBUG_PRINT("Writing %d items\n", n); + DEBUG_PRINT("Writing %ld items\n", n); memcpy_fn(rb->write_head, items, rb->struct_size * n); if(rb->write_head == rb->buffer_end) rb->write_head = rb->buffer; @@ -116,3 +117,20 @@ rb_pop_front(struct RingBuf *rb, void *item, MEMCPY_T memcpy_fn) rb->count--; return ReadOk; } + +void +rb_debug_print(struct RingBuf *rb) +{ + printf("============\n"); + printf("Count %lu\n", rb->count); + printf("Capacity: %ld\n", rb->capacity); + printf("Left: %ld\n", rb->capacity - rb->count); + printf("Base addr:\t%p\n", rb->buffer); + + printf("Read Head:\t%p (%ld:th position)\n", rb->read_head, + ((rb->read_head) - (rb->buffer)) / rb->struct_size); + printf("Write Head:\t%p (%ld:th position)\n", rb->write_head, + ((rb->write_head) - (rb->buffer)) / rb->struct_size); + + printf("============\n"); +} diff --git a/ringbuf.h b/ringbuf.h index 6d6d01f..46db542 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -2,8 +2,10 @@ #pragma once +#include + #ifndef rb_size_t -#define rb_size_t int +#define rb_size_t size_t #endif /** Signatures of allocators */ @@ -113,3 +115,8 @@ enum ReadResult rb_pop_front(struct RingBuf *rb, void *item, * @param free The free function */ void rb_destroy(struct RingBuf *rb, void(free)()); + +/** + * @brief Debug print + */ +void rb_debug_print(struct RingBuf *rb); From 6a4dc46b60ebffffb9a95fbc4d6034d38ce7136e Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Mon, 15 Jul 2024 21:50:59 +0200 Subject: [PATCH 24/46] DEBUG_PRINT --- ringbuf.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ringbuf.c b/ringbuf.c index bb94a01..d73affe 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -6,7 +6,6 @@ #include "ringbuf.h" #include -#include #ifdef DEBUG #include #define DEBUG_PRINT(fmt, ...) printf(fmt, __VA_ARGS__) @@ -121,16 +120,16 @@ rb_pop_front(struct RingBuf *rb, void *item, MEMCPY_T memcpy_fn) void rb_debug_print(struct RingBuf *rb) { - printf("============\n"); - printf("Count %lu\n", rb->count); - printf("Capacity: %ld\n", rb->capacity); - printf("Left: %ld\n", rb->capacity - rb->count); - printf("Base addr:\t%p\n", rb->buffer); + DEBUG_PRINT("============%s\n", ""); + DEBUG_PRINT("Count %lu\n", rb->count); + DEBUG_PRINT("Capacity: %ld\n", rb->capacity); + DEBUG_PRINT("Left: %ld\n", rb->capacity - rb->count); + DEBUG_PRINT("Base addr:\t%p\n", rb->buffer); - printf("Read Head:\t%p (%ld:th position)\n", rb->read_head, - ((rb->read_head) - (rb->buffer)) / rb->struct_size); - printf("Write Head:\t%p (%ld:th position)\n", rb->write_head, - ((rb->write_head) - (rb->buffer)) / rb->struct_size); + DEBUG_PRINT("Read Head:\t%p (%ld:th position)\n", rb->read_head, + ((rb->read_head) - (rb->buffer)) / rb->struct_size); + DEBUG_PRINT("Write Head:\t%p (%ld:th position)\n", rb->write_head, + ((rb->write_head) - (rb->buffer)) / rb->struct_size); - printf("============\n"); + DEBUG_PRINT("============%s\n", ""); } From 948aeab8251bda0eef86bfc0861f187330c3407d Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 14:00:38 +0100 Subject: [PATCH 25/46] clang-format: Short loops on single line --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 38abf14..f98fb9c 100644 --- a/.clang-format +++ b/.clang-format @@ -51,7 +51,7 @@ AllowShortEnumsOnASingleLine: true AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: All -AllowShortLoopsOnASingleLine: false +AllowShortLoopsOnASingleLine: true AlwaysBreakAfterDefinitionReturnType: All AlwaysBreakAfterReturnType: AllDefinitions AlwaysBreakBeforeMultilineStrings: false From 4a90d24069544f518d8f06f0eba2ec7bd4c7841c Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 14:01:55 +0100 Subject: [PATCH 26/46] Makefile: debug is default target, make sure include path is set to project root, better clean --- Makefile | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 10c26a0..ad23202 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,21 @@ # 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 -ifneq ($(DEBUG), 1) +CFLAGS += -I. + +ifneq ($(RELEASE), 1) +CFLAGS += -g -O0 -std=c99 -march=native -mtune=native +CFLAGS += -DDEBUG +else 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 @@ -50,7 +52,7 @@ install: @cp ringbuf.h /usr/local/include clean: - rm -f $(OBJECTS) $(ASMS) driver librbuf.a librbuf.so + rm -f $(OBJECTS) $(ASMS) driver librbuf.a librbuf.so **/*.o **/*.elf asm: $(ASMS) $(OBJECTS) wc -l $(ASMS) From 20b6e5c2fdb6e3ae2eb5403aee256ad51f12fa46 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 14:02:49 +0100 Subject: [PATCH 27/46] Prevent double-free in rb_destroy --- ringbuf.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ringbuf.c b/ringbuf.c index d73affe..ffc979e 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -29,7 +29,18 @@ rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T malloc_fn, void rb_destroy(struct RingBuf *rb, FREE_T free_fn) { - free_fn(rb->buffer); + 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; + } } void @@ -46,6 +57,8 @@ 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 From 3308e61dc1f74c4723a52494328d099461bfa027 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 14:03:11 +0100 Subject: [PATCH 28/46] Missing include from previous commit --- ringbuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ringbuf.c b/ringbuf.c index ffc979e..dc2078f 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -4,6 +4,7 @@ // #include #include "ringbuf.h" +#include #include #ifdef DEBUG From 259c048bd5cb2ed2ebecae49267f7e809139ff2f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 14:03:47 +0100 Subject: [PATCH 29/46] Move certain debug related functionality into header/source, feature gated by DEBUG flag --- driver.c | 21 +++++---------------- ringbuf.c | 17 +++++++++++++++++ ringbuf.h | 13 +++++++++++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/driver.c b/driver.c index 58045ed..19d0e74 100644 --- a/driver.c +++ b/driver.c @@ -4,27 +4,16 @@ #include #include -#define rb_size_t size_t +#ifndef DEBUG +#define DEBUG +#endif + +// #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 dc2078f..59626f7 100644 --- a/ringbuf.c +++ b/ringbuf.c @@ -131,6 +131,21 @@ 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) { @@ -147,3 +162,5 @@ rb_debug_print(struct RingBuf *rb) DEBUG_PRINT("============%s\n", ""); } + +#endif diff --git a/ringbuf.h b/ringbuf.h index 46db542..1844437 100644 --- a/ringbuf.h +++ b/ringbuf.h @@ -111,12 +111,25 @@ 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 From 4fab8ca207199b8c0f14dea208163e10c3b0408f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 14:03:57 +0100 Subject: [PATCH 30/46] Extended readme --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..1626f2c --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# 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 From 1e760664005072829c367b38562177955f7d5cbb Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 14:04:23 +0100 Subject: [PATCH 31/46] gitignore for chache, json and .elf --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 32aec77..f25f439 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ *.a build driver +*.elf +*.json +.cache From bfffbb92115ba4058281306008636a18e0359b96 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 14:05:22 +0100 Subject: [PATCH 32/46] Basic tests implemented using cmocka --- Makefile | 8 +++- test/test_ringbuf.c | 101 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 test/test_ringbuf.c diff --git a/Makefile b/Makefile index ad23202..50d4220 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,9 @@ ASMS = $(C_SOURCES:.c=.s) all: $(OBJECTS) +test: test/test.elf + ./test/test.elf + %.o: %.c $(C_HEADERS) @echo "CC $<" @$(CC) $(CFLAGS) -c $< -o $@ @@ -41,6 +44,9 @@ 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 @@ -64,4 +70,4 @@ tidy: format: clang-format -i $(C_SOURCES) $(C_HEADERS) -.PHONY: all clean format asm +.PHONY: all clean format asm test diff --git a/test/test_ringbuf.c b/test/test_ringbuf.c new file mode 100644 index 0000000..44756e9 --- /dev/null +++ b/test/test_ringbuf.c @@ -0,0 +1,101 @@ +#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); +} From 14e8b9e1c0ebcbcc56861f7a7b85f84bfe3e2c5a Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:19:28 +0100 Subject: [PATCH 33/46] Woodpecker test --- .woodpecker/test.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .woodpecker/test.yaml diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml new file mode 100644 index 0000000..ca74c80 --- /dev/null +++ b/.woodpecker/test.yaml @@ -0,0 +1,15 @@ +when: + - event: push + branch: main + +steps: + - name: build + image: debian + commands: + # - sudo apt install cmocka libcmocka + - echo "binary-data-123" > executable + - name: a-test-step + image: golang:1.16 + commands: + - echo "Testing ..." + - ./executable From 4e8f158c9155d2d57e1158b808632fcc051a39c4 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:22:39 +0100 Subject: [PATCH 34/46] Woke devs naming scheme fucked me again --- .woodpecker/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index ca74c80..ee09a18 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -1,6 +1,6 @@ when: - event: push - branch: main + branch: master steps: - name: build From 06a5b53bd9a513c3ae569f8da0ef64a2e326387a Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:36:52 +0100 Subject: [PATCH 35/46] CI test --- .woodpecker/test.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index ee09a18..83a9a0a 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -6,10 +6,11 @@ steps: - name: build image: debian commands: - # - sudo apt install cmocka libcmocka - - echo "binary-data-123" > executable + - sudo apt install libcmocka-dev + - echo "echo hello" > executable.sh + - chmod +x executable.sh - name: a-test-step image: golang:1.16 commands: - echo "Testing ..." - - ./executable + - ./executable.sh From 5fd22f005a2050f96667f965050cd6ec1ad82f9b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:37:30 +0100 Subject: [PATCH 36/46] ... --- .woodpecker/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index 83a9a0a..549d95c 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -6,7 +6,7 @@ steps: - name: build image: debian commands: - - sudo apt install libcmocka-dev + - apt install libcmocka-dev - echo "echo hello" > executable.sh - chmod +x executable.sh - name: a-test-step From 2acb3c5bd5627cf048547bad48d78fbec4942f80 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:38:23 +0100 Subject: [PATCH 37/46] CI --- .woodpecker/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index 549d95c..e2d53d0 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -6,6 +6,7 @@ steps: - name: build image: debian commands: + - apt update - apt install libcmocka-dev - echo "echo hello" > executable.sh - chmod +x executable.sh From b26ceeb8fe473bcee1453b84e2f1ec30e02441cc Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:39:17 +0100 Subject: [PATCH 38/46] Noconfirm --- .woodpecker/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index e2d53d0..1ac48f1 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -7,7 +7,7 @@ steps: image: debian commands: - apt update - - apt install libcmocka-dev + - apt install libcmocka-dev -y - echo "echo hello" > executable.sh - chmod +x executable.sh - name: a-test-step From b77d86ae12756eaca37b87615113f9b2fcd0707a Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:46:03 +0100 Subject: [PATCH 39/46] Tests --- .woodpecker/test.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index 1ac48f1..b33b0a7 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -8,10 +8,14 @@ steps: commands: - apt update - apt install libcmocka-dev -y + - apt install gcc -y + - apt install make -y + - make -j$(nproc) test/test.elf - echo "echo hello" > executable.sh - chmod +x executable.sh - name: a-test-step image: golang:1.16 commands: + - ./test/test.elf - echo "Testing ..." - ./executable.sh From 82388142f01c9309037b2b5448e734d20f1fdb85 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:50:43 +0100 Subject: [PATCH 40/46] CI --- .woodpecker/test.yaml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index b33b0a7..3797bc7 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -2,20 +2,19 @@ when: - event: push branch: master +image: debian + steps: - - name: build - image: debian + - name: install-dependencies commands: - apt update - apt install libcmocka-dev -y - apt install gcc -y - apt install make -y + + - name: build-tests - make -j$(nproc) test/test.elf - - echo "echo hello" > executable.sh - - chmod +x executable.sh - - name: a-test-step - image: golang:1.16 + + - name: run-tests commands: - ./test/test.elf - - echo "Testing ..." - - ./executable.sh From da47bcd82f45e05635f1226ef47a31a8288d517b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:52:31 +0100 Subject: [PATCH 41/46] CI --- .woodpecker/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index 3797bc7..1b8e422 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -13,6 +13,7 @@ steps: - apt install make -y - name: build-tests + commands: - make -j$(nproc) test/test.elf - name: run-tests From a6c8b5cdc1ea6d7fd40938a0682a2bdbbe9640dc Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:53:00 +0100 Subject: [PATCH 42/46] Fix CI --- .woodpecker/test.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index 1b8e422..d4c1819 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -2,10 +2,9 @@ when: - event: push branch: master -image: debian - steps: - name: install-dependencies + image: debian commands: - apt update - apt install libcmocka-dev -y @@ -13,9 +12,11 @@ steps: - apt install make -y - name: build-tests + image: debian commands: - make -j$(nproc) test/test.elf - name: run-tests + image: debian commands: - ./test/test.elf From ff984caf3584dff41bd6aee92cc24687853843d6 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 19:56:32 +0100 Subject: [PATCH 43/46] Fix CI --- .woodpecker/test.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index d4c1819..660e508 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -3,17 +3,13 @@ when: branch: master steps: - - name: install-dependencies + - name: build image: debian commands: - apt update - apt install libcmocka-dev -y - apt install gcc -y - apt install make -y - - - name: build-tests - image: debian - commands: - make -j$(nproc) test/test.elf - name: run-tests From 16639b24ef4539dd4438d3f6a72aa46895cbe24b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 20:01:58 +0100 Subject: [PATCH 44/46] Deps --- .woodpecker/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index 660e508..d3f403e 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -15,4 +15,5 @@ steps: - name: run-tests image: debian commands: + - apt install libcmocka0 -y - ./test/test.elf From b11edb59f4f233c9301a9b5d49ee4db87e74899c Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 20:03:35 +0100 Subject: [PATCH 45/46] ... --- .woodpecker/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index d3f403e..65c5188 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -15,5 +15,5 @@ steps: - name: run-tests image: debian commands: - - apt install libcmocka0 -y + - apt install libcmocka-dev -y - ./test/test.elf From a6651f05a5679fcb91fee10c9f3a1c8c11fbd5ac Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 25 Dec 2024 20:04:46 +0100 Subject: [PATCH 46/46] CI --- .woodpecker/test.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index 65c5188..9eece92 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -15,5 +15,6 @@ steps: - name: run-tests image: debian commands: - - apt install libcmocka-dev -y + - apt update + - apt install libcmocka0 -y - ./test/test.elf