diff --git a/kernel/util/ringbuffer.c b/kernel/util/ringbuffer.c index e1a7936..c1031c5 100644 --- a/kernel/util/ringbuffer.c +++ b/kernel/util/ringbuffer.c @@ -1,37 +1,24 @@ #ifndef RINGBUFFER_C #define RINGBUFFER_C -#include +#include "ringbuffer.h" #include "../memory.c" -/* - * Data layout: ↓head - * [ 4, undef, undef, 1, 2, 3 ] (1 is first added etc.) - * So enqueue adds after 4 and dequeue removes 1. - */ -struct ringbuffer { - int head; - int size; - void** buffer; - int buffer_n; -}; - -void rbfr_clear(struct ringbuffer* this) { - this->size = 0; - this->head = 0; -} - -struct ringbuffer* rbfr_create(const int capacity) { +struct ringbuffer* rbfr_create(const int capacity, void (*destroy_element)(void*)) { struct ringbuffer* const this = alloc(sizeof (struct ringbuffer)); this->buffer = alloc(capacity * sizeof (void*)); this->buffer_n = capacity; - rbfr_clear(this); + this->size = 0; + this->head = 0; + + this->destroy_element = destroy_element; return this; } void rbfr_destroy(struct ringbuffer* this) { + rbfr_clear(this); free(this->buffer); free(this); } @@ -82,4 +69,11 @@ bool rbfr_dequeue(struct ringbuffer* this, void** element) { return true; } +void rbfr_clear(struct ringbuffer* this) { + void* element; + while (rbfr_dequeue(this, &element)) { + this->destroy_element(element); + } +} + #endif // RINGBUFFER_C diff --git a/kernel/util/ringbuffer.h b/kernel/util/ringbuffer.h new file mode 100644 index 0000000..f117f2f --- /dev/null +++ b/kernel/util/ringbuffer.h @@ -0,0 +1,80 @@ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +#include + +// Data layout: ↓head +// buffer = [ 4, undef, undef, 1, 2, 3 ] +// where 1 is the oldest element and 4 the newest. +// Enqueue adds the element after 4 and dequeue removes 1. +struct ringbuffer { + int head; + int size; + void (*destroy_element)(void*); + int buffer_n; + void** buffer; +}; + +/** + * Allocate memory and initialize a circular queue with given capacity + * + * A ringbuffer, or circular queue, is like a regular queue, but if it's full when you add an + * element, it displaces the oldest element to make place. As such, adding elements never fails. + * + * Memory: this function allocates memory for the queue (free it with rbfr_destroy). The + * destroy_element function user has to provide must free an element's memory. + * + * @param capacity maximum amount of elements to accept without dropping oldest + * @param destroy_element function called when the queue has to drop an element, with the element + * passed as argument + * @return pointer to ringbuffer + */ +struct ringbuffer* rbfr_create(const int capacity, void (*destroy_element)(void*)); + +/** + * Free the queue's memory + * + * Memory: all elements still present are freed using the destroy_element function provided by user + * in rbfr_create. + */ +void rbfr_destroy(struct ringbuffer* this); + +int rbfr_size(const struct ringbuffer* this); + +int rbfr_capacity(const struct ringbuffer* this); + +/** + * Add one element at the end + * + * If the queue is full (if size==capacity), the oldest element is removed to make place. As such, + * this operation does not fail if the queue is full. + * + * Memory: if an element has to be removed to make place, it is freed using the destroy_element + * function provided by user in rbfr_create. + */ +void rbfr_enqueue(struct ringbuffer* this, void* element); + +/** + * Return first element without removing it from the buffer + * + * Memory: do not free the element's memory, it still lives in the ringbuffer. + */ +bool rbfr_peek(const struct ringbuffer* this, void** element); + +/** + * Remove first element and return it + * + * Memory: user is responsible for freeing the element's memory. + */ +bool rbfr_dequeue(struct ringbuffer* this, void** element); + +/** + * Remove all elements + * + * Memory: this function destroys elements using the destroy_element function provided by user in + * rbfr_create. + */ +void rbfr_clear(struct ringbuffer* this); + + +#endif // RINGBUFFER_H