c-libp2p/utils/threadsafe_buffer.c
2017-11-30 14:32:36 -05:00

123 lines
3.9 KiB
C

/**
* A thredsafe buffer
*/
#include <stdlib.h>
#include "libp2p/utils/threadsafe_buffer.h"
#include "libp2p/utils/logger.h"
/***
* Allocate a new context
* @returns a newly allocated context, or NULL on error (out of memory?)
*/
struct ThreadsafeBufferContext* threadsafe_buffer_context_new() {
struct ThreadsafeBufferContext* context = (struct ThreadsafeBufferContext*) malloc(sizeof(struct ThreadsafeBufferContext));
if (context != NULL) {
context->buffer_size = 0;
context->buffer = NULL;
pthread_mutex_init(&context->lock, NULL);
}
return context;
}
/***
* Free resources of a buffer context
* @param context the context
*/
void threadsafe_buffer_context_free(struct ThreadsafeBufferContext* context) {
if (context != NULL) {
if (context->buffer != NULL)
free(context->buffer);
free(context);
}
}
/***
* Read from the buffer without destroying its contents or moving its read pointer
* @param context the context
* @param results where to put the results
* @param results_size the size of the results
* @returns number of bytes read
*/
size_t threadsafe_buffer_peek(struct ThreadsafeBufferContext* context, uint8_t* results, size_t results_size) {
size_t bytes_read = 0;
if (context == NULL)
return 0;
pthread_mutex_lock(&context->lock);
// do the read
if (context->buffer != NULL && context->buffer_size > 0) {
bytes_read = results_size < context->buffer_size ? results_size : context->buffer_size;
memcpy(results, context->buffer, bytes_read);
}
pthread_mutex_unlock(&context->lock);
return bytes_read;
}
/***
* Read from the buffer.
* NOTE: If results_size is more than what is left in the buffer, this will read everything.
* @param context the context
* @param results where to put the results
* @param results_size the size of the buffer
* @returns number of bytes read
*/
size_t threadsafe_buffer_read(struct ThreadsafeBufferContext* context, uint8_t* results, size_t results_size) {
size_t bytes_read = 0;
if (context == NULL)
return 0;
pthread_mutex_lock(&context->lock);
// do the read
if (context->buffer != NULL && context->buffer_size > 0) {
bytes_read = results_size < context->buffer_size ? results_size : context->buffer_size;
libp2p_logger_debug("threadsafe_buffer", "read: We want to read %d bytes, and have %d in the buffer. Therefore, we will read %d.\n", results_size, context->buffer_size, bytes_read);
memcpy(results, context->buffer, bytes_read);
}
// adjust the size
if (bytes_read > 0) {
if (context->buffer_size - bytes_read > 0) {
// more remains
size_t new_buffer_size = context->buffer_size - bytes_read;
uint8_t* new_buffer = (uint8_t*) malloc(new_buffer_size);
memcpy(new_buffer, &context->buffer[bytes_read], new_buffer_size);
free(context->buffer);
context->buffer = new_buffer;
context->buffer_size = new_buffer_size;
} else {
// everything has been read
free(context->buffer);
context->buffer = NULL;
context->buffer_size = 0;
}
}
pthread_mutex_unlock(&context->lock);
return bytes_read;
}
/****
* Add bytes to the end of the buffer
* @param context the context
* @param bytes the bytes to add
* @param bytes_size the size of bytes
* @returns the size added to the buffer (0 on error)
*/
size_t threadsafe_buffer_write(struct ThreadsafeBufferContext* context, const uint8_t* bytes, size_t bytes_size) {
if (context == NULL)
return 0;
if (bytes_size == 0)
return 0;
size_t bytes_copied = 0;
pthread_mutex_lock(&context->lock);
// allocate memory
uint8_t* new_buffer = (uint8_t*) realloc(context->buffer, context->buffer_size + bytes_size);
if (new_buffer != NULL) {
// copy data
memcpy(&new_buffer[context->buffer_size], bytes, bytes_size);
context->buffer_size += bytes_size;
context->buffer = new_buffer;
bytes_copied = bytes_size;
libp2p_logger_debug("threadsafe_buffer", "write: Added %d bytes. Buffer now contains %d bytes.\n", bytes_size, context->buffer_size);
}
pthread_mutex_unlock(&context->lock);
return bytes_copied;
}