Last active
November 14, 2021 14:20
-
-
Save tripulse/f4de9f7600ce563680dab4122b2b287a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <threads.h> | |
/** | |
* @brief A single unit of data in a opusbuffer, that is capable of | |
* containing a contigous sequence of bytes being arbitrarily large. | |
*/ | |
struct opuspacket { | |
void* data; | |
size_t size; | |
size_t capacity; | |
struct opuspacket* next; | |
}; | |
/** | |
* @brief Mutually exclusive, thread safe, resizable, circular buffer | |
* implemented as a linked list for storing OPUS packets. | |
*/ | |
struct opusbuffer { | |
struct opuspacket* list; | |
struct opuspacket* read; | |
struct opuspacket* write; | |
size_t length; | |
size_t capacity; | |
mtx_t lock; | |
}; | |
struct opusbuffer* opusbuffer_create(size_t capacity) | |
{ | |
if(capacity == 0) | |
return NULL; | |
struct opusbuffer* buffer = malloc(sizeof(struct opusbuffer)); | |
if(buffer == NULL) | |
return NULL; | |
int ret; | |
ret = mtx_init(&buffer->lock, mtx_plain); | |
if(ret != thrd_success) | |
{ | |
free(buffer); | |
return NULL; | |
} | |
// why calloc() instead of malloc + memset? | |
// because it gurantess that allocated memory will be zero initialised. | |
// it takes the benefit that kernel usually provides zero-init memory, | |
// so it doesn't do any manual memory setting on its own depending | |
// on the platform. | |
buffer->capacity = capacity; | |
buffer->list = calloc(1, sizeof(struct opusbuffer)); | |
if(!buffer->list) | |
{ | |
free(buffer); | |
return NULL; | |
} | |
struct opuspacket* packet = buffer->list; | |
for(size_t i = 1; i < buffer->capacity; ++i) { | |
packet->next = calloc(1, sizeof(struct opusbuffer)); | |
if(!packet->next) | |
{ | |
buffer->capacity = i; | |
break; | |
} | |
packet = packet->next; // move to next one. | |
} | |
// circular linked list is like a "mala", each bead is a node. | |
// and you connect two ends of it, to make it a closed shape. | |
// | |
// https://en.wikipedia.org/wiki/Japamala | |
packet->next = buffer->list; | |
buffer->read = buffer->list; | |
buffer->write = buffer->list; | |
buffer->length = 0; | |
return buffer; | |
} | |
int opusbuffer_append(struct opusbuffer* buffer, void* data, size_t size) | |
{ | |
mtx_lock(&buffer->lock); | |
if(buffer->length == buffer->capacity) { | |
struct opuspacket* mid = calloc(1, sizeof(struct opuspacket)); | |
struct opuspacket* end = buffer->write->next; | |
if(!mid) | |
{ | |
mtx_unlock(&buffer->lock); | |
return -ENOMEM; | |
} | |
// connect middle part in between the two, | |
// which will be our current pointer and | |
// increment the capacity if it succeeds. | |
buffer->write->next = mid; | |
buffer->write = mid; | |
mid->next = end; | |
++buffer->capacity; | |
} | |
if(buffer->write->capacity < size) | |
{ | |
buffer->write->data = realloc(buffer->write->data, size); | |
if(!buffer->write->data) | |
{ | |
mtx_unlock(&buffer->lock); | |
return -ENOMEM; | |
} | |
} | |
memcpy(buffer->write->data, data, size); | |
buffer->write->size = size; | |
buffer->write = buffer->write->next; | |
++buffer->length; | |
mtx_unlock(&buffer->lock); | |
return 0; | |
} | |
int opusbuffer_get_acq(struct opusbuffer* buffer, void** data, size_t* size) | |
{ | |
int ret; | |
ret = mtx_trylock(&buffer->lock); | |
if(ret == thrd_busy) | |
return -EAGAIN; | |
if(!buffer->length) | |
{ | |
mtx_unlock(&buffer->lock); | |
return -EAGAIN; | |
} | |
*data = buffer->read->data; | |
*size = buffer->read->size; | |
return 0; | |
} | |
void opusbuffer_get_rel(struct opusbuffer* buffer) | |
{ | |
buffer->read = buffer->read->next; | |
--buffer->length; | |
mtx_unlock(&buffer->lock); | |
} | |
void opusbuffer_free(struct opusbuffer* buffer) | |
{ | |
struct opuspacket* packet = buffer->list; | |
for(size_t i = 0; i < buffer->capacity; ++i) | |
{ | |
struct opuspacket* nxt = packet->next; | |
free(packet); | |
packet = nxt; | |
} | |
mtx_destroy(&buffer->lock); | |
free(buffer); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment