summaryrefslogtreecommitdiffstats
path: root/third_party/pipewire/spa/pod/filter.h
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/pipewire/spa/pod/filter.h')
-rw-r--r--third_party/pipewire/spa/pod/filter.h422
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 */