]>
code.delx.au - pulseaudio/blob - src/polypcore/memblockq.c
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 <polyp/xmalloc.h>
35 #include <polypcore/log.h>
36 #include <polypcore/mcalign.h>
38 #include "memblockq.h"
40 struct memblock_list
{
41 struct memblock_list
*next
, *prev
;
47 struct memblock_list
*blocks
, *blocks_tail
;
49 size_t maxlength
, tlength
, base
, prebuf
, minreq
;
50 int64_t read_index
, write_index
;
51 enum { PREBUF
, RUNNING
} state
;
52 pa_memblock_stat
*memblock_stat
;
57 pa_memblockq
* pa_memblockq_new(
65 pa_memblock_stat
*s
) {
70 assert(maxlength
>= base
);
72 bq
= pa_xnew(pa_memblockq
, 1);
73 bq
->blocks
= bq
->blocks_tail
= NULL
;
77 bq
->read_index
= bq
->write_index
= idx
;
78 bq
->memblock_stat
= s
;
80 pa_log_debug(__FILE__
": memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
81 (unsigned long)maxlength
, (unsigned long)tlength
, (unsigned long)base
, (unsigned long)prebuf
, (unsigned long)minreq
);
83 bq
->maxlength
= ((maxlength
+base
-1)/base
)*base
;
84 assert(bq
->maxlength
>= base
);
86 bq
->tlength
= ((tlength
+base
-1)/base
)*base
;
87 if (!bq
->tlength
|| bq
->tlength
>= bq
->maxlength
)
88 bq
->tlength
= bq
->maxlength
;
90 bq
->prebuf
= (prebuf
== (size_t) -1) ? bq
->tlength
/2 : prebuf
;
91 bq
->prebuf
= ((bq
->prebuf
+base
-1)/base
)*base
;
92 if (bq
->prebuf
> bq
->maxlength
)
93 bq
->prebuf
= bq
->maxlength
;
95 bq
->minreq
= (minreq
/base
)*base
;
97 if (bq
->minreq
> bq
->tlength
- bq
->prebuf
)
98 bq
->minreq
= bq
->tlength
- bq
->prebuf
;
103 pa_log_debug(__FILE__
": memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
104 (unsigned long)bq
->maxlength
, (unsigned long)bq
->tlength
, (unsigned long)bq
->base
, (unsigned long)bq
->prebuf
, (unsigned long)bq
->minreq
);
106 bq
->state
= bq
->prebuf
? PREBUF
: RUNNING
;
107 bq
->silence
= silence
? pa_memblock_ref(silence
) : NULL
;
113 void pa_memblockq_free(pa_memblockq
* bq
) {
116 pa_memblockq_flush(bq
);
119 pa_memblock_unref(bq
->silence
);
122 pa_mcalign_free(bq
->mcalign
);
127 static void drop_block(pa_memblockq
*bq
, struct memblock_list
*q
) {
131 assert(bq
->n_blocks
>= 1);
134 q
->prev
->next
= q
->next
;
136 bq
->blocks
= q
->next
;
139 q
->next
->prev
= q
->prev
;
141 bq
->blocks_tail
= q
->prev
;
143 pa_memblock_unref(q
->chunk
.memblock
);
149 static int can_push(pa_memblockq
*bq
, size_t l
) {
154 if (bq
->read_index
> bq
->write_index
) {
155 size_t d
= bq
->read_index
- bq
->write_index
;
163 end
= bq
->blocks_tail
? bq
->blocks_tail
->index
+ bq
->blocks_tail
->chunk
.length
: 0;
165 /* Make sure that the list doesn't get too long */
166 if (bq
->write_index
+ (int64_t)l
> end
)
167 if (bq
->write_index
+ l
- bq
->read_index
> bq
->maxlength
)
173 int pa_memblockq_push(pa_memblockq
* bq
, const pa_memchunk
*uchunk
) {
175 struct memblock_list
*q
, *n
;
180 assert(uchunk
->memblock
);
181 assert(uchunk
->length
> 0);
182 assert(uchunk
->index
+ uchunk
->length
<= uchunk
->memblock
->length
);
184 if (uchunk
->length
% bq
->base
)
187 if (!can_push(bq
, uchunk
->length
))
192 if (bq
->read_index
> bq
->write_index
) {
194 /* We currently have a buffer underflow, we need to drop some
197 size_t d
= bq
->read_index
- bq
->write_index
;
199 if (chunk
.length
> d
) {
202 bq
->write_index
= bq
->read_index
;
204 /* We drop the incoming data completely */
205 bq
->write_index
+= chunk
.length
;
210 /* We go from back to front to look for the right place to add
211 * this new entry. Drop data we will overwrite on the way */
216 if (bq
->write_index
>= q
->index
+ (int64_t)q
->chunk
.length
)
217 /* We found the entry where we need to place the new entry immediately after */
219 else if (bq
->write_index
+ (int64_t)chunk
.length
<= q
->index
) {
220 /* This entry isn't touched at all, let's skip it */
222 } else if (bq
->write_index
<= q
->index
&&
223 bq
->write_index
+ chunk
.length
>= q
->index
+ q
->chunk
.length
) {
225 /* This entry is fully replaced by the new entry, so let's drop it */
227 struct memblock_list
*p
;
231 } else if (bq
->write_index
>= q
->index
) {
232 /* The write index points into this memblock, so let's
233 * truncate or split it */
235 if (bq
->write_index
+ chunk
.length
< q
->index
+ q
->chunk
.length
) {
237 /* We need to save the end of this memchunk */
238 struct memblock_list
*p
;
241 /* Create a new list entry for the end of thie memchunk */
242 p
= pa_xnew(struct memblock_list
, 1);
244 pa_memblock_ref(p
->chunk
.memblock
);
246 /* Calculate offset */
247 d
= bq
->write_index
+ chunk
.length
- q
->index
;
250 /* Drop it from the new entry */
251 p
->index
= q
->index
+ d
;
252 p
->chunk
.length
-= d
;
254 /* Add it to the list */
256 if ((p
->next
= q
->next
))
265 /* Truncate the chunk */
266 if (!(q
->chunk
.length
= bq
->write_index
- q
->index
)) {
267 struct memblock_list
*p
;
273 /* We had to truncate this block, hence we're now at the right position */
278 assert(bq
->write_index
+ (int64_t)chunk
.length
> q
->index
&&
279 bq
->write_index
+ (int64_t)chunk
.length
< q
->index
+ (int64_t)q
->chunk
.length
&&
280 bq
->write_index
< q
->index
);
282 /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
284 d
= bq
->write_index
+ chunk
.length
- q
->index
;
287 q
->chunk
.length
-= d
;
295 assert(bq
->write_index
>= q
->index
+ (int64_t)q
->chunk
.length
);
296 assert(!q
->next
|| (bq
->write_index
+ (int64_t)chunk
.length
<= q
->next
->index
));
298 /* Try to merge memory blocks */
300 if (q
->chunk
.memblock
== chunk
.memblock
&&
301 q
->chunk
.index
+ (int64_t)q
->chunk
.length
== chunk
.index
&&
302 bq
->write_index
== q
->index
+ (int64_t)q
->chunk
.length
) {
304 q
->chunk
.length
+= chunk
.length
;
305 bq
->write_index
+= chunk
.length
;
309 assert(!bq
->blocks
|| (bq
->write_index
+ (int64_t)chunk
.length
<= bq
->blocks
->index
));
312 n
= pa_xnew(struct memblock_list
, 1);
314 pa_memblock_ref(n
->chunk
.memblock
);
315 n
->index
= bq
->write_index
;
316 bq
->write_index
+= n
->chunk
.length
;
318 n
->next
= q
? q
->next
: bq
->blocks
;
335 int pa_memblockq_peek(pa_memblockq
* bq
, pa_memchunk
*chunk
) {
339 if (bq
->state
== PREBUF
) {
341 /* We need to pre-buffer */
342 if (pa_memblockq_get_length(bq
) < bq
->prebuf
)
347 } else if (bq
->prebuf
> 0 && bq
->read_index
>= bq
->write_index
) {
349 /* Buffer underflow protection */
354 /* Do we need to spit out silence? */
355 if (!bq
->blocks
|| bq
->blocks
->index
> bq
->read_index
) {
359 /* How much silence shall we return? */
360 length
= bq
->blocks
? bq
->blocks
->index
- bq
->read_index
: 0;
362 /* We need to return silence, since no data is yet available */
364 chunk
->memblock
= pa_memblock_ref(bq
->silence
);
366 if (!length
|| length
> chunk
->memblock
->length
)
367 length
= chunk
->memblock
->length
;
369 chunk
->length
= length
;
371 chunk
->memblock
= NULL
;
372 chunk
->length
= length
;
379 /* Ok, let's pass real data to the caller */
380 assert(bq
->blocks
->index
== bq
->read_index
);
382 *chunk
= bq
->blocks
->chunk
;
383 pa_memblock_ref(chunk
->memblock
);
388 void pa_memblockq_drop(pa_memblockq
*bq
, const pa_memchunk
*chunk
, size_t length
) {
390 assert(length
% bq
->base
== 0);
392 assert(!chunk
|| length
<= chunk
->length
);
396 if (bq
->blocks
&& bq
->blocks
->index
== bq
->read_index
) {
397 /* The first item in queue is valid */
399 /* Does the chunk match with what the user supplied us? */
400 if (memcmp(chunk
, &bq
->blocks
->chunk
, sizeof(pa_memchunk
)) != 0)
406 /* The first item in the queue is not yet relevant */
408 assert(!bq
->blocks
|| bq
->blocks
->index
> bq
->read_index
);
409 l
= bq
->blocks
? bq
->blocks
->index
- bq
->read_index
: 0;
413 if (!l
|| l
> bq
->silence
->length
)
414 l
= bq
->silence
->length
;
418 /* Do the entries still match? */
419 if (chunk
->index
!= 0 || chunk
->length
!= l
|| chunk
->memblock
!= bq
->silence
)
429 assert(bq
->blocks
->index
>= bq
->read_index
);
431 d
= (size_t) (bq
->blocks
->index
- bq
->read_index
);
434 /* The first block is too far in the future */
436 bq
->read_index
+= length
;
444 assert(bq
->blocks
->index
== bq
->read_index
);
446 if (bq
->blocks
->chunk
.length
<= length
) {
447 /* We need to drop the full block */
449 length
-= bq
->blocks
->chunk
.length
;
450 bq
->read_index
+= bq
->blocks
->chunk
.length
;
452 drop_block(bq
, bq
->blocks
);
454 /* Only the start of this block needs to be dropped */
456 bq
->blocks
->chunk
.index
+= length
;
457 bq
->blocks
->chunk
.length
-= length
;
458 bq
->blocks
->index
+= length
;
459 bq
->read_index
+= length
;
465 /* The list is empty, there's nothing we could drop */
466 bq
->read_index
+= length
;
472 int pa_memblockq_is_readable(pa_memblockq
*bq
) {
475 if (bq
->prebuf
> 0) {
476 size_t l
= pa_memblockq_get_length(bq
);
478 if (bq
->state
== PREBUF
&& l
< bq
->prebuf
)
488 int pa_memblockq_is_writable(pa_memblockq
*bq
, size_t length
) {
491 if (length
% bq
->base
)
494 return pa_memblockq_get_length(bq
) + length
<= bq
->tlength
;
497 size_t pa_memblockq_get_length(pa_memblockq
*bq
) {
500 if (bq
->write_index
<= bq
->read_index
)
503 return (size_t) (bq
->write_index
- bq
->read_index
);
506 size_t pa_memblockq_missing(pa_memblockq
*bq
) {
510 if ((l
= pa_memblockq_get_length(bq
)) >= bq
->tlength
)
514 return (l
>= bq
->minreq
) ? l
: 0;
517 size_t pa_memblockq_get_minreq(pa_memblockq
*bq
) {
523 void pa_memblockq_seek(pa_memblockq
*bq
, int64_t offset
, pa_seek_mode_t seek
) {
527 case PA_SEEK_RELATIVE
:
528 bq
->write_index
+= offset
;
530 case PA_SEEK_ABSOLUTE
:
531 bq
->write_index
= offset
;
533 case PA_SEEK_RELATIVE_ON_READ
:
534 bq
->write_index
= bq
->read_index
+ offset
;
536 case PA_SEEK_RELATIVE_END
:
537 bq
->write_index
= (bq
->blocks_tail
? bq
->blocks_tail
->index
+ (int64_t)bq
->blocks_tail
->chunk
.length
: bq
->read_index
) + offset
;
544 void pa_memblockq_flush(pa_memblockq
*bq
) {
548 drop_block(bq
, bq
->blocks
);
550 assert(bq
->n_blocks
== 0);
552 bq
->write_index
= bq
->read_index
;
554 pa_memblockq_prebuf_force(bq
);
557 size_t pa_memblockq_get_tlength(pa_memblockq
*bq
) {
563 int64_t pa_memblockq_get_read_index(pa_memblockq
*bq
) {
565 return bq
->read_index
;
568 int64_t pa_memblockq_get_write_index(pa_memblockq
*bq
) {
570 return bq
->write_index
;
573 int pa_memblockq_push_align(pa_memblockq
* bq
, const pa_memchunk
*chunk
) {
577 assert(chunk
&& bq
->base
);
580 return pa_memblockq_push(bq
, chunk
);
583 bq
->mcalign
= pa_mcalign_new(bq
->base
, bq
->memblock_stat
);
585 if (!can_push(bq
, pa_mcalign_csize(bq
->mcalign
, chunk
->length
)))
588 pa_mcalign_push(bq
->mcalign
, chunk
);
590 while (pa_mcalign_pop(bq
->mcalign
, &rchunk
) >= 0) {
592 r
= pa_memblockq_push(bq
, &rchunk
);
593 pa_memblock_unref(rchunk
.memblock
);
602 void pa_memblockq_shorten(pa_memblockq
*bq
, size_t length
) {
606 l
= pa_memblockq_get_length(bq
);
609 pa_memblockq_drop(bq
, NULL
, l
- length
);
612 void pa_memblockq_prebuf_disable(pa_memblockq
*bq
) {
615 if (bq
->state
== PREBUF
)
619 void pa_memblockq_prebuf_force(pa_memblockq
*bq
) {
622 if (bq
->state
== RUNNING
&& bq
->prebuf
> 0)
626 size_t pa_memblockq_get_maxlength(pa_memblockq
*bq
) {
629 return bq
->maxlength
;
632 size_t pa_memblockq_get_prebuf(pa_memblockq
*bq
) {