]> code.delx.au - pulseaudio/blob - polyp/memblockq.c
introduce pa_xmalloc() and friends
[pulseaudio] / polyp / memblockq.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 polypaudio 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 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <sys/time.h>
27 #include <time.h>
28 #include <stdio.h>
29 #include <assert.h>
30 #include <stdlib.h>
31
32 #include "memblockq.h"
33 #include "xmalloc.h"
34
35 struct memblock_list {
36 struct memblock_list *next;
37 struct pa_memchunk chunk;
38 struct timeval stamp;
39 };
40
41 struct pa_memblockq {
42 struct memblock_list *blocks, *blocks_tail;
43 unsigned n_blocks;
44 size_t current_length, maxlength, tlength, base, prebuf, minreq;
45 int measure_delay;
46 uint32_t delay;
47 struct pa_mcalign *mcalign;
48 };
49
50 struct pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq) {
51 struct pa_memblockq* bq;
52 assert(maxlength && base && maxlength);
53
54 bq = pa_xmalloc(sizeof(struct pa_memblockq));
55 bq->blocks = bq->blocks_tail = 0;
56 bq->n_blocks = 0;
57
58 bq->current_length = 0;
59
60 fprintf(stderr, "memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq);
61
62 bq->base = base;
63
64 bq->maxlength = ((maxlength+base-1)/base)*base;
65 assert(bq->maxlength >= base);
66
67 bq->tlength = ((tlength+base-1)/base)*base;
68 if (bq->tlength == 0 || bq->tlength >= bq->maxlength)
69 bq->tlength = bq->maxlength;
70
71 bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf;
72 bq->prebuf = (bq->prebuf/base)*base;
73 if (bq->prebuf > bq->maxlength)
74 bq->prebuf = bq->maxlength;
75
76 bq->minreq = (minreq/base)*base;
77 if (bq->minreq == 0)
78 bq->minreq = 1;
79
80 fprintf(stderr, "memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq);
81
82 bq->measure_delay = 0;
83 bq->delay = 0;
84
85 bq->mcalign = NULL;
86
87 return bq;
88 }
89
90 void pa_memblockq_free(struct pa_memblockq* bq) {
91 struct memblock_list *l;
92 assert(bq);
93
94 if (bq->mcalign)
95 pa_mcalign_free(bq->mcalign);
96
97 while ((l = bq->blocks)) {
98 bq->blocks = l->next;
99 pa_memblock_unref(l->chunk.memblock);
100 pa_xfree(l);
101 }
102
103 pa_xfree(bq);
104 }
105
106 void pa_memblockq_push(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) {
107 struct memblock_list *q;
108 assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0);
109
110 if (bq->blocks_tail && bq->blocks_tail->chunk.memblock == chunk->memblock) {
111 /* Try to merge memory chunks */
112
113 if (bq->blocks_tail->chunk.index+bq->blocks_tail->chunk.length == chunk->index) {
114 bq->blocks_tail->chunk.length += chunk->length;
115 bq->current_length += chunk->length;
116
117 /* fprintf(stderr, __FILE__": merge succeeded: %u\n", chunk->length);*/
118 return;
119 }
120 }
121
122 q = pa_xmalloc(sizeof(struct memblock_list));
123
124 if (bq->measure_delay)
125 gettimeofday(&q->stamp, NULL);
126 else
127 timerclear(&q->stamp);
128
129 q->chunk = *chunk;
130 pa_memblock_ref(q->chunk.memblock);
131 assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length);
132 q->next = NULL;
133
134 if (bq->blocks_tail)
135 bq->blocks_tail->next = q;
136 else
137 bq->blocks = q;
138
139 bq->blocks_tail = q;
140
141 bq->n_blocks++;
142 bq->current_length += chunk->length;
143
144 pa_memblockq_shorten(bq, bq->maxlength);
145 }
146
147 int pa_memblockq_peek(struct pa_memblockq* bq, struct pa_memchunk *chunk) {
148 assert(bq && chunk);
149
150 if (!bq->blocks || bq->current_length < bq->prebuf)
151 return -1;
152
153 bq->prebuf = 0;
154
155 *chunk = bq->blocks->chunk;
156 pa_memblock_ref(chunk->memblock);
157
158 /* if (chunk->memblock->ref != 2) */
159 /* fprintf(stderr, "block %p with ref %u peeked.\n", chunk->memblock, chunk->memblock->ref); */
160
161 return 0;
162 }
163
164 /*
165 int memblockq_pop(struct memblockq* bq, struct pa_memchunk *chunk) {
166 struct memblock_list *q;
167
168 assert(bq && chunk);
169
170 if (!bq->blocks || bq->current_length < bq->prebuf)
171 return -1;
172
173 bq->prebuf = 0;
174
175 q = bq->blocks;
176 bq->blocks = bq->blocks->next;
177
178 *chunk = q->chunk;
179
180 bq->n_blocks--;
181 bq->current_length -= chunk->length;
182
183 pa_xfree(q);
184 return 0;
185 }
186 */
187
188 static uint32_t age(struct timeval *tv) {
189 assert(tv);
190 struct timeval now;
191 uint32_t r;
192
193 if (tv->tv_sec == 0)
194 return 0;
195
196 gettimeofday(&now, NULL);
197
198 r = (now.tv_sec-tv->tv_sec) * 1000000;
199
200 if (now.tv_usec >= tv->tv_usec)
201 r += now.tv_usec - tv->tv_usec;
202 else
203 r -= tv->tv_usec - now.tv_usec;
204
205 return r;
206 }
207
208 void pa_memblockq_drop(struct pa_memblockq *bq, size_t length) {
209 assert(bq && length && (length % bq->base) == 0);
210
211 while (length > 0) {
212 size_t l = length;
213 assert(bq->blocks && bq->current_length >= length);
214
215 if (l > bq->blocks->chunk.length)
216 l = bq->blocks->chunk.length;
217
218 if (bq->measure_delay)
219 bq->delay = age(&bq->blocks->stamp);
220
221 bq->blocks->chunk.index += l;
222 bq->blocks->chunk.length -= l;
223 bq->current_length -= l;
224
225 if (bq->blocks->chunk.length == 0) {
226 struct memblock_list *q;
227
228 q = bq->blocks;
229 bq->blocks = bq->blocks->next;
230 if (bq->blocks == NULL)
231 bq->blocks_tail = NULL;
232 pa_memblock_unref(q->chunk.memblock);
233 pa_xfree(q);
234
235 bq->n_blocks--;
236 }
237
238 length -= l;
239 }
240 }
241
242 void pa_memblockq_shorten(struct pa_memblockq *bq, size_t length) {
243 size_t l;
244 assert(bq);
245
246 if (bq->current_length <= length)
247 return;
248
249 fprintf(stderr, "Warning! pa_memblockq_shorten()\n");
250
251 l = bq->current_length - length;
252 l /= bq->base;
253 l *= bq->base;
254
255 pa_memblockq_drop(bq, l);
256 }
257
258
259 void pa_memblockq_empty(struct pa_memblockq *bq) {
260 assert(bq);
261 pa_memblockq_shorten(bq, 0);
262 }
263
264 int pa_memblockq_is_readable(struct pa_memblockq *bq) {
265 assert(bq);
266
267 return bq->current_length && (bq->current_length >= bq->prebuf);
268 }
269
270 int pa_memblockq_is_writable(struct pa_memblockq *bq, size_t length) {
271 assert(bq);
272
273 return bq->current_length + length <= bq->tlength;
274 }
275
276 uint32_t pa_memblockq_get_delay(struct pa_memblockq *bq) {
277 assert(bq);
278 return bq->delay;
279 }
280
281 uint32_t pa_memblockq_get_length(struct pa_memblockq *bq) {
282 assert(bq);
283 return bq->current_length;
284 }
285
286 uint32_t pa_memblockq_missing(struct pa_memblockq *bq) {
287 size_t l;
288 assert(bq);
289
290 if (bq->current_length >= bq->tlength)
291 return 0;
292
293 l = bq->tlength - bq->current_length;
294 assert(l);
295
296 return (l >= bq->minreq) ? l : 0;
297 }
298
299 void pa_memblockq_push_align(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) {
300 struct pa_memchunk rchunk;
301 assert(bq && chunk && bq->base);
302
303 if (bq->base == 1) {
304 pa_memblockq_push(bq, chunk, delta);
305 return;
306 }
307
308 if (!bq->mcalign) {
309 bq->mcalign = pa_mcalign_new(bq->base);
310 assert(bq->mcalign);
311 }
312
313 pa_mcalign_push(bq->mcalign, chunk);
314
315 while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
316 pa_memblockq_push(bq, &rchunk, delta);
317 pa_memblock_unref(rchunk.memblock);
318 delta = 0;
319 }
320 }
321
322 uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq) {
323 assert(bq);
324 return bq->minreq;
325 }