/* $Id$ */ /*** This file is part of PulseAudio. 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 #include #ifdef HAVE_NETINET_IN_H #include #endif #include "winsock.h" #include #include "tagstruct.h" struct pa_tagstruct { uint8_t *data; size_t length, allocated; size_t rindex; int dynamic; }; pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) { pa_tagstruct*t; assert(!data || (data && length)); t = pa_xmalloc(sizeof(pa_tagstruct)); 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) { 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; assert(t && t->dynamic && l); p = t->data; *l = t->length; pa_xfree(t); return p; } static void extend(pa_tagstruct*t, size_t l) { assert(t); 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; 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) { 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) { 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; assert(t && 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; assert(t && p); extend(t, 5+length); t->data[t->length] = PA_TAG_ARBITRARY; tmp = htonl(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, int b) { assert(t); extend(t, 1); t->data[t->length] = 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; assert(t); extend(t, 9); t->data[t->length] = PA_TAG_TIMEVAL; tmp = htonl(tv->tv_sec); memcpy(t->data+t->length+1, &tmp, 4); tmp = htonl(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; 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; 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; 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; assert(t); extend(t, 2 + 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; 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); } } int pa_tagstruct_gets(pa_tagstruct*t, const char **s) { int error = 0; size_t n; char *c; assert(t && 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) { assert(t && 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) { assert(t && 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) { assert(t && 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; assert(t && 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) { assert(t); return t->rindex >= t->length; } const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) { assert(t && t->dynamic && l); *l = t->length; return t->data; } int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) { assert(t && b); if (t->rindex+1 > t->length) return -1; if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE) *b = 1; else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE) *b = 0; else return -1; t->rindex +=1; return 0; } int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *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 = ntohl(tv->tv_sec); memcpy(&tv->tv_usec, t->data+t->rindex+5, 4); tv->tv_usec = ntohl(tv->tv_usec); t->rindex += 9; return 0; } int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) { uint32_t tmp; assert(t && 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; assert(t && 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; assert(t && 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; assert(t); 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 + map->channels; return 0; } int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) { unsigned i; pa_volume_t vol; assert(t); 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; } void pa_tagstruct_put(pa_tagstruct *t, ...) { va_list va; 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; default: abort(); } } va_end(va); } int pa_tagstruct_get(pa_tagstruct *t, ...) { va_list va; int ret = 0; 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, int*)); 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; default: abort(); } } va_end(va); return ret; }