]>
code.delx.au - pulseaudio/blob - src/polypcore/memblockq.c
517495ebba8de9d3c780c8ffcb68e05750548b44
4 This file is part of polypaudio.
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.
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.
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
33 #include <polypcore/xmalloc.h>
34 #include <polypcore/log.h>
35 #include <polypcore/mcalign.h>
37 #include "memblockq.h"
39 struct memblock_list
{
40 struct memblock_list
*next
, *prev
;
46 struct memblock_list
*blocks
, *blocks_tail
;
48 size_t maxlength
, tlength
, base
, prebuf
, minreq
;
49 int64_t read_index
, write_index
;
50 enum { PREBUF
, RUNNING
} state
;
51 pa_memblock_stat
*memblock_stat
;
56 pa_memblockq
* pa_memblockq_new(
64 pa_memblock_stat
*s
) {
69 assert(maxlength
>= base
);
71 bq
= pa_xnew(pa_memblockq
, 1);
72 bq
->blocks
= bq
->blocks_tail
= NULL
;
76 bq
->read_index
= bq
->write_index
= idx
;
77 bq
->memblock_stat
= s
;
79 pa_log_debug(__FILE__
": memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
80 (unsigned long)maxlength
, (unsigned long)tlength
, (unsigned long)base
, (unsigned long)prebuf
, (unsigned long)minreq
);
82 bq
->maxlength
= ((maxlength
+base
-1)/base
)*base
;
83 assert(bq
->maxlength
>= base
);
85 bq
->tlength
= ((tlength
+base
-1)/base
)*base
;
86 if (!bq
->tlength
|| bq
->tlength
>= bq
->maxlength
)
87 bq
->tlength
= bq
->maxlength
;
89 bq
->prebuf
= (prebuf
== (size_t) -1) ? bq
->tlength
/2 : prebuf
;
90 bq
->prebuf
= ((bq
->prebuf
+base
-1)/base
)*base
;
91 if (bq
->prebuf
> bq
->maxlength
)
92 bq
->prebuf
= bq
->maxlength
;
94 bq
->minreq
= (minreq
/base
)*base
;
96 if (bq
->minreq
> bq
->tlength
- bq
->prebuf
)
97 bq
->minreq
= bq
->tlength
- bq
->prebuf
;
102 pa_log_debug(__FILE__
": memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
103 (unsigned long)bq
->maxlength
, (unsigned long)bq
->tlength
, (unsigned long)bq
->base
, (unsigned long)bq
->prebuf
, (unsigned long)bq
->minreq
);
105 bq
->state
= bq
->prebuf
? PREBUF
: RUNNING
;
106 bq
->silence
= silence
? pa_memblock_ref(silence
) : NULL
;
112 void pa_memblockq_free(pa_memblockq
* bq
) {
115 pa_memblockq_flush(bq
);
118 pa_memblock_unref(bq
->silence
);
121 pa_mcalign_free(bq
->mcalign
);
126 static void drop_block(pa_memblockq
*bq
, struct memblock_list
*q
) {
130 assert(bq
->n_blocks
>= 1);
133 q
->prev
->next
= q
->next
;
135 bq
->blocks
= q
->next
;
138 q
->next
->prev
= q
->prev
;
140 bq
->blocks_tail
= q
->prev
;
142 pa_memblock_unref(q
->chunk
.memblock
);
148 static int can_push(pa_memblockq
*bq
, size_t l
) {
153 if (bq
->read_index
> bq
->write_index
) {
154 size_t d
= bq
->read_index
- bq
->write_index
;
162 end
= bq
->blocks_tail
? bq
->blocks_tail
->index
+ bq
->blocks_tail
->chunk
.length
: 0;
164 /* Make sure that the list doesn't get too long */
165 if (bq
->write_index
+ (int64_t)l
> end
)
166 if (bq
->write_index
+ l
- bq
->read_index
> bq
->maxlength
)
172 int pa_memblockq_push(pa_memblockq
* bq
, const pa_memchunk
*uchunk
) {
174 struct memblock_list
*q
, *n
;
179 assert(uchunk
->memblock
);
180 assert(uchunk
->length
> 0);
181 assert(uchunk
->index
+ uchunk
->length
<= uchunk
->memblock
->length
);
183 if (uchunk
->length
% bq
->base
)
186 if (!can_push(bq
, uchunk
->length
))
191 if (bq
->read_index
> bq
->write_index
) {
193 /* We currently have a buffer underflow, we need to drop some
196 size_t d
= bq
->read_index
- bq
->write_index
;
198 if (chunk
.length
> d
) {
201 bq
->write_index
= bq
->read_index
;
203 /* We drop the incoming data completely */
204 bq
->write_index
+= chunk
.length
;
209 /* We go from back to front to look for the right place to add
210 * this new entry. Drop data we will overwrite on the way */
215 if (bq
->write_index
>= q
->index
+ (int64_t)q
->chunk
.length
)
216 /* We found the entry where we need to place the new entry immediately after */
218 else if (bq
->write_index
+ (int64_t)chunk
.length
<= q
->index
) {
219 /* This entry isn't touched at all, let's skip it */
221 } else if (bq
->write_index
<= q
->index
&&
222 bq
->write_index
+ chunk
.length
>= q
->index
+ q
->chunk
.length
) {
224 /* This entry is fully replaced by the new entry, so let's drop it */
226 struct memblock_list
*p
;
230 } else if (bq
->write_index
>= q
->index
) {
231 /* The write index points into this memblock, so let's
232 * truncate or split it */
234 if (bq
->write_index
+ chunk
.length
< q
->index
+ q
->chunk
.length
) {
236 /* We need to save the end of this memchunk */
237 struct memblock_list
*p
;
240 /* Create a new list entry for the end of thie memchunk */
241 p
= pa_xnew(struct memblock_list
, 1);
243 pa_memblock_ref(p
->chunk
.memblock
);
245 /* Calculate offset */
246 d
= bq
->write_index
+ chunk
.length
- q
->index
;
249 /* Drop it from the new entry */
250 p
->index
= q
->index
+ d
;
251 p
->chunk
.length
-= d
;
253 /* Add it to the list */
255 if ((p
->next
= q
->next
))
264 /* Truncate the chunk */
265 if (!(q
->chunk
.length
= bq
->write_index
- q
->index
)) {
266 struct memblock_list
*p
;
272 /* We had to truncate this block, hence we're now at the right position */
277 assert(bq
->write_index
+ (int64_t)chunk
.length
> q
->index
&&
278 bq
->write_index
+ (int64_t)chunk
.length
< q
->index
+ (int64_t)q
->chunk
.length
&&
279 bq
->write_index
< q
->index
);
281 /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
283 d
= bq
->write_index
+ chunk
.length
- q
->index
;
286 q
->chunk
.length
-= d
;
294 assert(bq
->write_index
>= q
->index
+ (int64_t)q
->chunk
.length
);
295 assert(!q
->next
|| (bq
->write_index
+ (int64_t)chunk
.length
<= q
->next
->index
));
297 /* Try to merge memory blocks */
299 if (q
->chunk
.memblock
== chunk
.memblock
&&
300 q
->chunk
.index
+ (int64_t)q
->chunk
.length
== chunk
.index
&&
301 bq
->write_index
== q
->index
+ (int64_t)q
->chunk
.length
) {
303 q
->chunk
.length
+= chunk
.length
;
304 bq
->write_index
+= chunk
.length
;
308 assert(!bq
->blocks
|| (bq
->write_index
+ (int64_t)chunk
.length
<= bq
->blocks
->index
));
311 n
= pa_xnew(struct memblock_list
, 1);
313 pa_memblock_ref(n
->chunk
.memblock
);
314 n
->index
= bq
->write_index
;
315 bq
->write_index
+= n
->chunk
.length
;
317 n
->next
= q
? q
->next
: bq
->blocks
;
334 int pa_memblockq_peek(pa_memblockq
* bq
, pa_memchunk
*chunk
) {
338 if (bq
->state
== PREBUF
) {
340 /* We need to pre-buffer */
341 if (pa_memblockq_get_length(bq
) < bq
->prebuf
)
346 } else if (bq
->prebuf
> 0 && bq
->read_index
>= bq
->write_index
) {
348 /* Buffer underflow protection */
353 /* Do we need to spit out silence? */
354 if (!bq
->blocks
|| bq
->blocks
->index
> bq
->read_index
) {
358 /* How much silence shall we return? */
359 length
= bq
->blocks
? bq
->blocks
->index
- bq
->read_index
: 0;
361 /* We need to return silence, since no data is yet available */
363 chunk
->memblock
= pa_memblock_ref(bq
->silence
);
365 if (!length
|| length
> chunk
->memblock
->length
)
366 length
= chunk
->memblock
->length
;
368 chunk
->length
= length
;
370 chunk
->memblock
= NULL
;
371 chunk
->length
= length
;
378 /* Ok, let's pass real data to the caller */
379 assert(bq
->blocks
->index
== bq
->read_index
);
381 *chunk
= bq
->blocks
->chunk
;
382 pa_memblock_ref(chunk
->memblock
);
387 void pa_memblockq_drop(pa_memblockq
*bq
, const pa_memchunk
*chunk
, size_t length
) {
389 assert(length
% bq
->base
== 0);
391 assert(!chunk
|| length
<= chunk
->length
);
395 if (bq
->blocks
&& bq
->blocks
->index
== bq
->read_index
) {
396 /* The first item in queue is valid */
398 /* Does the chunk match with what the user supplied us? */
399 if (memcmp(chunk
, &bq
->blocks
->chunk
, sizeof(pa_memchunk
)) != 0)
405 /* The first item in the queue is not yet relevant */
407 assert(!bq
->blocks
|| bq
->blocks
->index
> bq
->read_index
);
408 l
= bq
->blocks
? bq
->blocks
->index
- bq
->read_index
: 0;
412 if (!l
|| l
> bq
->silence
->length
)
413 l
= bq
->silence
->length
;
417 /* Do the entries still match? */
418 if (chunk
->index
!= 0 || chunk
->length
!= l
|| chunk
->memblock
!= bq
->silence
)
428 assert(bq
->blocks
->index
>= bq
->read_index
);
430 d
= (size_t) (bq
->blocks
->index
- bq
->read_index
);
433 /* The first block is too far in the future */
435 bq
->read_index
+= length
;
443 assert(bq
->blocks
->index
== bq
->read_index
);
445 if (bq
->blocks
->chunk
.length
<= length
) {
446 /* We need to drop the full block */
448 length
-= bq
->blocks
->chunk
.length
;
449 bq
->read_index
+= bq
->blocks
->chunk
.length
;
451 drop_block(bq
, bq
->blocks
);
453 /* Only the start of this block needs to be dropped */
455 bq
->blocks
->chunk
.index
+= length
;
456 bq
->blocks
->chunk
.length
-= length
;
457 bq
->blocks
->index
+= length
;
458 bq
->read_index
+= length
;
464 /* The list is empty, there's nothing we could drop */
465 bq
->read_index
+= length
;
471 int pa_memblockq_is_readable(pa_memblockq
*bq
) {
474 if (bq
->prebuf
> 0) {
475 size_t l
= pa_memblockq_get_length(bq
);
477 if (bq
->state
== PREBUF
&& l
< bq
->prebuf
)
487 int pa_memblockq_is_writable(pa_memblockq
*bq
, size_t length
) {
490 if (length
% bq
->base
)
493 return pa_memblockq_get_length(bq
) + length
<= bq
->tlength
;
496 size_t pa_memblockq_get_length(pa_memblockq
*bq
) {
499 if (bq
->write_index
<= bq
->read_index
)
502 return (size_t) (bq
->write_index
- bq
->read_index
);
505 size_t pa_memblockq_missing(pa_memblockq
*bq
) {
509 if ((l
= pa_memblockq_get_length(bq
)) >= bq
->tlength
)
513 return (l
>= bq
->minreq
) ? l
: 0;
516 size_t pa_memblockq_get_minreq(pa_memblockq
*bq
) {
522 void pa_memblockq_seek(pa_memblockq
*bq
, int64_t offset
, pa_seek_mode_t seek
) {
526 case PA_SEEK_RELATIVE
:
527 bq
->write_index
+= offset
;
529 case PA_SEEK_ABSOLUTE
:
530 bq
->write_index
= offset
;
532 case PA_SEEK_RELATIVE_ON_READ
:
533 bq
->write_index
= bq
->read_index
+ offset
;
535 case PA_SEEK_RELATIVE_END
:
536 bq
->write_index
= (bq
->blocks_tail
? bq
->blocks_tail
->index
+ (int64_t)bq
->blocks_tail
->chunk
.length
: bq
->read_index
) + offset
;
543 void pa_memblockq_flush(pa_memblockq
*bq
) {
547 drop_block(bq
, bq
->blocks
);
549 assert(bq
->n_blocks
== 0);
551 pa_memblockq_prebuf_force(bq
);
554 size_t pa_memblockq_get_tlength(pa_memblockq
*bq
) {
560 int64_t pa_memblockq_get_read_index(pa_memblockq
*bq
) {
562 return bq
->read_index
;
565 int64_t pa_memblockq_get_write_index(pa_memblockq
*bq
) {
567 return bq
->write_index
;
570 int pa_memblockq_push_align(pa_memblockq
* bq
, const pa_memchunk
*chunk
) {
574 assert(chunk
&& bq
->base
);
577 return pa_memblockq_push(bq
, chunk
);
580 bq
->mcalign
= pa_mcalign_new(bq
->base
, bq
->memblock_stat
);
582 if (!can_push(bq
, pa_mcalign_csize(bq
->mcalign
, chunk
->length
)))
585 pa_mcalign_push(bq
->mcalign
, chunk
);
587 while (pa_mcalign_pop(bq
->mcalign
, &rchunk
) >= 0) {
589 r
= pa_memblockq_push(bq
, &rchunk
);
590 pa_memblock_unref(rchunk
.memblock
);
599 void pa_memblockq_shorten(pa_memblockq
*bq
, size_t length
) {
603 l
= pa_memblockq_get_length(bq
);
606 pa_memblockq_drop(bq
, NULL
, l
- length
);
609 void pa_memblockq_prebuf_disable(pa_memblockq
*bq
) {
612 if (bq
->state
== PREBUF
)
616 void pa_memblockq_prebuf_force(pa_memblockq
*bq
) {
619 if (bq
->state
== RUNNING
&& bq
->prebuf
> 0)