...
 
Commits (2)
#ifndef RINGBUFFER_C
#define RINGBUFFER_C
#include <stdbool.h>
#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;
};
#define ERROR(msg) \
do {} while(0)
void rbfr_clear(struct ringbuffer* this) {
this->size = 0;
this->head = 0;
inline void assert_nonnull(const void* const ptr, const char* const msg) {
if (ptr == NULL) {
ERROR(msg);
}
}
struct ringbuffer* rbfr_create(const int capacity) {
struct ringbuffer* rbfr_create(const int capacity, void (*const destroy_element)(void*)) {
assert_nonnull(destroy_element, "rbfr_create got NULL for argument 'destroy_element'");
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) {
void rbfr_destroy(struct ringbuffer* const this) {
assert_nonnull(this, "rbfr_destroy got NULL for argument 'this'");
rbfr_clear(this);
free(this->buffer);
this->buffer = NULL;
free(this);
}
int rbfr_size(const struct ringbuffer* this) {
int rbfr_size(const struct ringbuffer* const this) {
assert_nonnull(this, "rbfr_size got NULL for argument 'this'");
return this->size;
}
int rbfr_capacity(const struct ringbuffer* this) {
assert_nonnull(this, "rbfr_capacity got NULL for argument 'this'");
return this->buffer_n;
}
void rbfr_enqueue(struct ringbuffer* this, void* element) {
void rbfr_enqueue(struct ringbuffer* this, void* const element) {
assert_nonnull(this, "rbfr_enqueue got NULL for argument 'this'");
if (this->buffer_n == 0) {
return;
}
......@@ -62,7 +71,10 @@ void rbfr_enqueue(struct ringbuffer* this, void* element) {
return;
}
bool rbfr_peek(const struct ringbuffer* this, void** element) {
bool rbfr_peek(const struct ringbuffer *const this, void** element) {
assert_nonnull(this, "rbfr_peek got NULL for argument 'this'");
assert_nonnull(element, "rbfr_peek got NULL for argument 'element'");
if (this->size == 0) {
return false;
}
......@@ -70,7 +82,10 @@ bool rbfr_peek(const struct ringbuffer* this, void** element) {
return true;
}
bool rbfr_dequeue(struct ringbuffer* this, void** element) {
bool rbfr_dequeue(struct ringbuffer *const this, void** element) {
assert_nonnull(this, "rbfr_dequeue got NULL for argument 'this'");
assert_nonnull(element, "rbfr_dequeue got NULL for argument 'element'");
if (!rbfr_peek(this, element)) {
return false;
}
......@@ -82,4 +97,13 @@ bool rbfr_dequeue(struct ringbuffer* this, void** element) {
return true;
}
void rbfr_clear(struct ringbuffer* this) {
assert_nonnull(this, "rbfr_clear got NULL for argument 'this'");
void* element;
while (rbfr_dequeue(this, &element)) {
this->destroy_element(element);
}
}
#endif // RINGBUFFER_C
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
#include <stdbool.h>
// 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