//buffer.c: /* * Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008-2011 * * This file is part of libroar a part of RoarAudio, * a cross-platform sound system for both, home and professional use. * See README for details. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 * as published by the Free Software Foundation. * * libroar 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 software; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * NOTE for everyone want's to change something and send patches: * read README and HACKING! There a addition information on * the license of this document you need to read before you send * any patches. * * NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc * or libpulse*: * The libs libroaresd, libroararts and libroarpulse link this lib * and are therefore GPL. Because of this it may be illigal to use * them with any software that uses libesd, libartsc or libpulse*. */ #include "libroar.h" struct roar_buffer_ring { size_t read_pos; size_t write_pos; }; struct roar_buffer { size_t refc; size_t len; size_t user_len; int flags; void * data; void * user_data; union { void * vp; int32_t i32; struct roar_buffer_ring ring; } meta; int type; struct roar_buffer * next; }; #define _ckbuf_free(m) if ( buf == NULL || (m) ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } #define _ckbuf(m) _ckbuf_free((m)); if ( _ckmem_corruption(buf) == -1 ) return -1; static int inline _ckmem_corruption(struct roar_buffer * buf) { int flags = buf->flags & (ROAR_BUFFER_FLAG_USEABLE|ROAR_BUFFER_FLAG_FREED); ROAR_DBG("_ckmem_corruption(buf=%p{.flags=0x%.4x, ...} = ?", buf, buf->flags); if ( flags == ROAR_BUFFER_FLAG_USEABLE ) { // seems ok, continue with next check. } else if ( flags == ROAR_BUFFER_FLAG_FREED ) { roar_panic(ROAR_FATAL_ERROR_MEMORY_USED_AFTER_FREE, NULL); roar_err_set(ROAR_ERROR_FAULT); return -1; } else { roar_panic(ROAR_FATAL_ERROR_MEMORY_CORRUPTION, NULL); roar_err_set(ROAR_ERROR_BADCKSUM); return -1; } if ( buf->refc == 0 ) { roar_panic(ROAR_FATAL_ERROR_MEMORY_CORRUPTION, NULL); } return 0; } int roar_buffer_new_data (struct roar_buffer ** buf, size_t len, void ** data) { void * bufdata; int err; if ((bufdata = roar_mm_malloc(len)) == NULL) { return -1; } if ( roar_buffer_new_no_ma(buf, len, bufdata) == -1 ) { err = roar_error; roar_mm_free(bufdata); roar_err_set(err); return -1; } if ( roar_buffer_set_flag(*buf, ROAR_BUFFER_FLAG_NOFREE, ROAR_BUFFER_RESET) == -1 ) { err = roar_error; roar_buffer_free(*buf); roar_mm_free(bufdata); roar_err_set(err); return -1; } if ( data != NULL ) *data = bufdata; return 0; } int roar_buffer_new_no_ma(struct roar_buffer ** buf, size_t len, void * data) { // no internal malloc struct roar_buffer * new; ROAR_DBG("buffer_new(buf=%p, len=%i) = ?", buf, len); _ckbuf_free(data == NULL) if ((new = roar_mm_malloc(sizeof(struct roar_buffer))) == NULL) { *buf = NULL; return -1; } new->refc = 1; new->data = data; new->flags = ROAR_BUFFER_FLAG_NONE|ROAR_BUFFER_FLAG_NOFREE|ROAR_BUFFER_FLAG_USEABLE; new->type = -1; new->user_data = new->data; new->next = NULL; new->len = len; new->user_len = len; *buf = new; ROAR_DBG("buffer_new(buf=%p, len=%i): New buffer at %p", buf, len, new); return 0; } int roar_buffer_ref (struct roar_buffer * buf) { _ckbuf(0) buf->refc++; return 0; } int roar_buffer_unref (struct roar_buffer * buf) { struct roar_buffer * next, * cur; int flags; ROAR_DBG("roar_buffer_unref(buf=%p) = ?", buf); _ckbuf_free(0) ROAR_DBG("roar_buffer_unref(buf=%p) = ?", buf); flags = buf->flags & (ROAR_BUFFER_FLAG_USEABLE|ROAR_BUFFER_FLAG_FREED); if ( flags == ROAR_BUFFER_FLAG_FREED ) { roar_panic(ROAR_FATAL_ERROR_MEMORY_DOUBLE_FREE, NULL); } _ckmem_corruption(buf); ROAR_DBG("roar_buffer_unref(buf=%p) = ?", buf); if ( buf->refc == 0 ) { ROAR_WARN("roar_buffer_unref(buf=%p): Ref counter is wrong. assuming one."); buf->refc = 1; roar_panic(ROAR_FATAL_ERROR_MEMORY_CORRUPTION, NULL); } buf->refc--; if ( buf->refc ) { ROAR_DBG("roar_buffer_unref(buf=%p) = 0", buf); return 0; } ROAR_DBG("roar_buffer_unref(buf=%p) = ?", buf); cur = buf->next; while (cur != NULL) { flags = cur->flags & (ROAR_BUFFER_FLAG_USEABLE|ROAR_BUFFER_FLAG_FREED); if ( flags == ROAR_BUFFER_FLAG_FREED ) { roar_panic(ROAR_FATAL_ERROR_MEMORY_DOUBLE_FREE, NULL); } ROAR_DBG("roar_buffer_unref(buf=%p) = ?", buf); _ckmem_corruption(cur); if ( roar_buffer_get_flag(cur, ROAR_BUFFER_FLAG_NOFREE) != 1 ) roar_mm_free(cur->data); cur->flags = ROAR_BUFFER_FLAG_FREED; next = cur->next; roar_mm_free(cur); cur = next; } ROAR_DBG("roar_buffer_unref(buf=%p) = ?", buf); if ( !(buf->flags & ROAR_BUFFER_FLAG_NOFREE) ) roar_mm_free(buf->data); ROAR_DBG("roar_buffer_unref(buf=%p): setting flags = ROAR_BUFFER_FLAG_FREED", buf); buf->flags = ROAR_BUFFER_FLAG_FREED; roar_mm_free(buf); ROAR_DBG("roar_buffer_unref(buf=%p) = 0", buf); return 0; } int roar_buffer_delete (struct roar_buffer * buf, struct roar_buffer ** next) { if ( buf == NULL ) { if ( next != NULL ) *next = NULL; roar_err_set(ROAR_ERROR_FAULT); return -1; } ROAR_DBG("buffer_delete(buf=%p, next=%p) = ?", buf, next); if ( next != NULL ) *next = buf->next; if ( roar_buffer_get_flag(buf, ROAR_BUFFER_FLAG_NOFREE) != 1 ) roar_mm_free(buf->data); roar_mm_free(buf); ROAR_DBG("buffer_delete(buf=%p, next=%p) = 0", buf, next); return 0; } int roar_buffer_add (struct roar_buffer * buf, struct roar_buffer * next) { unsigned int deep = 0; _ckbuf(0) ROAR_DBG("buffer_add(buf=%p, next=%p) = ?", buf, next); if ( buf->flags & ROAR_BUFFER_FLAG_RING ) { roar_err_set(ROAR_ERROR_INVAL); return -1; } if ( buf == next ) { ROAR_ERR("buffer_add(*): both pointer are of the same destination, This is a error in the application"); roar_err_set(ROAR_ERROR_INVAL); return -1; } while ( buf->next != NULL ) { ROAR_DBG("buffer_add(*): buf=%p, next=%p (len=%i)", buf, buf->next, buf->user_len); // ROAR_DBG("buffer_add(): buf=%p, buf->next=%p", buf, buf->next); buf = buf->next; deep++; if ( buf == next ) { ROAR_ERR("buffer_add(*): Can not add buffer: loop detected at deep %u. This is a error in the application", deep); // why don't we return here? } } buf->next = next; ROAR_DBG("buffer_add(*): adding buffer at deep %u", deep); return 0; } int roar_buffer_clear_next (struct roar_buffer * buf) { _ckbuf(0) buf->next = NULL; return 0; } int roar_buffer_get_next (struct roar_buffer * buf, struct roar_buffer ** next) { _ckbuf(0) *next = buf->next; return 0; } int roar_buffer_ring_new (struct roar_buffer ** buf, size_t len, int free_running) { struct roar_buffer * n; _ckbuf_free(0) if ( len == 0 ) { roar_err_set(ROAR_ERROR_RANGE); return -1; } // just to be sure: *buf = NULL; if ( roar_buffer_new(&n, len) == -1 ) return -1; n->flags |= ROAR_BUFFER_FLAG_RING; if ( free_running ) n->flags |= ROAR_BUFFER_FLAG_FREE_RUNNING; n->meta.ring.read_pos = 0; n->meta.ring.write_pos = 0; memset(n->data, 0, n->len); *buf = n; return 0; } int roar_buffer_get_data (struct roar_buffer * buf, void ** data) { _ckbuf(0) *data = buf->user_data; return 0; } int roar_buffer_set_offset (struct roar_buffer * buf, size_t off) { _ckbuf(0) if ( off > buf->user_len ) { roar_err_set(ROAR_ERROR_RANGE); return -1; } buf->user_len -= off; buf->user_data += off; return 0; } int roar_buffer_shift_out (struct roar_buffer ** buf, void * data, size_t * len) { size_t todo, cl; struct roar_buffer * cur; void * cd; _ckbuf_free(len == NULL || data == NULL); if ( *buf == NULL ) { ROAR_DBG("roar_buffer_shift_out(buf=%p, data=%p, len={%lu}) = -1 // Invalid pointer to buffer ring", buf, data, (unsigned long)len); roar_err_set(ROAR_ERROR_FAULT); return -1; } todo = *len; cur = *buf; *len = 0; _ckmem_corruption(cur); while (todo && cur != NULL) { ROAR_DBG("roar_buffer_shift_out(*): todo=%u, cur=%p", (unsigned int) todo, cur); if ( roar_buffer_get_len(cur, &cl) == -1 ) return -1; if ( cl > todo ) { if ( roar_buffer_get_data(cur, &cd) == -1 ) return -1; cl = todo; memcpy(data, cd, cl); todo -= cl; data += cl; *len += cl; if ( roar_buffer_set_offset(cur, cl) == -1 ) return -1; } else { if ( roar_buffer_get_data(cur, &cd) == -1 ) return -1; memcpy(data, cd, cl); todo -= cl; data += cl; *len += cl; if ( roar_buffer_next(&cur) == -1 ) return -1; _ckmem_corruption(cur); } /* if ( cur == NULL ) break; */ } *buf = cur; return 0; } int roar_buffer_set_meta (struct roar_buffer * buf, void * meta) { _ckbuf(0) buf->meta.vp = meta; return 0; } int roar_buffer_get_meta (struct roar_buffer * buf, void ** meta) { _ckbuf(meta == NULL) *meta = buf->meta.vp; return 0; } int roar_buffer_set_meta_i32(struct roar_buffer * buf, int32_t meta) { _ckbuf(0) buf->meta.i32 = meta; return 0; } int roar_buffer_get_meta_i32(struct roar_buffer * buf, int32_t * meta) { _ckbuf(meta == NULL) *meta = buf->meta.i32; return 0; } int roar_buffer_set_type (struct roar_buffer * buf, int type) { _ckbuf(0) switch (type) { case ROAR_VIO_DFT_RAW: case ROAR_VIO_DFT_PACKET: case ROAR_VIO_DFT_UNFRAMED: case ROAR_VIO_DFT_OGG_PACKET: case ROAR_VIO_DFT_OGG_PAGE: break; default: roar_err_set(ROAR_ERROR_NOTSUP); return -1; } buf->type = type; return 0; } int roar_buffer_get_type (struct roar_buffer * buf, int * type) { _ckbuf(type == NULL) *type = buf->type; return 0; } int roar_buffer_set_len (struct roar_buffer * buf, size_t len) { size_t totlen; void * newbuf; _ckbuf(0) // handle specal case where user length is zero: if ( len && !buf->user_len ) { buf->user_data = buf->data; buf->user_len = buf->len; } if ( len > buf->user_len ) { // we can only enlage a buffer if it's one of our own memory segments if ( buf->flags & ROAR_BUFFER_FLAG_NOFREE ) { roar_err_set(ROAR_ERROR_NOTSUP); return -1; } totlen = (buf->len - buf->user_len) + len; newbuf = roar_mm_realloc(buf->data, totlen); if ( newbuf == NULL ) return -1; buf->user_data = newbuf + (buf->user_data - buf->data); buf->user_len = len; buf->data = newbuf; buf->len = totlen; } else { buf->user_len = len; } return 0; } int roar_buffer_get_len (struct roar_buffer * buf, size_t * len) { _ckbuf(0) *len = buf->user_len; return 0; } int roar_buffer_set_flag (struct roar_buffer * buf, int flag, int reset) { _ckbuf(0) buf->flags |= flag; if ( reset ) buf->flags -= flag; return 0; } int roar_buffer_get_flag (struct roar_buffer * buf, int flag) { _ckbuf(0) return buf->flags & flag; } int roar_buffer_duplicate (struct roar_buffer * buf, struct roar_buffer ** copy) { struct roar_buffer * cur = buf; struct roar_buffer * new; void * od, * nd; int err; _ckbuf(copy == NULL) *copy = NULL; while (cur != NULL) { if ( roar_buffer_new(&new, cur->user_len) == -1 ) { err = roar_error; roar_buffer_free(*copy); roar_err_set(err); return -1; } if ( *copy == NULL ) *copy = new; roar_buffer_get_data(cur, &od); roar_buffer_get_data(new, &nd); memcpy(nd, od, cur->user_len); roar_buffer_add(*copy, new); cur = cur->next; } return 0; } int roar_buffer_ring_stats (struct roar_buffer * buf, struct roar_buffer_stats * stats) { _ckbuf(0) stats->parts = 0; stats->bytes = 0; stats->memory_usage = 0; while (buf != NULL ) { stats->parts++; stats->bytes += buf->user_len; stats->memory_usage += buf->len + sizeof(struct roar_buffer); buf = buf->next; } return 0; } int roar_buffer_ring_read (struct roar_buffer * buf, void * data, size_t * len) { struct roar_buffer_ring * ring; size_t havelen; size_t done; size_t tmp; _ckbuf(len == NULL) if ( buf == NULL || len == NULL ) return -1; if ( data == NULL && *len != 0 ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } if ( !(buf->flags & ROAR_BUFFER_FLAG_RING) ) { roar_err_set(ROAR_ERROR_TYPEMM); return -1; } if ( *len == 0 ) return 0; // we may handle this later: if ( *len > buf->user_len ) { roar_err_set(ROAR_ERROR_NOTSUP); return -1; } if ( (buf->flags & ROAR_BUFFER_FLAG_FREE_RUNNING) ) { if ( buf->meta.ring.read_pos >= buf->user_len ) buf->meta.ring.read_pos -= buf->user_len; if ( (*len + buf->meta.ring.read_pos) > buf->user_len ) { // wraped mode: memcpy(data, buf->user_data+buf->meta.ring.read_pos, buf->user_len - buf->meta.ring.read_pos); memcpy(data, buf->user_data, *len + buf->meta.ring.read_pos - buf->user_len); buf->meta.ring.read_pos += *len; buf->meta.ring.read_pos -= buf->user_len; return 0; } else { // unwarped mode: memcpy(data, buf->user_data+buf->meta.ring.read_pos, *len); buf->meta.ring.read_pos += *len; return 0; } } else { ring = &(buf->meta.ring); if ( ring->read_pos == ring->write_pos ) { *len = 0; return 0; } else if ( ring->read_pos < ring->write_pos ) { havelen = ring->write_pos - ring->read_pos; if ( havelen > *len ) havelen = *len; memcpy(data, buf->user_data+ring->read_pos, havelen); ring->read_pos += havelen; *len = havelen; return 0; } else { // write_pos < read_pos done = 0; // first pass: use data up to end of buffer havelen = buf->user_len - ring->read_pos; if ( havelen > *len ) havelen = *len; memcpy(data, buf->user_data+ring->read_pos, havelen); ring->read_pos += havelen; done += havelen; if ( ring->read_pos == buf->user_len ) { ring->read_pos = 0; } // second pass: use data from strat of buffer to write pointer if ( *len > done ) { tmp = *len - done; if ( roar_buffer_ring_read(buf, data+done, &tmp) == 0 ) { done += tmp; } } *len = done; return 0; } } roar_err_set(ROAR_ERROR_UNKNOWN); return -1; } int roar_buffer_ring_write (struct roar_buffer * buf, void * data, size_t * len) { struct roar_buffer_ring * ring; size_t havelen; size_t done; size_t tmp; ROAR_DBG("roar_buffer_ring_write(buf=%p, data=%p, len=%p) = ?", buf, data, len); _ckbuf(len == NULL) if ( data == NULL && *len != 0 ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } if ( !(buf->flags & ROAR_BUFFER_FLAG_RING) ) { roar_err_set(ROAR_ERROR_TYPEMM); return -1; } if ( *len == 0 ) return 0; // we may handle this later: if ( *len > buf->user_len ) { roar_err_set(ROAR_ERROR_NOTSUP); return -1; } if ( (buf->flags & ROAR_BUFFER_FLAG_FREE_RUNNING) ) { if ( buf->meta.ring.write_pos >= buf->user_len ) buf->meta.ring.write_pos -= buf->user_len; if ( (*len + buf->meta.ring.write_pos) > buf->user_len ) { // wraped mode: memcpy(buf->user_data+buf->meta.ring.write_pos, data, buf->user_len - buf->meta.ring.write_pos); memcpy(buf->user_data, data, *len + buf->meta.ring.write_pos - buf->user_len); buf->meta.ring.write_pos += *len; buf->meta.ring.write_pos -= buf->user_len; return 0; } else { // unwarped mode: memcpy(buf->user_data+buf->meta.ring.write_pos, data, *len); buf->meta.ring.write_pos += *len; return 0; } } else { ring = &(buf->meta.ring); done = 0; ROAR_DBG("roar_buffer_ring_write(buf=%p, data=%p, len=%p): write_pos=%u, read_pos=%u, user_len=%u", buf, data, len, (unsigned int)ring->write_pos, (unsigned int)ring->read_pos, (unsigned int)buf->user_len); if ( ring->write_pos >= ring->read_pos ) { ROAR_DBG("roar_buffer_ring_write(buf=%p, data=%p, len=%p) = ?", buf, data, len); havelen = buf->user_len - ring->write_pos; if ( ring->read_pos == 0 ) havelen--; if ( havelen > *len ) havelen = *len; memcpy(buf->user_data+ring->write_pos, data, havelen); done += havelen; ring->write_pos += havelen; if ( ring->write_pos == buf->user_len ) ring->write_pos = 0; if ( *len > done && ring->read_pos != 0 ) { tmp = *len - done; if ( roar_buffer_ring_write(buf, data+done, &tmp) == 0 ) { done += tmp; } } *len = done; ROAR_DBG("roar_buffer_ring_write(buf=%p, data=%p, len=%p) = 0", buf, data, len); return 0; } else { ROAR_DBG("roar_buffer_ring_write(buf=%p, data=%p, len=%p) = ?", buf, data, len); // test for buffer-is-full: if ( (ring->write_pos + 1) == ring->read_pos ) { *len = 0; ROAR_DBG("roar_buffer_ring_write(buf=%p, data=%p, len=%p) = 0", buf, data, len); return 0; } ROAR_DBG("roar_buffer_ring_write(buf=%p, data=%p, len=%p) = ?", buf, data, len); havelen = ring->read_pos - ring->write_pos - 1; if ( havelen > *len ) havelen = *len; memcpy(buf->user_data+ring->write_pos, data, havelen); ring->write_pos += havelen; *len = havelen; ROAR_DBG("roar_buffer_ring_write(buf=%p, data=%p, len=%p) = 0", buf, data, len); return 0; } } roar_err_set(ROAR_ERROR_UNKNOWN); return -1; } int roar_buffer_ring_avail(struct roar_buffer * buf, size_t * readlen, size_t * writelen) { struct roar_buffer_ring * ring; size_t have; _ckbuf(0) if ( !(buf->flags & ROAR_BUFFER_FLAG_RING) ) { roar_err_set(ROAR_ERROR_TYPEMM); return -1; } ring = &(buf->meta.ring); ROAR_DBG("roar_buffer_ring_avail(buf=%p, readlen=%p, writelen=%p) = ?", buf, readlen, writelen); if ( readlen != NULL ) { have = 0; if ( ring->write_pos >= ring->read_pos ) { have = ring->write_pos - ring->read_pos; } else { have = buf->user_len - ring->read_pos; have += ring->write_pos; have -= 1; } *readlen = have; ROAR_DBG("roar_buffer_ring_avail(buf=%p, readlen=%p, writelen=%p): readlen=%llu", buf, readlen, writelen, (unsigned long long int)have); } if ( writelen != NULL ) { have = 0; if ( ring->read_pos > ring->write_pos ) { have = ring->read_pos - ring->write_pos - 1; } else { have = buf->user_len - ring->write_pos; have += ring->read_pos; have -= 1; } *writelen = have; ROAR_DBG("roar_buffer_ring_avail(buf=%p, readlen=%p, writelen=%p): readlen=%llu", buf, readlen, writelen, (unsigned long long int)have); } ROAR_DBG("roar_buffer_ring_avail(buf=%p, readlen=%p, writelen=%p) = 0", buf, readlen, writelen); return 0; } int roar_buffer_ring_reset(struct roar_buffer * buf) { _ckbuf(0) if ( !(buf->flags & ROAR_BUFFER_FLAG_RING) ) { roar_err_set(ROAR_ERROR_TYPEMM); return -1; } buf->meta.ring.read_pos = buf->meta.ring.write_pos = 0; return 0; } //ll