1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2004-2006 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27 28#include <pulse/xmalloc.h> 29#include <pulsecore/macro.h> 30 31#include "mcalign.h" 32 33struct pa_mcalign { 34 size_t base; 35 pa_memchunk leftover, current; 36}; 37 38pa_mcalign *pa_mcalign_new(size_t base) { 39 pa_mcalign *m; 40 pa_assert(base); 41 42 m = pa_xnew(pa_mcalign, 1); 43 44 m->base = base; 45 pa_memchunk_reset(&m->leftover); 46 pa_memchunk_reset(&m->current); 47 48 return m; 49} 50 51void pa_mcalign_free(pa_mcalign *m) { 52 pa_assert(m); 53 54 if (m->leftover.memblock) 55 pa_memblock_unref(m->leftover.memblock); 56 57 if (m->current.memblock) 58 pa_memblock_unref(m->current.memblock); 59 60 pa_xfree(m); 61} 62 63void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) { 64 pa_assert(m); 65 pa_assert(c); 66 67 pa_assert(c->memblock); 68 pa_assert(c->length > 0); 69 70 pa_assert(!m->current.memblock); 71 72 /* Append to the leftover memory block */ 73 if (m->leftover.memblock) { 74 75 /* Try to merge */ 76 if (m->leftover.memblock == c->memblock && 77 m->leftover.index + m->leftover.length == c->index) { 78 79 /* Merge */ 80 m->leftover.length += c->length; 81 82 /* If the new chunk is larger than m->base, move it to current */ 83 if (m->leftover.length >= m->base) { 84 m->current = m->leftover; 85 pa_memchunk_reset(&m->leftover); 86 } 87 88 } else { 89 size_t l; 90 void *lo_data, *m_data; 91 92 /* We have to copy */ 93 pa_assert(m->leftover.length < m->base); 94 l = m->base - m->leftover.length; 95 96 if (l > c->length) 97 l = c->length; 98 99 /* Can we use the current block? */ 100 pa_memchunk_make_writable(&m->leftover, m->base); 101 102 lo_data = pa_memblock_acquire(m->leftover.memblock); 103 m_data = pa_memblock_acquire(c->memblock); 104 memcpy((uint8_t*) lo_data + m->leftover.index + m->leftover.length, (uint8_t*) m_data + c->index, l); 105 pa_memblock_release(m->leftover.memblock); 106 pa_memblock_release(c->memblock); 107 m->leftover.length += l; 108 109 pa_assert(m->leftover.length <= m->base); 110 pa_assert(m->leftover.length <= pa_memblock_get_length(m->leftover.memblock)); 111 112 if (c->length > l) { 113 /* Save the remainder of the memory block */ 114 m->current = *c; 115 m->current.index += l; 116 m->current.length -= l; 117 pa_memblock_ref(m->current.memblock); 118 } 119 } 120 } else { 121 /* Nothing to merge or copy, just store it */ 122 123 if (c->length >= m->base) 124 m->current = *c; 125 else 126 m->leftover = *c; 127 128 pa_memblock_ref(c->memblock); 129 } 130} 131 132int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) { 133 pa_assert(m); 134 pa_assert(c); 135 136 /* First test if there's a leftover memory block available */ 137 if (m->leftover.memblock) { 138 pa_assert(m->leftover.length > 0); 139 pa_assert(m->leftover.length <= m->base); 140 141 /* The leftover memory block is not yet complete */ 142 if (m->leftover.length < m->base) 143 return -1; 144 145 /* Return the leftover memory block */ 146 *c = m->leftover; 147 pa_memchunk_reset(&m->leftover); 148 149 /* If the current memblock is too small move it the leftover */ 150 if (m->current.memblock && m->current.length < m->base) { 151 m->leftover = m->current; 152 pa_memchunk_reset(&m->current); 153 } 154 155 return 0; 156 } 157 158 /* Now let's see if there is other data available */ 159 if (m->current.memblock) { 160 size_t l; 161 pa_assert(m->current.length >= m->base); 162 163 /* The length of the returned memory block */ 164 l = m->current.length; 165 l /= m->base; 166 l *= m->base; 167 pa_assert(l > 0); 168 169 /* Prepare the returned block */ 170 *c = m->current; 171 pa_memblock_ref(c->memblock); 172 c->length = l; 173 174 /* Drop that from the current memory block */ 175 pa_assert(l <= m->current.length); 176 m->current.index += l; 177 m->current.length -= l; 178 179 /* In case the whole block was dropped ... */ 180 if (m->current.length == 0) 181 pa_memblock_unref(m->current.memblock); 182 else { 183 /* Move the remainder to leftover */ 184 pa_assert(m->current.length < m->base && !m->leftover.memblock); 185 186 m->leftover = m->current; 187 } 188 189 pa_memchunk_reset(&m->current); 190 191 return 0; 192 } 193 194 /* There's simply nothing */ 195 return -1; 196} 197 198size_t pa_mcalign_csize(pa_mcalign *m, size_t l) { 199 pa_assert(m); 200 pa_assert(l > 0); 201 202 pa_assert(!m->current.memblock); 203 204 if (m->leftover.memblock) 205 l += m->leftover.length; 206 207 return (l/m->base)*m->base; 208} 209 210void pa_mcalign_flush(pa_mcalign *m) { 211 pa_memchunk chunk; 212 pa_assert(m); 213 214 while (pa_mcalign_pop(m, &chunk) >= 0) 215 pa_memblock_unref(chunk.memblock); 216} 217