]> code.delx.au - pulseaudio/blob - src/polypcore/memblockq.c
Cleaned up the includes after the restructuring. Indicate which headers are
[pulseaudio] / src / polypcore / 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 Lesser 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 Lesser 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 #include <string.h>
32
33 #include <polypcore/xmalloc.h>
34 #include <polypcore/log.h>
35 #include <polypcore/mcalign.h>
36
37 #include "memblockq.h"
38
39 struct memblock_list {
40 struct memblock_list *next, *prev;
41 pa_memchunk chunk;
42 };
43
44 struct pa_memblockq {
45 struct memblock_list *blocks, *blocks_tail;
46 unsigned n_blocks;
47 size_t current_length, maxlength, tlength, base, prebuf, orig_prebuf, minreq;
48 pa_mcalign *mcalign;
49 pa_memblock_stat *memblock_stat;
50 };
51
52 pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq, pa_memblock_stat *s) {
53 pa_memblockq* bq;
54 assert(maxlength && base && maxlength);
55
56 bq = pa_xmalloc(sizeof(pa_memblockq));
57 bq->blocks = bq->blocks_tail = 0;
58 bq->n_blocks = 0;
59
60 bq->current_length = 0;
61
62 pa_log_debug(__FILE__": memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq);
63
64 bq->base = base;
65
66 bq->maxlength = ((maxlength+base-1)/base)*base;
67 assert(bq->maxlength >= base);
68
69 bq->tlength = ((tlength+base-1)/base)*base;
70 if (!bq->tlength || bq->tlength >= bq->maxlength)
71 bq->tlength = bq->maxlength;
72
73 bq->minreq = (minreq/base)*base;
74 if (bq->minreq == 0)
75 bq->minreq = 1;
76
77 bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf;
78 bq->prebuf = (bq->prebuf/base)*base;
79 if (bq->prebuf > bq->maxlength)
80 bq->prebuf = bq->maxlength;
81
82 if (bq->prebuf > bq->tlength - bq->minreq)
83 bq->prebuf = bq->tlength - bq->minreq;
84
85 bq->orig_prebuf = bq->prebuf;
86
87 pa_log_debug(__FILE__": memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq);
88
89 bq->mcalign = NULL;
90
91 bq->memblock_stat = s;
92
93 return bq;
94 }
95
96 void pa_memblockq_free(pa_memblockq* bq) {
97 assert(bq);
98
99 pa_memblockq_flush(bq);
100
101 if (bq->mcalign)
102 pa_mcalign_free(bq->mcalign);
103
104 pa_xfree(bq);
105 }
106
107 void pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta) {
108 struct memblock_list *q;
109 assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0);
110
111 pa_memblockq_seek(bq, delta);
112
113 if (bq->blocks_tail && bq->blocks_tail->chunk.memblock == chunk->memblock) {
114 /* Try to merge memory chunks */
115
116 if (bq->blocks_tail->chunk.index+bq->blocks_tail->chunk.length == chunk->index) {
117 bq->blocks_tail->chunk.length += chunk->length;
118 bq->current_length += chunk->length;
119 return;
120 }
121 }
122
123 q = pa_xmalloc(sizeof(struct memblock_list));
124
125 q->chunk = *chunk;
126 pa_memblock_ref(q->chunk.memblock);
127 assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length);
128 q->next = NULL;
129 if ((q->prev = bq->blocks_tail))
130 bq->blocks_tail->next = q;
131 else
132 bq->blocks = q;
133
134 bq->blocks_tail = q;
135
136 bq->n_blocks++;
137 bq->current_length += chunk->length;
138
139 pa_memblockq_shorten(bq, bq->maxlength);
140 }
141
142 int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
143 assert(bq && chunk);
144
145 if (!bq->blocks || bq->current_length < bq->prebuf)
146 return -1;
147
148 bq->prebuf = 0;
149
150 *chunk = bq->blocks->chunk;
151 pa_memblock_ref(chunk->memblock);
152
153 return 0;
154 }
155
156 void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length) {
157 assert(bq && chunk && length);
158
159 if (!bq->blocks || memcmp(&bq->blocks->chunk, chunk, sizeof(pa_memchunk)))
160 return;
161
162 assert(length <= bq->blocks->chunk.length);
163 pa_memblockq_skip(bq, length);
164 }
165
166 static void remove_block(pa_memblockq *bq, struct memblock_list *q) {
167 assert(bq && q);
168
169 if (q->prev)
170 q->prev->next = q->next;
171 else {
172 assert(bq->blocks == q);
173 bq->blocks = q->next;
174 }
175
176 if (q->next)
177 q->next->prev = q->prev;
178 else {
179 assert(bq->blocks_tail == q);
180 bq->blocks_tail = q->prev;
181 }
182
183 pa_memblock_unref(q->chunk.memblock);
184 pa_xfree(q);
185
186 bq->n_blocks--;
187 }
188
189 void pa_memblockq_skip(pa_memblockq *bq, size_t length) {
190 assert(bq && length && (length % bq->base) == 0);
191
192 while (length > 0) {
193 size_t l = length;
194 assert(bq->blocks && bq->current_length >= length);
195
196 if (l > bq->blocks->chunk.length)
197 l = bq->blocks->chunk.length;
198
199 bq->blocks->chunk.index += l;
200 bq->blocks->chunk.length -= l;
201 bq->current_length -= l;
202
203 if (!bq->blocks->chunk.length)
204 remove_block(bq, bq->blocks);
205
206 length -= l;
207 }
208 }
209
210 void pa_memblockq_shorten(pa_memblockq *bq, size_t length) {
211 size_t l;
212 assert(bq);
213
214 if (bq->current_length <= length)
215 return;
216
217 /*pa_log(__FILE__": Warning! pa_memblockq_shorten()\n");*/
218
219 l = bq->current_length - length;
220 l /= bq->base;
221 l *= bq->base;
222
223 pa_memblockq_skip(bq, l);
224 }
225
226
227 void pa_memblockq_empty(pa_memblockq *bq) {
228 assert(bq);
229 pa_memblockq_shorten(bq, 0);
230 }
231
232 int pa_memblockq_is_readable(pa_memblockq *bq) {
233 assert(bq);
234
235 return bq->current_length && (bq->current_length >= bq->prebuf);
236 }
237
238 int pa_memblockq_is_writable(pa_memblockq *bq, size_t length) {
239 assert(bq);
240
241 return bq->current_length + length <= bq->tlength;
242 }
243
244 uint32_t pa_memblockq_get_length(pa_memblockq *bq) {
245 assert(bq);
246 return bq->current_length;
247 }
248
249 uint32_t pa_memblockq_missing(pa_memblockq *bq) {
250 size_t l;
251 assert(bq);
252
253 if (bq->current_length >= bq->tlength)
254 return 0;
255
256 l = bq->tlength - bq->current_length;
257 assert(l);
258
259 return (l >= bq->minreq) ? l : 0;
260 }
261
262 void pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta) {
263 pa_memchunk rchunk;
264 assert(bq && chunk && bq->base);
265
266 if (bq->base == 1) {
267 pa_memblockq_push(bq, chunk, delta);
268 return;
269 }
270
271 if (!bq->mcalign) {
272 bq->mcalign = pa_mcalign_new(bq->base, bq->memblock_stat);
273 assert(bq->mcalign);
274 }
275
276 pa_mcalign_push(bq->mcalign, chunk);
277
278 while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
279 pa_memblockq_push(bq, &rchunk, delta);
280 pa_memblock_unref(rchunk.memblock);
281 delta = 0;
282 }
283 }
284
285 uint32_t pa_memblockq_get_minreq(pa_memblockq *bq) {
286 assert(bq);
287 return bq->minreq;
288 }
289
290 void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
291 assert(bq);
292 bq->prebuf = 0;
293 }
294
295 void pa_memblockq_prebuf_reenable(pa_memblockq *bq) {
296 assert(bq);
297 bq->prebuf = bq->orig_prebuf;
298 }
299
300 void pa_memblockq_seek(pa_memblockq *bq, size_t length) {
301 assert(bq);
302
303 if (!length)
304 return;
305
306 while (length >= bq->base) {
307 size_t l = length;
308 if (!bq->current_length)
309 return;
310
311 assert(bq->blocks_tail);
312
313 if (l > bq->blocks_tail->chunk.length)
314 l = bq->blocks_tail->chunk.length;
315
316 bq->blocks_tail->chunk.length -= l;
317 bq->current_length -= l;
318
319 if (bq->blocks_tail->chunk.length == 0)
320 remove_block(bq, bq->blocks);
321
322 length -= l;
323 }
324 }
325
326 void pa_memblockq_flush(pa_memblockq *bq) {
327 struct memblock_list *l;
328 assert(bq);
329
330 while ((l = bq->blocks)) {
331 bq->blocks = l->next;
332 pa_memblock_unref(l->chunk.memblock);
333 pa_xfree(l);
334 }
335
336 bq->blocks_tail = NULL;
337 bq->n_blocks = 0;
338 bq->current_length = 0;
339 }
340
341 uint32_t pa_memblockq_get_tlength(pa_memblockq *bq) {
342 assert(bq);
343 return bq->tlength;
344 }