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 published
8  by the Free Software Foundation; either version 2.1 of the License,
9  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  General Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public License
17  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
30#include <pulsecore/log.h>
31#include <pulsecore/macro.h>
32#include <pulsecore/flist.h>
33
34#include "memblockq.h"
35
36/* #define MEMBLOCKQ_DEBUG */
37pa_memblockq* pa_memblockq_new(
38        const char *name,
39        int64_t idx,
40        size_t maxlength,
41        size_t tlength,
42        const pa_sample_spec *sample_spec,
43        size_t prebuf,
44        size_t minreq,
45        size_t maxrewind,
46        pa_memchunk *silence) {
47
48    pa_memblockq* bq;
49
50    pa_assert(sample_spec);
51    pa_assert(name);
52
53    bq = pa_xnew0(pa_memblockq, 1);
54    bq->name = pa_xstrdup(name);
55
56    bq->sample_spec = *sample_spec;
57    bq->base = pa_frame_size(sample_spec);
58    bq->read_index = bq->write_index = idx;
59
60    pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
61                 (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) bq->base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind);
62
63    bq->in_prebuf = true;
64
65    pa_memblockq_set_maxlength(bq, maxlength);
66    pa_memblockq_set_tlength(bq, tlength);
67    pa_memblockq_set_minreq(bq, minreq);
68    pa_memblockq_set_prebuf(bq, prebuf);
69    pa_memblockq_set_maxrewind(bq, maxrewind);
70
71    pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
72                 (unsigned long) bq->maxlength, (unsigned long) bq->tlength, (unsigned long) bq->base, (unsigned long) bq->prebuf, (unsigned long) bq->minreq, (unsigned long) bq->maxrewind);
73
74    if (silence) {
75        bq->silence = *silence;
76        pa_memblock_ref(bq->silence.memblock);
77    }
78
79    bq->mcalign = pa_mcalign_new(bq->base);
80
81    return bq;
82}
83
84void pa_memblockq_free(pa_memblockq* bq) {
85    pa_assert(bq);
86
87    pa_memblockq_silence(bq);
88
89    if (bq->silence.memblock)
90        pa_memblock_unref(bq->silence.memblock);
91
92    if (bq->mcalign)
93        pa_mcalign_free(bq->mcalign);
94
95    pa_xfree(bq->name);
96    pa_xfree(bq);
97}
98
99static void fix_current_read(pa_memblockq *bq) {
100    pa_assert(bq);
101
102    if (PA_UNLIKELY(!bq->blocks)) {
103        bq->current_read = NULL;
104        return;
105    }
106
107    if (PA_UNLIKELY(!bq->current_read))
108        bq->current_read = bq->blocks;
109
110    /* Scan left */
111    while (PA_UNLIKELY(bq->current_read->index > bq->read_index))
112
113        if (bq->current_read->prev)
114            bq->current_read = bq->current_read->prev;
115        else
116            break;
117
118    /* Scan right */
119    while (PA_LIKELY(bq->current_read != NULL) && PA_UNLIKELY(bq->current_read->index + (int64_t) bq->current_read->chunk.length <= bq->read_index))
120        bq->current_read = bq->current_read->next;
121
122    /* At this point current_read will either point at or left of the
123       next block to play. It may be NULL in case everything in
124       the queue was already played */
125}
126
127static void fix_current_write(pa_memblockq *bq) {
128    pa_assert(bq);
129
130    if (PA_UNLIKELY(!bq->blocks)) {
131        bq->current_write = NULL;
132        return;
133    }
134
135    if (PA_UNLIKELY(!bq->current_write))
136        bq->current_write = bq->blocks_tail;
137
138    /* Scan right */
139    while (PA_UNLIKELY(bq->current_write->index + (int64_t) bq->current_write->chunk.length <= bq->write_index))
140
141        if (bq->current_write->next)
142            bq->current_write = bq->current_write->next;
143        else
144            break;
145
146    /* Scan left */
147    while (PA_LIKELY(bq->current_write != NULL) && PA_UNLIKELY(bq->current_write->index > bq->write_index))
148        bq->current_write = bq->current_write->prev;
149
150    /* At this point current_write will either point at or right of
151       the next block to write data to. It may be NULL in case
152       everything in the queue is still to be played */
153}
154
155static bool can_push(pa_memblockq *bq, size_t l) {
156    int64_t end;
157
158    pa_assert(bq);
159
160    if (bq->read_index > bq->write_index) {
161        int64_t d = bq->read_index - bq->write_index;
162
163        if ((int64_t) l > d)
164            l -= (size_t) d;
165        else
166            return true;
167    }
168
169    end = bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->write_index;
170
171    /* Make sure that the list doesn't get too long */
172    if (bq->write_index + (int64_t) l > end)
173        if (bq->write_index + (int64_t) l - bq->read_index > (int64_t) bq->maxlength)
174            return false;
175
176    return true;
177}
178
179static void write_index_changed(pa_memblockq *bq, int64_t old_write_index, bool account) {
180    int64_t delta;
181
182    pa_assert(bq);
183
184    delta = bq->write_index - old_write_index;
185
186    if (account)
187        bq->requested -= delta;
188    else
189        bq->missing -= delta;
190
191#ifdef MEMBLOCKQ_DEBUG
192     pa_log_debug("[%s] pushed/seeked %lli: requested counter at %lli, account=%i", bq->name, (long long) delta, (long long) bq->requested, account);
193#endif
194}
195
196static void read_index_changed(pa_memblockq *bq, int64_t old_read_index) {
197    int64_t delta;
198
199    pa_assert(bq);
200
201    delta = bq->read_index - old_read_index;
202    bq->missing += delta;
203
204#ifdef MEMBLOCKQ_DEBUG
205    pa_log_debug("[%s] popped %lli: missing counter at %lli", bq->name, (long long) delta, (long long) bq->missing);
206#endif
207}
208
209int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
210    struct list_item *q, *n;
211    pa_memchunk chunk;
212    int64_t old;
213
214    pa_assert(bq);
215    pa_assert(uchunk);
216    pa_assert(uchunk->memblock);
217    pa_assert(uchunk->length > 0);
218    pa_assert(uchunk->index + uchunk->length <= pa_memblock_get_length(uchunk->memblock));
219
220    pa_assert(uchunk->length % bq->base == 0);
221    pa_assert(uchunk->index % bq->base == 0);
222
223    if (!can_push(bq, uchunk->length))
224        return -1;
225
226    old = bq->write_index;
227    chunk = *uchunk;
228
229    fix_current_write(bq);
230    q = bq->current_write;
231
232    /* First we advance the q pointer right of where we want to
233     * write to */
234
235    if (q) {
236        while (bq->write_index + (int64_t) chunk.length > q->index)
237            if (q->next)
238                q = q->next;
239            else
240                break;
241    }
242
243    if (!q)
244        q = bq->blocks_tail;
245
246    /* We go from back to front to look for the right place to add
247     * this new entry. Drop data we will overwrite on the way */
248
249    while (q) {
250
251        if (bq->write_index >= q->index + (int64_t) q->chunk.length)
252            /* We found the entry where we need to place the new entry immediately after */
253            break;
254        else if (bq->write_index + (int64_t) chunk.length <= q->index) {
255            /* This entry isn't touched at all, let's skip it */
256            q = q->prev;
257        } else if (bq->write_index <= q->index &&
258                   bq->write_index + (int64_t) chunk.length >= q->index + (int64_t) q->chunk.length) {
259
260            /* This entry is fully replaced by the new entry, so let's drop it */
261
262            struct list_item *p;
263            p = q;
264            q = q->prev;
265            drop_block(bq, p);
266        } else if (bq->write_index >= q->index) {
267            /* The write index points into this memblock, so let's
268             * truncate or split it */
269
270            if (bq->write_index + (int64_t) chunk.length < q->index + (int64_t) q->chunk.length) {
271
272                /* We need to save the end of this memchunk */
273                struct list_item *p;
274                size_t d;
275
276                /* Create a new list entry for the end of the memchunk */
277                if (!(p = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
278                    p = pa_xnew(struct list_item, 1);
279
280                p->chunk = q->chunk;
281                pa_memblock_ref(p->chunk.memblock);
282
283                /* Calculate offset */
284                d = (size_t) (bq->write_index + (int64_t) chunk.length - q->index);
285                pa_assert(d > 0);
286
287                /* Drop it from the new entry */
288                p->index = q->index + (int64_t) d;
289                p->chunk.length -= d;
290
291                /* Add it to the list */
292                p->prev = q;
293                if ((p->next = q->next))
294                    q->next->prev = p;
295                else
296                    bq->blocks_tail = p;
297                q->next = p;
298
299                bq->n_blocks++;
300            }
301
302            /* Truncate the chunk */
303            if (!(q->chunk.length = (size_t) (bq->write_index - q->index))) {
304                struct list_item *p;
305                p = q;
306                q = q->prev;
307                drop_block(bq, p);
308            }
309
310            /* We had to truncate this block, hence we're now at the right position */
311            break;
312        } else {
313            size_t d;
314
315            pa_assert(bq->write_index + (int64_t)chunk.length > q->index &&
316                      bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length &&
317                      bq->write_index < q->index);
318
319            /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
320
321            d = (size_t) (bq->write_index + (int64_t) chunk.length - q->index);
322            q->index += (int64_t) d;
323            q->chunk.index += d;
324            q->chunk.length -= d;
325
326            q = q->prev;
327        }
328    }
329
330    if (q) {
331        pa_assert(bq->write_index >=  q->index + (int64_t)q->chunk.length);
332        pa_assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index));
333
334        /* Try to merge memory blocks */
335
336        if (q->chunk.memblock == chunk.memblock &&
337            q->chunk.index + q->chunk.length == chunk.index &&
338            bq->write_index == q->index + (int64_t) q->chunk.length) {
339
340            q->chunk.length += chunk.length;
341            bq->write_index += (int64_t) chunk.length;
342            goto finish;
343        }
344    } else
345        pa_assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index));
346
347    if (!(n = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
348        n = pa_xnew(struct list_item, 1);
349
350    n->chunk = chunk;
351    pa_memblock_ref(n->chunk.memblock);
352    n->index = bq->write_index;
353    bq->write_index += (int64_t) n->chunk.length;
354
355    n->next = q ? q->next : bq->blocks;
356    n->prev = q;
357
358    if (n->next)
359        n->next->prev = n;
360    else
361        bq->blocks_tail = n;
362
363    if (n->prev)
364        n->prev->next = n;
365    else
366        bq->blocks = n;
367
368    bq->n_blocks++;
369
370finish:
371
372    write_index_changed(bq, old, true);
373    return 0;
374}
375
376bool pa_memblockq_prebuf_active(pa_memblockq *bq) {
377    pa_assert(bq);
378
379    if (bq->in_prebuf)
380        return pa_memblockq_get_length(bq) < bq->prebuf;
381    else
382        return bq->prebuf > 0 && bq->read_index >= bq->write_index;
383}
384
385static bool update_prebuf(pa_memblockq *bq) {
386    pa_assert(bq);
387
388    if (bq->in_prebuf) {
389
390        if (pa_memblockq_get_length(bq) < bq->prebuf)
391            return true;
392
393        bq->in_prebuf = false;
394        return false;
395    } else {
396
397        if (bq->prebuf > 0 && bq->read_index >= bq->write_index) {
398            bq->in_prebuf = true;
399            return true;
400        }
401
402        return false;
403    }
404}
405
406int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
407    int64_t d;
408    pa_assert(bq);
409    pa_assert(chunk);
410
411    /* We need to pre-buffer */
412    if (update_prebuf(bq))
413        return -1;
414
415    fix_current_read(bq);
416
417    /* Do we need to spit out silence? */
418    if (!bq->current_read || bq->current_read->index > bq->read_index) {
419        size_t length;
420
421        /* How much silence shall we return? */
422        if (bq->current_read)
423            length = (size_t) (bq->current_read->index - bq->read_index);
424        else if (bq->write_index > bq->read_index)
425            length = (size_t) (bq->write_index - bq->read_index);
426        else
427            length = 0;
428
429        /* We need to return silence, since no data is yet available */
430        if (bq->silence.memblock) {
431            *chunk = bq->silence;
432            pa_memblock_ref(chunk->memblock);
433
434            if (length > 0 && length < chunk->length)
435                chunk->length = length;
436
437        } else {
438
439            /* If the memblockq is empty, return -1, otherwise return
440             * the time to sleep */
441            if (length <= 0)
442                return -1;
443
444            chunk->memblock = NULL;
445            chunk->length = length;
446        }
447
448        chunk->index = 0;
449        return 0;
450    }
451
452    /* Ok, let's pass real data to the caller */
453    *chunk = bq->current_read->chunk;
454    pa_memblock_ref(chunk->memblock);
455
456    pa_assert(bq->read_index >= bq->current_read->index);
457    d = bq->read_index - bq->current_read->index;
458    chunk->index += (size_t) d;
459    chunk->length -= (size_t) d;
460
461    return 0;
462}
463
464int pa_memblockq_peek_fixed_size(pa_memblockq *bq, size_t block_size, pa_memchunk *chunk) {
465    pa_mempool *pool;
466    pa_memchunk tchunk, rchunk;
467    int64_t ri;
468    struct list_item *item;
469
470    pa_assert(bq);
471    pa_assert(block_size > 0);
472    pa_assert(chunk);
473    pa_assert(bq->silence.memblock);
474
475    if (pa_memblockq_peek(bq, &tchunk) < 0)
476        return -1;
477
478    if (tchunk.length >= block_size) {
479        *chunk = tchunk;
480        chunk->length = block_size;
481        return 0;
482    }
483
484    pool = pa_memblock_get_pool(tchunk.memblock);
485    rchunk.memblock = pa_memblock_new(pool, block_size);
486    rchunk.index = 0;
487    rchunk.length = tchunk.length;
488    pa_mempool_unref(pool), pool = NULL;
489
490    pa_memchunk_memcpy(&rchunk, &tchunk);
491    pa_memblock_unref(tchunk.memblock);
492
493    rchunk.index += tchunk.length;
494
495    /* We don't need to call fix_current_read() here, since
496     * pa_memblock_peek() already did that */
497    item = bq->current_read;
498    ri = bq->read_index + tchunk.length;
499
500    while (rchunk.index < block_size) {
501
502        if (!item || item->index > ri) {
503            /* Do we need to append silence? */
504            tchunk = bq->silence;
505
506            if (item)
507                tchunk.length = PA_MIN(tchunk.length, (size_t) (item->index - ri));
508
509        } else {
510            int64_t d;
511
512            /* We can append real data! */
513            tchunk = item->chunk;
514
515            d = ri - item->index;
516            tchunk.index += (size_t) d;
517            tchunk.length -= (size_t) d;
518
519            /* Go to next item for the next iteration */
520            item = item->next;
521        }
522
523        rchunk.length = tchunk.length = PA_MIN(tchunk.length, block_size - rchunk.index);
524        pa_memchunk_memcpy(&rchunk, &tchunk);
525
526        rchunk.index += rchunk.length;
527        ri += rchunk.length;
528    }
529
530    rchunk.index = 0;
531    rchunk.length = block_size;
532
533    *chunk = rchunk;
534    return 0;
535}
536
537void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
538    int64_t old;
539    pa_assert(bq);
540    pa_assert(length % bq->base == 0);
541
542    old = bq->read_index;
543
544    while (length > 0) {
545
546        /* Do not drop any data when we are in prebuffering mode */
547        if (update_prebuf(bq))
548            break;
549
550        fix_current_read(bq);
551
552        if (bq->current_read) {
553            int64_t p, d;
554
555            /* We go through this piece by piece to make sure we don't
556             * drop more than allowed by prebuf */
557
558            p = bq->current_read->index + (int64_t) bq->current_read->chunk.length;
559            pa_assert(p >= bq->read_index);
560            d = p - bq->read_index;
561
562            if (d > (int64_t) length)
563                d = (int64_t) length;
564
565            bq->read_index += d;
566            length -= (size_t) d;
567
568        } else {
569
570            /* The list is empty, there's nothing we could drop */
571            bq->read_index += (int64_t) length;
572            break;
573        }
574    }
575
576    drop_backlog(bq);
577    read_index_changed(bq, old);
578}
579
580void pa_memblockq_rewind(pa_memblockq *bq, size_t length) {
581    int64_t old;
582    pa_assert(bq);
583    pa_assert(length % bq->base == 0);
584
585    old = bq->read_index;
586
587    /* This is kind of the inverse of pa_memblockq_drop() */
588
589    bq->read_index -= (int64_t) length;
590
591    read_index_changed(bq, old);
592}
593
594bool pa_memblockq_is_readable(pa_memblockq *bq) {
595    pa_assert(bq);
596
597    if (pa_memblockq_prebuf_active(bq))
598        return false;
599
600    if (pa_memblockq_get_length(bq) <= 0)
601        return false;
602
603    return true;
604}
605
606size_t pa_memblockq_get_length(pa_memblockq *bq) {
607    pa_assert(bq);
608
609    if (bq->write_index <= bq->read_index)
610        return 0;
611
612    return (size_t) (bq->write_index - bq->read_index);
613}
614
615void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek, bool account) {
616    int64_t old;
617    pa_assert(bq);
618
619    old = bq->write_index;
620
621    switch (seek) {
622        case PA_SEEK_RELATIVE:
623            bq->write_index += offset;
624            break;
625        case PA_SEEK_ABSOLUTE:
626            bq->write_index = offset;
627            break;
628        case PA_SEEK_RELATIVE_ON_READ:
629            bq->write_index = bq->read_index + offset;
630            break;
631        case PA_SEEK_RELATIVE_END:
632            bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->read_index) + offset;
633            break;
634        default:
635            pa_assert_not_reached();
636    }
637
638    drop_backlog(bq);
639    write_index_changed(bq, old, account);
640}
641
642void pa_memblockq_flush_write(pa_memblockq *bq, bool account) {
643    int64_t old;
644    pa_assert(bq);
645
646    pa_memblockq_silence(bq);
647
648    old = bq->write_index;
649    bq->write_index = bq->read_index;
650
651    pa_memblockq_prebuf_force(bq);
652    write_index_changed(bq, old, account);
653}
654
655void pa_memblockq_flush_read(pa_memblockq *bq) {
656    int64_t old;
657    pa_assert(bq);
658
659    pa_memblockq_silence(bq);
660
661    old = bq->read_index;
662    bq->read_index = bq->write_index;
663
664    pa_memblockq_prebuf_force(bq);
665    read_index_changed(bq, old);
666}
667
668size_t pa_memblockq_get_tlength(pa_memblockq *bq) {
669    pa_assert(bq);
670
671    return bq->tlength;
672}
673
674size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
675    pa_assert(bq);
676
677    return bq->minreq;
678}
679
680size_t pa_memblockq_get_maxrewind(pa_memblockq *bq) {
681    pa_assert(bq);
682
683    return bq->maxrewind;
684}
685
686int64_t pa_memblockq_get_read_index(pa_memblockq *bq) {
687    pa_assert(bq);
688
689    return bq->read_index;
690}
691
692int64_t pa_memblockq_get_write_index(pa_memblockq *bq) {
693    pa_assert(bq);
694
695    return bq->write_index;
696}
697
698int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
699    pa_memchunk rchunk;
700
701    pa_assert(bq);
702    pa_assert(chunk);
703
704    if (bq->base == 1)
705        return pa_memblockq_push(bq, chunk);
706
707    if (!can_push(bq, pa_mcalign_csize(bq->mcalign, chunk->length)))
708        return -1;
709
710    pa_mcalign_push(bq->mcalign, chunk);
711
712    while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
713        int r;
714        r = pa_memblockq_push(bq, &rchunk);
715        pa_memblock_unref(rchunk.memblock);
716
717        if (r < 0) {
718            pa_mcalign_flush(bq->mcalign);
719            return -1;
720        }
721    }
722
723    return 0;
724}
725
726void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
727    pa_assert(bq);
728
729    bq->in_prebuf = false;
730}
731
732void pa_memblockq_prebuf_force(pa_memblockq *bq) {
733    pa_assert(bq);
734
735    if (bq->prebuf > 0)
736        bq->in_prebuf = true;
737}
738
739size_t pa_memblockq_get_maxlength(pa_memblockq *bq) {
740    pa_assert(bq);
741
742    return bq->maxlength;
743}
744
745size_t pa_memblockq_get_prebuf(pa_memblockq *bq) {
746    pa_assert(bq);
747
748    return bq->prebuf;
749}
750
751size_t pa_memblockq_pop_missing(pa_memblockq *bq) {
752    size_t l;
753
754    pa_assert(bq);
755
756#ifdef MEMBLOCKQ_DEBUG
757    pa_log_debug("[%s] pop: %lli", bq->name, (long long) bq->missing);
758#endif
759
760    if (bq->missing <= 0)
761        return 0;
762
763    if (((size_t) bq->missing < bq->minreq) &&
764        !pa_memblockq_prebuf_active(bq))
765        return 0;
766
767    l = (size_t) bq->missing;
768
769    bq->requested += bq->missing;
770    bq->missing = 0;
771
772#ifdef MEMBLOCKQ_DEBUG
773    pa_log_debug("[%s] sent %lli: request counter is at %lli", bq->name, (long long) l, (long long) bq->requested);
774#endif
775
776    return l;
777}
778
779void pa_memblockq_set_maxlength(pa_memblockq *bq, size_t maxlength) {
780    pa_assert(bq);
781
782    bq->maxlength = ((maxlength+bq->base-1)/bq->base)*bq->base;
783
784    if (bq->maxlength < bq->base)
785        bq->maxlength = bq->base;
786
787    if (bq->tlength > bq->maxlength)
788        pa_memblockq_set_tlength(bq, bq->maxlength);
789}
790
791void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
792    size_t old_tlength;
793    pa_assert(bq);
794
795    if (tlength <= 0 || tlength == (size_t) -1)
796        tlength = bq->maxlength;
797
798    old_tlength = bq->tlength;
799    bq->tlength = ((tlength+bq->base-1)/bq->base)*bq->base;
800
801    if (bq->tlength > bq->maxlength)
802        bq->tlength = bq->maxlength;
803
804    if (bq->minreq > bq->tlength)
805        pa_memblockq_set_minreq(bq, bq->tlength);
806
807    if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
808        pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
809
810    bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
811}
812
813void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
814    pa_assert(bq);
815
816    bq->minreq = (minreq/bq->base)*bq->base;
817
818    if (bq->minreq > bq->tlength)
819        bq->minreq = bq->tlength;
820
821    if (bq->minreq < bq->base)
822        bq->minreq = bq->base;
823
824    if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
825        pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
826}
827
828void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
829    pa_assert(bq);
830
831    if (prebuf == (size_t) -1)
832        prebuf = bq->tlength+bq->base-bq->minreq;
833
834    bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base;
835
836    if (prebuf > 0 && bq->prebuf < bq->base)
837        bq->prebuf = bq->base;
838
839    if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
840        bq->prebuf = bq->tlength+bq->base-bq->minreq;
841
842    if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf)
843        bq->in_prebuf = false;
844}
845
846void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
847    pa_assert(bq);
848
849    bq->maxrewind = (maxrewind/bq->base)*bq->base;
850}
851
852void pa_memblockq_apply_attr(pa_memblockq *bq, const pa_buffer_attr *a) {
853    pa_assert(bq);
854    pa_assert(a);
855
856    pa_memblockq_set_maxlength(bq, a->maxlength);
857    pa_memblockq_set_tlength(bq, a->tlength);
858    pa_memblockq_set_minreq(bq, a->minreq);
859    pa_memblockq_set_prebuf(bq, a->prebuf);
860}
861
862void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a) {
863    pa_assert(bq);
864    pa_assert(a);
865
866    a->maxlength = (uint32_t) pa_memblockq_get_maxlength(bq);
867    a->tlength = (uint32_t) pa_memblockq_get_tlength(bq);
868    a->prebuf = (uint32_t) pa_memblockq_get_prebuf(bq);
869    a->minreq = (uint32_t) pa_memblockq_get_minreq(bq);
870}
871
872int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
873
874    pa_assert(bq);
875    pa_assert(source);
876
877    pa_memblockq_prebuf_disable(bq);
878
879    for (;;) {
880        pa_memchunk chunk;
881
882        if (pa_memblockq_peek(source, &chunk) < 0)
883            return 0;
884
885        pa_assert(chunk.length > 0);
886
887        if (chunk.memblock) {
888
889            if (pa_memblockq_push_align(bq, &chunk) < 0) {
890                pa_memblock_unref(chunk.memblock);
891                return -1;
892            }
893
894            pa_memblock_unref(chunk.memblock);
895        } else
896            pa_memblockq_seek(bq, (int64_t) chunk.length, PA_SEEK_RELATIVE, true);
897
898        pa_memblockq_drop(bq, chunk.length);
899    }
900}
901
902void pa_memblockq_willneed(pa_memblockq *bq) {
903    struct list_item *q;
904
905    pa_assert(bq);
906
907    fix_current_read(bq);
908
909    for (q = bq->current_read; q; q = q->next)
910        pa_memchunk_will_need(&q->chunk);
911}
912
913void pa_memblockq_set_silence(pa_memblockq *bq, pa_memchunk *silence) {
914    pa_assert(bq);
915
916    if (bq->silence.memblock)
917        pa_memblock_unref(bq->silence.memblock);
918
919    if (silence) {
920        bq->silence = *silence;
921        pa_memblock_ref(bq->silence.memblock);
922    } else
923        pa_memchunk_reset(&bq->silence);
924}
925
926bool pa_memblockq_is_empty(pa_memblockq *bq) {
927    pa_assert(bq);
928
929    return !bq->blocks;
930}
931
932void pa_memblockq_silence(pa_memblockq *bq) {
933    pa_assert(bq);
934
935    while (bq->blocks)
936        drop_block(bq, bq->blocks);
937
938    pa_assert(bq->n_blocks == 0);
939}
940
941unsigned pa_memblockq_get_nblocks(pa_memblockq *bq) {
942    pa_assert(bq);
943
944    return bq->n_blocks;
945}
946
947size_t pa_memblockq_get_base(pa_memblockq *bq) {
948    pa_assert(bq);
949
950    return bq->base;
951}
952