#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, size_t sample_size) { sa_assert(q); sa_assert(sample_size > 0); sa_assert(nchannels > 0); memset(q, 0, sizeof(*q)); q->sample_size = sample_size; 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 > nbytes) l = nbytes; idx += l; data = (const uint8_t*) data + l; nbytes -= 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 + i->size; i = i->bufferq_next) ; /* Shorten the current entry if necessary */ if (i && idx > i->idx) { i->size = 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 + i->size < j->idx + 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 + 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) { int first = 1; 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 = l; first = 0; } else *bytes = l < *bytes ? l : *bytes; } else { int64_t l; i[u] = (uint8_t*) q->items[u]->data + q->read_index - q->items[u]->idx; l = q->items[u]->size - (q->read_index - q->items[u]->idx); if (first) { *bytes = l; first = 0; } else *bytes = l < *bytes ? 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 + 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; int fail = 0; sa_assert(q); for (u = 0; u < q->nchannels; u++) { sa_bufferq_item_t *i; 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 = 1; /* 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; }