1 /* Simple Plugin API
2  *
3  * Copyright © 2018 Wim Taymans
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #ifndef SPA_POD_FILTER_H
26 #define SPA_POD_FILTER_H
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
32 #include <errno.h>
33 #include <stdint.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <string.h>
37 
38 #include <spa/param/props.h>
39 #include <spa/pod/iter.h>
40 #include <spa/pod/builder.h>
41 #include <spa/pod/compare.h>
42 
43 /**
44  * \addtogroup spa_pod
45  * \{
46  */
47 
spa_pod_choice_fix_default(struct spa_pod_choice * choice)48 static inline int spa_pod_choice_fix_default(struct spa_pod_choice *choice)
49 {
50 	void *val, *alt;
51 	int i, nvals;
52 	uint32_t type, size;
53 
54 	nvals = SPA_POD_CHOICE_N_VALUES(choice);
55 	type = SPA_POD_CHOICE_VALUE_TYPE(choice);
56 	size = SPA_POD_CHOICE_VALUE_SIZE(choice);
57 	alt = val = SPA_POD_CHOICE_VALUES(choice);
58 
59 	switch (choice->body.type) {
60 	case SPA_CHOICE_None:
61 		break;
62 	case SPA_CHOICE_Range:
63 	case SPA_CHOICE_Step:
64 		if (nvals > 1) {
65 			alt = SPA_PTROFF(alt, size, void);
66 			if (spa_pod_compare_value(type, val, alt, size) < 0)
67 				memcpy(val, alt, size);
68 		}
69 		if (nvals > 2) {
70 			alt = SPA_PTROFF(alt, size, void);
71 			if (spa_pod_compare_value(type, val, alt, size) > 0)
72 				memcpy(val, alt, size);
73 		}
74 		break;
75 	case SPA_CHOICE_Flags:
76 	case SPA_CHOICE_Enum:
77 	{
78 		void *best = NULL;
79 
80 		for (i = 1; i < nvals; i++) {
81 			alt = SPA_PTROFF(alt, size, void);
82 			if (spa_pod_compare_value(type, val, alt, size) == 0) {
83 				best = alt;
84 				break;
85 			}
86 			if (best == NULL)
87 				best = alt;
88 		}
89 		if (best)
90 			memcpy(val, best, size);
91 
92 		if (nvals <= 1)
93 			choice->body.type = SPA_CHOICE_None;
94 		break;
95 	}
96 	}
97 	return 0;
98 }
99 
spa_pod_filter_flags_value(struct spa_pod_builder * b,uint32_t type,const void * r1,const void * r2,uint32_t size)100 static inline int spa_pod_filter_flags_value(struct spa_pod_builder *b,
101 		uint32_t type, const void *r1, const void *r2, uint32_t size)
102 {
103 	switch (type) {
104 	case SPA_TYPE_Int:
105 	{
106 		int32_t val = (*(int32_t *) r1) & (*(int32_t *) r2);
107 		if (val == 0)
108 			return 0;
109 		spa_pod_builder_int(b, val);
110 		break;
111 	}
112 	case SPA_TYPE_Long:
113 	{
114 		int64_t val = (*(int64_t *) r1) & (*(int64_t *) r2);
115 		if (val == 0)
116 			return 0;
117 		spa_pod_builder_long(b, val);
118 		break;
119 	}
120 	default:
121 		return -ENOTSUP;
122 	}
123 	return 1;
124 }
125 
126 
127 static inline int
spa_pod_filter_prop(struct spa_pod_builder * b,const struct spa_pod_prop * p1,const struct spa_pod_prop * p2)128 spa_pod_filter_prop(struct spa_pod_builder *b,
129 	    const struct spa_pod_prop *p1,
130 	    const struct spa_pod_prop *p2)
131 {
132 	const struct spa_pod *v1, *v2;
133 	struct spa_pod_choice *nc;
134 	uint32_t j, k, nalt1, nalt2;
135 	void *alt1, *alt2, *a1, *a2;
136 	uint32_t type, size, p1c, p2c;
137 	struct spa_pod_frame f;
138 
139 	v1 = spa_pod_get_values(&p1->value, &nalt1, &p1c);
140 	alt1 = SPA_POD_BODY(v1);
141 	v2 = spa_pod_get_values(&p2->value, &nalt2, &p2c);
142 	alt2 = SPA_POD_BODY(v2);
143 
144 	type = v1->type;
145 	size = v1->size;
146 
147 	/* incompatible property types */
148 	if (type != v2->type || size != v2->size || p1->key != p2->key)
149 		return -EINVAL;
150 
151 	if (p1c == SPA_CHOICE_None || p1c == SPA_CHOICE_Flags) {
152 		nalt1 = 1;
153 	} else {
154 		alt1 = SPA_PTROFF(alt1, size, void);
155 		nalt1--;
156 	}
157 
158 	if (p2c == SPA_CHOICE_None || p2c == SPA_CHOICE_Flags) {
159 		nalt2 = 1;
160 	} else {
161 		alt2 = SPA_PTROFF(alt2, size, void);
162 		nalt2--;
163 	}
164 
165 	/* start with copying the property */
166 	spa_pod_builder_prop(b, p1->key, p1->flags & p2->flags);
167 	spa_pod_builder_push_choice(b, &f, 0, 0);
168 	nc = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f);
169 
170 	/* default value */
171 	spa_pod_builder_primitive(b, v1);
172 
173 	if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_None) ||
174 	    (p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Enum) ||
175 	    (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_None) ||
176 	    (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Enum)) {
177 		int n_copied = 0;
178 		/* copy all equal values but don't copy the default value again */
179 		for (j = 0, a1 = alt1; j < nalt1; j++, a1 = SPA_PTROFF(a1, size, void)) {
180 			for (k = 0, a2 = alt2; k < nalt2; k++, a2 = SPA_PTROFF(a2,size,void)) {
181 				if (spa_pod_compare_value(type, a1, a2, size) == 0) {
182 					if (p1c == SPA_CHOICE_Enum || j > 0)
183 						spa_pod_builder_raw(b, a1, size);
184 					n_copied++;
185 				}
186 			}
187 		}
188 		if (n_copied == 0)
189 			return -EINVAL;
190 		nc->body.type = SPA_CHOICE_Enum;
191 	}
192 
193 	if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Range) ||
194 	    (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Range)) {
195 		int n_copied = 0;
196 		/* copy all values inside the range */
197 		for (j = 0, a1 = alt1, a2 = alt2; j < nalt1; j++, a1 = SPA_PTROFF(a1,size,void)) {
198 			if (spa_pod_compare_value(type, a1, a2, size) < 0)
199 				continue;
200 			if (spa_pod_compare_value(type, a1, SPA_PTROFF(a2,size,void), size) > 0)
201 				continue;
202 			spa_pod_builder_raw(b, a1, size);
203 			n_copied++;
204 		}
205 		if (n_copied == 0)
206 			return -EINVAL;
207 		nc->body.type = SPA_CHOICE_Enum;
208 	}
209 
210 	if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Step) ||
211 	    (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Step)) {
212 		return -ENOTSUP;
213 	}
214 
215 	if ((p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_None) ||
216 	    (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Enum)) {
217 		int n_copied = 0;
218 		/* copy all values inside the range */
219 		for (k = 0, a1 = alt1, a2 = alt2; k < nalt2; k++, a2 = SPA_PTROFF(a2,size,void)) {
220 			if (spa_pod_compare_value(type, a2, a1, size) < 0)
221 				continue;
222 			if (spa_pod_compare_value(type, a2, SPA_PTROFF(a1,size,void), size) > 0)
223 				continue;
224 			spa_pod_builder_raw(b, a2, size);
225 			n_copied++;
226 		}
227 		if (n_copied == 0)
228 			return -EINVAL;
229 		nc->body.type = SPA_CHOICE_Enum;
230 	}
231 
232 	if ((p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Range) ||
233 	    (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Step) ||
234 	    (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Range) ||
235 	    (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Step)) {
236 		if (spa_pod_compare_value(type, alt1, alt2, size) < 0)
237 			spa_pod_builder_raw(b, alt2, size);
238 		else
239 			spa_pod_builder_raw(b, alt1, size);
240 
241 		alt1 = SPA_PTROFF(alt1,size,void);
242 		alt2 = SPA_PTROFF(alt2,size,void);
243 
244 		if (spa_pod_compare_value(type, alt1, alt2, size) < 0)
245 			spa_pod_builder_raw(b, alt1, size);
246 		else
247 			spa_pod_builder_raw(b, alt2, size);
248 
249 		nc->body.type = SPA_CHOICE_Range;
250 	}
251 
252 	if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Flags) ||
253 	    (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_None) ||
254 	    (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Flags)) {
255 		if (spa_pod_filter_flags_value(b, type, alt1, alt2, size) != 1)
256 			return -EINVAL;
257 		nc->body.type = SPA_CHOICE_Flags;
258 	}
259 
260 	if (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Flags)
261 		return -ENOTSUP;
262 
263 	if (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Flags)
264 		return -ENOTSUP;
265 
266 	if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_None)
267 		return -ENOTSUP;
268 	if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Enum)
269 		return -ENOTSUP;
270 	if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Flags)
271 		return -ENOTSUP;
272 
273 	if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Range)
274 		return -ENOTSUP;
275 	if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Step)
276 		return -ENOTSUP;
277 	if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Enum)
278 		return -ENOTSUP;
279 
280 	spa_pod_builder_pop(b, &f);
281 	spa_pod_choice_fix_default(nc);
282 
283 	return 0;
284 }
285 
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)286 static inline int spa_pod_filter_part(struct spa_pod_builder *b,
287 	       const struct spa_pod *pod, uint32_t pod_size,
288 	       const struct spa_pod *filter, uint32_t filter_size)
289 {
290 	const struct spa_pod *pp, *pf;
291 	int res = 0;
292 
293 	pf = filter;
294 
295 	SPA_POD_FOREACH(pod, pod_size, pp) {
296 		bool do_copy = false, do_advance = false;
297 		uint32_t filter_offset = 0;
298 		struct spa_pod_frame f;
299 
300 		switch (SPA_POD_TYPE(pp)) {
301 		case SPA_TYPE_Object:
302 			if (pf != NULL) {
303 				struct spa_pod_object *op = (struct spa_pod_object *) pp;
304 				struct spa_pod_object *of = (struct spa_pod_object *) pf;
305 				const struct spa_pod_prop *p1, *p2;
306 
307 				if (SPA_POD_TYPE(pf) != SPA_POD_TYPE(pp))
308 					return -EINVAL;
309 
310 				spa_pod_builder_push_object(b, &f, op->body.type, op->body.id);
311 				p2 = NULL;
312 				SPA_POD_OBJECT_FOREACH(op, p1) {
313 					p2 = spa_pod_object_find_prop(of, p2, p1->key);
314 					if (p2 != NULL)
315 						res = spa_pod_filter_prop(b, p1, p2);
316 					else if ((p1->flags & SPA_POD_PROP_FLAG_MANDATORY) != 0)
317 						res = -EINVAL;
318 					else
319 						spa_pod_builder_raw_padded(b, p1, SPA_POD_PROP_SIZE(p1));
320 					if (res < 0)
321 						break;
322 				}
323 				if (res >= 0) {
324 					p1 = NULL;
325 					SPA_POD_OBJECT_FOREACH(of, p2) {
326 						p1 = spa_pod_object_find_prop(op, p1, p2->key);
327 						if (p1 != NULL)
328 							continue;
329 						if ((p2->flags & SPA_POD_PROP_FLAG_MANDATORY) != 0)
330 							res = -EINVAL;
331 						if (res < 0)
332 							break;
333 						spa_pod_builder_raw_padded(b, p2, SPA_POD_PROP_SIZE(p2));
334 					}
335 				}
336 				spa_pod_builder_pop(b, &f);
337 				do_advance = true;
338 			}
339 			else
340 				do_copy = true;
341 			break;
342 
343 		case SPA_TYPE_Struct:
344 			if (pf != NULL) {
345 				if (SPA_POD_TYPE(pf) != SPA_POD_TYPE(pp))
346 					return -EINVAL;
347 
348 				filter_offset = sizeof(struct spa_pod_struct);
349 				spa_pod_builder_push_struct(b, &f);
350 				res = spa_pod_filter_part(b,
351 					SPA_PTROFF(pp,filter_offset,const struct spa_pod),
352 					SPA_POD_SIZE(pp) - filter_offset,
353 					SPA_PTROFF(pf,filter_offset,const struct spa_pod),
354 					SPA_POD_SIZE(pf) - filter_offset);
355 			        spa_pod_builder_pop(b, &f);
356 				do_advance = true;
357 			}
358 			else
359 				do_copy = true;
360 			break;
361 
362 		default:
363 			if (pf != NULL) {
364 				if (SPA_POD_SIZE(pp) != SPA_POD_SIZE(pf))
365 					return -EINVAL;
366 				if (memcmp(pp, pf, SPA_POD_SIZE(pp)) != 0)
367 					return -EINVAL;
368 				do_advance = true;
369 			}
370 			do_copy = true;
371 			break;
372 		}
373 		if (do_copy)
374 			spa_pod_builder_raw_padded(b, pp, SPA_POD_SIZE(pp));
375 		if (do_advance) {
376 			pf = (const struct spa_pod*)spa_pod_next(pf);
377 			if (!spa_pod_is_inside(filter, filter_size, pf))
378 				pf = NULL;
379 		}
380 		if (res < 0)
381 			break;
382 	}
383 	return res;
384 }
385 
386 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)387 spa_pod_filter(struct spa_pod_builder *b,
388 	       struct spa_pod **result,
389 	       const struct spa_pod *pod,
390 	       const struct spa_pod *filter)
391 {
392 	int res;
393 	struct spa_pod_builder_state state;
394 
395         spa_return_val_if_fail(pod != NULL, -EINVAL);
396         spa_return_val_if_fail(b != NULL, -EINVAL);
397 
398 	spa_pod_builder_get_state(b, &state);
399 	if (filter == NULL)
400 		res = spa_pod_builder_raw_padded(b, pod, SPA_POD_SIZE(pod));
401 	else
402 		res = spa_pod_filter_part(b, pod, SPA_POD_SIZE(pod), filter, SPA_POD_SIZE(filter));
403 
404 	if (res < 0) {
405 		spa_pod_builder_reset(b, &state);
406 	} else if (result) {
407 		*result = (struct spa_pod*)spa_pod_builder_deref(b, state.offset);
408 		if (*result == NULL)
409 			res = -ENOSPC;
410 	}
411 	return res;
412 }
413 
414 /**
415  * \}
416  */
417 
418 #ifdef __cplusplus
419 } /* extern "C" */
420 #endif
421 
422 #endif /* SPA_POD_FILTER_H */
423