/* movstat/ringbuf.c
*
* Ring buffer module
*
* Copyright (C) 2018 Patrick Alken
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __GSL_RINGBUF_C__
#define __GSL_RINGBUF_C__
/*typedef int ringbuf_type;*/
typedef struct
{
ringbuf_type_t *array;
int head;
int tail;
int size; /* total elements allocated */
} ringbuf;
static size_t ringbuf_size(const size_t n);
static int ringbuf_empty(ringbuf * d);
static int ringbuf_is_empty(const ringbuf * d);
static int ringbuf_is_full(const ringbuf * d);
static int ringbuf_insert(const ringbuf_type_t x, ringbuf * d);
static int ringbuf_pop_back(ringbuf * b);
static ringbuf_type_t ringbuf_peek(const int i, const ringbuf * b);
static ringbuf_type_t ringbuf_peek_front(const ringbuf * d);
static ringbuf_type_t ringbuf_peek_back(const ringbuf * d);
static size_t ringbuf_copy(double * dest, const ringbuf * b);
static size_t
ringbuf_size(const size_t n)
{
size_t size = 0;
size += sizeof(ringbuf);
size += n * sizeof(ringbuf_type_t); /* b->array */
return size;
}
static int
ringbuf_init(const size_t n, ringbuf * b)
{
b->array = (ringbuf_type_t *) ((char *) b + sizeof(ringbuf));
b->head = -1;
b->tail = 0;
b->size = (int) n;
return GSL_SUCCESS;
}
/* empty the buffer */
static int
ringbuf_empty(ringbuf * b)
{
b->head = -1;
b->tail = 0;
return GSL_SUCCESS;
}
/* check if buffer is empty */
static int
ringbuf_is_empty(const ringbuf * b)
{
return (b->head == -1);
}
/* check if buffer is full */
static int
ringbuf_is_full(const ringbuf * b)
{
return ((b->head == 0 && b->tail == b->size - 1) ||
(b->head == b->tail + 1));
}
/* insert element into buffer, overwriting oldest element if necessary */
static int
ringbuf_insert(const ringbuf_type_t x, ringbuf * b)
{
if (b->head == -1) /* buffer is empty */
{
b->head = 0;
b->tail = 0;
}
else if (b->head == 0) /* head is in first position, wrap to end */
{
b->head = b->size - 1;
if (b->tail == b->head && b->size > 1)
--(b->tail); /* buffer is full so decrease tail */
}
else /* decrement head */
{
--(b->head);
if (b->tail == b->head)
{
/* buffer is full so update tail */
if (b->tail == 0)
b->tail = b->size - 1;
else
--(b->tail);
}
}
/* insert element */
b->array[b->head] = x;
return GSL_SUCCESS;
}
static int
ringbuf_pop_back(ringbuf * b)
{
if (ringbuf_is_empty(b) || b->tail < 0)
{
GSL_ERROR("buffer is empty", GSL_EBADLEN);
}
else
{
if (b->head == b->tail) /* buffer has only one element */
{
b->head = -1;
b->tail = -1;
}
else if (b->tail == 0) /* tail is in first position, wrap to end */
{
b->tail = b->size - 1;
}
else /* decrement tail */
{
--(b->tail);
}
return GSL_SUCCESS;
}
}
static ringbuf_type_t
ringbuf_peek(const int i, const ringbuf * b)
{
if (ringbuf_is_empty(b))
{
GSL_ERROR("buffer is empty", GSL_EBADLEN);
}
else
{
return b->array[(b->head + i) % b->size];
}
}
static ringbuf_type_t
ringbuf_peek_front(const ringbuf * b)
{
if (ringbuf_is_empty(b))
{
GSL_ERROR("buffer is empty", GSL_EBADLEN);
}
else
{
return b->array[b->head];
}
}
static ringbuf_type_t
ringbuf_peek_back(const ringbuf * b)
{
if (ringbuf_is_empty(b) || b->tail < 0)
{
GSL_ERROR("buffer is empty", GSL_EBADLEN);
}
else
{
return b->array[b->tail];
}
}
static size_t
ringbuf_copy(double * dest, const ringbuf * b)
{
if (ringbuf_is_empty(b) || b->tail < 0)
{
return 0;
}
else
{
const int n = (b->head > b->tail) ? (b->size - b->head + b->tail + 1) : (b->tail - b->head + 1);
int i;
for (i = 0; i < n; ++i)
dest[n - i - 1] = b->array[(b->head + i) % b->size];
return (size_t) n;
}
}
#endif /* __GSL_RINGBUF_C__ */