pulsecore: Add a couple pa_asserts() on pa_tagstruct* calls.
[platform/upstream/pulseaudio.git] / src / pulsecore / tagstruct.c
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
8   published by the Free Software Foundation; either version 2.1 of the
9   License, 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   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with PulseAudio; 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 <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/time.h>
30 #include <stdarg.h>
31
32 #ifdef HAVE_NETINET_IN_H
33 #include <netinet/in.h>
34 #endif
35
36 #include <pulse/xmalloc.h>
37
38 #include <pulsecore/socket.h>
39 #include <pulsecore/macro.h>
40
41 #include "tagstruct.h"
42
43 #define MAX_TAG_SIZE (64*1024)
44
45 struct pa_tagstruct {
46     uint8_t *data;
47     size_t length, allocated;
48     size_t rindex;
49
50     pa_bool_t dynamic;
51 };
52
53 pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
54     pa_tagstruct*t;
55
56     pa_assert(!data || (data && length));
57
58     t = pa_xnew(pa_tagstruct, 1);
59     t->data = (uint8_t*) data;
60     t->allocated = t->length = data ? length : 0;
61     t->rindex = 0;
62     t->dynamic = !data;
63
64     return t;
65 }
66
67 void pa_tagstruct_free(pa_tagstruct*t) {
68     pa_assert(t);
69
70     if (t->dynamic)
71         pa_xfree(t->data);
72     pa_xfree(t);
73 }
74
75 uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
76     uint8_t *p;
77
78     pa_assert(t);
79     pa_assert(t->dynamic);
80     pa_assert(l);
81
82     p = t->data;
83     *l = t->length;
84     pa_xfree(t);
85     return p;
86 }
87
88 static void extend(pa_tagstruct*t, size_t l) {
89     pa_assert(t);
90     pa_assert(t->dynamic);
91
92     if (t->length+l <= t->allocated)
93         return;
94
95     t->data = pa_xrealloc(t->data, t->allocated = t->length+l+100);
96 }
97
98 void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
99     size_t l;
100     pa_assert(t);
101
102     if (s) {
103         l = strlen(s)+2;
104         extend(t, l);
105         t->data[t->length] = PA_TAG_STRING;
106         strcpy((char*) (t->data+t->length+1), s);
107         t->length += l;
108     } else {
109         extend(t, 1);
110         t->data[t->length] = PA_TAG_STRING_NULL;
111         t->length += 1;
112     }
113 }
114
115 void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
116     pa_assert(t);
117
118     extend(t, 5);
119     t->data[t->length] = PA_TAG_U32;
120     i = htonl(i);
121     memcpy(t->data+t->length+1, &i, 4);
122     t->length += 5;
123 }
124
125 void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
126     pa_assert(t);
127
128     extend(t, 2);
129     t->data[t->length] = PA_TAG_U8;
130     *(t->data+t->length+1) = c;
131     t->length += 2;
132 }
133
134 void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
135     uint32_t rate;
136
137     pa_assert(t);
138     pa_assert(ss);
139
140     extend(t, 7);
141     t->data[t->length] = PA_TAG_SAMPLE_SPEC;
142     t->data[t->length+1] = (uint8_t) ss->format;
143     t->data[t->length+2] = ss->channels;
144     rate = htonl(ss->rate);
145     memcpy(t->data+t->length+3, &rate, 4);
146     t->length += 7;
147 }
148
149 void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
150     uint32_t tmp;
151
152     pa_assert(t);
153     pa_assert(p);
154
155     extend(t, 5+length);
156     t->data[t->length] = PA_TAG_ARBITRARY;
157     tmp = htonl((uint32_t) length);
158     memcpy(t->data+t->length+1, &tmp, 4);
159     if (length)
160         memcpy(t->data+t->length+5, p, length);
161     t->length += 5+length;
162 }
163
164 void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) {
165     pa_assert(t);
166
167     extend(t, 1);
168     t->data[t->length] = (uint8_t) (b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE);
169     t->length += 1;
170 }
171
172 void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
173     uint32_t tmp;
174     pa_assert(t);
175
176     extend(t, 9);
177     t->data[t->length] = PA_TAG_TIMEVAL;
178     tmp = htonl((uint32_t) tv->tv_sec);
179     memcpy(t->data+t->length+1, &tmp, 4);
180     tmp = htonl((uint32_t) tv->tv_usec);
181     memcpy(t->data+t->length+5, &tmp, 4);
182     t->length += 9;
183 }
184
185 void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
186     uint32_t tmp;
187
188     pa_assert(t);
189
190     extend(t, 9);
191     t->data[t->length] = PA_TAG_USEC;
192     tmp = htonl((uint32_t) (u >> 32));
193     memcpy(t->data+t->length+1, &tmp, 4);
194     tmp = htonl((uint32_t) u);
195     memcpy(t->data+t->length+5, &tmp, 4);
196     t->length += 9;
197 }
198
199 void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
200     uint32_t tmp;
201
202     pa_assert(t);
203
204     extend(t, 9);
205     t->data[t->length] = PA_TAG_U64;
206     tmp = htonl((uint32_t) (u >> 32));
207     memcpy(t->data+t->length+1, &tmp, 4);
208     tmp = htonl((uint32_t) u);
209     memcpy(t->data+t->length+5, &tmp, 4);
210     t->length += 9;
211 }
212
213 void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
214     uint32_t tmp;
215
216     pa_assert(t);
217
218     extend(t, 9);
219     t->data[t->length] = PA_TAG_S64;
220     tmp = htonl((uint32_t) ((uint64_t) u >> 32));
221     memcpy(t->data+t->length+1, &tmp, 4);
222     tmp = htonl((uint32_t) ((uint64_t) u));
223     memcpy(t->data+t->length+5, &tmp, 4);
224     t->length += 9;
225 }
226
227 void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {
228     unsigned i;
229
230     pa_assert(t);
231     pa_assert(map);
232     extend(t, 2 + (size_t) map->channels);
233
234     t->data[t->length++] = PA_TAG_CHANNEL_MAP;
235     t->data[t->length++] = map->channels;
236
237     for (i = 0; i < map->channels; i ++)
238         t->data[t->length++] = (uint8_t) map->map[i];
239 }
240
241 void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
242     unsigned i;
243     pa_volume_t vol;
244
245     pa_assert(t);
246     pa_assert(cvolume);
247     extend(t, 2 + cvolume->channels * sizeof(pa_volume_t));
248
249     t->data[t->length++] = PA_TAG_CVOLUME;
250     t->data[t->length++] = cvolume->channels;
251
252     for (i = 0; i < cvolume->channels; i ++) {
253         vol = htonl(cvolume->values[i]);
254         memcpy(t->data + t->length, &vol, sizeof(pa_volume_t));
255         t->length += sizeof(pa_volume_t);
256     }
257 }
258
259 void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t vol) {
260     uint32_t u;
261     pa_assert(t);
262
263     extend(t, 5);
264     t->data[t->length] = PA_TAG_VOLUME;
265     u = htonl((uint32_t) vol);
266     memcpy(t->data+t->length+1, &u, 4);
267     t->length += 5;
268 }
269
270 void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
271     void *state = NULL;
272     pa_assert(t);
273     pa_assert(p);
274
275     extend(t, 1);
276
277     t->data[t->length++] = PA_TAG_PROPLIST;
278
279     for (;;) {
280         const char *k;
281         const void *d;
282         size_t l;
283
284         if (!(k = pa_proplist_iterate(p, &state)))
285             break;
286
287         pa_tagstruct_puts(t, k);
288         pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0);
289         pa_tagstruct_putu32(t, (uint32_t) l);
290         pa_tagstruct_put_arbitrary(t, d, l);
291     }
292
293     pa_tagstruct_puts(t, NULL);
294 }
295
296 void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f) {
297     pa_assert(t);
298     pa_assert(f);
299
300     extend(t, 1);
301
302     t->data[t->length++] = PA_TAG_FORMAT_INFO;
303     pa_tagstruct_putu8(t, (uint8_t) f->encoding);
304     pa_tagstruct_put_proplist(t, f->plist);
305 }
306
307 int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
308     int error = 0;
309     size_t n;
310     char *c;
311
312     pa_assert(t);
313     pa_assert(s);
314
315     if (t->rindex+1 > t->length)
316         return -1;
317
318     if (t->data[t->rindex] == PA_TAG_STRING_NULL) {
319         t->rindex++;
320         *s = NULL;
321         return 0;
322     }
323
324     if (t->rindex+2 > t->length)
325         return -1;
326
327     if (t->data[t->rindex] != PA_TAG_STRING)
328         return -1;
329
330     error = 1;
331     for (n = 0, c = (char*) (t->data+t->rindex+1); t->rindex+1+n < t->length; n++, c++)
332         if (!*c) {
333             error = 0;
334             break;
335         }
336
337     if (error)
338         return -1;
339
340     *s = (char*) (t->data+t->rindex+1);
341
342     t->rindex += n+2;
343     return 0;
344 }
345
346 int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {
347     pa_assert(t);
348     pa_assert(i);
349
350     if (t->rindex+5 > t->length)
351         return -1;
352
353     if (t->data[t->rindex] != PA_TAG_U32)
354         return -1;
355
356     memcpy(i, t->data+t->rindex+1, 4);
357     *i = ntohl(*i);
358     t->rindex += 5;
359     return 0;
360 }
361
362 int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {
363     pa_assert(t);
364     pa_assert(c);
365
366     if (t->rindex+2 > t->length)
367         return -1;
368
369     if (t->data[t->rindex] != PA_TAG_U8)
370         return -1;
371
372     *c = t->data[t->rindex+1];
373     t->rindex +=2;
374     return 0;
375 }
376
377 int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
378     pa_assert(t);
379     pa_assert(ss);
380
381     if (t->rindex+7 > t->length)
382         return -1;
383
384     if (t->data[t->rindex] != PA_TAG_SAMPLE_SPEC)
385         return -1;
386
387     ss->format = t->data[t->rindex+1];
388     ss->channels = t->data[t->rindex+2];
389     memcpy(&ss->rate, t->data+t->rindex+3, 4);
390     ss->rate = ntohl(ss->rate);
391
392     t->rindex += 7;
393     return 0;
394 }
395
396 int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
397     uint32_t len;
398
399     pa_assert(t);
400     pa_assert(p);
401
402     if (t->rindex+5+length > t->length)
403         return -1;
404
405     if (t->data[t->rindex] != PA_TAG_ARBITRARY)
406         return -1;
407
408     memcpy(&len, t->data+t->rindex+1, 4);
409     if (ntohl(len) != length)
410         return -1;
411
412     *p = t->data+t->rindex+5;
413     t->rindex += 5+length;
414     return 0;
415 }
416
417 int pa_tagstruct_eof(pa_tagstruct*t) {
418     pa_assert(t);
419
420     return t->rindex >= t->length;
421 }
422
423 const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
424     pa_assert(t);
425     pa_assert(t->dynamic);
426     pa_assert(l);
427
428     *l = t->length;
429     return t->data;
430 }
431
432 int pa_tagstruct_get_boolean(pa_tagstruct*t, pa_bool_t *b) {
433     pa_assert(t);
434     pa_assert(b);
435
436     if (t->rindex+1 > t->length)
437         return -1;
438
439     if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE)
440         *b = TRUE;
441     else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE)
442         *b = FALSE;
443     else
444         return -1;
445
446     t->rindex +=1;
447     return 0;
448 }
449
450 int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
451
452     pa_assert(t);
453     pa_assert(tv);
454
455     if (t->rindex+9 > t->length)
456         return -1;
457
458     if (t->data[t->rindex] != PA_TAG_TIMEVAL)
459         return -1;
460
461     memcpy(&tv->tv_sec, t->data+t->rindex+1, 4);
462     tv->tv_sec = (time_t) ntohl((uint32_t) tv->tv_sec);
463     memcpy(&tv->tv_usec, t->data+t->rindex+5, 4);
464     tv->tv_usec = (suseconds_t) ntohl((uint32_t) tv->tv_usec);
465     t->rindex += 9;
466     return 0;
467 }
468
469 int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {
470     uint32_t tmp;
471
472     pa_assert(t);
473     pa_assert(u);
474
475     if (t->rindex+9 > t->length)
476         return -1;
477
478     if (t->data[t->rindex] != PA_TAG_USEC)
479         return -1;
480
481     memcpy(&tmp, t->data+t->rindex+1, 4);
482     *u = (pa_usec_t) ntohl(tmp) << 32;
483     memcpy(&tmp, t->data+t->rindex+5, 4);
484     *u |= (pa_usec_t) ntohl(tmp);
485     t->rindex +=9;
486     return 0;
487 }
488
489 int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
490     uint32_t tmp;
491
492     pa_assert(t);
493     pa_assert(u);
494
495     if (t->rindex+9 > t->length)
496         return -1;
497
498     if (t->data[t->rindex] != PA_TAG_U64)
499         return -1;
500
501     memcpy(&tmp, t->data+t->rindex+1, 4);
502     *u = (uint64_t) ntohl(tmp) << 32;
503     memcpy(&tmp, t->data+t->rindex+5, 4);
504     *u |= (uint64_t) ntohl(tmp);
505     t->rindex +=9;
506     return 0;
507 }
508
509 int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
510     uint32_t tmp;
511
512     pa_assert(t);
513     pa_assert(u);
514
515     if (t->rindex+9 > t->length)
516         return -1;
517
518     if (t->data[t->rindex] != PA_TAG_S64)
519         return -1;
520
521     memcpy(&tmp, t->data+t->rindex+1, 4);
522     *u = (int64_t) ((uint64_t) ntohl(tmp) << 32);
523     memcpy(&tmp, t->data+t->rindex+5, 4);
524     *u |= (int64_t) ntohl(tmp);
525     t->rindex +=9;
526     return 0;
527 }
528
529 int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
530     unsigned i;
531
532     pa_assert(t);
533     pa_assert(map);
534
535     if (t->rindex+2 > t->length)
536         return -1;
537
538     if (t->data[t->rindex] != PA_TAG_CHANNEL_MAP)
539         return -1;
540
541     if ((map->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
542         return -1;
543
544     if (t->rindex+2+map->channels > t->length)
545         return -1;
546
547     for (i = 0; i < map->channels; i ++)
548         map->map[i] = (int8_t) t->data[t->rindex + 2 + i];
549
550     t->rindex += 2 + (size_t) map->channels;
551     return 0;
552 }
553
554 int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
555     unsigned i;
556     pa_volume_t vol;
557
558     pa_assert(t);
559     pa_assert(cvolume);
560
561     if (t->rindex+2 > t->length)
562         return -1;
563
564     if (t->data[t->rindex] != PA_TAG_CVOLUME)
565         return -1;
566
567     if ((cvolume->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
568         return -1;
569
570     if (t->rindex+2+cvolume->channels*sizeof(pa_volume_t) > t->length)
571         return -1;
572
573     for (i = 0; i < cvolume->channels; i ++) {
574         memcpy(&vol, t->data + t->rindex + 2 + i * sizeof(pa_volume_t), sizeof(pa_volume_t));
575         cvolume->values[i] = (pa_volume_t) ntohl(vol);
576     }
577
578     t->rindex += 2 + cvolume->channels * sizeof(pa_volume_t);
579     return 0;
580 }
581
582 int pa_tagstruct_get_volume(pa_tagstruct*t, pa_volume_t *vol) {
583     uint32_t u;
584
585     pa_assert(t);
586     pa_assert(vol);
587
588     if (t->rindex+5 > t->length)
589         return -1;
590
591     if (t->data[t->rindex] != PA_TAG_VOLUME)
592         return -1;
593
594     memcpy(&u, t->data+t->rindex+1, 4);
595     *vol = (pa_volume_t) ntohl(u);
596
597     t->rindex += 5;
598     return 0;
599 }
600
601 int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) {
602     size_t saved_rindex;
603
604     pa_assert(t);
605     pa_assert(p);
606
607     if (t->rindex+1 > t->length)
608         return -1;
609
610     if (t->data[t->rindex] != PA_TAG_PROPLIST)
611         return -1;
612
613     saved_rindex = t->rindex;
614     t->rindex++;
615
616     for (;;) {
617         const char *k;
618         const void *d;
619         uint32_t length;
620
621         if (pa_tagstruct_gets(t, &k) < 0)
622             goto fail;
623
624         if (!k)
625             break;
626
627         if (pa_tagstruct_getu32(t, &length) < 0)
628             goto fail;
629
630         if (length > MAX_TAG_SIZE)
631             goto fail;
632
633         if (pa_tagstruct_get_arbitrary(t, &d, length) < 0)
634             goto fail;
635
636         if (pa_proplist_set(p, k, d, length) < 0)
637             goto fail;
638     }
639
640     return 0;
641
642 fail:
643     t->rindex = saved_rindex;
644     return -1;
645 }
646
647 int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f) {
648     size_t saved_rindex;
649     uint8_t encoding;
650
651     pa_assert(t);
652     pa_assert(f);
653
654     if (t->rindex+1 > t->length)
655         return -1;
656
657     if (t->data[t->rindex] != PA_TAG_FORMAT_INFO)
658         return -1;
659
660     saved_rindex = t->rindex;
661     t->rindex++;
662
663     if (pa_tagstruct_getu8(t, &encoding) < 0)
664         goto fail;
665
666     f->encoding = encoding;
667
668     if (pa_tagstruct_get_proplist(t, f->plist) < 0)
669         goto fail;
670
671     return 0;
672
673 fail:
674     t->rindex = saved_rindex;
675     return -1;
676 }
677
678 void pa_tagstruct_put(pa_tagstruct *t, ...) {
679     va_list va;
680     pa_assert(t);
681
682     va_start(va, t);
683
684     for (;;) {
685         int tag = va_arg(va, int);
686
687         if (tag == PA_TAG_INVALID)
688             break;
689
690         switch (tag) {
691             case PA_TAG_STRING:
692             case PA_TAG_STRING_NULL:
693                 pa_tagstruct_puts(t, va_arg(va, char*));
694                 break;
695
696             case PA_TAG_U32:
697                 pa_tagstruct_putu32(t, va_arg(va, uint32_t));
698                 break;
699
700             case PA_TAG_U8:
701                 pa_tagstruct_putu8(t, (uint8_t) va_arg(va, int));
702                 break;
703
704             case PA_TAG_U64:
705                 pa_tagstruct_putu64(t, va_arg(va, uint64_t));
706                 break;
707
708             case PA_TAG_SAMPLE_SPEC:
709                 pa_tagstruct_put_sample_spec(t, va_arg(va, pa_sample_spec*));
710                 break;
711
712             case PA_TAG_ARBITRARY: {
713                 void *p = va_arg(va, void*);
714                 size_t size = va_arg(va, size_t);
715                 pa_tagstruct_put_arbitrary(t, p, size);
716                 break;
717             }
718
719             case PA_TAG_BOOLEAN_TRUE:
720             case PA_TAG_BOOLEAN_FALSE:
721                 pa_tagstruct_put_boolean(t, va_arg(va, int));
722                 break;
723
724             case PA_TAG_TIMEVAL:
725                 pa_tagstruct_put_timeval(t, va_arg(va, struct timeval*));
726                 break;
727
728             case PA_TAG_USEC:
729                 pa_tagstruct_put_usec(t, va_arg(va, pa_usec_t));
730                 break;
731
732             case PA_TAG_CHANNEL_MAP:
733                 pa_tagstruct_put_channel_map(t, va_arg(va, pa_channel_map *));
734                 break;
735
736             case PA_TAG_CVOLUME:
737                 pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *));
738                 break;
739
740             case PA_TAG_VOLUME:
741                 pa_tagstruct_put_volume(t, va_arg(va, pa_volume_t));
742                 break;
743
744             case PA_TAG_PROPLIST:
745                 pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *));
746                 break;
747
748             default:
749                 pa_assert_not_reached();
750         }
751     }
752
753     va_end(va);
754 }
755
756 int pa_tagstruct_get(pa_tagstruct *t, ...) {
757     va_list va;
758     int ret = 0;
759
760     pa_assert(t);
761
762     va_start(va, t);
763     while (ret == 0) {
764         int tag = va_arg(va, int);
765
766         if (tag == PA_TAG_INVALID)
767             break;
768
769         switch (tag) {
770             case PA_TAG_STRING:
771             case PA_TAG_STRING_NULL:
772                 ret = pa_tagstruct_gets(t, va_arg(va, const char**));
773                 break;
774
775             case PA_TAG_U32:
776                 ret = pa_tagstruct_getu32(t, va_arg(va, uint32_t*));
777                 break;
778
779             case PA_TAG_U8:
780                 ret = pa_tagstruct_getu8(t, va_arg(va, uint8_t*));
781                 break;
782
783             case PA_TAG_U64:
784                 ret = pa_tagstruct_getu64(t, va_arg(va, uint64_t*));
785                 break;
786
787             case PA_TAG_SAMPLE_SPEC:
788                 ret = pa_tagstruct_get_sample_spec(t, va_arg(va, pa_sample_spec*));
789                 break;
790
791             case PA_TAG_ARBITRARY: {
792                 const void **p = va_arg(va, const void**);
793                 size_t size = va_arg(va, size_t);
794                 ret = pa_tagstruct_get_arbitrary(t, p, size);
795                 break;
796             }
797
798             case PA_TAG_BOOLEAN_TRUE:
799             case PA_TAG_BOOLEAN_FALSE:
800                 ret = pa_tagstruct_get_boolean(t, va_arg(va, pa_bool_t*));
801                 break;
802
803             case PA_TAG_TIMEVAL:
804                 ret = pa_tagstruct_get_timeval(t, va_arg(va, struct timeval*));
805                 break;
806
807             case PA_TAG_USEC:
808                 ret = pa_tagstruct_get_usec(t, va_arg(va, pa_usec_t*));
809                 break;
810
811             case PA_TAG_CHANNEL_MAP:
812                 ret = pa_tagstruct_get_channel_map(t, va_arg(va, pa_channel_map *));
813                 break;
814
815             case PA_TAG_CVOLUME:
816                 ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
817                 break;
818
819             case PA_TAG_VOLUME:
820                 ret = pa_tagstruct_get_volume(t, va_arg(va, pa_volume_t *));
821                 break;
822
823             case PA_TAG_PROPLIST:
824                 ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *));
825                 break;
826
827             default:
828                 pa_assert_not_reached();
829         }
830
831     }
832
833     va_end(va);
834     return ret;
835 }