/* Spa * * Copyright © 2018 Wim Taymans * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef SPA_POD_PARSER_H #define SPA_POD_PARSER_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include /** * \addtogroup spa_pod * \{ */ struct spa_pod_parser_state { uint32_t offset; uint32_t flags; struct spa_pod_frame *frame; }; struct spa_pod_parser { const void *data; uint32_t size; uint32_t _padding; struct spa_pod_parser_state state; }; #define SPA_POD_PARSER_INIT(buffer,size) (struct spa_pod_parser){ buffer, size, 0, {} } static inline void spa_pod_parser_init(struct spa_pod_parser *parser, const void *data, uint32_t size) { *parser = SPA_POD_PARSER_INIT(data, size); } static inline void spa_pod_parser_pod(struct spa_pod_parser *parser, const struct spa_pod *pod) { spa_pod_parser_init(parser, pod, SPA_POD_SIZE(pod)); } static inline void spa_pod_parser_get_state(struct spa_pod_parser *parser, struct spa_pod_parser_state *state) { *state = parser->state; } static inline void spa_pod_parser_reset(struct spa_pod_parser *parser, struct spa_pod_parser_state *state) { parser->state = *state; } static inline struct spa_pod * spa_pod_parser_deref(struct spa_pod_parser *parser, uint32_t offset, uint32_t size) { if (offset + 8 <= size) { struct spa_pod *pod = SPA_PTROFF(parser->data, offset, struct spa_pod); if (offset + SPA_POD_SIZE(pod) <= size) return pod; } return NULL; } static inline struct spa_pod *spa_pod_parser_frame(struct spa_pod_parser *parser, struct spa_pod_frame *frame) { return SPA_PTROFF(parser->data, frame->offset, struct spa_pod); } static inline void spa_pod_parser_push(struct spa_pod_parser *parser, struct spa_pod_frame *frame, const struct spa_pod *pod, uint32_t offset) { frame->pod = *pod; frame->offset = offset; frame->parent = parser->state.frame; frame->flags = parser->state.flags; parser->state.frame = frame; } static inline struct spa_pod *spa_pod_parser_current(struct spa_pod_parser *parser) { struct spa_pod_frame *f = parser->state.frame; uint32_t size = f ? f->offset + SPA_POD_SIZE(&f->pod) : parser->size; return spa_pod_parser_deref(parser, parser->state.offset, size); } static inline void spa_pod_parser_advance(struct spa_pod_parser *parser, const struct spa_pod *pod) { parser->state.offset += SPA_ROUND_UP_N(SPA_POD_SIZE(pod), 8); } static inline struct spa_pod *spa_pod_parser_next(struct spa_pod_parser *parser) { struct spa_pod *pod = spa_pod_parser_current(parser); if (pod) spa_pod_parser_advance(parser, pod); return pod; } static inline int spa_pod_parser_pop(struct spa_pod_parser *parser, struct spa_pod_frame *frame) { parser->state.frame = frame->parent; parser->state.offset = frame->offset + SPA_ROUND_UP_N(SPA_POD_SIZE(&frame->pod), 8); return 0; } static inline int spa_pod_parser_get_bool(struct spa_pod_parser *parser, bool *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_bool(pod, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_id(struct spa_pod_parser *parser, uint32_t *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_id(pod, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_int(struct spa_pod_parser *parser, int32_t *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_int(pod, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_long(struct spa_pod_parser *parser, int64_t *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_long(pod, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_float(struct spa_pod_parser *parser, float *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_float(pod, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_double(struct spa_pod_parser *parser, double *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_double(pod, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_string(struct spa_pod_parser *parser, const char **value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_string(pod, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_bytes(struct spa_pod_parser *parser, const void **value, uint32_t *len) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_bytes(pod, value, len)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_pointer(struct spa_pod_parser *parser, uint32_t *type, const void **value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_pointer(pod, type, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_fd(struct spa_pod_parser *parser, int64_t *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_fd(pod, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_rectangle(struct spa_pod_parser *parser, struct spa_rectangle *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_rectangle(pod, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_fraction(struct spa_pod_parser *parser, struct spa_fraction *value) { int res = -EPIPE; const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod != NULL && (res = spa_pod_get_fraction(pod, value)) >= 0) spa_pod_parser_advance(parser, pod); return res; } static inline int spa_pod_parser_get_pod(struct spa_pod_parser *parser, struct spa_pod **value) { struct spa_pod *pod = spa_pod_parser_current(parser); if (pod == NULL) return -EPIPE; *value = pod; spa_pod_parser_advance(parser, pod); return 0; } static inline int spa_pod_parser_push_struct(struct spa_pod_parser *parser, struct spa_pod_frame *frame) { const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod == NULL) return -EPIPE; if (!spa_pod_is_struct(pod)) return -EINVAL; spa_pod_parser_push(parser, frame, pod, parser->state.offset); parser->state.offset += sizeof(struct spa_pod_struct); return 0; } static inline int spa_pod_parser_push_object(struct spa_pod_parser *parser, struct spa_pod_frame *frame, uint32_t type, uint32_t *id) { const struct spa_pod *pod = spa_pod_parser_current(parser); if (pod == NULL) return -EPIPE; if (!spa_pod_is_object(pod)) return -EINVAL; if (type != SPA_POD_OBJECT_TYPE(pod)) return -EPROTO; if (id != NULL) *id = SPA_POD_OBJECT_ID(pod); spa_pod_parser_push(parser, frame, pod, parser->state.offset); parser->state.offset = parser->size; return 0; } static inline bool spa_pod_parser_can_collect(const struct spa_pod *pod, char type) { if (pod == NULL) return false; if (spa_pod_is_choice(pod) && SPA_POD_CHOICE_TYPE(pod) == SPA_CHOICE_None && spa_pod_parser_can_collect(SPA_POD_CHOICE_CHILD(pod), type)) return true; switch (type) { case 'P': return true; case 'b': return spa_pod_is_bool(pod); case 'I': return spa_pod_is_id(pod); case 'i': return spa_pod_is_int(pod); case 'l': return spa_pod_is_long(pod); case 'f': return spa_pod_is_float(pod); case 'd': return spa_pod_is_double(pod); case 's': return spa_pod_is_string(pod) || spa_pod_is_none(pod); case 'S': return spa_pod_is_string(pod); case 'y': return spa_pod_is_bytes(pod); case 'R': return spa_pod_is_rectangle(pod); case 'F': return spa_pod_is_fraction(pod); case 'B': return spa_pod_is_bitmap(pod); case 'a': return spa_pod_is_array(pod); case 'p': return spa_pod_is_pointer(pod); case 'h': return spa_pod_is_fd(pod); case 'T': return spa_pod_is_struct(pod) || spa_pod_is_none(pod); case 'O': return spa_pod_is_object(pod) || spa_pod_is_none(pod); case 'V': return spa_pod_is_choice(pod); default: return false; } } #define SPA_POD_PARSER_COLLECT(pod,_type,args) \ do { \ switch (_type) { \ case 'b': \ *va_arg(args, bool*) = SPA_POD_VALUE(struct spa_pod_bool, pod); \ break; \ case 'I': \ case 'i': \ *va_arg(args, int32_t*) = SPA_POD_VALUE(struct spa_pod_int, pod); \ break; \ case 'l': \ *va_arg(args, int64_t*) = SPA_POD_VALUE(struct spa_pod_long, pod); \ break; \ case 'f': \ *va_arg(args, float*) = SPA_POD_VALUE(struct spa_pod_float, pod); \ break; \ case 'd': \ *va_arg(args, double*) = SPA_POD_VALUE(struct spa_pod_double, pod); \ break; \ case 's': \ *va_arg(args, char**) = \ (pod == NULL || (SPA_POD_TYPE(pod) == SPA_TYPE_None) \ ? NULL \ : (char *)SPA_POD_CONTENTS(struct spa_pod_string, pod)); \ break; \ case 'S': \ { \ char *dest = va_arg(args, char*); \ uint32_t maxlen = va_arg(args, uint32_t); \ strncpy(dest, (char *)SPA_POD_CONTENTS(struct spa_pod_string, pod), maxlen-1); \ dest[maxlen-1] = '\0'; \ break; \ } \ case 'y': \ *(va_arg(args, void **)) = SPA_POD_CONTENTS(struct spa_pod_bytes, pod); \ *(va_arg(args, uint32_t *)) = SPA_POD_BODY_SIZE(pod); \ break; \ case 'R': \ *va_arg(args, struct spa_rectangle*) = \ SPA_POD_VALUE(struct spa_pod_rectangle, pod); \ break; \ case 'F': \ *va_arg(args, struct spa_fraction*) = \ SPA_POD_VALUE(struct spa_pod_fraction, pod); \ break; \ case 'B': \ *va_arg(args, uint32_t **) = \ (uint32_t *) SPA_POD_CONTENTS(struct spa_pod_bitmap, pod); \ break; \ case 'a': \ *va_arg(args, uint32_t*) = SPA_POD_ARRAY_VALUE_SIZE(pod); \ *va_arg(args, uint32_t*) = SPA_POD_ARRAY_VALUE_TYPE(pod); \ *va_arg(args, uint32_t*) = SPA_POD_ARRAY_N_VALUES(pod); \ *va_arg(args, void**) = SPA_POD_ARRAY_VALUES(pod); \ break; \ case 'p': \ { \ struct spa_pod_pointer_body *b = \ (struct spa_pod_pointer_body *) SPA_POD_BODY(pod); \ *(va_arg(args, uint32_t *)) = b->type; \ *(va_arg(args, const void **)) = b->value; \ break; \ } \ case 'h': \ *va_arg(args, int64_t*) = SPA_POD_VALUE(struct spa_pod_fd, pod); \ break; \ case 'P': \ case 'T': \ case 'O': \ case 'V': \ { \ const struct spa_pod **d = va_arg(args, const struct spa_pod**); \ if (d) \ *d = (pod == NULL || (SPA_POD_TYPE(pod) == SPA_TYPE_None) \ ? NULL : pod); \ break; \ } \ default: \ break; \ } \ } while(false) #define SPA_POD_PARSER_SKIP(_type,args) \ do { \ switch (_type) { \ case 'S': \ va_arg(args, char*); \ va_arg(args, uint32_t); \ break; \ case 'a': \ va_arg(args, void*); \ va_arg(args, void*); \ SPA_FALLTHROUGH \ case 'p': \ case 'y': \ va_arg(args, void*); \ SPA_FALLTHROUGH \ case 'b': \ case 'I': \ case 'i': \ case 'l': \ case 'f': \ case 'd': \ case 's': \ case 'R': \ case 'F': \ case 'B': \ case 'h': \ case 'V': \ case 'P': \ case 'T': \ case 'O': \ va_arg(args, void*); \ break; \ } \ } while(false) static inline int spa_pod_parser_getv(struct spa_pod_parser *parser, va_list args) { struct spa_pod_frame *f = parser->state.frame; uint32_t ftype = f ? f->pod.type : (uint32_t)SPA_TYPE_Struct; const struct spa_pod_prop *prop = NULL; int count = 0; do { bool optional; const struct spa_pod *pod = NULL; const char *format; if (ftype == SPA_TYPE_Object) { uint32_t key = va_arg(args, uint32_t); const struct spa_pod_object *object; if (key == 0) break; object = (const struct spa_pod_object *)spa_pod_parser_frame(parser, f); prop = spa_pod_object_find_prop(object, prop, key); pod = prop ? &prop->value : NULL; } if ((format = va_arg(args, char *)) == NULL) break; if (ftype == SPA_TYPE_Struct) pod = spa_pod_parser_next(parser); if ((optional = (*format == '?'))) format++; if (!spa_pod_parser_can_collect(pod, *format)) { if (!optional) { if (pod == NULL) return -ESRCH; else return -EPROTO; } SPA_POD_PARSER_SKIP(*format, args); } else { if (pod->type == SPA_TYPE_Choice && *format != 'V' && SPA_POD_CHOICE_TYPE(pod) == SPA_CHOICE_None) pod = SPA_POD_CHOICE_CHILD(pod); SPA_POD_PARSER_COLLECT(pod, *format, args); count++; } } while (true); return count; } static inline int spa_pod_parser_get(struct spa_pod_parser *parser, ...) { int res; va_list args; va_start(args, parser); res = spa_pod_parser_getv(parser, args); va_end(args); return res; } #define SPA_POD_OPT_Bool(val) "?" SPA_POD_Bool(val) #define SPA_POD_OPT_Id(val) "?" SPA_POD_Id(val) #define SPA_POD_OPT_Int(val) "?" SPA_POD_Int(val) #define SPA_POD_OPT_Long(val) "?" SPA_POD_Long(val) #define SPA_POD_OPT_Float(val) "?" SPA_POD_Float(val) #define SPA_POD_OPT_Double(val) "?" SPA_POD_Double(val) #define SPA_POD_OPT_String(val) "?" SPA_POD_String(val) #define SPA_POD_OPT_Stringn(val,len) "?" SPA_POD_Stringn(val,len) #define SPA_POD_OPT_Bytes(val,len) "?" SPA_POD_Bytes(val,len) #define SPA_POD_OPT_Rectangle(val) "?" SPA_POD_Rectangle(val) #define SPA_POD_OPT_Fraction(val) "?" SPA_POD_Fraction(val) #define SPA_POD_OPT_Array(csize,ctype,n_vals,vals) "?" SPA_POD_Array(csize,ctype,n_vals,vals) #define SPA_POD_OPT_Pointer(type,val) "?" SPA_POD_Pointer(type,val) #define SPA_POD_OPT_Fd(val) "?" SPA_POD_Fd(val) #define SPA_POD_OPT_Pod(val) "?" SPA_POD_Pod(val) #define SPA_POD_OPT_PodObject(val) "?" SPA_POD_PodObject(val) #define SPA_POD_OPT_PodStruct(val) "?" SPA_POD_PodStruct(val) #define SPA_POD_OPT_PodChoice(val) "?" SPA_POD_PodChoice(val) #define spa_pod_parser_get_object(p,type,id,...) \ ({ \ struct spa_pod_frame _f; \ int _res; \ if ((_res = spa_pod_parser_push_object(p, &_f, type, id)) == 0) { \ _res = spa_pod_parser_get(p,##__VA_ARGS__, 0); \ spa_pod_parser_pop(p, &_f); \ } \ _res; \ }) #define spa_pod_parser_get_struct(p,...) \ ({ \ struct spa_pod_frame _f; \ int _res; \ if ((_res = spa_pod_parser_push_struct(p, &_f)) == 0) { \ _res = spa_pod_parser_get(p,##__VA_ARGS__, NULL); \ spa_pod_parser_pop(p, &_f); \ } \ _res; \ }) #define spa_pod_parse_object(pod,type,id,...) \ ({ \ struct spa_pod_parser _p; \ spa_pod_parser_pod(&_p, pod); \ spa_pod_parser_get_object(&_p,type,id,##__VA_ARGS__); \ }) #define spa_pod_parse_struct(pod,...) \ ({ \ struct spa_pod_parser _p; \ spa_pod_parser_pod(&_p, pod); \ spa_pod_parser_get_struct(&_p,##__VA_ARGS__); \ }) /** * \} */ #ifdef __cplusplus } /* extern "C" */ #endif #endif /* SPA_POD_PARSER_H */