Skip to content

Instantly share code, notes, and snippets.

@ast
Last active July 4, 2020 15:55
Show Gist options
  • Save ast/aed82fa71a92a0066835c76a3556bdf2 to your computer and use it in GitHub Desktop.
Save ast/aed82fa71a92a0066835c76a3556bdf2 to your computer and use it in GitHub Desktop.
My double mapped ringbuffer implementation intended for DSP applications.
//
// doublemap.c
// spieserver
//
// Created by Albin Stigö on 2019-12-08.
// Copyright © 2019 Albin Stigo. All rights reserved.
//
#include "doublemap.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdint.h>
int doublemunlock(const void *addr, size_t size) {
return munlock(addr, 2 * size);
}
void* doublemap(size_t size) {
// Virtual memory magic starts here.
char path[] = "/tmp/buffer-XXXXXX";
int fd;
int ret;
void *buff;
void *addr;
// Create temp file
if((fd = mkstemp(path)) < 0) {
perror("mkstemp");
return NULL;
}
// Remove link from filesystem
if((ret = unlink(path)) < 0) {
perror("unlink");
return NULL;
}
// Set size
if((ret = ftruncate(fd, size)) < 0) {
perror("ftruncate");
return NULL;
}
// Try to map a continuous area of memory of 2 * size.
buff = mmap(NULL, 2 * size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if(buff == MAP_FAILED) {
perror("mmap(NULL, 2 * size)");
return NULL;
}
// Then map size bytes twice.
addr = mmap(buff, size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0);
if (addr != buff) {
perror("mmap(buff, size)");
return NULL;
}
addr = mmap(buff + size , size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0);
if (addr != buff + size) {
perror("mmap(buff + size, size)");
return NULL;
}
if(close(fd) < 0) {
perror("close");
return NULL;
}
// Zero buffer
memset(buff, 0x00, size);
// All ok
return buff;
}
//
// doublemap.h
// spieserver
//
// Created by Albin Stigö on 2019-12-08.
// Copyright © 2019 Albin Stigo. All rights reserved.
//
#ifndef doublemap_h
#define doublemap_h
#include <stdio.h>
int doublemunlock(const void *addr, size_t size);
void* doublemap(size_t size);
#endif /* doublemap_h */
//
// ringbuffer.c
// spieserver
//
// Created by Albin Stigö on 2019-12-08.
// Copyright © 2019 Albin Stigo. All rights reserved.
//
#include "ringbuffer.h"
#include "doublemap.h"
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
// Round up to page size
size_t roundup_mult_pagesize(size_t size) {
size_t pagesize =getpagesize();
return size + pagesize - 1 - (size - 1) % pagesize;
}
// Create
ringbuffer_t* rb_create(size_t size) {
size = roundup_mult_pagesize(size);
ringbuffer_t *rb = malloc(sizeof(ringbuffer_t));
assert(rb != NULL);
rb->buffer = doublemap(size);
assert(rb->buffer != NULL);
rb->read = 0;
rb->write = 0;
rb->size = size;
return rb;
}
void rb_free(ringbuffer_t *rb) {
doublemunlock(rb->buffer, rb->size);
free(rb);
}
//
// ringbuffer.h
// spieserver
//
// Created by Albin Stigö on 2019-12-08.
// Copyright © 2019 Albin Stigo. All rights reserved.
//
#ifndef ringbuffer_h
#define ringbuffer_h
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <string.h>
#include <assert.h>
/*
* Inspired by:
* https://www.snellman.net/blog/archive/2016-12-13-ring-buffers/
* https://github.com/willemt/cbuffer/
* https://github.com/michaeltyson/TPCircularBuffer
*/
typedef struct {
void *buffer;
size_t read;
size_t write;
// size MUST be power of two!!
size_t size;
} ringbuffer_t;
// Creation
ringbuffer_t* rb_create(size_t size);
void rb_free(ringbuffer_t *rb);
#define RB_MASK(rb, val) (val & (rb->size - 1))
// Access
static inline void rb_produce(ringbuffer_t *rb, size_t amount)
{
rb->write += amount;
}
static inline void rb_consume(ringbuffer_t *rb, size_t amount)
{
rb->read += amount;
}
// Pointer to head ready for writing
static inline void* rb_writeptr(ringbuffer_t *rb)
{
return &rb->buffer[RB_MASK(rb, rb->write)];
}
// Pointer to head ready for writing
static inline void* rb_writeptr_avail(ringbuffer_t *rb, size_t *avail)
{
// available for writing
*avail = rb->size - (rb->write - rb->read);
return &rb->buffer[RB_MASK(rb, rb->write)];
}
static inline void rb_memcpy(ringbuffer_t *rb, const void *src, size_t n)
{
size_t avail;
void *dst = rb_writeptr_avail(rb, &avail);
assert(n <= avail);
memcpy(dst, src, n);
rb_produce(rb, n);
}
// Pointer to tail, ready for reading
static inline void* rb_readptr(ringbuffer_t *rb)
{
return &rb->buffer[RB_MASK(rb, rb->read)];
}
// Pointer to tail, ready for reading
static inline void* rb_readptr_avail(ringbuffer_t *rb, size_t *avail)
{
// available for reading
*avail = rb->write - rb->read;
return &rb->buffer[RB_MASK(rb, rb->read)];
}
// Get a pointer hist bytes behind writeptr
static inline void* rb_histptr(ringbuffer_t *rb, size_t hist)
{
return &rb->buffer[RB_MASK(rb, rb->write - hist)];
}
#endif /* ringbuffer_h */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment