/*** This file is part of libsydney. Copyright 2007-2008 Lennart Poettering libsydney is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. libsydney 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with libsydney. If not, see . ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include "malloc.h" #include "macro.h" #include "bufferq.h" #define SA_BUFFERQ_ITEM_CONCAT_DATA(x) ((void*) ((uint8_t*) (x) + SA_ALIGN(sizeof(sa_bufferq_item_t)))) int sa_bufferq_init(sa_bufferq_t *q, unsigned nchannels) { sa_assert(q); sa_assert(nchannels > 0); memset(q, 0, sizeof(*q)); q->nchannels = nchannels; if (!(q->items = sa_new0(sa_bufferq_item_t*, nchannels))) return SA_ERROR_OOM; if (!(q->last = sa_new0(sa_bufferq_item_t*, nchannels))) { sa_free(q->items); q->items = NULL; return SA_ERROR_OOM; } return SA_SUCCESS; } void sa_bufferq_done(sa_bufferq_t *q) { unsigned u; sa_assert(q); for (u = 0; u < q->nchannels; u++) { sa_bufferq_item_t *i; while ((i = q->items[u])) { SA_LLIST_REMOVE(sa_bufferq_item_t, bufferq, q->items[u], i); sa_free(i); } } sa_free(q->items); sa_free(q->last); } static sa_bufferq_item_t* bufferq_item_new(const void *d, int64_t idx, size_t size, sa_bufferq_item_type_t type) { sa_bufferq_item_t *i; sa_assert(size > 0); if (type == SA_BUFFERQ_ITEM_STATIC) { if (!(i = sa_new(sa_bufferq_item_t, 1))) return NULL; i->data = (void*) d; } else if (type == SA_BUFFERQ_ITEM_CONCATENATED) { if (!(i = sa_malloc(SA_ALIGN(sizeof(sa_bufferq_item_t)) + size))) return NULL; i->data = SA_BUFFERQ_ITEM_CONCAT_DATA(i); memcpy(i->data, d, size); } else { sa_assert(type == SA_BUFFERQ_ITEM_DYNAMIC); if (!(i = sa_new(sa_bufferq_item_t, 1))) return NULL; i->data = (void*) d; } i->type = type; i->idx = idx; i->size = size; SA_LLIST_ITEM_INIT(sa_bufferq_item_t, bufferq, i); return i; } static sa_bufferq_item_t* bufferq_item_make_writable(sa_bufferq_item_t *i) { void *d; sa_assert(i); if (i->type == SA_BUFFERQ_ITEM_CONCATENATED || i->type == SA_BUFFERQ_ITEM_DYNAMIC) return i; if (!(d = sa_memdup(i->data, i->size))) return NULL; i->data = d; i->type = SA_BUFFERQ_ITEM_DYNAMIC; return i; } static void bufferq_item_free(sa_bufferq_item_t *i) { sa_assert(i); if (i->type == SA_BUFFERQ_ITEM_DYNAMIC) sa_free(i->data); sa_free(i); } int sa_bufferq_push(sa_bufferq_t *q, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence, sa_bufferq_item_type_t type) { int64_t idx; sa_bufferq_item_t *i, *j, *n; sa_assert(q); switch (whence) { case SA_SEEK_RELATIVE: idx = q->write_index + offset; break; case SA_SEEK_ABSOLUTE: idx = offset; break; case SA_SEEK_RELATIVE_END: idx = q->end_index + offset; break; case _SA_SEEK_MAX: sa_assert_not_reached(); } if (q->read_index > idx && type != SA_BUFFERQ_ITEM_DYNAMIC) { int64_t l = q->read_index - idx; if (l > (int64_t) nbytes) l = (int64_t) nbytes; idx += l; data = (const uint8_t*) data + l; nbytes -= (size_t) l; } /* Allocate the new entry */ if (!(j = bufferq_item_new(data, idx, nbytes, type))) return SA_ERROR_OOM; /* Find the position where we need to insert the new entry */ for (i = q->items[channel]; i && idx >= i->idx + (int64_t) i->size; i = i->bufferq_next) ; /* Shorten the current entry if necessary */ if (i && idx > i->idx) { i->size = (size_t) (idx - i->idx); i = i->bufferq_next; } /* Insert the entry */ if (i) SA_LLIST_INSERT_BEFORE(sa_bufferq_item_t, bufferq, q->items[channel], i, j); else { if (q->last[channel]) SA_LLIST_INSERT_AFTER(sa_bufferq_item_t, bufferq, q->items[channel], q->last[channel], j); else SA_LLIST_PREPEND(sa_bufferq_item_t, bufferq, q->items[channel], j); q->last[channel] = j; } /* Now kick all the entries that overlap entirely with our new entry */ for (i = j->bufferq_next; i && i->idx + (int64_t) i->size < j->idx + (int64_t) j->size ; i = n) { n = i->bufferq_next; SA_LLIST_REMOVE(sa_bufferq_item_t, bufferq, q->items[channel], i); bufferq_item_free(i); } q->write_index = idx + (int64_t) nbytes; if (q->write_index > q->end_index) q->end_index = q->write_index; return SA_SUCCESS; } void sa_bufferq_get(sa_bufferq_t *q, void *i[], size_t *bytes) { sa_bool_t first = TRUE; unsigned u; sa_assert(q); *bytes = 0; for (u = 0; u < q->nchannels; u++) { if (q->items[u]) { if (q->items[u]->idx > q->read_index) { int64_t l; i[u] = NULL; l = q->items[u]->idx - q->read_index; if (first) { *bytes = (size_t) l; first = FALSE; } else *bytes = l < (int64_t) *bytes ? (size_t) l : *bytes; } else { int64_t l; i[u] = (uint8_t*) q->items[u]->data + q->read_index - q->items[u]->idx; l = (int64_t) q->items[u]->size - (q->read_index - q->items[u]->idx); if (first) { *bytes = (size_t) l; first = FALSE; } else *bytes = l < (int64_t) *bytes ? (size_t) l : *bytes; } } else i[u] = NULL; } } void sa_bufferq_drop(sa_bufferq_t *q, int64_t bytes) { unsigned u; sa_assert(q); q->read_index += bytes; for (u = 0; u < q->nchannels; u++) { sa_bufferq_item_t *i; i = q->items[u]; while (i && q->read_index >= i->idx + (int64_t) i->size) { sa_bufferq_item_t *n = i->bufferq_next; SA_LLIST_REMOVE(sa_bufferq_item_t, bufferq, q->items[u], i); bufferq_item_free(i); i = n; } if (!i) q->last[u] = NULL; } } int sa_bufferq_realloc(sa_bufferq_t *q) { unsigned u; sa_bool_t fail = FALSE; sa_assert(q); for (u = 0; u < q->nchannels; u++) { sa_bufferq_item_t *i; /* FIXME: This goes through the list linearly. We might need * to optimize this a little */ for (i = q->items[u]; i; i = i->bufferq_next) { if (i->type != SA_BUFFERQ_ITEM_STATIC) continue; if (!bufferq_item_make_writable(i)) { fail = TRUE; /* Hmm, we couldn't allocate memory, but we can't * return without doing anything, hence let's at least * drop the reference to the statically allocated * data */ i->size = 0; i->data = SA_BUFFERQ_ITEM_CONCAT_DATA(i); i->type = SA_BUFFERQ_ITEM_CONCATENATED; } } } return fail ? SA_ERROR_OOM : SA_SUCCESS; }