/*** This file is part of PulseAudio. Copyright 2004-2006 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #ifdef HAVE_NETINET_IN_H #include #endif #include #include #include #include "tagstruct.h" #define MAX_TAG_SIZE (64*1024) struct pa_tagstruct { uint8_t *data; size_t length, allocated; size_t rindex; pa_bool_t dynamic; }; pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) { pa_tagstruct*t; pa_assert(!data || (data && length)); t = pa_xnew(pa_tagstruct, 1); t->data = (uint8_t*) data; t->allocated = t->length = data ? length : 0; t->rindex = 0; t->dynamic = !data; return t; } void pa_tagstruct_free(pa_tagstruct*t) { pa_assert(t); if (t->dynamic) pa_xfree(t->data); pa_xfree(t); } uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) { uint8_t *p; pa_assert(t); pa_assert(t->dynamic); pa_assert(l); p = t->data; *l = t->length; pa_xfree(t); return p; } static void extend(pa_tagstruct*t, size_t l) { pa_assert(t); pa_assert(t->dynamic); if (t->length+l <= t->allocated) return; t->data = pa_xrealloc(t->data, t->allocated = t->length+l+100); } void pa_tagstruct_puts(pa_tagstruct*t, const char *s) { size_t l; pa_assert(t); if (s) { l = strlen(s)+2; extend(t, l); t->data[t->length] = PA_TAG_STRING; strcpy((char*) (t->data+t->length+1), s); t->length += l; } else { extend(t, 1); t->data[t->length] = PA_TAG_STRING_NULL; t->length += 1; } } void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) { pa_assert(t); extend(t, 5); t->data[t->length] = PA_TAG_U32; i = htonl(i); memcpy(t->data+t->length+1, &i, 4); t->length += 5; } void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) { pa_assert(t); extend(t, 2); t->data[t->length] = PA_TAG_U8; *(t->data+t->length+1) = c; t->length += 2; } void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) { uint32_t rate; pa_assert(t); pa_assert(ss); extend(t, 7); t->data[t->length] = PA_TAG_SAMPLE_SPEC; t->data[t->length+1] = (uint8_t) ss->format; t->data[t->length+2] = ss->channels; rate = htonl(ss->rate); memcpy(t->data+t->length+3, &rate, 4); t->length += 7; } void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) { uint32_t tmp; pa_assert(t); pa_assert(p); extend(t, 5+length); t->data[t->length] = PA_TAG_ARBITRARY; tmp = htonl((uint32_t) length); memcpy(t->data+t->length+1, &tmp, 4); if (length) memcpy(t->data+t->length+5, p, length); t->length += 5+length; } void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) { pa_assert(t); extend(t, 1); t->data[t->length] = (uint8_t) (b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE); t->length += 1; } void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) { uint32_t tmp; pa_assert(t); extend(t, 9); t->data[t->length] = PA_TAG_TIMEVAL; tmp = htonl((uint32_t) tv->tv_sec); memcpy(t->data+t->length+1, &tmp, 4); tmp = htonl((uint32_t) tv->tv_usec); memcpy(t->data+t->length+5, &tmp, 4); t->length += 9; } void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) { uint32_t tmp; pa_assert(t); extend(t, 9); t->data[t->length] = PA_TAG_USEC; tmp = htonl((uint32_t) (u >> 32)); memcpy(t->data+t->length+1, &tmp, 4); tmp = htonl((uint32_t) u); memcpy(t->data+t->length+5, &tmp, 4); t->length += 9; } void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) { uint32_t tmp; pa_assert(t); extend(t, 9); t->data[t->length] = PA_TAG_U64; tmp = htonl((uint32_t) (u >> 32)); memcpy(t->data+t->length+1, &tmp, 4); tmp = htonl((uint32_t) u); memcpy(t->data+t->length+5, &tmp, 4); t->length += 9; } void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) { uint32_t tmp; pa_assert(t); extend(t, 9); t->data[t->length] = PA_TAG_S64; tmp = htonl((uint32_t) ((uint64_t) u >> 32)); memcpy(t->data+t->length+1, &tmp, 4); tmp = htonl((uint32_t) ((uint64_t) u)); memcpy(t->data+t->length+5, &tmp, 4); t->length += 9; } void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) { unsigned i; pa_assert(t); extend(t, 2 + (size_t) map->channels); t->data[t->length++] = PA_TAG_CHANNEL_MAP; t->data[t->length++] = map->channels; for (i = 0; i < map->channels; i ++) t->data[t->length++] = (uint8_t) map->map[i]; } void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) { unsigned i; pa_volume_t vol; pa_assert(t); extend(t, 2 + cvolume->channels * sizeof(pa_volume_t)); t->data[t->length++] = PA_TAG_CVOLUME; t->data[t->length++] = cvolume->channels; for (i = 0; i < cvolume->channels; i ++) { vol = htonl(cvolume->values[i]); memcpy(t->data + t->length, &vol, sizeof(pa_volume_t)); t->length += sizeof(pa_volume_t); } } void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t vol) { uint32_t u; pa_assert(t); extend(t, 5); t->data[t->length] = PA_TAG_VOLUME; u = htonl((uint32_t) vol); memcpy(t->data+t->length+1, &u, 4); t->length += 5; } void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) { void *state = NULL; pa_assert(t); pa_assert(p); extend(t, 1); t->data[t->length++] = PA_TAG_PROPLIST; for (;;) { const char *k; const void *d; size_t l; if (!(k = pa_proplist_iterate(p, &state))) break; pa_tagstruct_puts(t, k); pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0); pa_tagstruct_putu32(t, (uint32_t) l); pa_tagstruct_put_arbitrary(t, d, l); } pa_tagstruct_puts(t, NULL); } int pa_tagstruct_gets(pa_tagstruct*t, const char **s) { int error = 0; size_t n; char *c; pa_assert(t); pa_assert(s); if (t->rindex+1 > t->length) return -1; if (t->data[t->rindex] == PA_TAG_STRING_NULL) { t->rindex++; *s = NULL; return 0; } if (t->rindex+2 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_STRING) return -1; error = 1; for (n = 0, c = (char*) (t->data+t->rindex+1); t->rindex+1+n < t->length; n++, c++) if (!*c) { error = 0; break; } if (error) return -1; *s = (char*) (t->data+t->rindex+1); t->rindex += n+2; return 0; } int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) { pa_assert(t); pa_assert(i); if (t->rindex+5 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_U32) return -1; memcpy(i, t->data+t->rindex+1, 4); *i = ntohl(*i); t->rindex += 5; return 0; } int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) { pa_assert(t); pa_assert(c); if (t->rindex+2 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_U8) return -1; *c = t->data[t->rindex+1]; t->rindex +=2; return 0; } int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) { pa_assert(t); pa_assert(ss); if (t->rindex+7 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_SAMPLE_SPEC) return -1; ss->format = t->data[t->rindex+1]; ss->channels = t->data[t->rindex+2]; memcpy(&ss->rate, t->data+t->rindex+3, 4); ss->rate = ntohl(ss->rate); t->rindex += 7; return 0; } int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) { uint32_t len; pa_assert(t); pa_assert(p); if (t->rindex+5+length > t->length) return -1; if (t->data[t->rindex] != PA_TAG_ARBITRARY) return -1; memcpy(&len, t->data+t->rindex+1, 4); if (ntohl(len) != length) return -1; *p = t->data+t->rindex+5; t->rindex += 5+length; return 0; } int pa_tagstruct_eof(pa_tagstruct*t) { pa_assert(t); return t->rindex >= t->length; } const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) { pa_assert(t); pa_assert(t->dynamic); pa_assert(l); *l = t->length; return t->data; } int pa_tagstruct_get_boolean(pa_tagstruct*t, pa_bool_t *b) { pa_assert(t); pa_assert(b); if (t->rindex+1 > t->length) return -1; if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE) *b = TRUE; else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE) *b = FALSE; else return -1; t->rindex +=1; return 0; } int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) { pa_assert(t); pa_assert(tv); if (t->rindex+9 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_TIMEVAL) return -1; memcpy(&tv->tv_sec, t->data+t->rindex+1, 4); tv->tv_sec = (time_t) ntohl((uint32_t) tv->tv_sec); memcpy(&tv->tv_usec, t->data+t->rindex+5, 4); tv->tv_usec = (suseconds_t) ntohl((uint32_t) tv->tv_usec); t->rindex += 9; return 0; } int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) { uint32_t tmp; pa_assert(t); pa_assert(u); if (t->rindex+9 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_USEC) return -1; memcpy(&tmp, t->data+t->rindex+1, 4); *u = (pa_usec_t) ntohl(tmp) << 32; memcpy(&tmp, t->data+t->rindex+5, 4); *u |= (pa_usec_t) ntohl(tmp); t->rindex +=9; return 0; } int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) { uint32_t tmp; pa_assert(t); pa_assert(u); if (t->rindex+9 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_U64) return -1; memcpy(&tmp, t->data+t->rindex+1, 4); *u = (uint64_t) ntohl(tmp) << 32; memcpy(&tmp, t->data+t->rindex+5, 4); *u |= (uint64_t) ntohl(tmp); t->rindex +=9; return 0; } int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) { uint32_t tmp; pa_assert(t); pa_assert(u); if (t->rindex+9 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_S64) return -1; memcpy(&tmp, t->data+t->rindex+1, 4); *u = (int64_t) ((uint64_t) ntohl(tmp) << 32); memcpy(&tmp, t->data+t->rindex+5, 4); *u |= (int64_t) ntohl(tmp); t->rindex +=9; return 0; } int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) { unsigned i; pa_assert(t); pa_assert(map); if (t->rindex+2 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_CHANNEL_MAP) return -1; if ((map->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX) return -1; if (t->rindex+2+map->channels > t->length) return -1; for (i = 0; i < map->channels; i ++) map->map[i] = (int8_t) t->data[t->rindex + 2 + i]; t->rindex += 2 + (size_t) map->channels; return 0; } int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) { unsigned i; pa_volume_t vol; pa_assert(t); pa_assert(cvolume); if (t->rindex+2 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_CVOLUME) return -1; if ((cvolume->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX) return -1; if (t->rindex+2+cvolume->channels*sizeof(pa_volume_t) > t->length) return -1; for (i = 0; i < cvolume->channels; i ++) { memcpy(&vol, t->data + t->rindex + 2 + i * sizeof(pa_volume_t), sizeof(pa_volume_t)); cvolume->values[i] = (pa_volume_t) ntohl(vol); } t->rindex += 2 + cvolume->channels * sizeof(pa_volume_t); return 0; } int pa_tagstruct_get_volume(pa_tagstruct*t, pa_volume_t *vol) { uint32_t u; pa_assert(t); pa_assert(vol); if (t->rindex+5 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_VOLUME) return -1; memcpy(&u, t->data+t->rindex+1, 4); *vol = (pa_volume_t) ntohl(u); t->rindex += 5; return 0; } int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) { size_t saved_rindex; pa_assert(t); pa_assert(p); if (t->rindex+1 > t->length) return -1; if (t->data[t->rindex] != PA_TAG_PROPLIST) return -1; saved_rindex = t->rindex; t->rindex++; for (;;) { const char *k; const void *d; uint32_t length; if (pa_tagstruct_gets(t, &k) < 0) goto fail; if (!k) break; if (pa_tagstruct_getu32(t, &length) < 0) goto fail; if (length > MAX_TAG_SIZE) goto fail; if (pa_tagstruct_get_arbitrary(t, &d, length) < 0) goto fail; if (pa_proplist_set(p, k, d, length) < 0) goto fail; } return 0; fail: t->rindex = saved_rindex; return -1; } void pa_tagstruct_put(pa_tagstruct *t, ...) { va_list va; pa_assert(t); va_start(va, t); for (;;) { int tag = va_arg(va, int); if (tag == PA_TAG_INVALID) break; switch (tag) { case PA_TAG_STRING: case PA_TAG_STRING_NULL: pa_tagstruct_puts(t, va_arg(va, char*)); break; case PA_TAG_U32: pa_tagstruct_putu32(t, va_arg(va, uint32_t)); break; case PA_TAG_U8: pa_tagstruct_putu8(t, (uint8_t) va_arg(va, int)); break; case PA_TAG_U64: pa_tagstruct_putu64(t, va_arg(va, uint64_t)); break; case PA_TAG_SAMPLE_SPEC: pa_tagstruct_put_sample_spec(t, va_arg(va, pa_sample_spec*)); break; case PA_TAG_ARBITRARY: { void *p = va_arg(va, void*); size_t size = va_arg(va, size_t); pa_tagstruct_put_arbitrary(t, p, size); break; } case PA_TAG_BOOLEAN_TRUE: case PA_TAG_BOOLEAN_FALSE: pa_tagstruct_put_boolean(t, va_arg(va, int)); break; case PA_TAG_TIMEVAL: pa_tagstruct_put_timeval(t, va_arg(va, struct timeval*)); break; case PA_TAG_USEC: pa_tagstruct_put_usec(t, va_arg(va, pa_usec_t)); break; case PA_TAG_CHANNEL_MAP: pa_tagstruct_put_channel_map(t, va_arg(va, pa_channel_map *)); break; case PA_TAG_CVOLUME: pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *)); break; case PA_TAG_VOLUME: pa_tagstruct_put_volume(t, va_arg(va, pa_volume_t)); break; case PA_TAG_PROPLIST: pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *)); break; default: pa_assert_not_reached(); } } va_end(va); } int pa_tagstruct_get(pa_tagstruct *t, ...) { va_list va; int ret = 0; pa_assert(t); va_start(va, t); while (ret == 0) { int tag = va_arg(va, int); if (tag == PA_TAG_INVALID) break; switch (tag) { case PA_TAG_STRING: case PA_TAG_STRING_NULL: ret = pa_tagstruct_gets(t, va_arg(va, const char**)); break; case PA_TAG_U32: ret = pa_tagstruct_getu32(t, va_arg(va, uint32_t*)); break; case PA_TAG_U8: ret = pa_tagstruct_getu8(t, va_arg(va, uint8_t*)); break; case PA_TAG_U64: ret = pa_tagstruct_getu64(t, va_arg(va, uint64_t*)); break; case PA_TAG_SAMPLE_SPEC: ret = pa_tagstruct_get_sample_spec(t, va_arg(va, pa_sample_spec*)); break; case PA_TAG_ARBITRARY: { const void **p = va_arg(va, const void**); size_t size = va_arg(va, size_t); ret = pa_tagstruct_get_arbitrary(t, p, size); break; } case PA_TAG_BOOLEAN_TRUE: case PA_TAG_BOOLEAN_FALSE: ret = pa_tagstruct_get_boolean(t, va_arg(va, pa_bool_t*)); break; case PA_TAG_TIMEVAL: ret = pa_tagstruct_get_timeval(t, va_arg(va, struct timeval*)); break; case PA_TAG_USEC: ret = pa_tagstruct_get_usec(t, va_arg(va, pa_usec_t*)); break; case PA_TAG_CHANNEL_MAP: ret = pa_tagstruct_get_channel_map(t, va_arg(va, pa_channel_map *)); break; case PA_TAG_CVOLUME: ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *)); break; case PA_TAG_VOLUME: ret = pa_tagstruct_get_volume(t, va_arg(va, pa_volume_t *)); break; case PA_TAG_PROPLIST: ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *)); break; default: pa_assert_not_reached(); } } va_end(va); return ret; }