PipeWire  1.6.4
filter.h
Go to the documentation of this file.
1 /* Simple Plugin API */
2 /* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
3 /* SPDX-License-Identifier: MIT */
4 
5 #ifndef SPA_POD_FILTER_H
6 #define SPA_POD_FILTER_H
7 
8 #include <errno.h>
9 #include <stdint.h>
10 #include <stddef.h>
11 #include <stdio.h>
12 #include <string.h>
13 
14 #include <spa/param/props.h>
15 #include <spa/pod/iter.h>
16 #include <spa/pod/builder.h>
17 #include <spa/pod/compare.h>
18 #include <spa/pod/dynamic.h>
19 
20 #ifdef __cplusplus
21 extern "C" {
22 #endif
23 
24 #ifndef SPA_API_POD_FILTER
25  #ifdef SPA_API_IMPL
26  #define SPA_API_POD_FILTER SPA_API_IMPL
27  #else
28  #define SPA_API_POD_FILTER static inline
29  #endif
30 #endif
31 
38  uint32_t type, const void *r1, const void *r2, uint32_t size)
39 {
40  switch (type) {
41  case SPA_TYPE_Int:
42  {
43  int32_t val;
44  if (size < sizeof(int32_t))
45  return -EINVAL;
46  val = (*(int32_t *) r1) & (*(int32_t *) r2);
47  if (val == 0)
48  return 0;
49  spa_pod_builder_int(b, val);
50  break;
51  }
52  case SPA_TYPE_Long:
53  {
54  int64_t val;
55  if (size < sizeof(int64_t))
56  return -EINVAL;
57  val = (*(int64_t *) r1) & (*(int64_t *) r2);
58  if (val == 0)
59  return 0;
60  spa_pod_builder_long(b, val);
61  break;
62  }
63  default:
64  return -ENOTSUP;
65  }
66  return 1;
67 }
68 
71  const struct spa_pod_prop *p1,
72  const struct spa_pod_prop *p2)
73 {
74  const struct spa_pod *v1, *v2;
75  struct spa_pod_choice *nc, dummy;
76  uint32_t j, k, nalt1, nalt2, nc_offs;
77  void *alt1, *alt2, *a1, *a2;
78  uint32_t type, size, p1c, p2c;
79  struct spa_pod_frame f;
80  int res, n_copied = 0;
81 
82  v1 = spa_pod_get_values(&p1->value, &nalt1, &p1c);
83  v2 = spa_pod_get_values(&p2->value, &nalt2, &p2c);
84 
85  /* empty/invalid choices */
86  if (nalt1 < 1 || nalt2 < 1)
87  return -EINVAL;
88 
89  alt1 = SPA_POD_BODY(v1);
90  alt2 = SPA_POD_BODY(v2);
91 
92  type = v1->type;
93  size = v1->size;
94 
95  /* incompatible property types */
96  if (type != v2->type || size != v2->size || p1->key != p2->key)
97  return -EINVAL;
98 
99  /* start with copying the property */
100  spa_pod_builder_prop(b, p1->key, p1->flags & p2->flags);
102  spa_zero(dummy);
103 
104  nc_offs = f.offset;
105 
106  /* start with an empty child and we will select a good default
107  * below */
108  spa_pod_builder_child(b, size, type);
109 
110  /* we should prefer alt2 values but only if they are within the
111  * range. Swap the order otherwise. */
112  if (!spa_pod_compare_is_valid_choice(type, size, alt2, alt2, nalt2, p2c)) {
113  SPA_SWAP(alt2, alt1);
114  SPA_SWAP(nalt2, nalt1);
115  SPA_SWAP(p2c, p1c);
116  }
117 
118  if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_None) ||
119  (p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Enum) ||
120  (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_None) ||
121  (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Enum)) {
122  /* copy all equal values. Start with alt2 so that they are prefered. */
123  for (j = 0, a2 = alt2; j < nalt2; j++, a2 = SPA_PTROFF(a2, size, void)) {
124  for (k = 0, a1 = alt1; k < nalt1; k++, a1 = SPA_PTROFF(a1,size,void)) {
125  if (spa_pod_compare_value(type, a1, a2, size) == 0) {
126  if (n_copied++ == 0)
127  spa_pod_builder_raw(b, a1, size);
128  spa_pod_builder_raw(b, a1, size);
129  }
130  }
131  }
132  }
133  else if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Range) ||
134  (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Range) ||
135  (p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Step) ||
136  (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Step)) {
137  void *min = SPA_PTROFF(alt2,size,void);
138  void *max = SPA_PTROFF(min,size,void);
139  void *step = p2c == SPA_CHOICE_Step ? SPA_PTROFF(max,size,void) : NULL;
140  bool found_def = false;
141 
142  /* we should prefer the alt2 range default value but only if valid */
143  if (spa_pod_compare_value(type, alt2, min, size) >= 0 &&
144  spa_pod_compare_value(type, alt2, max, size) <= 0) {
145  for (j = 0, a1 = alt1; j < nalt1; j++, a1 = SPA_PTROFF(a1,size,void)) {
146  if (spa_pod_compare_value(type, a1, alt2, size) == 0) {
147  /* it is in the enum, use as default then */
148  spa_pod_builder_raw(b, a1, size);
149  found_def = true;
150  break;
151  }
152  }
153  }
154  /* copy all values inside the range */
155  for (j = 0, a1 = alt1; j < nalt1; j++, a1 = SPA_PTROFF(a1,size,void)) {
156  if ((res = spa_pod_compare_is_in_range(type, a1, min, max, step, size)) < 0)
157  return res;
158  if (res == 0)
159  continue;
160  if (n_copied++ == 0 && !found_def)
161  spa_pod_builder_raw(b, a1, size);
162  spa_pod_builder_raw(b, a1, size);
163  }
164  }
165  else if ((p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_None) ||
166  (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Enum) ||
167  (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_None) ||
168  (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Enum)) {
169  void *min = SPA_PTROFF(alt1,size,void);
170  void *max = SPA_PTROFF(min,size,void);
171  void *step = p1c == SPA_CHOICE_Step ? SPA_PTROFF(max,size,void) : NULL;
172 
173  /* copy all values inside the range, this will automatically prefer
174  * a valid alt2 value */
175  for (j = 0, a2 = alt2; j < nalt2; j++, a2 = SPA_PTROFF(a2,size,void)) {
176  if ((res = spa_pod_compare_is_in_range(type, a2, min, max, step, size)) < 0)
177  return res;
178  if (res == 0)
179  continue;
180  if (n_copied++ == 0)
181  spa_pod_builder_raw(b, a2, size);
182  spa_pod_builder_raw(b, a2, size);
183  }
184  }
185  else if ((p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Range) ||
186  (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Step) ||
187  (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Range) ||
188  (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Step)) {
189  void *min1 = SPA_PTROFF(alt1,size,void);
190  void *max1 = SPA_PTROFF(min1,size,void);
191  void *min2 = SPA_PTROFF(alt2,size,void);
192  void *max2 = SPA_PTROFF(min2,size,void);
193 
194  /* max of min */
195  if (spa_pod_compare_value(type, min1, min2, size) < 0)
196  min1 = min2;
197  /* min of max */
198  if (spa_pod_compare_value(type, max2, max1, size) < 0)
199  max1 = max2;
200 
201  /* reject impossible range */
202  if (spa_pod_compare_value(type, max1, min1, size) < 0)
203  return -EINVAL;
204 
205  /* prefer alt2 if in new range */
206  a1 = alt2;
207  if ((res = spa_pod_compare_is_in_range(type, a1, min1, max1, NULL, size)) < 0)
208  return res;
209  if (res == 0) {
210  /* try alt1 otherwise */
211  a1 = alt1;
212  if ((res = spa_pod_compare_is_in_range(type, a1, min1, max1, NULL, size)) < 0)
213  return res;
214  /* fall back to new min value then */
215  if (res == 0)
216  a1 = min1;
217  }
218 
219  spa_pod_builder_raw(b, a1, size);
220  spa_pod_builder_raw(b, min1, size);
221  spa_pod_builder_raw(b, max1, size);
222  nc = (struct spa_pod_choice*)spa_pod_builder_deref_fallback(b, nc_offs, &dummy.pod);
223  nc->body.type = SPA_CHOICE_Range;
224  }
225  else if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Flags) ||
226  (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_None) ||
227  (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Flags)) {
228  if (spa_pod_filter_flags_value(b, type, alt1, alt2, size) != 1)
229  return -EINVAL;
230  nc = (struct spa_pod_choice*)spa_pod_builder_deref_fallback(b, nc_offs, &dummy.pod);
231  nc->body.type = SPA_CHOICE_Flags;
232  }
233  else if (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Flags)
234  return -ENOTSUP;
235  else if (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Flags)
236  return -ENOTSUP;
237  else if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Flags)
238  return -ENOTSUP;
239  else if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Range)
240  return -ENOTSUP;
241  else if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Step)
242  return -ENOTSUP;
243  else if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Enum)
244  return -ENOTSUP;
245 
246  nc = (struct spa_pod_choice*)spa_pod_builder_deref_fallback(b, nc_offs, &dummy.pod);
247  if (nc->body.type == SPA_CHOICE_None) {
248  if (n_copied == 0) {
249  return -EINVAL;
250  } else if (n_copied == 1) {
251  /* we always copy the default value twice, so remove it
252  * again when it was the only one added */
253  spa_pod_builder_remove(b, size);
254  } else if (n_copied > 1) {
255  nc->body.type = SPA_CHOICE_Enum;
256  }
257  }
258  spa_pod_builder_pop(b, &f);
259 
260  return 0;
261 }
262 
264  const struct spa_pod *pod, uint32_t pod_size,
265  const struct spa_pod *filter, uint32_t filter_size)
266 {
267  const struct spa_pod *pp, *pf;
268  int res = 0;
269 
270  pf = filter;
271 
272  SPA_POD_FOREACH(pod, pod_size, pp) {
273  bool do_copy = false, do_advance = false;
274  uint32_t filter_offset = 0;
275  struct spa_pod_frame f;
276 
277  switch (pp->type) {
278  case SPA_TYPE_Object:
279  if (pf != NULL) {
280  struct spa_pod_object *op = (struct spa_pod_object *) pp;
281  struct spa_pod_object *of = (struct spa_pod_object *) pf;
282  const struct spa_pod_prop *p1, *p2;
283 
284  if (pf->type != pp->type)
285  return -EINVAL;
286 
287  spa_pod_builder_push_object(b, &f, op->body.type, op->body.id);
288  p2 = NULL;
289  SPA_POD_OBJECT_FOREACH(op, p1) {
290  p2 = spa_pod_object_find_prop(of, p2, p1->key);
291  if (p2 != NULL)
292  res = spa_pod_filter_prop(b, p1, p2);
294  res = -EINVAL;
297  if (res < 0)
298  break;
299  }
300  if (res >= 0) {
301  p1 = NULL;
302  SPA_POD_OBJECT_FOREACH(of, p2) {
303  p1 = spa_pod_object_find_prop(op, p1, p2->key);
304  if (p1 != NULL)
305  continue;
307  res = -EINVAL;
310  if (res < 0)
311  break;
312  }
313  }
314  spa_pod_builder_pop(b, &f);
315  do_advance = true;
316  }
317  else
318  do_copy = true;
319  break;
320 
321  case SPA_TYPE_Struct:
322  if (pf != NULL) {
323  if (pf->type != pp->type)
324  return -EINVAL;
325 
326  filter_offset = sizeof(struct spa_pod_struct);
329  SPA_PTROFF(pp,filter_offset,const struct spa_pod),
330  SPA_POD_SIZE(pp) - filter_offset,
331  SPA_PTROFF(pf,filter_offset,const struct spa_pod),
332  SPA_POD_SIZE(pf) - filter_offset);
333  spa_pod_builder_pop(b, &f);
334  do_advance = true;
335  }
336  else
337  do_copy = true;
338  break;
339 
340  default:
341  if (pf != NULL) {
342  if (spa_pod_memcmp(pp, pf) != 0)
343  return -EINVAL;
344  do_advance = true;
345  }
346  do_copy = true;
347  break;
348  }
349  if (do_copy)
351  if (do_advance) {
352  pf = (const struct spa_pod*)spa_pod_next(pf);
353  if (!spa_pod_is_inside(filter, filter_size, pf))
354  pf = NULL;
355  }
356  if (res < 0)
357  break;
358  }
359  return res;
360 }
361 
364  struct spa_pod **result,
365  const struct spa_pod *pod,
366  const struct spa_pod *filter)
367 {
368  int res;
369  struct spa_pod_builder_state state;
370 
371  spa_return_val_if_fail(pod != NULL, -EINVAL);
372  spa_return_val_if_fail(b != NULL, -EINVAL);
373 
374  spa_pod_builder_get_state(b, &state);
375  if (filter == NULL) {
377  } else {
378  struct spa_pod_dynamic_builder db;
379  spa_pod_dynamic_builder_continue(&db, b);
380  res = spa_pod_filter_part(&db.b, pod, SPA_POD_SIZE(pod), filter, SPA_POD_SIZE(filter));
381  if (res >= 0)
382  res = spa_pod_builder_raw_padded(b, db.b.data, db.b.state.offset);
383  spa_pod_dynamic_builder_clean(&db);
384  }
385  if (res >= 0 && result) {
386  *result = (struct spa_pod*)spa_pod_builder_deref(b, state.offset);
387  if (*result == NULL)
388  res = -ENOSPC;
389  }
390  return res;
391 }
392 
394 {
395  struct spa_pod_prop *res;
396  int count = 0;
397 
399  if (spa_pod_is_choice(&res->value) &&
401  uint32_t nvals, choice;
402  struct spa_pod *v = spa_pod_get_values(&res->value, &nvals, &choice);
403  const void *vals = SPA_POD_BODY(v);
404 
405  if (nvals < 1)
406  continue;
407 
409  vals, vals, nvals, choice)) {
410  ((struct spa_pod_choice*)&res->value)->body.type = SPA_CHOICE_None;
411  count++;
412  }
413  }
414  }
415  return count;
416 }
418 {
419  if (!spa_pod_is_object(pod))
420  return -EINVAL;
422 }
427 #ifdef __cplusplus
428 } /* extern "C" */
429 #endif
430 
431 #endif /* SPA_POD_FILTER_H */
spa/pod/builder.h
spa/pod/compare.h
uint32_t int int res
Definition: core.h:433
SPA_API_POD_BUILDER int spa_pod_builder_int(struct spa_pod_builder *builder, int32_t val)
Definition: builder.h:296
SPA_API_POD_BUILDER int spa_pod_builder_long(struct spa_pod_builder *builder, int64_t val)
Definition: builder.h:305
SPA_API_POD_BUILDER int spa_pod_builder_push_choice(struct spa_pod_builder *builder, struct spa_pod_frame *frame, uint32_t type, uint32_t flags)
Definition: builder.h:446
SPA_API_POD_COMPARE int spa_pod_compare_is_in_range(uint32_t type, const void *v, const void *min, const void *max, const void *step, uint32_t size 1)
Definition: compare.h:225
SPA_API_POD_BUILDER int spa_pod_builder_child(struct spa_pod_builder *builder, uint32_t size, uint32_t type)
Definition: builder.h:268
SPA_API_POD_ITER void * spa_pod_next(const void *iter)
Definition: iter.h:46
SPA_API_POD_ITER struct spa_pod * spa_pod_get_values(const struct spa_pod *pod, uint32_t *n_vals, uint32_t *choice)
Definition: iter.h:229
SPA_API_POD_FILTER int spa_pod_filter_flags_value(struct spa_pod_builder *b, uint32_t type, const void *r1, const void *r2, uint32_t size)
Definition: filter.h:44
#define SPA_POD_PROP_FLAG_MANDATORY
is mandatory, when filtering, both sides need this property or filtering fails.
Definition: pod.h:241
SPA_API_POD_COMPARE int spa_pod_compare_is_valid_choice(uint32_t type, uint32_t size, const void *val, const void *vals, uint32_t n_vals, uint32_t choice)
Definition: compare.h:236
SPA_API_POD_FILTER int spa_pod_filter_prop(struct spa_pod_builder *b, const struct spa_pod_prop *p1, const struct spa_pod_prop *p2)
Definition: filter.h:77
SPA_API_POD_BUILDER void spa_pod_builder_get_state(struct spa_pod_builder *builder, struct spa_pod_builder_state *state)
Definition: builder.h:75
#define SPA_POD_PROP_FLAG_DROP
drop property, when filtering, both sides need the property or it will be dropped.
Definition: pod.h:246
SPA_API_POD_BUILDER struct spa_pod * spa_pod_builder_deref(struct spa_pod_builder *builder, uint32_t offset)
Definition: builder.h:121
SPA_API_POD_COMPARE int spa_pod_compare_value(uint32_t type, const void *r1, const void *r2, uint32_t size)
Definition: compare.h:43
#define SPA_POD_OBJECT_FOREACH(obj, iter)
Definition: iter.h:118
#define SPA_POD_BODY(pod)
Definition: pod.h:44
SPA_API_POD_BODY int spa_pod_is_object(const struct spa_pod *pod)
Definition: body.h:428
SPA_API_POD_ITER const struct spa_pod_prop * spa_pod_object_find_prop(const struct spa_pod_object *pod, const struct spa_pod_prop *start, uint32_t key)
Definition: iter.h:254
SPA_API_POD_BUILDER int spa_pod_builder_push_struct(struct spa_pod_builder *builder, struct spa_pod_frame *frame)
Definition: builder.h:462
SPA_API_POD_FILTER int spa_pod_filter_make(struct spa_pod *pod)
Definition: filter.h:424
SPA_API_POD_BUILDER int spa_pod_builder_push_object(struct spa_pod_builder *builder, struct spa_pod_frame *frame, uint32_t type, uint32_t id)
Definition: builder.h:475
#define SPA_POD_PROP_FLAG_DONT_FIXATE
choices need no fixation
Definition: pod.h:244
SPA_API_POD_BUILDER void spa_pod_builder_remove(struct spa_pod_builder *builder, uint32_t size)
Definition: builder.h:187
SPA_API_POD_FILTER int spa_pod_filter(struct spa_pod_builder *b, struct spa_pod **result, const struct spa_pod *pod, const struct spa_pod *filter)
Definition: filter.h:370
#define SPA_POD_FOREACH(pod, size, iter)
Definition: iter.h:105
#define SPA_POD_PROP_SIZE(prop)
Definition: pod.h:224
SPA_API_POD_ITER bool spa_pod_is_inside(const void *pod, uint32_t size, const void *iter)
Definition: iter.h:38
SPA_API_POD_BUILDER int spa_pod_builder_raw(struct spa_pod_builder *builder, const void *data, uint32_t size)
Definition: builder.h:150
SPA_API_POD_FILTER int spa_pod_filter_object_make(struct spa_pod_object *pod)
Definition: filter.h:400
SPA_API_POD_BUILDER int spa_pod_builder_raw_padded(struct spa_pod_builder *builder, const void *data, uint32_t size)
Definition: builder.h:205
SPA_API_POD_BUILDER void * spa_pod_builder_pop(struct spa_pod_builder *builder, struct spa_pod_frame *frame)
Definition: builder.h:213
SPA_API_POD_BUILDER struct spa_pod * spa_pod_builder_deref_fallback(struct spa_pod_builder *builder, uint32_t offset, struct spa_pod *fallback)
Definition: builder.h:108
SPA_API_POD_FILTER 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)
Definition: filter.h:270
#define SPA_POD_SIZE(pod)
Definition: pod.h:35
SPA_API_POD_COMPARE int spa_pod_memcmp(const struct spa_pod *a, const struct spa_pod *b)
Definition: compare.h:90
SPA_API_POD_BODY int spa_pod_is_choice(const struct spa_pod *pod)
Definition: body.h:389
SPA_API_POD_BUILDER int spa_pod_builder_prop(struct spa_pod_builder *builder, uint32_t key, uint32_t flags)
Definition: builder.h:490
@ SPA_CHOICE_Step
range with step: default, min, max, step
Definition: pod.h:163
@ SPA_CHOICE_None
no choice, first value is current
Definition: pod.h:161
@ SPA_CHOICE_Flags
flags: first value is flags
Definition: pod.h:165
@ SPA_CHOICE_Range
range: default, min, max
Definition: pod.h:162
@ SPA_CHOICE_Enum
list: default, alternative,...
Definition: pod.h:164
@ SPA_TYPE_Int
Definition: type.h:45
@ SPA_TYPE_Long
Definition: type.h:46
@ SPA_TYPE_Object
Definition: type.h:56
@ SPA_TYPE_Struct
Definition: type.h:55
#define spa_zero(x)
Definition: defs.h:512
#define spa_return_val_if_fail(expr, val)
Definition: defs.h:460
#define SPA_SWAP(a, b)
Definition: defs.h:195
#define SPA_FLAG_IS_SET(field, flag)
Definition: defs.h:90
#define SPA_PTROFF(ptr_, offset_, type_)
Return the address (buffer + offset) as pointer of type.
Definition: defs.h:222
spa/pod/iter.h
#define SPA_API_POD_FILTER
Definition: filter.h:35
spa/utils/string.h
Definition: builder.h:42
Definition: builder.h:63
void * data
Definition: builder.h:64
uint32_t type
type of choice, one of enum spa_choice_type
Definition: pod.h:169
Definition: pod.h:176
struct spa_pod_choice_body body
Definition: pod.h:178
struct spa_pod pod
Definition: pod.h:177
Definition: dynamic.h:29
struct spa_pod_builder b
Definition: dynamic.h:30
Definition: body.h:38
uint32_t type
one of enum spa_type
Definition: pod.h:197
uint32_t id
id of the object, depends on the object type
Definition: pod.h:198
Definition: pod.h:202
struct spa_pod_object_body body
Definition: pod.h:204
Definition: pod.h:227
uint32_t key
key of property, list of valid keys depends on the object type
Definition: pod.h:228
uint32_t flags
flags for property
Definition: pod.h:248
struct spa_pod value
Definition: pod.h:249
Definition: pod.h:186
Definition: pod.h:57
uint32_t type
Definition: pod.h:59
uint32_t size
Definition: pod.h:58