Source code
Revision control
Copy as Markdown
Other Tools
/* 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 <errno.h>
#include <stdarg.h>
#include <spa/pod/iter.h>
#include <spa/pod/vararg.h>
/**
* \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 */