1 /*
2  * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
3  *
4  * This is free software: you can redistribute it and/or modify
5  * it under the terms of the Artistic License 2.0 as published by
6  * The Perl Foundation.
7  *
8  * This source is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * Artistic License 2.0 for more details.
12  *
13  * You should have received a copy of the Artistic License 2.0
14  * along the source as a COPYING file. If not, obtain it from
15  * http://www.perlfoundation.org/artistic_license_2_0.
16  */
17 
18 #ifndef _LV2_PROPS_H_
19 #define _LV2_PROPS_H_
20 
21 #ifdef __cplusplus
22 extern "C" {
23 #endif
24 
25 #include <stdlib.h>
26 #include <stdatomic.h>
27 #include <stdio.h>
28 
29 #include <lv2/lv2plug.in/ns/lv2core/lv2.h>
30 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
31 #include <lv2/lv2plug.in/ns/ext/atom/atom.h>
32 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
33 #include <lv2/lv2plug.in/ns/ext/patch/patch.h>
34 #include <lv2/lv2plug.in/ns/ext/state/state.h>
35 
36 /*****************************************************************************
37  * API START
38  *****************************************************************************/
39 
40 // structures
41 typedef struct _props_def_t props_def_t;
42 typedef struct _props_impl_t props_impl_t;
43 typedef struct _props_dyn_t props_dyn_t;
44 typedef struct _props_t props_t;
45 
46 typedef enum _props_dyn_ev_t {
47 	PROPS_DYN_EV_ADD,
48 	PROPS_DYN_EV_REM,
49 	PROPS_DYN_EV_SET
50 } props_dyn_ev_t;
51 
52 // function callbacks
53 typedef void (*props_event_cb_t)(
54 	void *data,
55 	int64_t frames,
56 	props_impl_t *impl);
57 
58 typedef void (*props_dyn_prop_cb_t)(
59 	void *data,
60 	props_dyn_ev_t ev,
61 	LV2_URID subj,
62 	LV2_URID prop,
63 	const LV2_Atom *body);
64 
65 struct _props_def_t {
66 	const char *property;
67 	const char *type;
68 	const char *access;
69 	size_t offset;
70 	bool hidden;
71 
72 	uint32_t max_size;
73 	props_event_cb_t event_cb;
74 };
75 
76 struct _props_impl_t {
77 	LV2_URID property;
78 	LV2_URID type;
79 	LV2_URID access;
80 
81 	struct {
82 		uint32_t size;
83 		void *body;
84 	} value;
85 	struct {
86 		uint32_t size;
87 		void *body;
88 	} stash;
89 
90 	const props_def_t *def;
91 
92 	atomic_int state;
93 	bool stashing;
94 };
95 
96 struct _props_dyn_t {
97 	props_dyn_prop_cb_t prop;
98 };
99 
100 struct _props_t {
101 	struct {
102 		LV2_URID subject;
103 
104 		LV2_URID patch_get;
105 		LV2_URID patch_set;
106 		LV2_URID patch_put;
107 		LV2_URID patch_patch;
108 		LV2_URID patch_wildcard;
109 		LV2_URID patch_add;
110 		LV2_URID patch_remove;
111 		LV2_URID patch_subject;
112 		LV2_URID patch_body;
113 		LV2_URID patch_property;
114 		LV2_URID patch_value;
115 		LV2_URID patch_writable;
116 		LV2_URID patch_readable;
117 		LV2_URID patch_sequence;
118 		LV2_URID patch_error;
119 		LV2_URID patch_ack;
120 
121 		LV2_URID atom_int;
122 		LV2_URID atom_long;
123 		LV2_URID atom_float;
124 		LV2_URID atom_double;
125 		LV2_URID atom_bool;
126 		LV2_URID atom_urid;
127 		LV2_URID atom_path;
128 		LV2_URID atom_literal;
129 		LV2_URID atom_vector;
130 		LV2_URID atom_object;
131 		LV2_URID atom_sequence;
132 
133 		LV2_URID state_StateChanged;
134 	} urid;
135 
136 	void *data;
137 
138 	bool stashing;
139 	atomic_bool restoring;
140 
141 	uint32_t max_size;
142 
143 	const props_dyn_t *dyn;
144 
145 	unsigned nimpls;
146 	props_impl_t impls [1];
147 };
148 
149 #define PROPS_T(PROPS, MAX_NIMPLS) \
150 	props_t (PROPS); \
151 	props_impl_t _impls [MAX_NIMPLS]
152 
153 // rt-safe
154 static inline int
155 props_init(props_t *props, const char *subject,
156 	const props_def_t *defs, int nimpls,
157 	void *value_base, void *stash_base,
158 	LV2_URID_Map *map, void *data);
159 
160 // rt-safe
161 static inline void
162 props_dyn(props_t *props, const props_dyn_t *dyn);
163 
164 // rt-safe
165 static inline void
166 props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
167 	LV2_Atom_Forge_Ref *ref);
168 
169 // rt-safe
170 static inline int
171 props_advance(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
172 	const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref);
173 
174 // rt-safe
175 static inline void
176 props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
177 	LV2_URID property, LV2_Atom_Forge_Ref *ref);
178 
179 // rt-safe
180 static inline void
181 props_get(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
182 	LV2_URID property, LV2_Atom_Forge_Ref *ref);
183 
184 // rt-safe
185 static inline void
186 props_stash(props_t *props, LV2_URID property);
187 
188 // rt-safe
189 static inline LV2_URID
190 props_map(props_t *props, const char *property);
191 
192 // rt-safe
193 static inline const char *
194 props_unmap(props_t *props, LV2_URID property);
195 
196 // non-rt
197 static inline LV2_State_Status
198 props_save(props_t *props, LV2_State_Store_Function store,
199 	LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);
200 
201 // non-rt
202 static inline LV2_State_Status
203 props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
204 	LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);
205 
206 /*****************************************************************************
207  * API END
208  *****************************************************************************/
209 
210 // enumerations
211 typedef enum _props_state_t {
212 	PROP_STATE_NONE    = 0,
213 	PROP_STATE_LOCK    = 1,
214 	PROP_STATE_RESTORE = 2
215 } props_state_t;
216 
217 static inline void
_props_impl_spin_lock(props_impl_t * impl,int from,int to)218 _props_impl_spin_lock(props_impl_t *impl, int from, int to)
219 {
220 	int expected = from;
221 	const int desired = to;
222 
223 	while(!atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
224 		memory_order_acquire, memory_order_acquire))
225 	{
226 		// spin
227 	}
228 }
229 
230 static inline bool
_props_impl_try_lock(props_impl_t * impl,int from,int to)231 _props_impl_try_lock(props_impl_t *impl, int from, int to)
232 {
233 	int expected = from;
234 	const int desired = to;
235 
236 	return atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
237 		memory_order_acquire, memory_order_acquire);
238 }
239 
240 static inline void
_props_impl_unlock(props_impl_t * impl,int to)241 _props_impl_unlock(props_impl_t *impl, int to)
242 {
243 	atomic_store_explicit(&impl->state, to, memory_order_release);
244 }
245 
246 static inline bool
_props_restoring_get(props_t * props)247 _props_restoring_get(props_t *props)
248 {
249 	return atomic_exchange_explicit(&props->restoring, false, memory_order_acquire);
250 }
251 
252 static inline void
_props_restoring_set(props_t * props)253 _props_restoring_set(props_t *props)
254 {
255 	atomic_store_explicit(&props->restoring, true, memory_order_release);
256 }
257 
258 static inline void
_props_qsort(props_impl_t * A,int n)259 _props_qsort(props_impl_t *A, int n)
260 {
261 	if(n < 2)
262 		return;
263 
264 	const props_impl_t *p = A;
265 
266 	int i = -1;
267 	int j = n;
268 
269 	while(true)
270 	{
271 		do {
272 			i += 1;
273 		} while(A[i].property < p->property);
274 
275 		do {
276 			j -= 1;
277 		} while(A[j].property > p->property);
278 
279 		if(i >= j)
280 			break;
281 
282 		const props_impl_t tmp = A[i];
283 		A[i] = A[j];
284 		A[j] = tmp;
285 	}
286 
287 	_props_qsort(A, j + 1);
288 	_props_qsort(A + j + 1, n - j - 1);
289 }
290 
291 static inline props_impl_t *
_props_impl_get(props_t * props,LV2_URID property)292 _props_impl_get(props_t *props, LV2_URID property)
293 {
294 	props_impl_t *base = props->impls;
295 
296 	for(int N = props->nimpls, half; N > 1; N -= half)
297 	{
298 		half = N/2;
299 		props_impl_t *dst = &base[half];
300 		base = (dst->property > property) ? base : dst;
301 	}
302 
303 	return (base->property == property) ? base : NULL;
304 }
305 
306 static inline LV2_Atom_Forge_Ref
_props_patch_set(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,props_impl_t * impl,int32_t sequence_num)307 _props_patch_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
308 	props_impl_t *impl, int32_t sequence_num)
309 {
310 	LV2_Atom_Forge_Frame obj_frame;
311 
312 	LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
313 
314 	if(ref)
315 		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_set);
316 	{
317 		if(props->urid.subject) // is optional
318 		{
319 			if(ref)
320 				ref = lv2_atom_forge_key(forge, props->urid.patch_subject);
321 			if(ref)
322 				ref = lv2_atom_forge_urid(forge, props->urid.subject);
323 		}
324 
325 		if(sequence_num) // is optional
326 		{
327 			if(ref)
328 				ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
329 			if(ref)
330 				ref = lv2_atom_forge_int(forge, sequence_num);
331 		}
332 
333 		if(ref)
334 			ref = lv2_atom_forge_key(forge, props->urid.patch_property);
335 		if(ref)
336 			ref = lv2_atom_forge_urid(forge, impl->property);
337 
338 		if(ref)
339 			lv2_atom_forge_key(forge, props->urid.patch_value);
340 		if(ref)
341 			ref = lv2_atom_forge_atom(forge, impl->value.size, impl->type);
342 		if(ref)
343 			ref = lv2_atom_forge_write(forge, impl->value.body, impl->value.size);
344 	}
345 	if(ref)
346 		lv2_atom_forge_pop(forge, &obj_frame);
347 
348 	if(ref)
349 		ref = lv2_atom_forge_frame_time(forge, frames);
350 	if(ref)
351 		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.state_StateChanged);
352 	if(ref)
353 		lv2_atom_forge_pop(forge, &obj_frame);
354 
355 	return ref;
356 }
357 
358 static inline LV2_Atom_Forge_Ref
_props_patch_get(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,props_impl_t * impl,int32_t sequence_num)359 _props_patch_get(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
360 	props_impl_t *impl, int32_t sequence_num)
361 {
362 	LV2_Atom_Forge_Frame obj_frame;
363 
364 	LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
365 
366 	if(ref)
367 		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_get);
368 	{
369 		if(props->urid.subject) // is optional
370 		{
371 			if(ref)
372 				ref = lv2_atom_forge_key(forge, props->urid.patch_subject);
373 			if(ref)
374 				ref = lv2_atom_forge_urid(forge, props->urid.subject);
375 		}
376 
377 		if(sequence_num) // is optional
378 		{
379 			if(ref)
380 				ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
381 			if(ref)
382 				ref = lv2_atom_forge_int(forge, sequence_num);
383 		}
384 
385 		if(ref)
386 			ref = lv2_atom_forge_key(forge, props->urid.patch_property);
387 		if(ref)
388 			ref = lv2_atom_forge_urid(forge, impl->property);
389 	}
390 	if(ref)
391 		lv2_atom_forge_pop(forge, &obj_frame);
392 
393 	return ref;
394 }
395 
396 static inline LV2_Atom_Forge_Ref
_props_patch_error(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,int32_t sequence_num)397 _props_patch_error(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
398 	int32_t sequence_num)
399 {
400 	LV2_Atom_Forge_Frame obj_frame;
401 
402 	LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
403 
404 	if(ref)
405 		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_error);
406 	{
407 		if(ref)
408 			ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
409 		if(ref)
410 			ref = lv2_atom_forge_int(forge, sequence_num);
411 	}
412 	if(ref)
413 		lv2_atom_forge_pop(forge, &obj_frame);
414 
415 	return ref;
416 }
417 
418 static inline LV2_Atom_Forge_Ref
_props_patch_ack(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,int32_t sequence_num)419 _props_patch_ack(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
420 	int32_t sequence_num)
421 {
422 	LV2_Atom_Forge_Frame obj_frame;
423 
424 	LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
425 
426 	if(ref)
427 		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_ack);
428 	{
429 		if(ref)
430 			ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
431 		if(ref)
432 			ref = lv2_atom_forge_int(forge, sequence_num);
433 	}
434 	if(ref)
435 		lv2_atom_forge_pop(forge, &obj_frame);
436 
437 	return ref;
438 }
439 
440 static inline void
_props_impl_stash(props_t * props,props_impl_t * impl)441 _props_impl_stash(props_t *props, props_impl_t *impl)
442 {
443 	if(_props_impl_try_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK))
444 	{
445 		impl->stashing = false;
446 		impl->stash.size = impl->value.size;
447 		memcpy(impl->stash.body, impl->value.body, impl->value.size);
448 
449 		_props_impl_unlock(impl, PROP_STATE_NONE);
450 	}
451 	else
452 	{
453 		impl->stashing = true; // try again later
454 		props->stashing = true;
455 	}
456 }
457 
458 static inline void
_props_impl_restore(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,props_impl_t * impl,LV2_Atom_Forge_Ref * ref)459 _props_impl_restore(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
460 	props_impl_t *impl, LV2_Atom_Forge_Ref *ref)
461 {
462 	if(_props_impl_try_lock(impl, PROP_STATE_RESTORE, PROP_STATE_LOCK))
463 	{
464 		impl->stashing = false; // makes no sense to stash a recently restored value
465 		impl->value.size = impl->stash.size;
466 		memcpy(impl->value.body, impl->stash.body, impl->stash.size);
467 
468 		_props_impl_unlock(impl, PROP_STATE_NONE);
469 
470 		if(*ref && !impl->def->hidden)
471 			*ref = _props_patch_set(props, forge, frames, impl, 0);
472 
473 		const props_def_t *def = impl->def;
474 		if(def->event_cb)
475 			def->event_cb(props->data, 0, impl);
476 	}
477 }
478 
479 static inline void
_props_impl_set(props_t * props,props_impl_t * impl,LV2_URID type,uint32_t size,const void * body)480 _props_impl_set(props_t *props, props_impl_t *impl, LV2_URID type,
481 	uint32_t size, const void *body)
482 {
483 	if(  (impl->type == type)
484 		&& ( (impl->def->max_size == 0) || (size <= impl->def->max_size)) )
485 	{
486 		impl->value.size = size;
487 		memcpy(impl->value.body, body, size);
488 
489 		_props_impl_stash(props, impl);
490 	}
491 }
492 
493 static inline int
_props_impl_init(props_t * props,props_impl_t * impl,const props_def_t * def,void * value_base,void * stash_base,LV2_URID_Map * map)494 _props_impl_init(props_t *props, props_impl_t *impl, const props_def_t *def,
495 	void *value_base, void *stash_base, LV2_URID_Map *map)
496 {
497 	if(!def->property || !def->type)
498 		return 0;
499 
500 	const LV2_URID type = map->map(map->handle, def->type);
501 	const LV2_URID property = map->map(map->handle, def->property);
502 	const LV2_URID access = def->access
503 		? map->map(map->handle, def->access)
504 		: map->map(map->handle, LV2_PATCH__writable);
505 
506 	if(!type || !property || !access)
507 		return 0;
508 
509 	impl->property = property;
510 	impl->access = access;
511 	impl->def = def;
512 	impl->value.body = (uint8_t *)value_base + def->offset;
513 	impl->stash.body = (uint8_t *)stash_base + def->offset;
514 
515 	uint32_t size;
516 	if(  (type == props->urid.atom_int)
517 		|| (type == props->urid.atom_float)
518 		|| (type == props->urid.atom_bool)
519 		|| (type == props->urid.atom_urid) )
520 	{
521 		size = 4;
522 	}
523 	else if((type == props->urid.atom_long)
524 		|| (type == props->urid.atom_double) )
525 	{
526 		size = 8;
527 	}
528 	else if(type == props->urid.atom_literal)
529 	{
530 		size = sizeof(LV2_Atom_Literal_Body);
531 	}
532 	else if(type == props->urid.atom_vector)
533 	{
534 		size = sizeof(LV2_Atom_Vector_Body);
535 	}
536 	else if(type == props->urid.atom_object)
537 	{
538 		size = sizeof(LV2_Atom_Object_Body);
539 	}
540 	else if(type == props->urid.atom_sequence)
541 	{
542 		size = sizeof(LV2_Atom_Sequence_Body);
543 	}
544 	else
545 	{
546 		size = 0; // assume everything else as having size 0
547 	}
548 
549 	impl->type = type;
550 	impl->value.size = size;
551 	impl->stash.size = size;
552 
553 	atomic_init(&impl->state, PROP_STATE_NONE);
554 
555 	// update maximal value size
556 	const uint32_t max_size = def->max_size
557 		? def->max_size
558 		: size;
559 
560 	if(max_size > props->max_size)
561 	{
562 		props->max_size = max_size;
563 	}
564 
565 	return 1;
566 }
567 
568 static inline int
props_init(props_t * props,const char * subject,const props_def_t * defs,int nimpls,void * value_base,void * stash_base,LV2_URID_Map * map,void * data)569 props_init(props_t *props, const char *subject,
570 	const props_def_t *defs, int nimpls,
571 	void *value_base, void *stash_base,
572 	LV2_URID_Map *map, void *data)
573 {
574 	if(!props || !defs || !value_base || !stash_base || !map)
575 		return 0;
576 
577 	props->nimpls = nimpls;
578 	props->data = data;
579 
580 	props->urid.subject = subject ? map->map(map->handle, subject) : 0;
581 
582 	props->urid.patch_get = map->map(map->handle, LV2_PATCH__Get);
583 	props->urid.patch_set = map->map(map->handle, LV2_PATCH__Set);
584 	props->urid.patch_put = map->map(map->handle, LV2_PATCH__Put);
585 	props->urid.patch_patch = map->map(map->handle, LV2_PATCH__Patch);
586 	props->urid.patch_wildcard = map->map(map->handle, LV2_PATCH__wildcard);
587 	props->urid.patch_add = map->map(map->handle, LV2_PATCH__add);
588 	props->urid.patch_remove = map->map(map->handle, LV2_PATCH__remove);
589 	props->urid.patch_subject = map->map(map->handle, LV2_PATCH__subject);
590 	props->urid.patch_body = map->map(map->handle, LV2_PATCH__body);
591 	props->urid.patch_property = map->map(map->handle, LV2_PATCH__property);
592 	props->urid.patch_value = map->map(map->handle, LV2_PATCH__value);
593 	props->urid.patch_writable = map->map(map->handle, LV2_PATCH__writable);
594 	props->urid.patch_readable = map->map(map->handle, LV2_PATCH__readable);
595 	props->urid.patch_sequence = map->map(map->handle, LV2_PATCH__sequenceNumber);
596 	props->urid.patch_ack = map->map(map->handle, LV2_PATCH__Ack);
597 	props->urid.patch_error = map->map(map->handle, LV2_PATCH__Error);
598 
599 	props->urid.atom_int = map->map(map->handle, LV2_ATOM__Int);
600 	props->urid.atom_long = map->map(map->handle, LV2_ATOM__Long);
601 	props->urid.atom_float = map->map(map->handle, LV2_ATOM__Float);
602 	props->urid.atom_double = map->map(map->handle, LV2_ATOM__Double);
603 	props->urid.atom_bool = map->map(map->handle, LV2_ATOM__Bool);
604 	props->urid.atom_urid = map->map(map->handle, LV2_ATOM__URID);
605 	props->urid.atom_path = map->map(map->handle, LV2_ATOM__Path);
606 	props->urid.atom_literal = map->map(map->handle, LV2_ATOM__Literal);
607 	props->urid.atom_vector = map->map(map->handle, LV2_ATOM__Vector);
608 	props->urid.atom_object = map->map(map->handle, LV2_ATOM__Object);
609 	props->urid.atom_sequence = map->map(map->handle, LV2_ATOM__Sequence);
610 
611 	props->urid.state_StateChanged = map->map(map->handle, LV2_STATE__StateChanged);
612 
613 	atomic_init(&props->restoring, false);
614 
615 	int status = 1;
616 	for(unsigned i = 0; i < props->nimpls; i++)
617 	{
618 		props_impl_t *impl = &props->impls[i];
619 
620 		status = status
621 			&& _props_impl_init(props, impl, &defs[i], value_base, stash_base, map);
622 	}
623 
624 	_props_qsort(props->impls, props->nimpls);
625 
626 	return status;
627 }
628 
629 static inline void
props_dyn(props_t * props,const props_dyn_t * dyn)630 props_dyn(props_t *props, const props_dyn_t *dyn)
631 {
632 	props->dyn = dyn;
633 }
634 
635 static inline void
props_idle(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,LV2_Atom_Forge_Ref * ref)636 props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
637 	LV2_Atom_Forge_Ref *ref)
638 {
639 	if(_props_restoring_get(props))
640 	{
641 		for(unsigned i = 0; i < props->nimpls; i++)
642 		{
643 			props_impl_t *impl = &props->impls[i];
644 
645 			_props_impl_restore(props, forge, frames, impl, ref);
646 		}
647 	}
648 
649 	if(props->stashing)
650 	{
651 		props->stashing = false;
652 
653 		for(unsigned i = 0; i < props->nimpls; i++)
654 		{
655 			props_impl_t *impl = &props->impls[i];
656 
657 			if(impl->stashing)
658 				_props_impl_stash(props, impl);
659 		}
660 	}
661 }
662 
663 static inline int
props_advance(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,const LV2_Atom_Object * obj,LV2_Atom_Forge_Ref * ref)664 props_advance(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
665 	const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref)
666 {
667 	if(!lv2_atom_forge_is_object_type(forge, obj->atom.type))
668 	{
669 		return 0;
670 	}
671 
672 	if(obj->body.otype == props->urid.patch_get)
673 	{
674 		const LV2_Atom_URID *subject = NULL;
675 		const LV2_Atom_URID *property = NULL;
676 		const LV2_Atom_Int *sequence = NULL;
677 
678 		lv2_atom_object_get(obj,
679 			props->urid.patch_subject, &subject,
680 			props->urid.patch_property, &property,
681 			props->urid.patch_sequence, &sequence,
682 			0);
683 
684 		// check for a matching optional subject
685 		if(  (subject && props->urid.subject)
686 			&& ( (subject->atom.type != props->urid.atom_urid)
687 				|| (subject->body != props->urid.subject) ) )
688 		{
689 			return 0;
690 		}
691 
692 		int32_t sequence_num = 0;
693 		if(sequence && (sequence->atom.type == props->urid.atom_int))
694 		{
695 			sequence_num = sequence->body;
696 		}
697 
698 		if(!property)
699 		{
700 			for(unsigned i = 0; i < props->nimpls; i++)
701 			{
702 				props_impl_t *impl = &props->impls[i];
703 
704 				if(*ref && !impl->def->hidden)
705 					*ref = _props_patch_set(props, forge, frames, impl, sequence_num);
706 			}
707 
708 			return 1;
709 		}
710 		else if(property->atom.type == props->urid.atom_urid)
711 		{
712 			props_impl_t *impl = _props_impl_get(props, property->body);
713 
714 			if(impl)
715 			{
716 				if(*ref && !impl->def->hidden)
717 					*ref = _props_patch_set(props, forge, frames, impl, sequence_num);
718 
719 				return 1;
720 			}
721 			else if(sequence_num)
722 			{
723 				if(*ref)
724 					*ref = _props_patch_error(props, forge, frames, sequence_num);
725 			}
726 		}
727 		else if(sequence_num)
728 		{
729 			if(*ref)
730 				*ref = _props_patch_error(props, forge, frames, sequence_num);
731 		}
732 	}
733 	else if(obj->body.otype == props->urid.patch_set)
734 	{
735 		const LV2_Atom_URID *subject = NULL;
736 		const LV2_Atom_URID *property = NULL;
737 		const LV2_Atom_Int *sequence = NULL;
738 		const LV2_Atom *value = NULL;
739 
740 		lv2_atom_object_get(obj,
741 			props->urid.patch_subject, &subject,
742 			props->urid.patch_property, &property,
743 			props->urid.patch_sequence, &sequence,
744 			props->urid.patch_value, &value,
745 			0);
746 
747 		// check for a matching optional subject
748 		if(  (subject && props->urid.subject)
749 			&& ( (subject->atom.type != props->urid.atom_urid)
750 				|| (subject->body != props->urid.subject) ) )
751 		{
752 			return 0;
753 		}
754 
755 		int32_t sequence_num = 0;
756 		if(sequence && (sequence->atom.type == props->urid.atom_int))
757 		{
758 			sequence_num = sequence->body;
759 		}
760 
761 		if(!property || (property->atom.type != props->urid.atom_urid) || !value)
762 		{
763 			if(sequence_num)
764 			{
765 				if(ref)
766 					*ref = _props_patch_error(props, forge, frames, sequence_num);
767 			}
768 
769 			return 0;
770 		}
771 
772 		props_impl_t *impl = _props_impl_get(props, property->body);
773 		if(impl)
774 		{
775 			_props_impl_set(props, impl, value->type, value->size,
776 				LV2_ATOM_BODY_CONST(value));
777 
778 			// send on (e.g. to UI)
779 			if(*ref && !impl->def->hidden)
780 				*ref = _props_patch_set(props, forge, frames, impl, sequence_num);
781 
782 			const props_def_t *def = impl->def;
783 			if(def->event_cb)
784 				def->event_cb(props->data, frames, impl);
785 
786 			if(sequence_num)
787 			{
788 				if(*ref)
789 					*ref = _props_patch_ack(props, forge, frames, sequence_num);
790 			}
791 
792 			return 1;
793 		}
794 		else if(props->dyn && props->dyn->prop)
795 		{
796 			const LV2_URID subj = subject ? subject->body : 0;
797 
798 			props->dyn->prop(props->data, PROPS_DYN_EV_SET, subj, property->body, value);
799 
800 			//TODO send ack
801 		}
802 		else if(sequence_num)
803 		{
804 			if(*ref)
805 				*ref = _props_patch_error(props, forge, frames, sequence_num);
806 		}
807 	}
808 	else if(obj->body.otype == props->urid.patch_put)
809 	{
810 		const LV2_Atom_URID *subject = NULL;
811 		const LV2_Atom_Int *sequence = NULL;
812 		const LV2_Atom_Object *body = NULL;
813 
814 		lv2_atom_object_get(obj,
815 			props->urid.patch_subject, &subject,
816 			props->urid.patch_sequence, &sequence,
817 			props->urid.patch_body, &body,
818 			0);
819 
820 		// check for a matching optional subject
821 		if(  (subject && props->urid.subject)
822 			&& ( (subject->atom.type != props->urid.atom_urid)
823 				|| (subject->body != props->urid.subject) ) )
824 		{
825 			return 0;
826 		}
827 
828 		int32_t sequence_num = 0;
829 		if(sequence && (sequence->atom.type == props->urid.atom_int))
830 		{
831 			sequence_num = sequence->body;
832 		}
833 
834 		if(!body || !lv2_atom_forge_is_object_type(forge, body->atom.type))
835 		{
836 			if(sequence_num)
837 			{
838 				if(*ref)
839 					*ref = _props_patch_error(props, forge, frames, sequence_num);
840 			}
841 
842 			return 0;
843 		}
844 
845 		LV2_ATOM_OBJECT_FOREACH(body, prop)
846 		{
847 			const LV2_URID property = prop->key;
848 			const LV2_Atom *value = &prop->value;
849 
850 			props_impl_t *impl = _props_impl_get(props, property);
851 			if(impl)
852 			{
853 				_props_impl_set(props, impl, value->type, value->size,
854 					LV2_ATOM_BODY_CONST(value));
855 
856 				// send on (e.g. to UI)
857 				if(*ref && !impl->def->hidden)
858 					*ref = _props_patch_set(props, forge, frames, impl, sequence_num);
859 
860 				const props_def_t *def = impl->def;
861 				if(def->event_cb)
862 					def->event_cb(props->data, frames, impl);
863 			}
864 			else if(props->dyn && props->dyn->prop)
865 			{
866 				const LV2_URID subj = subject ? subject->body : 0;
867 
868 				props->dyn->prop(props->data, PROPS_DYN_EV_SET, subj, property, value);
869 
870 			//TODO send ack
871 			}
872 		}
873 
874 		if(sequence_num)
875 		{
876 			if(*ref)
877 				*ref = _props_patch_ack(props, forge, frames, sequence_num);
878 		}
879 
880 		return 1;
881 	}
882 	else if(obj->body.otype == props->urid.patch_patch)
883 	{
884 		const LV2_Atom_URID *subject = NULL;
885 		const LV2_Atom_Int *sequence = NULL;
886 		const LV2_Atom_Object *add = NULL;
887 		const LV2_Atom_Object *rem = NULL;
888 
889 		lv2_atom_object_get(obj,
890 			props->urid.patch_subject, &subject,
891 			props->urid.patch_sequence, &sequence,
892 			props->urid.patch_add, &add,
893 			props->urid.patch_remove, &rem,
894 			0);
895 
896 		LV2_URID subj = 0;
897 		if(subject && (subject->atom.type == props->urid.atom_urid))
898 		{
899 			subj = subject->body;
900 		}
901 
902 		int32_t sequence_num = 0;
903 		if(sequence && (sequence->atom.type == props->urid.atom_int))
904 		{
905 			sequence_num = sequence->body;
906 		}
907 
908 		if(rem && lv2_atom_forge_is_object_type(forge, rem->atom.type))
909 		{
910 			LV2_ATOM_OBJECT_FOREACH(rem, prop)
911 			{
912 				const LV2_URID property = prop->key;
913 				const LV2_Atom *value = &prop->value;
914 
915 				if(props->dyn && props->dyn->prop)
916 				{
917 					props->dyn->prop(props->data, PROPS_DYN_EV_REM, subj, property, value);
918 				}
919 			}
920 		}
921 
922 		if(add && lv2_atom_forge_is_object_type(forge, add->atom.type))
923 		{
924 			LV2_ATOM_OBJECT_FOREACH(add, prop)
925 			{
926 				const LV2_URID property = prop->key;
927 				const LV2_Atom *value = &prop->value;
928 
929 				if(props->dyn && props->dyn->prop)
930 				{
931 					props->dyn->prop(props->data, PROPS_DYN_EV_ADD, subj, property, value);
932 				}
933 			}
934 		}
935 
936 		if(sequence_num && *ref)
937 		{
938 			*ref = _props_patch_ack(props, forge, frames, sequence_num);
939 		}
940 
941 		return 1;
942 	}
943 
944 	return 0; // did not handle a patch event
945 }
946 
947 static inline void
props_set(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,LV2_URID property,LV2_Atom_Forge_Ref * ref)948 props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
949 	LV2_URID property, LV2_Atom_Forge_Ref *ref)
950 {
951 	props_impl_t *impl = _props_impl_get(props, property);
952 
953 	if(impl)
954 	{
955 		_props_impl_stash(props, impl);
956 
957 		if(*ref && !impl->def->hidden) //TODO use patch:sequenceNumber
958 			*ref = _props_patch_set(props, forge, frames, impl, 0);
959 	}
960 }
961 
962 static inline void
props_get(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,LV2_URID property,LV2_Atom_Forge_Ref * ref)963 props_get(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
964 	LV2_URID property, LV2_Atom_Forge_Ref *ref)
965 {
966 	props_impl_t *impl = _props_impl_get(props, property);
967 
968 	if(impl)
969 	{
970 		if(*ref && !impl->def->hidden) //TODO use patch:sequenceNumber
971 			*ref = _props_patch_get(props, forge, frames, impl, 0);
972 	}
973 }
974 
975 static inline void
props_stash(props_t * props,LV2_URID property)976 props_stash(props_t *props, LV2_URID property)
977 {
978 	props_impl_t *impl = _props_impl_get(props, property);
979 
980 	if(impl)
981 		_props_impl_stash(props, impl);
982 }
983 
984 static inline LV2_URID
props_map(props_t * props,const char * uri)985 props_map(props_t *props, const char *uri)
986 {
987 	for(unsigned i = 0; i < props->nimpls; i++)
988 	{
989 		props_impl_t *impl = &props->impls[i];
990 
991 		if(!strcmp(impl->def->property, uri))
992 			return impl->property;
993 	}
994 
995 	return 0;
996 }
997 
998 static inline const char *
props_unmap(props_t * props,LV2_URID property)999 props_unmap(props_t *props, LV2_URID property)
1000 {
1001 	props_impl_t *impl = _props_impl_get(props, property);
1002 
1003 	if(impl)
1004 		return impl->def->property;
1005 
1006 	return NULL;
1007 }
1008 
1009 static inline int
_copy_file(const char * to,const char * from)1010 _copy_file(const char *to, const char *from)
1011 {
1012 	FILE *dst = NULL;
1013 	FILE *src = NULL;
1014 	int ch;
1015 
1016 	dst = fopen(to, "wb");
1017 	if(!dst)
1018 	{
1019 		return 1;
1020 	}
1021 
1022 	src = fopen(from, "rb");
1023 	if(!src)
1024 	{
1025 		fclose(dst);
1026 
1027 		return 1;
1028 	}
1029 
1030 	while( (ch = fgetc(src)) != EOF)
1031 	{
1032 		fputc(ch, dst);
1033 	}
1034 
1035 	fclose(dst);
1036 	fclose(src);
1037 
1038 	return 0;
1039 }
1040 
1041 static inline void
_free_path(const LV2_State_Free_Path * free_path,char * path)1042 _free_path(const LV2_State_Free_Path *free_path, char *path)
1043 {
1044 	if(free_path && free_path->free_path)
1045 	{
1046 		free_path->free_path(free_path->handle, path);
1047 	}
1048 	else
1049 	{
1050 		free(path);
1051 	}
1052 }
1053 
1054 static inline LV2_State_Status
props_save(props_t * props,LV2_State_Store_Function store,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)1055 props_save(props_t *props, LV2_State_Store_Function store,
1056 	LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features)
1057 {
1058 	const LV2_State_Map_Path *map_path = NULL;
1059 	const LV2_State_Make_Path *make_path = NULL;
1060 	const LV2_State_Free_Path *free_path = NULL;
1061 
1062 	// set POD flag if not already set by host
1063 	flags |= LV2_STATE_IS_POD;
1064 
1065 	for(unsigned i = 0; features[i]; i++)
1066 	{
1067 		if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
1068 		{
1069 			map_path = features[i]->data;
1070 		}
1071 		else if(!strcmp(features[i]->URI, LV2_STATE__makePath))
1072 		{
1073 			make_path = features[i]->data;
1074 		}
1075 		else if(!strcmp(features[i]->URI, LV2_STATE__freePath))
1076 		{
1077 			free_path = features[i]->data;
1078 		}
1079 	}
1080 
1081 	void *body = malloc(props->max_size); // create memory to store widest value
1082 	if(body)
1083 	{
1084 		for(unsigned i = 0; i < props->nimpls; i++)
1085 		{
1086 			props_impl_t *impl = &props->impls[i];
1087 
1088 			if(impl->access == props->urid.patch_readable)
1089 				continue; // skip read-only, as it makes no sense to restore them
1090 
1091 			_props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
1092 
1093 			// create temporary copy of value, store() may well be blocking
1094 			const uint32_t size = impl->stash.size;
1095 			memcpy(body, impl->stash.body, size);
1096 
1097 			_props_impl_unlock(impl, PROP_STATE_NONE);
1098 
1099 			if(  map_path && map_path->abstract_path
1100 				&& (impl->type == props->urid.atom_path) )
1101 			{
1102 				const char *path = strstr(body, "file://") == body
1103 					? (char *)body + 7 // skip "file://"
1104 					: (char *)body;
1105 
1106 				char *abstract = NULL;
1107 
1108 				if(  make_path && make_path->path
1109 					&& (strstr(path, "/tmp") == path) )
1110 				{
1111 					char *absolute = make_path->path(make_path->handle, basename(path));
1112 
1113 					if(absolute)
1114 					{
1115 						if(_copy_file(absolute, path) == 0)
1116 						{
1117 							abstract = map_path->abstract_path(map_path->handle, absolute);
1118 						}
1119 
1120 						_free_path(free_path, absolute);
1121 					}
1122 				}
1123 				else
1124 				{
1125 					abstract = map_path->abstract_path(map_path->handle, path);
1126 				}
1127 
1128 				if(abstract)
1129 				{
1130 					const uint32_t sz = strlen(abstract) + 1;
1131 					store(state, impl->property, abstract, sz, impl->type, flags);
1132 
1133 					_free_path(free_path, abstract);
1134 				}
1135 			}
1136 			else // !Path
1137 			{
1138 				store(state, impl->property, body, size, impl->type, flags);
1139 			}
1140 		}
1141 
1142 		free(body);
1143 	}
1144 
1145 	return LV2_STATE_SUCCESS;
1146 }
1147 
1148 static inline LV2_State_Status
props_restore(props_t * props,LV2_State_Retrieve_Function retrieve,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)1149 props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
1150 	LV2_State_Handle state, uint32_t flags __attribute__((unused)),
1151 	const LV2_Feature *const *features)
1152 {
1153 	const LV2_State_Map_Path *map_path = NULL;
1154 	const LV2_State_Free_Path *free_path = NULL;
1155 
1156 	for(unsigned i = 0; features[i]; i++)
1157 	{
1158 		if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
1159 		{
1160 			map_path = features[i]->data;
1161 		}
1162 		if(!strcmp(features[i]->URI, LV2_STATE__freePath))
1163 		{
1164 			free_path = features[i]->data;
1165 		}
1166 	}
1167 
1168 	for(unsigned i = 0; i < props->nimpls; i++)
1169 	{
1170 		props_impl_t *impl = &props->impls[i];
1171 
1172 		if(impl->access == props->urid.patch_readable)
1173 			continue; // skip read-only, as it makes no sense to restore them
1174 
1175 		size_t size;
1176 		uint32_t type;
1177 		uint32_t _flags;
1178 		const void *body = retrieve(state, impl->property, &size, &type, &_flags);
1179 
1180 		if(  body
1181 			&& (type == impl->type)
1182 			&& ( (impl->def->max_size == 0) || (size <= impl->def->max_size) ) )
1183 		{
1184 			if(  map_path && map_path->absolute_path
1185 				&& (type == props->urid.atom_path) )
1186 			{
1187 				char *absolute = map_path->absolute_path(map_path->handle, body);
1188 				if(absolute)
1189 				{
1190 					const uint32_t sz = strlen(absolute) + 1;
1191 
1192 					_props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
1193 
1194 					impl->stash.size = sz;
1195 					memcpy(impl->stash.body, absolute, sz);
1196 
1197 					_props_impl_unlock(impl, PROP_STATE_RESTORE);
1198 
1199 					_free_path(free_path, absolute);
1200 				}
1201 			}
1202 			else // !Path
1203 			{
1204 				_props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
1205 
1206 				impl->stash.size = size;
1207 				memcpy(impl->stash.body, body, size);
1208 
1209 				_props_impl_unlock(impl, PROP_STATE_RESTORE);
1210 			}
1211 		}
1212 	}
1213 
1214 	_props_restoring_set(props);
1215 
1216 	return LV2_STATE_SUCCESS;
1217 }
1218 
1219 #ifdef __cplusplus
1220 }
1221 #endif
1222 
1223 #endif // _LV2_PROPS_H_
1224