|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
SFIFO 1.3 Simple portable lock-free FIFO
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
(c) 2000-2002, David Olofson - free software under the terms of the LGPL 2.1
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
-----------------------------------------------------------
|
|
Packit |
c32a2d |
TODO:
|
|
Packit |
c32a2d |
* Is there a way to avoid losing one byte of buffer
|
|
Packit |
c32a2d |
space to avoid extra variables or locking?
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
* Test more compilers and environments.
|
|
Packit |
c32a2d |
-----------------------------------------------------------
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include <string.h>
|
|
Packit |
c32a2d |
#include <stdlib.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "sfifo.h"
|
|
Packit |
c32a2d |
#include "debug.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
* Alloc buffer, init FIFO etc...
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
SFIFO_SCOPE int sfifo_init(sfifo_t *f, int size)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
memset(f, 0, sizeof(sfifo_t));
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(size > SFIFO_MAX_BUFFER_SIZE)
|
|
Packit |
c32a2d |
return -EINVAL;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
* Set sufficient power-of-2 size.
|
|
Packit |
c32a2d |
*
|
|
Packit |
c32a2d |
* No, there's no bug. If you need
|
|
Packit |
c32a2d |
* room for N bytes, the buffer must
|
|
Packit |
c32a2d |
* be at least N+1 bytes. (The fifo
|
|
Packit |
c32a2d |
* can't tell 'empty' from 'full'
|
|
Packit |
c32a2d |
* without unsafe index manipulations
|
|
Packit |
c32a2d |
* otherwise.)
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
f->size = 1;
|
|
Packit |
c32a2d |
for(; f->size <= size; f->size <<= 1)
|
|
Packit |
c32a2d |
;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Get buffer */
|
|
Packit |
c32a2d |
if( 0 == (f->buffer = (void *)malloc(f->size)) )
|
|
Packit |
c32a2d |
return -ENOMEM;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
* Dealloc buffer etc...
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
SFIFO_SCOPE void sfifo_close(sfifo_t *f)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(f->buffer) {
|
|
Packit |
c32a2d |
free(f->buffer);
|
|
Packit |
c32a2d |
f->buffer = NULL; /* Prevent double free */
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
* Empty FIFO buffer
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
SFIFO_SCOPE void sfifo_flush(sfifo_t *f)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
debug("sfifo_flush()");
|
|
Packit |
c32a2d |
/* Reset positions */
|
|
Packit |
c32a2d |
f->readpos = 0;
|
|
Packit |
c32a2d |
f->writepos = 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
* Write bytes to a FIFO
|
|
Packit |
c32a2d |
* Return number of bytes written, or an error code
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
SFIFO_SCOPE int sfifo_write(sfifo_t *f, const void *_buf, int len)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int total;
|
|
Packit |
c32a2d |
int i;
|
|
Packit |
c32a2d |
const char *buf = (const char *)_buf;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(!f->buffer)
|
|
Packit |
c32a2d |
return -ENODEV; /* No buffer! */
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* total = len = min(space, len) */
|
|
Packit |
c32a2d |
total = sfifo_space(f);
|
|
Packit |
c32a2d |
debug1("sfifo_space() = %d",total);
|
|
Packit |
c32a2d |
if(len > total)
|
|
Packit |
c32a2d |
len = total;
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
total = len;
|
|
Packit |
c32a2d |
debug1("sfifo_write() = %d", total);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
i = f->writepos;
|
|
Packit |
c32a2d |
if(i + len > f->size)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
memcpy(f->buffer + i, buf, f->size - i);
|
|
Packit |
c32a2d |
buf += f->size - i;
|
|
Packit |
c32a2d |
len -= f->size - i;
|
|
Packit |
c32a2d |
i = 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
memcpy(f->buffer + i, buf, len);
|
|
Packit |
c32a2d |
f->writepos = i + len;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return total;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
* Read bytes from a FIFO
|
|
Packit |
c32a2d |
* Return number of bytes read, or an error code
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
SFIFO_SCOPE int sfifo_read(sfifo_t *f, void *_buf, int len)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int total;
|
|
Packit |
c32a2d |
int i;
|
|
Packit |
c32a2d |
char *buf = (char *)_buf;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(!f->buffer)
|
|
Packit |
c32a2d |
return -ENODEV; /* No buffer! */
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* total = len = min(used, len) */
|
|
Packit |
c32a2d |
total = sfifo_used(f);
|
|
Packit |
c32a2d |
debug1("sfifo_used() = %d",total);
|
|
Packit |
c32a2d |
if(len > total)
|
|
Packit |
c32a2d |
len = total;
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
total = len;
|
|
Packit |
c32a2d |
debug1("sfifo_read() = %d", total);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
i = f->readpos;
|
|
Packit |
c32a2d |
if(i + len > f->size)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
memcpy(buf, f->buffer + i, f->size - i);
|
|
Packit |
c32a2d |
buf += f->size - i;
|
|
Packit |
c32a2d |
len -= f->size - i;
|
|
Packit |
c32a2d |
i = 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
memcpy(buf, f->buffer + i, len);
|
|
Packit |
c32a2d |
f->readpos = i + len;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return total;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|