diff options
Diffstat (limited to 'third_party/pipewire/spa/pod/filter.h')
-rw-r--r-- | third_party/pipewire/spa/pod/filter.h | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/third_party/pipewire/spa/pod/filter.h b/third_party/pipewire/spa/pod/filter.h new file mode 100644 index 0000000000..944b4cc64f --- /dev/null +++ b/third_party/pipewire/spa/pod/filter.h @@ -0,0 +1,422 @@ +/* Simple Plugin API + * + * 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_FILTER_H +#define SPA_POD_FILTER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <errno.h> +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +#include <spa/param/props.h> +#include <spa/pod/iter.h> +#include <spa/pod/builder.h> +#include <spa/pod/compare.h> + +/** + * \addtogroup spa_pod + * \{ + */ + +static inline int spa_pod_choice_fix_default(struct spa_pod_choice *choice) +{ + void *val, *alt; + int i, nvals; + uint32_t type, size; + + nvals = SPA_POD_CHOICE_N_VALUES(choice); + type = SPA_POD_CHOICE_VALUE_TYPE(choice); + size = SPA_POD_CHOICE_VALUE_SIZE(choice); + alt = val = SPA_POD_CHOICE_VALUES(choice); + + switch (choice->body.type) { + case SPA_CHOICE_None: + break; + case SPA_CHOICE_Range: + case SPA_CHOICE_Step: + if (nvals > 1) { + alt = SPA_PTROFF(alt, size, void); + if (spa_pod_compare_value(type, val, alt, size) < 0) + memcpy(val, alt, size); + } + if (nvals > 2) { + alt = SPA_PTROFF(alt, size, void); + if (spa_pod_compare_value(type, val, alt, size) > 0) + memcpy(val, alt, size); + } + break; + case SPA_CHOICE_Flags: + case SPA_CHOICE_Enum: + { + void *best = NULL; + + for (i = 1; i < nvals; i++) { + alt = SPA_PTROFF(alt, size, void); + if (spa_pod_compare_value(type, val, alt, size) == 0) { + best = alt; + break; + } + if (best == NULL) + best = alt; + } + if (best) + memcpy(val, best, size); + + if (nvals <= 1) + choice->body.type = SPA_CHOICE_None; + break; + } + } + return 0; +} + +static inline int spa_pod_filter_flags_value(struct spa_pod_builder *b, + uint32_t type, const void *r1, const void *r2, uint32_t size) +{ + switch (type) { + case SPA_TYPE_Int: + { + int32_t val = (*(int32_t *) r1) & (*(int32_t *) r2); + if (val == 0) + return 0; + spa_pod_builder_int(b, val); + break; + } + case SPA_TYPE_Long: + { + int64_t val = (*(int64_t *) r1) & (*(int64_t *) r2); + if (val == 0) + return 0; + spa_pod_builder_long(b, val); + break; + } + default: + return -ENOTSUP; + } + return 1; +} + + +static inline int +spa_pod_filter_prop(struct spa_pod_builder *b, + const struct spa_pod_prop *p1, + const struct spa_pod_prop *p2) +{ + const struct spa_pod *v1, *v2; + struct spa_pod_choice *nc; + uint32_t j, k, nalt1, nalt2; + void *alt1, *alt2, *a1, *a2; + uint32_t type, size, p1c, p2c; + struct spa_pod_frame f; + + v1 = spa_pod_get_values(&p1->value, &nalt1, &p1c); + alt1 = SPA_POD_BODY(v1); + v2 = spa_pod_get_values(&p2->value, &nalt2, &p2c); + alt2 = SPA_POD_BODY(v2); + + type = v1->type; + size = v1->size; + + /* incompatible property types */ + if (type != v2->type || size != v2->size || p1->key != p2->key) + return -EINVAL; + + if (p1c == SPA_CHOICE_None || p1c == SPA_CHOICE_Flags) { + nalt1 = 1; + } else { + alt1 = SPA_PTROFF(alt1, size, void); + nalt1--; + } + + if (p2c == SPA_CHOICE_None || p2c == SPA_CHOICE_Flags) { + nalt2 = 1; + } else { + alt2 = SPA_PTROFF(alt2, size, void); + nalt2--; + } + + /* start with copying the property */ + spa_pod_builder_prop(b, p1->key, p1->flags & p2->flags); + spa_pod_builder_push_choice(b, &f, 0, 0); + nc = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f); + + /* default value */ + spa_pod_builder_primitive(b, v1); + + if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_None) || + (p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Enum) || + (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_None) || + (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Enum)) { + int n_copied = 0; + /* copy all equal values but don't copy the default value again */ + for (j = 0, a1 = alt1; j < nalt1; j++, a1 = SPA_PTROFF(a1, size, void)) { + for (k = 0, a2 = alt2; k < nalt2; k++, a2 = SPA_PTROFF(a2,size,void)) { + if (spa_pod_compare_value(type, a1, a2, size) == 0) { + if (p1c == SPA_CHOICE_Enum || j > 0) + spa_pod_builder_raw(b, a1, size); + n_copied++; + } + } + } + if (n_copied == 0) + return -EINVAL; + nc->body.type = SPA_CHOICE_Enum; + } + + if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Range) || + (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Range)) { + int n_copied = 0; + /* copy all values inside the range */ + for (j = 0, a1 = alt1, a2 = alt2; j < nalt1; j++, a1 = SPA_PTROFF(a1,size,void)) { + if (spa_pod_compare_value(type, a1, a2, size) < 0) + continue; + if (spa_pod_compare_value(type, a1, SPA_PTROFF(a2,size,void), size) > 0) + continue; + spa_pod_builder_raw(b, a1, size); + n_copied++; + } + if (n_copied == 0) + return -EINVAL; + nc->body.type = SPA_CHOICE_Enum; + } + + if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Step) || + (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Step)) { + return -ENOTSUP; + } + + if ((p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_None) || + (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Enum)) { + int n_copied = 0; + /* copy all values inside the range */ + for (k = 0, a1 = alt1, a2 = alt2; k < nalt2; k++, a2 = SPA_PTROFF(a2,size,void)) { + if (spa_pod_compare_value(type, a2, a1, size) < 0) + continue; + if (spa_pod_compare_value(type, a2, SPA_PTROFF(a1,size,void), size) > 0) + continue; + spa_pod_builder_raw(b, a2, size); + n_copied++; + } + if (n_copied == 0) + return -EINVAL; + nc->body.type = SPA_CHOICE_Enum; + } + + if ((p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Range) || + (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Step) || + (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Range) || + (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Step)) { + if (spa_pod_compare_value(type, alt1, alt2, size) < 0) + spa_pod_builder_raw(b, alt2, size); + else + spa_pod_builder_raw(b, alt1, size); + + alt1 = SPA_PTROFF(alt1,size,void); + alt2 = SPA_PTROFF(alt2,size,void); + + if (spa_pod_compare_value(type, alt1, alt2, size) < 0) + spa_pod_builder_raw(b, alt1, size); + else + spa_pod_builder_raw(b, alt2, size); + + nc->body.type = SPA_CHOICE_Range; + } + + if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Flags) || + (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_None) || + (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Flags)) { + if (spa_pod_filter_flags_value(b, type, alt1, alt2, size) != 1) + return -EINVAL; + nc->body.type = SPA_CHOICE_Flags; + } + + if (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Flags) + return -ENOTSUP; + + if (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Flags) + return -ENOTSUP; + + if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_None) + return -ENOTSUP; + if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Enum) + return -ENOTSUP; + if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Flags) + return -ENOTSUP; + + if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Range) + return -ENOTSUP; + if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Step) + return -ENOTSUP; + if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Enum) + return -ENOTSUP; + + spa_pod_builder_pop(b, &f); + spa_pod_choice_fix_default(nc); + + return 0; +} + +static inline int spa_pod_filter_part(struct spa_pod_builder *b, + const struct spa_pod *pod, uint32_t pod_size, + const struct spa_pod *filter, uint32_t filter_size) +{ + const struct spa_pod *pp, *pf; + int res = 0; + + pf = filter; + + SPA_POD_FOREACH(pod, pod_size, pp) { + bool do_copy = false, do_advance = false; + uint32_t filter_offset = 0; + struct spa_pod_frame f; + + switch (SPA_POD_TYPE(pp)) { + case SPA_TYPE_Object: + if (pf != NULL) { + struct spa_pod_object *op = (struct spa_pod_object *) pp; + struct spa_pod_object *of = (struct spa_pod_object *) pf; + const struct spa_pod_prop *p1, *p2; + + if (SPA_POD_TYPE(pf) != SPA_POD_TYPE(pp)) + return -EINVAL; + + spa_pod_builder_push_object(b, &f, op->body.type, op->body.id); + p2 = NULL; + SPA_POD_OBJECT_FOREACH(op, p1) { + p2 = spa_pod_object_find_prop(of, p2, p1->key); + if (p2 != NULL) + res = spa_pod_filter_prop(b, p1, p2); + else if ((p1->flags & SPA_POD_PROP_FLAG_MANDATORY) != 0) + res = -EINVAL; + else + spa_pod_builder_raw_padded(b, p1, SPA_POD_PROP_SIZE(p1)); + if (res < 0) + break; + } + if (res >= 0) { + p1 = NULL; + SPA_POD_OBJECT_FOREACH(of, p2) { + p1 = spa_pod_object_find_prop(op, p1, p2->key); + if (p1 != NULL) + continue; + if ((p2->flags & SPA_POD_PROP_FLAG_MANDATORY) != 0) + res = -EINVAL; + if (res < 0) + break; + spa_pod_builder_raw_padded(b, p2, SPA_POD_PROP_SIZE(p2)); + } + } + spa_pod_builder_pop(b, &f); + do_advance = true; + } + else + do_copy = true; + break; + + case SPA_TYPE_Struct: + if (pf != NULL) { + if (SPA_POD_TYPE(pf) != SPA_POD_TYPE(pp)) + return -EINVAL; + + filter_offset = sizeof(struct spa_pod_struct); + spa_pod_builder_push_struct(b, &f); + res = spa_pod_filter_part(b, + SPA_PTROFF(pp,filter_offset,const struct spa_pod), + SPA_POD_SIZE(pp) - filter_offset, + SPA_PTROFF(pf,filter_offset,const struct spa_pod), + SPA_POD_SIZE(pf) - filter_offset); + spa_pod_builder_pop(b, &f); + do_advance = true; + } + else + do_copy = true; + break; + + default: + if (pf != NULL) { + if (SPA_POD_SIZE(pp) != SPA_POD_SIZE(pf)) + return -EINVAL; + if (memcmp(pp, pf, SPA_POD_SIZE(pp)) != 0) + return -EINVAL; + do_advance = true; + } + do_copy = true; + break; + } + if (do_copy) + spa_pod_builder_raw_padded(b, pp, SPA_POD_SIZE(pp)); + if (do_advance) { + pf = (const struct spa_pod*)spa_pod_next(pf); + if (!spa_pod_is_inside(filter, filter_size, pf)) + pf = NULL; + } + if (res < 0) + break; + } + return res; +} + +static inline int +spa_pod_filter(struct spa_pod_builder *b, + struct spa_pod **result, + const struct spa_pod *pod, + const struct spa_pod *filter) +{ + int res; + struct spa_pod_builder_state state; + + spa_return_val_if_fail(pod != NULL, -EINVAL); + spa_return_val_if_fail(b != NULL, -EINVAL); + + spa_pod_builder_get_state(b, &state); + if (filter == NULL) + res = spa_pod_builder_raw_padded(b, pod, SPA_POD_SIZE(pod)); + else + res = spa_pod_filter_part(b, pod, SPA_POD_SIZE(pod), filter, SPA_POD_SIZE(filter)); + + if (res < 0) { + spa_pod_builder_reset(b, &state); + } else if (result) { + *result = (struct spa_pod*)spa_pod_builder_deref(b, state.offset); + if (*result == NULL) + res = -ENOSPC; + } + return res; +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_POD_FILTER_H */ |