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_BUILDER_H
26 #define SPA_POD_BUILDER_H
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
32 /** \defgroup spa_pod POD
33  * Binary data serialization format
34  */
35 
36 /**
37  * \addtogroup spa_pod
38  * \{
39  */
40 
41 #include <stdarg.h>
42 
43 #include <spa/utils/hook.h>
44 #include <spa/pod/iter.h>
45 #include <spa/pod/vararg.h>
46 
47 struct spa_pod_builder_state {
48 	uint32_t offset;
49 #define SPA_POD_BUILDER_FLAG_BODY	(1<<0)
50 #define SPA_POD_BUILDER_FLAG_FIRST	(1<<1)
51 	uint32_t flags;
52 	struct spa_pod_frame *frame;
53 };
54 
55 struct spa_pod_builder;
56 
57 struct spa_pod_builder_callbacks {
58 #define SPA_VERSION_POD_BUILDER_CALLBACKS 0
59 	uint32_t version;
60 
61 	int (*overflow) (void *data, uint32_t size);
62 };
63 
64 struct spa_pod_builder {
65 	void *data;
66 	uint32_t size;
67 	uint32_t _padding;
68 	struct spa_pod_builder_state state;
69 	struct spa_callbacks callbacks;
70 };
71 
72 #define SPA_POD_BUILDER_INIT(buffer,size)  (struct spa_pod_builder){ buffer, size, 0, {}, {} }
73 
74 static inline void
spa_pod_builder_get_state(struct spa_pod_builder * builder,struct spa_pod_builder_state * state)75 spa_pod_builder_get_state(struct spa_pod_builder *builder, struct spa_pod_builder_state *state)
76 {
77 	*state = builder->state;
78 }
79 
80 static inline void
spa_pod_builder_set_callbacks(struct spa_pod_builder * builder,const struct spa_pod_builder_callbacks * callbacks,void * data)81 spa_pod_builder_set_callbacks(struct spa_pod_builder *builder,
82 		const struct spa_pod_builder_callbacks *callbacks, void *data)
83 {
84 	builder->callbacks = SPA_CALLBACKS_INIT(callbacks, data);
85 }
86 
87 static inline void
spa_pod_builder_reset(struct spa_pod_builder * builder,struct spa_pod_builder_state * state)88 spa_pod_builder_reset(struct spa_pod_builder *builder, struct spa_pod_builder_state *state)
89 {
90 	struct spa_pod_frame *f;
91 	uint32_t size = builder->state.offset - state->offset;
92 	builder->state = *state;
93 	for (f = builder->state.frame; f ; f = f->parent)
94 		f->pod.size -= size;
95 }
96 
spa_pod_builder_init(struct spa_pod_builder * builder,void * data,uint32_t size)97 static inline void spa_pod_builder_init(struct spa_pod_builder *builder, void *data, uint32_t size)
98 {
99 	*builder = SPA_POD_BUILDER_INIT(data, size);
100 }
101 
102 static inline struct spa_pod *
spa_pod_builder_deref(struct spa_pod_builder * builder,uint32_t offset)103 spa_pod_builder_deref(struct spa_pod_builder *builder, uint32_t offset)
104 {
105 	uint32_t size = builder->size;
106 	if (offset + 8 <= size) {
107 		struct spa_pod *pod = SPA_PTROFF(builder->data, offset, struct spa_pod);
108 		if (offset + SPA_POD_SIZE(pod) <= size)
109 			return pod;
110 	}
111 	return NULL;
112 }
113 
114 static inline struct spa_pod *
spa_pod_builder_frame(struct spa_pod_builder * builder,struct spa_pod_frame * frame)115 spa_pod_builder_frame(struct spa_pod_builder *builder, struct spa_pod_frame *frame)
116 {
117 	if (frame->offset + SPA_POD_SIZE(&frame->pod) <= builder->size)
118 		return SPA_PTROFF(builder->data, frame->offset, struct spa_pod);
119 	return NULL;
120 }
121 
122 static inline void
spa_pod_builder_push(struct spa_pod_builder * builder,struct spa_pod_frame * frame,const struct spa_pod * pod,uint32_t offset)123 spa_pod_builder_push(struct spa_pod_builder *builder,
124 		     struct spa_pod_frame *frame,
125 		     const struct spa_pod *pod,
126 		     uint32_t offset)
127 {
128 	frame->pod = *pod;
129 	frame->offset = offset;
130 	frame->parent = builder->state.frame;
131 	frame->flags = builder->state.flags;
132 	builder->state.frame = frame;
133 
134 	if (frame->pod.type == SPA_TYPE_Array || frame->pod.type == SPA_TYPE_Choice)
135 		builder->state.flags = SPA_POD_BUILDER_FLAG_FIRST | SPA_POD_BUILDER_FLAG_BODY;
136 }
137 
spa_pod_builder_raw(struct spa_pod_builder * builder,const void * data,uint32_t size)138 static inline int spa_pod_builder_raw(struct spa_pod_builder *builder, const void *data, uint32_t size)
139 {
140 	int res = 0;
141 	struct spa_pod_frame *f;
142 	uint32_t offset = builder->state.offset;
143 
144 	if (offset + size > builder->size) {
145 		res = -ENOSPC;
146 		spa_callbacks_call_res(&builder->callbacks, struct spa_pod_builder_callbacks, res,
147 				overflow, 0, offset + size);
148 	}
149 	if (res == 0 && data)
150 		memcpy(SPA_PTROFF(builder->data, offset, void), data, size);
151 
152 	builder->state.offset += size;
153 
154 	for (f = builder->state.frame; f ; f = f->parent)
155 		f->pod.size += size;
156 
157 	return res;
158 }
159 
spa_pod_builder_pad(struct spa_pod_builder * builder,uint32_t size)160 static inline int spa_pod_builder_pad(struct spa_pod_builder *builder, uint32_t size)
161 {
162 	uint64_t zeroes = 0;
163 	size = SPA_ROUND_UP_N(size, 8) - size;
164 	return size ? spa_pod_builder_raw(builder, &zeroes, size) : 0;
165 }
166 
167 static inline int
spa_pod_builder_raw_padded(struct spa_pod_builder * builder,const void * data,uint32_t size)168 spa_pod_builder_raw_padded(struct spa_pod_builder *builder, const void *data, uint32_t size)
169 {
170 	int r, res = spa_pod_builder_raw(builder, data, size);
171 	if ((r = spa_pod_builder_pad(builder, size)) < 0)
172 		res = r;
173 	return res;
174 }
175 
spa_pod_builder_pop(struct spa_pod_builder * builder,struct spa_pod_frame * frame)176 static inline void *spa_pod_builder_pop(struct spa_pod_builder *builder, struct spa_pod_frame *frame)
177 {
178 	struct spa_pod *pod;
179 
180 	if (SPA_FLAG_IS_SET(builder->state.flags, SPA_POD_BUILDER_FLAG_FIRST)) {
181 		const struct spa_pod p = { 0, SPA_TYPE_None };
182 		spa_pod_builder_raw(builder, &p, sizeof(p));
183 	}
184 	if ((pod = (struct spa_pod*)spa_pod_builder_frame(builder, frame)) != NULL)
185 		*pod = frame->pod;
186 
187 	builder->state.frame = frame->parent;
188 	builder->state.flags = frame->flags;
189 	spa_pod_builder_pad(builder, builder->state.offset);
190 	return pod;
191 }
192 
193 static inline int
spa_pod_builder_primitive(struct spa_pod_builder * builder,const struct spa_pod * p)194 spa_pod_builder_primitive(struct spa_pod_builder *builder, const struct spa_pod *p)
195 {
196 	const void *data;
197 	uint32_t size;
198 	int r, res;
199 
200 	if (builder->state.flags == SPA_POD_BUILDER_FLAG_BODY) {
201 		data = SPA_POD_BODY_CONST(p);
202 		size = SPA_POD_BODY_SIZE(p);
203 	} else {
204 		data = p;
205 		size = SPA_POD_SIZE(p);
206 		SPA_FLAG_CLEAR(builder->state.flags, SPA_POD_BUILDER_FLAG_FIRST);
207 	}
208 	res = spa_pod_builder_raw(builder, data, size);
209 	if (builder->state.flags != SPA_POD_BUILDER_FLAG_BODY)
210 		if ((r = spa_pod_builder_pad(builder, size)) < 0)
211 			res = r;
212 	return res;
213 }
214 
215 #define SPA_POD_INIT(size,type) (struct spa_pod) { size, type }
216 
217 #define SPA_POD_INIT_None() SPA_POD_INIT(0, SPA_TYPE_None)
218 
spa_pod_builder_none(struct spa_pod_builder * builder)219 static inline int spa_pod_builder_none(struct spa_pod_builder *builder)
220 {
221 	const struct spa_pod p = SPA_POD_INIT_None();
222 	return spa_pod_builder_primitive(builder, &p);
223 }
224 
spa_pod_builder_child(struct spa_pod_builder * builder,uint32_t size,uint32_t type)225 static inline int spa_pod_builder_child(struct spa_pod_builder *builder, uint32_t size, uint32_t type)
226 {
227 	const struct spa_pod p = SPA_POD_INIT(size,type);
228 	SPA_FLAG_CLEAR(builder->state.flags, SPA_POD_BUILDER_FLAG_FIRST);
229 	return spa_pod_builder_raw(builder, &p, sizeof(p));
230 }
231 
232 #define SPA_POD_INIT_Bool(val) (struct spa_pod_bool){ { sizeof(uint32_t), SPA_TYPE_Bool }, val ? 1 : 0, 0 }
233 
spa_pod_builder_bool(struct spa_pod_builder * builder,bool val)234 static inline int spa_pod_builder_bool(struct spa_pod_builder *builder, bool val)
235 {
236 	const struct spa_pod_bool p = SPA_POD_INIT_Bool(val);
237 	return spa_pod_builder_primitive(builder, &p.pod);
238 }
239 
240 #define SPA_POD_INIT_Id(val) (struct spa_pod_id){ { sizeof(uint32_t), SPA_TYPE_Id }, (uint32_t)val, 0 }
241 
spa_pod_builder_id(struct spa_pod_builder * builder,uint32_t val)242 static inline int spa_pod_builder_id(struct spa_pod_builder *builder, uint32_t val)
243 {
244 	const struct spa_pod_id p = SPA_POD_INIT_Id(val);
245 	return spa_pod_builder_primitive(builder, &p.pod);
246 }
247 
248 #define SPA_POD_INIT_Int(val) (struct spa_pod_int){ { sizeof(int32_t), SPA_TYPE_Int }, (int32_t)val, 0 }
249 
spa_pod_builder_int(struct spa_pod_builder * builder,int32_t val)250 static inline int spa_pod_builder_int(struct spa_pod_builder *builder, int32_t val)
251 {
252 	const struct spa_pod_int p = SPA_POD_INIT_Int(val);
253 	return spa_pod_builder_primitive(builder, &p.pod);
254 }
255 
256 #define SPA_POD_INIT_Long(val) (struct spa_pod_long){ { sizeof(int64_t), SPA_TYPE_Long }, (int64_t)val }
257 
spa_pod_builder_long(struct spa_pod_builder * builder,int64_t val)258 static inline int spa_pod_builder_long(struct spa_pod_builder *builder, int64_t val)
259 {
260 	const struct spa_pod_long p = SPA_POD_INIT_Long(val);
261 	return spa_pod_builder_primitive(builder, &p.pod);
262 }
263 
264 #define SPA_POD_INIT_Float(val) (struct spa_pod_float){ { sizeof(float), SPA_TYPE_Float }, val, 0 }
265 
spa_pod_builder_float(struct spa_pod_builder * builder,float val)266 static inline int spa_pod_builder_float(struct spa_pod_builder *builder, float val)
267 {
268 	const struct spa_pod_float p = SPA_POD_INIT_Float(val);
269 	return spa_pod_builder_primitive(builder, &p.pod);
270 }
271 
272 #define SPA_POD_INIT_Double(val) (struct spa_pod_double){ { sizeof(double), SPA_TYPE_Double }, val }
273 
spa_pod_builder_double(struct spa_pod_builder * builder,double val)274 static inline int spa_pod_builder_double(struct spa_pod_builder *builder, double val)
275 {
276 	const struct spa_pod_double p = SPA_POD_INIT_Double(val);
277 	return spa_pod_builder_primitive(builder, &p.pod);
278 }
279 
280 #define SPA_POD_INIT_String(len) (struct spa_pod_string){ { len, SPA_TYPE_String } }
281 
282 static inline int
spa_pod_builder_write_string(struct spa_pod_builder * builder,const char * str,uint32_t len)283 spa_pod_builder_write_string(struct spa_pod_builder *builder, const char *str, uint32_t len)
284 {
285 	int r, res;
286 	res = spa_pod_builder_raw(builder, str, len);
287 	if ((r = spa_pod_builder_raw(builder, "", 1)) < 0)
288 		res = r;
289 	if ((r = spa_pod_builder_pad(builder, builder->state.offset)) < 0)
290 		res = r;
291 	return res;
292 }
293 
294 static inline int
spa_pod_builder_string_len(struct spa_pod_builder * builder,const char * str,uint32_t len)295 spa_pod_builder_string_len(struct spa_pod_builder *builder, const char *str, uint32_t len)
296 {
297 	const struct spa_pod_string p = SPA_POD_INIT_String(len+1);
298 	int r, res = spa_pod_builder_raw(builder, &p, sizeof(p));
299 	if ((r = spa_pod_builder_write_string(builder, str, len)) < 0)
300 		res = r;
301 	return res;
302 }
303 
spa_pod_builder_string(struct spa_pod_builder * builder,const char * str)304 static inline int spa_pod_builder_string(struct spa_pod_builder *builder, const char *str)
305 {
306 	uint32_t len = str ? strlen(str) : 0;
307 	return spa_pod_builder_string_len(builder, str ? str : "", len);
308 }
309 
310 #define SPA_POD_INIT_Bytes(len) (struct spa_pod_bytes){ { len, SPA_TYPE_Bytes } }
311 
312 static inline int
spa_pod_builder_bytes(struct spa_pod_builder * builder,const void * bytes,uint32_t len)313 spa_pod_builder_bytes(struct spa_pod_builder *builder, const void *bytes, uint32_t len)
314 {
315 	const struct spa_pod_bytes p = SPA_POD_INIT_Bytes(len);
316 	int r, res = spa_pod_builder_raw(builder, &p, sizeof(p));
317 	if ((r = spa_pod_builder_raw_padded(builder, bytes, len)) < 0)
318 		res = r;
319 	return res;
320 }
321 static inline void *
spa_pod_builder_reserve_bytes(struct spa_pod_builder * builder,uint32_t len)322 spa_pod_builder_reserve_bytes(struct spa_pod_builder *builder, uint32_t len)
323 {
324 	uint32_t offset = builder->state.offset;
325 	if (spa_pod_builder_bytes(builder, NULL, len) < 0)
326 		return NULL;
327 	return SPA_POD_BODY(spa_pod_builder_deref(builder, offset));
328 }
329 
330 #define SPA_POD_INIT_Pointer(type,value) (struct spa_pod_pointer){ { sizeof(struct spa_pod_pointer_body), SPA_TYPE_Pointer }, { type, 0, value } }
331 
332 static inline int
spa_pod_builder_pointer(struct spa_pod_builder * builder,uint32_t type,const void * val)333 spa_pod_builder_pointer(struct spa_pod_builder *builder, uint32_t type, const void *val)
334 {
335 	const struct spa_pod_pointer p = SPA_POD_INIT_Pointer(type, val);
336 	return spa_pod_builder_primitive(builder, &p.pod);
337 }
338 
339 #define SPA_POD_INIT_Fd(fd) (struct spa_pod_fd){ { sizeof(int64_t), SPA_TYPE_Fd }, fd }
340 
spa_pod_builder_fd(struct spa_pod_builder * builder,int64_t fd)341 static inline int spa_pod_builder_fd(struct spa_pod_builder *builder, int64_t fd)
342 {
343 	const struct spa_pod_fd p = SPA_POD_INIT_Fd(fd);
344 	return spa_pod_builder_primitive(builder, &p.pod);
345 }
346 
347 #define SPA_POD_INIT_Rectangle(val) (struct spa_pod_rectangle){ { sizeof(struct spa_rectangle), SPA_TYPE_Rectangle }, val }
348 
349 static inline int
spa_pod_builder_rectangle(struct spa_pod_builder * builder,uint32_t width,uint32_t height)350 spa_pod_builder_rectangle(struct spa_pod_builder *builder, uint32_t width, uint32_t height)
351 {
352 	const struct spa_pod_rectangle p = SPA_POD_INIT_Rectangle(SPA_RECTANGLE(width, height));
353 	return spa_pod_builder_primitive(builder, &p.pod);
354 }
355 
356 #define SPA_POD_INIT_Fraction(val) (struct spa_pod_fraction){ { sizeof(struct spa_fraction), SPA_TYPE_Fraction }, val }
357 
358 static inline int
spa_pod_builder_fraction(struct spa_pod_builder * builder,uint32_t num,uint32_t denom)359 spa_pod_builder_fraction(struct spa_pod_builder *builder, uint32_t num, uint32_t denom)
360 {
361 	const struct spa_pod_fraction p = SPA_POD_INIT_Fraction(SPA_FRACTION(num, denom));
362 	return spa_pod_builder_primitive(builder, &p.pod);
363 }
364 
365 static inline int
spa_pod_builder_push_array(struct spa_pod_builder * builder,struct spa_pod_frame * frame)366 spa_pod_builder_push_array(struct spa_pod_builder *builder, struct spa_pod_frame *frame)
367 {
368 	const struct spa_pod_array p =
369 	    { {sizeof(struct spa_pod_array_body) - sizeof(struct spa_pod), SPA_TYPE_Array},
370 	    {{0, 0}} };
371 	uint32_t offset = builder->state.offset;
372 	int res = spa_pod_builder_raw(builder, &p, sizeof(p) - sizeof(struct spa_pod));
373 	spa_pod_builder_push(builder, frame, &p.pod, offset);
374 	return res;
375 }
376 
377 static inline int
spa_pod_builder_array(struct spa_pod_builder * builder,uint32_t child_size,uint32_t child_type,uint32_t n_elems,const void * elems)378 spa_pod_builder_array(struct spa_pod_builder *builder,
379 		      uint32_t child_size, uint32_t child_type, uint32_t n_elems, const void *elems)
380 {
381 	const struct spa_pod_array p = {
382 		{(uint32_t)(sizeof(struct spa_pod_array_body) + n_elems * child_size), SPA_TYPE_Array},
383 		{{child_size, child_type}}
384 	};
385 	int r, res = spa_pod_builder_raw(builder, &p, sizeof(p));
386 	if ((r = spa_pod_builder_raw_padded(builder, elems, child_size * n_elems)) < 0)
387 		res = r;
388 	return res;
389 }
390 
391 #define SPA_POD_INIT_CHOICE_BODY(type, flags, child_size, child_type)				\
392 	(struct spa_pod_choice_body) { type, flags, { child_size, child_type }}
393 
394 #define SPA_POD_INIT_Choice(type, ctype, child_type, n_vals, ...)				\
395 	(struct { struct spa_pod_choice choice; ctype vals[n_vals];})				\
396 	{ { { n_vals * sizeof(ctype) + sizeof(struct spa_pod_choice_body), SPA_TYPE_Choice },	\
397 		{ type, 0, { sizeof(ctype), child_type } } }, { __VA_ARGS__ } }
398 
399 static inline int
spa_pod_builder_push_choice(struct spa_pod_builder * builder,struct spa_pod_frame * frame,uint32_t type,uint32_t flags)400 spa_pod_builder_push_choice(struct spa_pod_builder *builder, struct spa_pod_frame *frame,
401 		uint32_t type, uint32_t flags)
402 {
403 	const struct spa_pod_choice p =
404 	    { {sizeof(struct spa_pod_choice_body) - sizeof(struct spa_pod), SPA_TYPE_Choice},
405 	    { type, flags, {0, 0}} };
406 	uint32_t offset = builder->state.offset;
407 	int res = spa_pod_builder_raw(builder, &p, sizeof(p) - sizeof(struct spa_pod));
408 	spa_pod_builder_push(builder, frame, &p.pod, offset);
409 	return res;
410 }
411 
412 #define SPA_POD_INIT_Struct(size) (struct spa_pod_struct){ { size, SPA_TYPE_Struct } }
413 
414 static inline int
spa_pod_builder_push_struct(struct spa_pod_builder * builder,struct spa_pod_frame * frame)415 spa_pod_builder_push_struct(struct spa_pod_builder *builder, struct spa_pod_frame *frame)
416 {
417 	const struct spa_pod_struct p = SPA_POD_INIT_Struct(0);
418 	uint32_t offset = builder->state.offset;
419 	int res = spa_pod_builder_raw(builder, &p, sizeof(p));
420 	spa_pod_builder_push(builder, frame, &p.pod, offset);
421 	return res;
422 }
423 
424 #define SPA_POD_INIT_Object(size,type,id,...)	(struct spa_pod_object){ { size, SPA_TYPE_Object }, { type, id }, ##__VA_ARGS__ }
425 
426 static inline int
spa_pod_builder_push_object(struct spa_pod_builder * builder,struct spa_pod_frame * frame,uint32_t type,uint32_t id)427 spa_pod_builder_push_object(struct spa_pod_builder *builder, struct spa_pod_frame *frame,
428 		uint32_t type, uint32_t id)
429 {
430 	const struct spa_pod_object p =
431 	    SPA_POD_INIT_Object(sizeof(struct spa_pod_object_body), type, id);
432 	uint32_t offset = builder->state.offset;
433 	int res = spa_pod_builder_raw(builder, &p, sizeof(p));
434 	spa_pod_builder_push(builder, frame, &p.pod, offset);
435 	return res;
436 }
437 
438 #define SPA_POD_INIT_Prop(key,flags,size,type)	\
439 	(struct spa_pod_prop){ key, flags, { size, type } }
440 
441 static inline int
spa_pod_builder_prop(struct spa_pod_builder * builder,uint32_t key,uint32_t flags)442 spa_pod_builder_prop(struct spa_pod_builder *builder, uint32_t key, uint32_t flags)
443 {
444 	const struct { uint32_t key; uint32_t flags; } p = { key, flags };
445 	return spa_pod_builder_raw(builder, &p, sizeof(p));
446 }
447 
448 #define SPA_POD_INIT_Sequence(size,unit)	\
449 	(struct spa_pod_sequence){ { size, SPA_TYPE_Sequence}, {unit, 0 } }
450 
451 static inline int
spa_pod_builder_push_sequence(struct spa_pod_builder * builder,struct spa_pod_frame * frame,uint32_t unit)452 spa_pod_builder_push_sequence(struct spa_pod_builder *builder, struct spa_pod_frame *frame, uint32_t unit)
453 {
454 	const struct spa_pod_sequence p =
455 	    SPA_POD_INIT_Sequence(sizeof(struct spa_pod_sequence_body), unit);
456 	uint32_t offset = builder->state.offset;
457 	int res = spa_pod_builder_raw(builder, &p, sizeof(p));
458 	spa_pod_builder_push(builder, frame, &p.pod, offset);
459 	return res;
460 }
461 
462 static inline uint32_t
spa_pod_builder_control(struct spa_pod_builder * builder,uint32_t offset,uint32_t type)463 spa_pod_builder_control(struct spa_pod_builder *builder, uint32_t offset, uint32_t type)
464 {
465 	const struct { uint32_t offset; uint32_t type; } p = { offset, type };
466 	return spa_pod_builder_raw(builder, &p, sizeof(p));
467 }
468 
spa_choice_from_id(char id)469 static inline uint32_t spa_choice_from_id(char id)
470 {
471 	switch (id) {
472 	case 'r':
473 		return SPA_CHOICE_Range;
474 	case 's':
475 		return SPA_CHOICE_Step;
476 	case 'e':
477 		return SPA_CHOICE_Enum;
478 	case 'f':
479 		return SPA_CHOICE_Flags;
480 	case 'n':
481 	default:
482 		return SPA_CHOICE_None;
483 	}
484 }
485 
486 #define SPA_POD_BUILDER_COLLECT(builder,type,args)				\
487 do {										\
488 	switch (type) {								\
489 	case 'b':								\
490 		spa_pod_builder_bool(builder, !!va_arg(args, int));		\
491 		break;								\
492 	case 'I':								\
493 		spa_pod_builder_id(builder, va_arg(args, uint32_t));		\
494 		break;								\
495 	case 'i':								\
496 		spa_pod_builder_int(builder, va_arg(args, int));		\
497 		break;								\
498 	case 'l':								\
499 		spa_pod_builder_long(builder, va_arg(args, int64_t));		\
500 		break;								\
501 	case 'f':								\
502 		spa_pod_builder_float(builder, va_arg(args, double));		\
503 		break;								\
504 	case 'd':								\
505 		spa_pod_builder_double(builder, va_arg(args, double));		\
506 		break;								\
507 	case 's':								\
508 	{									\
509 		char *strval = va_arg(args, char *);				\
510 		if (strval != NULL) {						\
511 			size_t len = strlen(strval);				\
512 			spa_pod_builder_string_len(builder, strval, len);	\
513 		}								\
514 		else								\
515 			spa_pod_builder_none(builder);				\
516 		break;								\
517 	}									\
518 	case 'S':								\
519 	{									\
520 		char *strval = va_arg(args, char *);				\
521 		size_t len = va_arg(args, int);					\
522 		spa_pod_builder_string_len(builder, strval, len);		\
523 		break;								\
524 	}									\
525 	case 'y':								\
526 	{									\
527 		void *ptr  = va_arg(args, void *);				\
528 		int len = va_arg(args, int);					\
529 		spa_pod_builder_bytes(builder, ptr, len);			\
530 		break;								\
531 	}									\
532 	case 'R':								\
533 	{									\
534 		struct spa_rectangle *rectval =					\
535 			va_arg(args, struct spa_rectangle *);			\
536 		spa_pod_builder_rectangle(builder,				\
537 				rectval->width, rectval->height);		\
538 		break;								\
539 	}									\
540 	case 'F':								\
541 	{									\
542 		struct spa_fraction *fracval =					\
543 			va_arg(args, struct spa_fraction *);			\
544 		spa_pod_builder_fraction(builder, fracval->num, fracval->denom);\
545 		break;								\
546 	}									\
547 	case 'a':								\
548 	{									\
549 		int child_size = va_arg(args, int);				\
550 		int child_type = va_arg(args, int);				\
551 		int n_elems = va_arg(args, int);				\
552 		void *elems = va_arg(args, void *);				\
553 		spa_pod_builder_array(builder, child_size,			\
554 				child_type, n_elems, elems);			\
555 		break;								\
556 	}									\
557 	case 'p':								\
558 	{									\
559 		int t = va_arg(args, uint32_t);					\
560 		spa_pod_builder_pointer(builder, t, va_arg(args, void *));	\
561 		break;								\
562 	}									\
563 	case 'h':								\
564 		spa_pod_builder_fd(builder, va_arg(args, int));			\
565 		break;								\
566 	case 'P':								\
567 	case 'O':								\
568 	case 'T':								\
569 	case 'V':								\
570 	{									\
571 		struct spa_pod *pod = va_arg(args, struct spa_pod *);		\
572 		if (pod == NULL)						\
573 			spa_pod_builder_none(builder);				\
574 		else								\
575 			spa_pod_builder_primitive(builder, pod);		\
576 		break;								\
577 	}									\
578 	}									\
579 } while(false)
580 
581 static inline int
spa_pod_builder_addv(struct spa_pod_builder * builder,va_list args)582 spa_pod_builder_addv(struct spa_pod_builder *builder, va_list args)
583 {
584 	int res = 0;
585 	struct spa_pod_frame *frame = builder->state.frame;
586 	uint32_t ftype = frame ? frame->pod.type : (uint32_t)SPA_TYPE_None;
587 
588 	do {
589 		const char *format;
590 		int n_values = 1;
591 		struct spa_pod_frame f;
592 		bool choice;
593 
594 		switch (ftype) {
595 		case SPA_TYPE_Object:
596 		{
597 			uint32_t key = va_arg(args, uint32_t);
598 			if (key == 0)
599 				goto exit;
600 			spa_pod_builder_prop(builder, key, 0);
601 			break;
602 		}
603 		case SPA_TYPE_Sequence:
604 		{
605 			uint32_t offset = va_arg(args, uint32_t);
606 			uint32_t type = va_arg(args, uint32_t);
607 			if (type == 0)
608 				goto exit;
609 			spa_pod_builder_control(builder, offset, type);
610 			SPA_FALLTHROUGH
611 		}
612 		default:
613 			break;
614 		}
615 		if ((format = va_arg(args, const char *)) == NULL)
616 			break;
617 
618 		choice = *format == '?';
619 		if (choice) {
620 			uint32_t type = spa_choice_from_id(*++format);
621 			if (*format != '\0')
622 				format++;
623 
624 			spa_pod_builder_push_choice(builder, &f, type, 0);
625 
626 			n_values = va_arg(args, int);
627 		}
628 		while (n_values-- > 0)
629 			SPA_POD_BUILDER_COLLECT(builder, *format, args);
630 
631 		if (choice)
632 			spa_pod_builder_pop(builder, &f);
633 	} while (true);
634 
635       exit:
636 	return res;
637 }
638 
spa_pod_builder_add(struct spa_pod_builder * builder,...)639 static inline int spa_pod_builder_add(struct spa_pod_builder *builder, ...)
640 {
641 	int res;
642 	va_list args;
643 
644 	va_start(args, builder);
645 	res = spa_pod_builder_addv(builder, args);
646 	va_end(args);
647 
648 	return res;
649 }
650 
651 #define spa_pod_builder_add_object(b,type,id,...)				\
652 ({										\
653 	struct spa_pod_frame _f;						\
654 	spa_pod_builder_push_object(b, &_f, type, id);				\
655 	spa_pod_builder_add(b, ##__VA_ARGS__, 0);				\
656 	spa_pod_builder_pop(b, &_f);						\
657 })
658 
659 #define spa_pod_builder_add_struct(b,...)					\
660 ({										\
661 	struct spa_pod_frame _f;						\
662 	spa_pod_builder_push_struct(b, &_f);					\
663 	spa_pod_builder_add(b, ##__VA_ARGS__, NULL);				\
664 	spa_pod_builder_pop(b, &_f);						\
665 })
666 
667 #define spa_pod_builder_add_sequence(b,unit,...)				\
668 ({										\
669 	struct spa_pod_frame _f;						\
670 	spa_pod_builder_push_sequence(b, &_f, unit);				\
671 	spa_pod_builder_add(b, ##__VA_ARGS__, 0, 0);				\
672 	spa_pod_builder_pop(b, &_f);						\
673 })
674 
675 /** Copy a pod structure */
676 static inline struct spa_pod *
spa_pod_copy(const struct spa_pod * pod)677 spa_pod_copy(const struct spa_pod *pod)
678 {
679 	size_t size;
680 	struct spa_pod *c;
681 
682 	size = SPA_POD_SIZE(pod);
683 	if ((c = (struct spa_pod *) malloc(size)) == NULL)
684 		return NULL;
685 	return (struct spa_pod *) memcpy(c, pod, size);
686 }
687 
688 /**
689  * \}
690  */
691 
692 #ifdef __cplusplus
693 }  /* extern "C" */
694 #endif
695 
696 #endif /* SPA_POD_BUILDER_H */
697