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
33 struct pa_mcalign {
34 size_t base;
35 pa_memchunk leftover, current;
36 };
37
pa_mcalign_new(size_t base)38 pa_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
pa_mcalign_free(pa_mcalign * m)51 void 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
pa_mcalign_push(pa_mcalign * m,const pa_memchunk * c)63 void 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
pa_mcalign_pop(pa_mcalign * m,pa_memchunk * c)132 int 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
pa_mcalign_csize(pa_mcalign * m,size_t l)198 size_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
pa_mcalign_flush(pa_mcalign * m)210 void 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