Skip to content

Instantly share code, notes, and snippets.

@tbodt
Created August 4, 2017 02:57
Show Gist options
  • Save tbodt/cc7b9169a5bb2cc95dc7e90dda32103b to your computer and use it in GitHub Desktop.
Save tbodt/cc7b9169a5bb2cc95dc7e90dda32103b to your computer and use it in GitHub Desktop.
circular buffer
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include "buffer.h"
#define lock(thing) pthread_mutex_lock(&(thing)->lock)
#define unlock(thing) pthread_mutex_unlock(&(thing)->lock)
#define wait_for(thing, what) pthread_cond_wait(&(thing)->what, &(thing)->lock)
#define signal(thing, what) pthread_cond_broadcast(&(thing)->what)
int buf_init(struct buffer *buf, size_t capacity) {
buf->data = malloc(capacity);
if (buf->data == NULL)
return ENOMEM;
buf->capacity = capacity;
buf->start = buf->unread = 0;
pthread_mutex_init(&buf->lock, NULL);
pthread_cond_init(&buf->changed, NULL);
return 0;
}
void buf_free(struct buffer *buf, size_t capacity) {
free(buf->data);
}
size_t buf_unread(struct buffer *buf) {
lock(buf);
size_t unread = buf->unread;
unlock(buf);
return unread;
}
size_t buf_remaining(struct buffer *buf) {
lock(buf);
size_t remaining = buf->capacity - buf->unread;
unlock(buf);
return remaining;
}
size_t buf_read(struct buffer *buf, char *str, size_t len, int flags) {
lock(buf);
while (len > buf_unread(buf)) {
if (flags & BUF_BLOCK)
wait_for(buf, changed);
else
return 0;
}
// read up to the end of the buffer
size_t len1 = len;
if (buf->start + len > buf->capacity)
len1 = buf->capacity - buf->start;
memcpy(str, buf->data + buf->start, len1);
// wrap around if necessary
memcpy(str + len1, buf->data, len - len1);
buf->start += len;
if (buf->start >= buf->capacity)
buf->start -= buf->capacity;
buf->unread -= len;
signal(buf, changed);
unlock(buf);
return len;
}
size_t buf_write(struct buffer *buf, char *str, size_t len, int flags) {
lock(buf);
while (len > buf_remaining(buf)) {
if (flags & BUF_BLOCK)
wait_for(buf, changed);
else
return 0;
}
size_t end = buf->start + buf->unread;
// copy data up to the end of the buffer
size_t len1 = len;
if (end + len > buf->capacity)
len1 = buf->capacity - end;
memcpy(buf->data + end, str, len1);
// if we have to wrap around, do so
memcpy(buf->data, str + len1, len - len1);
buf->unread += len;
signal(buf, changed);
unlock(buf);
return len;
}
#ifndef BUFFER_H
#define BUFFER_H
#include <pthread.h>
// A circular buffer.
struct buffer {
char *data;
size_t capacity;
size_t start; // first byte of data
size_t unread;
pthread_mutex_t lock;
pthread_cond_t changed;
};
int buf_init(struct buffer *buf, size_t size);
void buf_free(struct buffer *buf, size_t capacity);
// Returns how many bytes of data are currently in the buffer.
size_t buf_unread(struct buffer *buf);
// Returns how much more room there is in the buffer.
size_t buf_remaining(struct buffer *buf);
// If flags is specified as BUF_BLOCK, will block if there's not enough data or
// not enough space. Otherwise, just return 0.
#define BUF_BLOCK 1
size_t buf_read(struct buffer *buf, char *str, size_t len, int flags);
size_t buf_write(struct buffer *buf, char *str, size_t len, int flags);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment