Compare commits
No commits in common. "bfffbb92115ba4058281306008636a18e0359b96" and "6a4dc46b60ebffffb9a95fbc4d6034d38ce7136e" have entirely different histories.
bfffbb9211
...
6a4dc46b60
8 changed files with 26 additions and 215 deletions
|
@ -51,7 +51,7 @@ AllowShortEnumsOnASingleLine: true
|
||||||
AllowShortFunctionsOnASingleLine: All
|
AllowShortFunctionsOnASingleLine: All
|
||||||
AllowShortIfStatementsOnASingleLine: Never
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
AllowShortLambdasOnASingleLine: All
|
AllowShortLambdasOnASingleLine: All
|
||||||
AllowShortLoopsOnASingleLine: true
|
AllowShortLoopsOnASingleLine: false
|
||||||
AlwaysBreakAfterDefinitionReturnType: All
|
AlwaysBreakAfterDefinitionReturnType: All
|
||||||
AlwaysBreakAfterReturnType: AllDefinitions
|
AlwaysBreakAfterReturnType: AllDefinitions
|
||||||
AlwaysBreakBeforeMultilineStrings: false
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -4,6 +4,3 @@
|
||||||
*.a
|
*.a
|
||||||
build
|
build
|
||||||
driver
|
driver
|
||||||
*.elf
|
|
||||||
*.json
|
|
||||||
.cache
|
|
||||||
|
|
20
Makefile
20
Makefile
|
@ -6,16 +6,14 @@ CFLAGS += -Wno-unused-variable -Wno-unused-function
|
||||||
CFLAGS += -Wno-unused-but-set-variable -Wno-unused-value -Wno-unused-label
|
CFLAGS += -Wno-unused-but-set-variable -Wno-unused-value -Wno-unused-label
|
||||||
CFLAGS += -Wno-unused-result -Wno-unused-const-variable
|
CFLAGS += -Wno-unused-result -Wno-unused-const-variable
|
||||||
|
|
||||||
CFLAGS += -I.
|
ifneq ($(DEBUG), 1)
|
||||||
|
|
||||||
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-exceptions -fno-asynchronous-unwind-tables -fno-ident
|
||||||
CFLAGS += -fno-unwind-tables -fno-stack-protector -fno-plt -fno-pic
|
CFLAGS += -fno-unwind-tables -fno-stack-protector -fno-plt -fno-pic
|
||||||
CFLAGS += -O3 -std=c99 -march=native -mtune=native -fomit-frame-pointer
|
CFLAGS += -O3 -std=c99 -march=native -mtune=native -fomit-frame-pointer
|
||||||
CFLAGS += -fshort-enums
|
CFLAGS += -fshort-enums
|
||||||
|
else
|
||||||
|
CFLAGS += -g -O0 -std=c99 -march=native -mtune=native
|
||||||
|
CFLAGS += -DDEBUG
|
||||||
endif # DEBUG
|
endif # DEBUG
|
||||||
|
|
||||||
LDFLAGS = -lm
|
LDFLAGS = -lm
|
||||||
|
@ -27,9 +25,6 @@ ASMS = $(C_SOURCES:.c=.s)
|
||||||
|
|
||||||
all: $(OBJECTS)
|
all: $(OBJECTS)
|
||||||
|
|
||||||
test: test/test.elf
|
|
||||||
./test/test.elf
|
|
||||||
|
|
||||||
%.o: %.c $(C_HEADERS)
|
%.o: %.c $(C_HEADERS)
|
||||||
@echo "CC $<"
|
@echo "CC $<"
|
||||||
@$(CC) $(CFLAGS) -c $< -o $@
|
@$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
@ -44,9 +39,6 @@ driver: $(OBJECTS)
|
||||||
run: driver
|
run: driver
|
||||||
@./driver
|
@./driver
|
||||||
|
|
||||||
test/test.elf: test/test_ringbuf.o ringbuf.o
|
|
||||||
@$(CC) $(CFLAGS) $^ -o $@ -lpthread -lcmocka
|
|
||||||
|
|
||||||
lib: $(OBJECTS)
|
lib: $(OBJECTS)
|
||||||
@ar rcs librbuf.a ringbuf.o
|
@ar rcs librbuf.a ringbuf.o
|
||||||
|
|
||||||
|
@ -58,7 +50,7 @@ install:
|
||||||
@cp ringbuf.h /usr/local/include
|
@cp ringbuf.h /usr/local/include
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJECTS) $(ASMS) driver librbuf.a librbuf.so **/*.o **/*.elf
|
rm -f $(OBJECTS) $(ASMS) driver librbuf.a librbuf.so
|
||||||
|
|
||||||
asm: $(ASMS) $(OBJECTS)
|
asm: $(ASMS) $(OBJECTS)
|
||||||
wc -l $(ASMS)
|
wc -l $(ASMS)
|
||||||
|
@ -70,4 +62,4 @@ tidy:
|
||||||
format:
|
format:
|
||||||
clang-format -i $(C_SOURCES) $(C_HEADERS)
|
clang-format -i $(C_SOURCES) $(C_HEADERS)
|
||||||
|
|
||||||
.PHONY: all clean format asm test
|
.PHONY: all clean format asm
|
||||||
|
|
44
README.md
44
README.md
|
@ -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
|
|
21
driver.c
21
driver.c
|
@ -4,16 +4,27 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#ifndef DEBUG
|
#define rb_size_t size_t
|
||||||
#define DEBUG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// #define rb_size_t size_t
|
|
||||||
// #define rb_size_t int
|
// #define rb_size_t int
|
||||||
#include "ringbuf.h"
|
#include "ringbuf.h"
|
||||||
|
|
||||||
typedef int DATATYPE;
|
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
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
|
|
33
ringbuf.c
33
ringbuf.c
|
@ -4,7 +4,6 @@
|
||||||
// #include <string.h>
|
// #include <string.h>
|
||||||
|
|
||||||
#include "ringbuf.h"
|
#include "ringbuf.h"
|
||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -30,18 +29,7 @@ rb_init(struct RingBuf *rb, rb_size_t capacity, ALLOC_T malloc_fn,
|
||||||
void
|
void
|
||||||
rb_destroy(struct RingBuf *rb, FREE_T free_fn)
|
rb_destroy(struct RingBuf *rb, FREE_T free_fn)
|
||||||
{
|
{
|
||||||
if(rb->buffer) { // Prevent double-free
|
free_fn(rb->buffer);
|
||||||
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
|
void
|
||||||
|
@ -58,8 +46,6 @@ rb_push_back(struct RingBuf *rb, const void *item, MEMCPY_T memcpy_fn)
|
||||||
if(rb->count == rb->capacity)
|
if(rb->count == rb->capacity)
|
||||||
return Full;
|
return Full;
|
||||||
|
|
||||||
assert(rb->buffer != NULL);
|
|
||||||
|
|
||||||
memcpy_fn(rb->write_head, item, rb->struct_size);
|
memcpy_fn(rb->write_head, item, rb->struct_size);
|
||||||
|
|
||||||
// Advance the write head
|
// Advance the write head
|
||||||
|
@ -131,21 +117,6 @@ rb_pop_front(struct RingBuf *rb, void *item, MEMCPY_T memcpy_fn)
|
||||||
return ReadOk;
|
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
|
void
|
||||||
rb_debug_print(struct RingBuf *rb)
|
rb_debug_print(struct RingBuf *rb)
|
||||||
{
|
{
|
||||||
|
@ -162,5 +133,3 @@ rb_debug_print(struct RingBuf *rb)
|
||||||
|
|
||||||
DEBUG_PRINT("============%s\n", "");
|
DEBUG_PRINT("============%s\n", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
13
ringbuf.h
13
ringbuf.h
|
@ -111,25 +111,12 @@ enum ReadResult rb_pop_front(struct RingBuf *rb, void *item,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Free the ring buffer
|
* @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 rb The ring buffer
|
||||||
* @param free The free function
|
* @param free The free function
|
||||||
*/
|
*/
|
||||||
void rb_destroy(struct RingBuf *rb, void(free)());
|
void rb_destroy(struct RingBuf *rb, void(free)());
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Debug print
|
* @brief Debug print
|
||||||
*/
|
*/
|
||||||
void rb_debug_print(struct RingBuf *rb);
|
void rb_debug_print(struct RingBuf *rb);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Debug print and empty the ringbuf
|
|
||||||
*/
|
|
||||||
void rb_debug_empty(struct RingBuf *rb);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue