210 lines
4.3 KiB
C
210 lines
4.3 KiB
C
/*
|
|
* Copyright 2008-2013 Various Authors
|
|
* Copyright 2004 Timo Hirvonen
|
|
*
|
|
* 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 2 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "buffer.h"
|
|
#include "xmalloc.h"
|
|
#include "locking.h"
|
|
#include "debug.h"
|
|
|
|
/*
|
|
* chunk can be accessed by either consumer OR producer, not both at same time
|
|
* -> no need to lock
|
|
*/
|
|
struct chunk {
|
|
char data[CHUNK_SIZE];
|
|
|
|
/* index to data, first filled byte */
|
|
unsigned int l;
|
|
|
|
/* index to data, last filled byte + 1
|
|
*
|
|
* there are h - l bytes available (filled)
|
|
*/
|
|
unsigned int h : 31;
|
|
|
|
/* if chunk is marked filled it can only be accessed by consumer
|
|
* otherwise only producer is allowed to access the chunk
|
|
*/
|
|
unsigned int filled : 1;
|
|
};
|
|
|
|
unsigned int buffer_nr_chunks;
|
|
|
|
static pthread_mutex_t buffer_mutex = CMUS_MUTEX_INITIALIZER;
|
|
static struct chunk *buffer_chunks = NULL;
|
|
static unsigned int buffer_ridx;
|
|
static unsigned int buffer_widx;
|
|
|
|
void buffer_init(void)
|
|
{
|
|
free(buffer_chunks);
|
|
buffer_chunks = xnew(struct chunk, buffer_nr_chunks);
|
|
buffer_reset();
|
|
}
|
|
|
|
void buffer_free(void)
|
|
{
|
|
free(buffer_chunks);
|
|
}
|
|
|
|
/*
|
|
* @pos: returned pointer to available data
|
|
*
|
|
* Returns number of bytes available at @pos
|
|
*
|
|
* After reading bytes mark them consumed calling buffer_consume().
|
|
*/
|
|
int buffer_get_rpos(char **pos)
|
|
{
|
|
struct chunk *c;
|
|
int size = 0;
|
|
|
|
cmus_mutex_lock(&buffer_mutex);
|
|
c = &buffer_chunks[buffer_ridx];
|
|
if (c->filled) {
|
|
size = c->h - c->l;
|
|
*pos = c->data + c->l;
|
|
}
|
|
cmus_mutex_unlock(&buffer_mutex);
|
|
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
* @pos: pointer to buffer position where data can be written
|
|
*
|
|
* Returns number of bytes can be written to @pos. If the return value is
|
|
* non-zero it is guaranteed to be >= 1024.
|
|
*
|
|
* After writing bytes mark them filled calling buffer_fill().
|
|
*/
|
|
int buffer_get_wpos(char **pos)
|
|
{
|
|
struct chunk *c;
|
|
int size = 0;
|
|
|
|
cmus_mutex_lock(&buffer_mutex);
|
|
c = &buffer_chunks[buffer_widx];
|
|
if (!c->filled) {
|
|
size = CHUNK_SIZE - c->h;
|
|
*pos = c->data + c->h;
|
|
}
|
|
cmus_mutex_unlock(&buffer_mutex);
|
|
|
|
return size;
|
|
}
|
|
|
|
void buffer_consume(int count)
|
|
{
|
|
struct chunk *c;
|
|
|
|
BUG_ON(count < 0);
|
|
cmus_mutex_lock(&buffer_mutex);
|
|
c = &buffer_chunks[buffer_ridx];
|
|
BUG_ON(!c->filled);
|
|
c->l += count;
|
|
if (c->l == c->h) {
|
|
c->l = 0;
|
|
c->h = 0;
|
|
c->filled = 0;
|
|
buffer_ridx++;
|
|
buffer_ridx %= buffer_nr_chunks;
|
|
}
|
|
cmus_mutex_unlock(&buffer_mutex);
|
|
}
|
|
|
|
/* chunk is marked filled if free bytes < 1024 or count == 0 */
|
|
int buffer_fill(int count)
|
|
{
|
|
struct chunk *c;
|
|
int filled = 0;
|
|
|
|
cmus_mutex_lock(&buffer_mutex);
|
|
c = &buffer_chunks[buffer_widx];
|
|
BUG_ON(c->filled);
|
|
c->h += count;
|
|
|
|
if (CHUNK_SIZE - c->h < 1024 || (count == 0 && c->h > 0)) {
|
|
c->filled = 1;
|
|
buffer_widx++;
|
|
buffer_widx %= buffer_nr_chunks;
|
|
filled = 1;
|
|
}
|
|
|
|
cmus_mutex_unlock(&buffer_mutex);
|
|
return filled;
|
|
}
|
|
|
|
void buffer_reset(void)
|
|
{
|
|
int i;
|
|
|
|
cmus_mutex_lock(&buffer_mutex);
|
|
buffer_ridx = 0;
|
|
buffer_widx = 0;
|
|
for (i = 0; i < buffer_nr_chunks; i++) {
|
|
buffer_chunks[i].l = 0;
|
|
buffer_chunks[i].h = 0;
|
|
buffer_chunks[i].filled = 0;
|
|
}
|
|
cmus_mutex_unlock(&buffer_mutex);
|
|
}
|
|
|
|
int buffer_get_filled_chunks(void)
|
|
{
|
|
int c;
|
|
|
|
cmus_mutex_lock(&buffer_mutex);
|
|
if (buffer_ridx < buffer_widx) {
|
|
/*
|
|
* |__##########____|
|
|
* r w
|
|
*
|
|
* |############____|
|
|
* r w
|
|
*/
|
|
c = buffer_widx - buffer_ridx;
|
|
} else if (buffer_ridx > buffer_widx) {
|
|
/*
|
|
* |#######______###|
|
|
* w r
|
|
*
|
|
* |_____________###|
|
|
* w r
|
|
*/
|
|
c = buffer_nr_chunks - buffer_ridx + buffer_widx;
|
|
} else {
|
|
/*
|
|
* |################|
|
|
* r
|
|
* w
|
|
*
|
|
* |________________|
|
|
* r
|
|
* w
|
|
*/
|
|
if (buffer_chunks[buffer_ridx].filled) {
|
|
c = buffer_nr_chunks;
|
|
} else {
|
|
c = 0;
|
|
}
|
|
}
|
|
cmus_mutex_unlock(&buffer_mutex);
|
|
return c;
|
|
}
|