Compare commits

..

No commits in common. "bfffbb92115ba4058281306008636a18e0359b96" and "6a4dc46b60ebffffb9a95fbc4d6034d38ce7136e" have entirely different histories.

8 changed files with 26 additions and 215 deletions

View file

@ -51,7 +51,7 @@ AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: All
AlwaysBreakAfterReturnType: AllDefinitions
AlwaysBreakBeforeMultilineStrings: false

3
.gitignore vendored
View file

@ -4,6 +4,3 @@
*.a
build
driver
*.elf
*.json
.cache

View file

@ -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

View file

@ -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

View file

@ -4,16 +4,27 @@
#include <string.h>
#include <stdio.h>
#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)
{

View file

@ -4,7 +4,6 @@
// #include <string.h>
#include "ringbuf.h"
#include <assert.h>
#include <stdint.h>
#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 <stdio.h>
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

View file

@ -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

View file

@ -1,101 +0,0 @@
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <cmocka.h>
#include <stdio.h>
#include <stdlib.h>
#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);
}