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