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_t props_t;
44 
45 // function callbacks
46 typedef void (*props_event_cb_t)(
47 	void *data,
48 	int64_t frames,
49 	props_impl_t *impl);
50 
51 struct _props_def_t {
52 	const char *property;
53 	const char *type;
54 	const char *access;
55 	size_t offset;
56 
57 	uint32_t max_size;
58 	props_event_cb_t event_cb;
59 };
60 
61 struct _props_impl_t {
62 	LV2_URID property;
63 	LV2_URID type;
64 	LV2_URID access;
65 
66 	struct {
67 		uint32_t size;
68 		void *body;
69 	} value;
70 	struct {
71 		uint32_t size;
72 		void *body;
73 	} stash;
74 
75 	const props_def_t *def;
76 
77 	atomic_int state;
78 	bool stashing;
79 };
80 
81 struct _props_t {
82 	struct {
83 		LV2_URID subject;
84 
85 		LV2_URID patch_get;
86 		LV2_URID patch_set;
87 		LV2_URID patch_put;
88 		LV2_URID patch_patch;
89 		LV2_URID patch_wildcard;
90 		LV2_URID patch_add;
91 		LV2_URID patch_remove;
92 		LV2_URID patch_subject;
93 		LV2_URID patch_body;
94 		LV2_URID patch_property;
95 		LV2_URID patch_value;
96 		LV2_URID patch_writable;
97 		LV2_URID patch_readable;
98 		LV2_URID patch_sequence;
99 		LV2_URID patch_error;
100 		LV2_URID patch_ack;
101 
102 		LV2_URID atom_int;
103 		LV2_URID atom_long;
104 		LV2_URID atom_float;
105 		LV2_URID atom_double;
106 		LV2_URID atom_bool;
107 		LV2_URID atom_urid;
108 		LV2_URID atom_path;
109 		LV2_URID atom_literal;
110 		LV2_URID atom_vector;
111 		LV2_URID atom_object;
112 		LV2_URID atom_sequence;
113 	} urid;
114 
115 	void *data;
116 
117 	bool stashing;
118 	atomic_bool restoring;
119 
120 	uint32_t max_size;
121 
122 	unsigned nimpls;
123 	props_impl_t impls [0];
124 };
125 
126 #define PROPS_T(PROPS, MAX_NIMPLS) \
127 	props_t (PROPS); \
128 	props_impl_t _impls [(MAX_NIMPLS)];
129 
130 // rt-safe
131 static inline int
132 props_init(props_t *props, const char *subject,
133 	const props_def_t *defs, int nimpls,
134 	void *value_base, void *stash_base,
135 	LV2_URID_Map *map, void *data);
136 
137 // rt-safe
138 static inline void
139 props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
140 	LV2_Atom_Forge_Ref *ref);
141 
142 // rt-safe
143 static inline int
144 props_advance(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
145 	const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref);
146 
147 // rt-safe
148 static inline void
149 props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
150 	LV2_URID property, LV2_Atom_Forge_Ref *ref);
151 
152 // rt-safe
153 static inline void
154 props_stash(props_t *props, LV2_URID property);
155 
156 // rt-safe
157 static inline LV2_URID
158 props_map(props_t *props, const char *property);
159 
160 // rt-safe
161 static inline const char *
162 props_unmap(props_t *props, LV2_URID property);
163 
164 // non-rt
165 static inline LV2_State_Status
166 props_save(props_t *props, LV2_State_Store_Function store,
167 	LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);
168 
169 // non-rt
170 static inline LV2_State_Status
171 props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
172 	LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);
173 
174 /*****************************************************************************
175  * API END
176  *****************************************************************************/
177 
178 // enumerations
179 typedef enum _props_state_t props_state_t;
180 
181 enum _props_state_t {
182 	PROP_STATE_NONE    = 0,
183 	PROP_STATE_LOCK    = 1,
184 	PROP_STATE_RESTORE = 2
185 };
186 
187 static inline void
_props_impl_spin_lock(props_impl_t * impl,int from,int to)188 _props_impl_spin_lock(props_impl_t *impl, int from, int to)
189 {
190 	int expected = from;
191 	const int desired = to;
192 
193 	while(!atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
194 		memory_order_acquire, memory_order_acquire))
195 	{
196 		// spin
197 	}
198 }
199 
200 static inline bool
_props_impl_try_lock(props_impl_t * impl,int from,int to)201 _props_impl_try_lock(props_impl_t *impl, int from, int to)
202 {
203 	int expected = from;
204 	const int desired = to;
205 
206 	return atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
207 		memory_order_acquire, memory_order_acquire);
208 }
209 
210 static inline void
_props_impl_unlock(props_impl_t * impl,int to)211 _props_impl_unlock(props_impl_t *impl, int to)
212 {
213 	atomic_store_explicit(&impl->state, to, memory_order_release);
214 }
215 
216 static inline bool
_props_restoring_get(props_t * props)217 _props_restoring_get(props_t *props)
218 {
219 	return atomic_exchange_explicit(&props->restoring, false, memory_order_acquire);
220 }
221 
222 static inline void
_props_restoring_set(props_t * props)223 _props_restoring_set(props_t *props)
224 {
225 	atomic_store_explicit(&props->restoring, true, memory_order_release);
226 }
227 
228 static inline void
_props_qsort(props_impl_t * A,int n)229 _props_qsort(props_impl_t *A, int n)
230 {
231 	if(n < 2)
232 		return;
233 
234 	const props_impl_t *p = A;
235 
236 	int i = -1;
237 	int j = n;
238 
239 	while(true)
240 	{
241 		do {
242 			i += 1;
243 		} while(A[i].property < p->property);
244 
245 		do {
246 			j -= 1;
247 		} while(A[j].property > p->property);
248 
249 		if(i >= j)
250 			break;
251 
252 		const props_impl_t tmp = A[i];
253 		A[i] = A[j];
254 		A[j] = tmp;
255 	}
256 
257 	_props_qsort(A, j + 1);
258 	_props_qsort(A + j + 1, n - j - 1);
259 }
260 
261 static inline props_impl_t *
_props_bsearch(props_t * props,LV2_URID property)262 _props_bsearch(props_t *props, LV2_URID property)
263 {
264 	props_impl_t *base = props->impls;
265 
266 	for(int N = props->nimpls, half; N > 1; N -= half)
267 	{
268 		half = N/2;
269 		props_impl_t *dst = &base[half];
270 		base = (dst->property > property) ? base : dst;
271 	}
272 
273 	return (base->property == property) ? base : NULL;
274 }
275 
276 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)277 _props_patch_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
278 	props_impl_t *impl, int32_t sequence_num)
279 {
280 	LV2_Atom_Forge_Frame obj_frame;
281 
282 	LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
283 
284 	if(ref)
285 		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_set);
286 	{
287 		if(props->urid.subject) // is optional
288 		{
289 			if(ref)
290 				ref = lv2_atom_forge_key(forge, props->urid.patch_subject);
291 			if(ref)
292 				ref = lv2_atom_forge_urid(forge, props->urid.subject);
293 		}
294 
295 		if(sequence_num) // is optional
296 		{
297 			if(ref)
298 				ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
299 			if(ref)
300 				ref = lv2_atom_forge_int(forge, sequence_num);
301 		}
302 
303 		if(ref)
304 			ref = lv2_atom_forge_key(forge, props->urid.patch_property);
305 		if(ref)
306 			ref = lv2_atom_forge_urid(forge, impl->property);
307 
308 		if(ref)
309 			lv2_atom_forge_key(forge, props->urid.patch_value);
310 		if(ref)
311 			ref = lv2_atom_forge_atom(forge, impl->value.size, impl->type);
312 		if(ref)
313 			ref = lv2_atom_forge_write(forge, impl->value.body, impl->value.size);
314 	}
315 	if(ref)
316 		lv2_atom_forge_pop(forge, &obj_frame);
317 
318 	return ref;
319 }
320 
321 static inline LV2_Atom_Forge_Ref
_props_patch_error(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,int32_t sequence_num)322 _props_patch_error(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
323 	int32_t sequence_num)
324 {
325 	LV2_Atom_Forge_Frame obj_frame;
326 
327 	LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
328 
329 	if(ref)
330 		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_error);
331 	{
332 		if(ref)
333 			ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
334 		if(ref)
335 			ref = lv2_atom_forge_int(forge, sequence_num);
336 	}
337 	if(ref)
338 		lv2_atom_forge_pop(forge, &obj_frame);
339 
340 	return ref;
341 }
342 
343 static inline LV2_Atom_Forge_Ref
_props_patch_ack(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,int32_t sequence_num)344 _props_patch_ack(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
345 	int32_t sequence_num)
346 {
347 	LV2_Atom_Forge_Frame obj_frame;
348 
349 	LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
350 
351 	if(ref)
352 		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_ack);
353 	{
354 		if(ref)
355 			ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
356 		if(ref)
357 			ref = lv2_atom_forge_int(forge, sequence_num);
358 	}
359 	if(ref)
360 		lv2_atom_forge_pop(forge, &obj_frame);
361 
362 	return ref;
363 }
364 
365 static inline void
_props_impl_stash(props_t * props,props_impl_t * impl)366 _props_impl_stash(props_t *props, props_impl_t *impl)
367 {
368 	if(_props_impl_try_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK))
369 	{
370 		impl->stashing = false;
371 		impl->stash.size = impl->value.size;
372 		memcpy(impl->stash.body, impl->value.body, impl->value.size);
373 
374 		_props_impl_unlock(impl, PROP_STATE_NONE);
375 	}
376 	else
377 	{
378 		impl->stashing = true; // try again later
379 		props->stashing = true;
380 	}
381 }
382 
383 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)384 _props_impl_restore(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
385 	props_impl_t *impl, LV2_Atom_Forge_Ref *ref)
386 {
387 	if(_props_impl_try_lock(impl, PROP_STATE_RESTORE, PROP_STATE_LOCK))
388 	{
389 		impl->stashing = false; // makes no sense to stash a recently restored value
390 		impl->value.size = impl->stash.size;
391 		memcpy(impl->value.body, impl->stash.body, impl->stash.size);
392 
393 		_props_impl_unlock(impl, PROP_STATE_NONE);
394 
395 		if(*ref)
396 			*ref = _props_patch_set(props, forge, frames, impl, 0);
397 
398 		const props_def_t *def = impl->def;
399 		if(def->event_cb)
400 			def->event_cb(props->data, 0, impl);
401 	}
402 }
403 
404 static inline void
_props_impl_set(props_t * props,props_impl_t * impl,LV2_URID type,uint32_t size,const void * body)405 _props_impl_set(props_t *props, props_impl_t *impl, LV2_URID type,
406 	uint32_t size, const void *body)
407 {
408 	if(  (impl->type == type)
409 		&& ( (impl->def->max_size == 0) || (size <= impl->def->max_size)) )
410 	{
411 		impl->value.size = size;
412 		memcpy(impl->value.body, body, size);
413 
414 		_props_impl_stash(props, impl);
415 	}
416 }
417 
418 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)419 _props_impl_init(props_t *props, props_impl_t *impl, const props_def_t *def,
420 	void *value_base, void *stash_base, LV2_URID_Map *map)
421 {
422 	if(!def->property || !def->type)
423 		return 0;
424 
425 	const LV2_URID type = map->map(map->handle, def->type);
426 	const LV2_URID property = map->map(map->handle, def->property);
427 	const LV2_URID access = def->access
428 		? map->map(map->handle, def->access)
429 		: map->map(map->handle, LV2_PATCH__writable);
430 
431 	if(!type || !property || !access)
432 		return 0;
433 
434 	impl->property = property;
435 	impl->access = access;
436 	impl->def = def;
437 	impl->value.body = value_base + def->offset;
438 	impl->stash.body = stash_base + def->offset;
439 
440 	uint32_t size;
441 	if(  (type == props->urid.atom_int)
442 		|| (type == props->urid.atom_float)
443 		|| (type == props->urid.atom_bool)
444 		|| (type == props->urid.atom_urid) )
445 	{
446 		size = 4;
447 	}
448 	else if((type == props->urid.atom_long)
449 		|| (type == props->urid.atom_double) )
450 	{
451 		size = 8;
452 	}
453 	else if(type == props->urid.atom_literal)
454 	{
455 		size = sizeof(LV2_Atom_Literal_Body);
456 	}
457 	else if(type == props->urid.atom_vector)
458 	{
459 		size = sizeof(LV2_Atom_Vector_Body);
460 	}
461 	else if(type == props->urid.atom_object)
462 	{
463 		size = sizeof(LV2_Atom_Object_Body);
464 	}
465 	else if(type == props->urid.atom_sequence)
466 	{
467 		size = sizeof(LV2_Atom_Sequence_Body);
468 	}
469 	else
470 	{
471 		size = 0; // assume everything else as having size 0
472 	}
473 
474 	impl->type = type;
475 	impl->value.size = size;
476 	impl->stash.size = size;
477 
478 	atomic_init(&impl->state, PROP_STATE_NONE);
479 
480 	// update maximal value size
481 	const uint32_t max_size = def->max_size
482 		? def->max_size
483 		: size;
484 
485 	if(max_size > props->max_size)
486 	{
487 		props->max_size = max_size;
488 	}
489 
490 	return 1;
491 }
492 
493 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)494 props_init(props_t *props, const char *subject,
495 	const props_def_t *defs, int nimpls,
496 	void *value_base, void *stash_base,
497 	LV2_URID_Map *map, void *data)
498 {
499 	if(!props || !defs || !value_base || !stash_base || !map)
500 		return 0;
501 
502 	props->nimpls = nimpls;
503 	props->data = data;
504 
505 	props->urid.subject = subject ? map->map(map->handle, subject) : 0;
506 
507 	props->urid.patch_get = map->map(map->handle, LV2_PATCH__Get);
508 	props->urid.patch_set = map->map(map->handle, LV2_PATCH__Set);
509 	props->urid.patch_put = map->map(map->handle, LV2_PATCH__Put);
510 	props->urid.patch_patch = map->map(map->handle, LV2_PATCH__Patch);
511 	props->urid.patch_wildcard = map->map(map->handle, LV2_PATCH__wildcard);
512 	props->urid.patch_add = map->map(map->handle, LV2_PATCH__add);
513 	props->urid.patch_remove = map->map(map->handle, LV2_PATCH__remove);
514 	props->urid.patch_subject = map->map(map->handle, LV2_PATCH__subject);
515 	props->urid.patch_body = map->map(map->handle, LV2_PATCH__body);
516 	props->urid.patch_property = map->map(map->handle, LV2_PATCH__property);
517 	props->urid.patch_value = map->map(map->handle, LV2_PATCH__value);
518 	props->urid.patch_writable = map->map(map->handle, LV2_PATCH__writable);
519 	props->urid.patch_readable = map->map(map->handle, LV2_PATCH__readable);
520 	props->urid.patch_sequence = map->map(map->handle, LV2_PATCH__sequenceNumber);
521 	props->urid.patch_ack = map->map(map->handle, LV2_PATCH__Ack);
522 	props->urid.patch_error = map->map(map->handle, LV2_PATCH__Error);
523 
524 	props->urid.atom_int = map->map(map->handle, LV2_ATOM__Int);
525 	props->urid.atom_long = map->map(map->handle, LV2_ATOM__Long);
526 	props->urid.atom_float = map->map(map->handle, LV2_ATOM__Float);
527 	props->urid.atom_double = map->map(map->handle, LV2_ATOM__Double);
528 	props->urid.atom_bool = map->map(map->handle, LV2_ATOM__Bool);
529 	props->urid.atom_urid = map->map(map->handle, LV2_ATOM__URID);
530 	props->urid.atom_path = map->map(map->handle, LV2_ATOM__Path);
531 	props->urid.atom_literal = map->map(map->handle, LV2_ATOM__Literal);
532 	props->urid.atom_vector = map->map(map->handle, LV2_ATOM__Vector);
533 	props->urid.atom_object = map->map(map->handle, LV2_ATOM__Object);
534 	props->urid.atom_sequence = map->map(map->handle, LV2_ATOM__Sequence);
535 
536 	atomic_init(&props->restoring, false);
537 
538 	int status = 1;
539 	for(unsigned i = 0; i < props->nimpls; i++)
540 	{
541 		props_impl_t *impl = &props->impls[i];
542 
543 		status = status
544 			&& _props_impl_init(props, impl, &defs[i], value_base, stash_base, map);
545 	}
546 
547 	_props_qsort(props->impls, props->nimpls);
548 
549 	return status;
550 }
551 
552 static inline void
props_idle(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,LV2_Atom_Forge_Ref * ref)553 props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
554 	LV2_Atom_Forge_Ref *ref)
555 {
556 	if(_props_restoring_get(props))
557 	{
558 		for(unsigned i = 0; i < props->nimpls; i++)
559 		{
560 			props_impl_t *impl = &props->impls[i];
561 
562 			_props_impl_restore(props, forge, frames, impl, ref);
563 		}
564 	}
565 
566 	if(props->stashing)
567 	{
568 		props->stashing = false;
569 
570 		for(unsigned i = 0; i < props->nimpls; i++)
571 		{
572 			props_impl_t *impl = &props->impls[i];
573 
574 			if(impl->stashing)
575 				_props_impl_stash(props, impl);
576 		}
577 	}
578 }
579 
580 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)581 props_advance(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
582 	const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref)
583 {
584 	if(!lv2_atom_forge_is_object_type(forge, obj->atom.type))
585 	{
586 		return 0;
587 	}
588 
589 	if(obj->body.otype == props->urid.patch_get)
590 	{
591 		const LV2_Atom_URID *subject = NULL;
592 		const LV2_Atom_URID *property = NULL;
593 		const LV2_Atom_Int *sequence = NULL;
594 
595 		LV2_Atom_Object_Query q [] = {
596 			{ props->urid.patch_subject, (const LV2_Atom **)&subject },
597 			{ props->urid.patch_property, (const LV2_Atom **)&property },
598 			{ props->urid.patch_sequence, (const LV2_Atom **)&sequence },
599 			LV2_ATOM_OBJECT_QUERY_END
600 		};
601 		lv2_atom_object_query(obj, q);
602 
603 		// check for a matching optional subject
604 		if(  (subject && props->urid.subject)
605 			&& ( (subject->atom.type != props->urid.atom_urid)
606 				|| (subject->body != props->urid.subject) ) )
607 		{
608 			return 0;
609 		}
610 
611 		int32_t sequence_num = 0;
612 		if(sequence && (sequence->atom.type == props->urid.atom_int))
613 		{
614 			sequence_num = sequence->body;
615 		}
616 
617 		if(!property)
618 		{
619 			for(unsigned i = 0; i < props->nimpls; i++)
620 			{
621 				props_impl_t *impl = &props->impls[i];
622 				const props_def_t *def = impl->def;
623 
624 				if(*ref)
625 					*ref = _props_patch_set(props, forge, frames, impl, sequence_num);
626 			}
627 
628 			return 1;
629 		}
630 		else if(property->atom.type == props->urid.atom_urid)
631 		{
632 			props_impl_t *impl = _props_bsearch(props, property->body);
633 
634 			if(impl)
635 			{
636 				*ref = _props_patch_set(props, forge, frames, impl, sequence_num);
637 
638 				const props_def_t *def = impl->def;
639 
640 				return 1;
641 			}
642 			else if(sequence_num)
643 			{
644 				*ref = _props_patch_error(props, forge, frames, sequence_num);
645 			}
646 		}
647 		else if(sequence_num)
648 		{
649 			*ref = _props_patch_error(props, forge, frames, sequence_num);
650 		}
651 	}
652 	else if(obj->body.otype == props->urid.patch_set)
653 	{
654 		const LV2_Atom_URID *subject = NULL;
655 		const LV2_Atom_URID *property = NULL;
656 		const LV2_Atom_Int *sequence = NULL;
657 		const LV2_Atom *value = NULL;
658 
659 		LV2_Atom_Object_Query q [] = {
660 			{ props->urid.patch_subject, (const LV2_Atom **)&subject },
661 			{ props->urid.patch_property, (const LV2_Atom **)&property },
662 			{ props->urid.patch_sequence, (const LV2_Atom **)&sequence },
663 			{ props->urid.patch_value, &value },
664 			LV2_ATOM_OBJECT_QUERY_END
665 		};
666 		lv2_atom_object_query(obj, q);
667 
668 		// check for a matching optional subject
669 		if(  (subject && props->urid.subject)
670 			&& ( (subject->atom.type != props->urid.atom_urid)
671 				|| (subject->body != props->urid.subject) ) )
672 		{
673 			return 0;
674 		}
675 
676 		int32_t sequence_num = 0;
677 		if(sequence && (sequence->atom.type == props->urid.atom_int))
678 		{
679 			sequence_num = sequence->body;
680 		}
681 
682 		if(!property || (property->atom.type != props->urid.atom_urid) || !value)
683 		{
684 			if(sequence_num)
685 			{
686 				*ref = _props_patch_error(props, forge, frames, sequence_num);
687 			}
688 
689 			return 0;
690 		}
691 
692 		props_impl_t *impl = _props_bsearch(props, property->body);
693 		if(impl && (impl->access == props->urid.patch_writable) )
694 		{
695 			_props_impl_set(props, impl, value->type, value->size,
696 				LV2_ATOM_BODY_CONST(value));
697 
698 			const props_def_t *def = impl->def;
699 			if(def->event_cb)
700 				def->event_cb(props->data, frames, impl);
701 
702 			if(sequence_num)
703 			{
704 				*ref = _props_patch_ack(props, forge, frames, sequence_num);
705 			}
706 
707 			return 1;
708 		}
709 		else if(sequence_num)
710 		{
711 			*ref = _props_patch_error(props, forge, frames, sequence_num);
712 		}
713 	}
714 	else if(obj->body.otype == props->urid.patch_put)
715 	{
716 		const LV2_Atom_URID *subject = NULL;
717 		const LV2_Atom_Int *sequence = NULL;
718 		const LV2_Atom_Object *body = NULL;
719 
720 		LV2_Atom_Object_Query q [] = {
721 			{ props->urid.patch_subject, (const LV2_Atom **)&subject },
722 			{ props->urid.patch_sequence, (const LV2_Atom **)&sequence},
723 			{ props->urid.patch_body, (const LV2_Atom **)&body },
724 			LV2_ATOM_OBJECT_QUERY_END
725 		};
726 		lv2_atom_object_query(obj, q);
727 
728 		// check for a matching optional subject
729 		if(  (subject && props->urid.subject)
730 			&& ( (subject->atom.type != props->urid.atom_urid)
731 				|| (subject->body != props->urid.subject) ) )
732 		{
733 			return 0;
734 		}
735 
736 		int32_t sequence_num = 0;
737 		if(sequence && (sequence->atom.type == props->urid.atom_int))
738 		{
739 			sequence_num = sequence->body;
740 		}
741 
742 		if(!body || !lv2_atom_forge_is_object_type(forge, body->atom.type))
743 		{
744 			if(sequence_num)
745 			{
746 				*ref = _props_patch_error(props, forge, frames, sequence_num);
747 			}
748 
749 			return 0;
750 		}
751 
752 		LV2_ATOM_OBJECT_FOREACH(body, prop)
753 		{
754 			const LV2_URID property = prop->key;
755 			const LV2_Atom *value = &prop->value;
756 
757 			props_impl_t *impl = _props_bsearch(props, property);
758 			if(impl && (impl->access == props->urid.patch_writable) )
759 			{
760 				_props_impl_set(props, impl, value->type, value->size,
761 					LV2_ATOM_BODY_CONST(value));
762 
763 				const props_def_t *def = impl->def;
764 				if(def->event_cb)
765 					def->event_cb(props->data, frames, impl);
766 			}
767 		}
768 
769 		if(sequence_num)
770 		{
771 			*ref = _props_patch_ack(props, forge, frames, sequence_num);
772 		}
773 
774 		return 1;
775 	}
776 
777 	return 0; // did not handle a patch event
778 }
779 
780 static inline void
props_set(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,LV2_URID property,LV2_Atom_Forge_Ref * ref)781 props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
782 	LV2_URID property, LV2_Atom_Forge_Ref *ref)
783 {
784 	props_impl_t *impl = _props_bsearch(props, property);
785 
786 	if(impl)
787 	{
788 		_props_impl_stash(props, impl);
789 
790 		if(*ref) //TODO use patch:sequenceNumber
791 			*ref = _props_patch_set(props, forge, frames, impl, 0);
792 	}
793 }
794 
795 static inline void
props_stash(props_t * props,LV2_URID property)796 props_stash(props_t *props, LV2_URID property)
797 {
798 	props_impl_t *impl = _props_bsearch(props, property);
799 
800 	if(impl)
801 		_props_impl_stash(props, impl);
802 }
803 
804 static inline LV2_URID
props_map(props_t * props,const char * uri)805 props_map(props_t *props, const char *uri)
806 {
807 	for(unsigned i = 0; i < props->nimpls; i++)
808 	{
809 		props_impl_t *impl = &props->impls[i];
810 
811 		if(!strcmp(impl->def->property, uri))
812 			return impl->property;
813 	}
814 
815 	return 0;
816 }
817 
818 static inline const char *
props_unmap(props_t * props,LV2_URID property)819 props_unmap(props_t *props, LV2_URID property)
820 {
821 	props_impl_t *impl = _props_bsearch(props, property);
822 
823 	if(impl)
824 		return impl->def->property;
825 
826 	return NULL;
827 }
828 
829 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)830 props_save(props_t *props, LV2_State_Store_Function store,
831 	LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features)
832 {
833 	const LV2_State_Map_Path *map_path = NULL;
834 
835 	// set POD flag if not already set by host
836 	flags |= LV2_STATE_IS_POD;
837 
838 	for(unsigned i = 0; features[i]; i++)
839 	{
840 		if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
841 		{
842 			map_path = features[i]->data;
843 			break;
844 		}
845 	}
846 
847 	void *body = malloc(props->max_size); // create memory to store widest value
848 	if(body)
849 	{
850 		for(unsigned i = 0; i < props->nimpls; i++)
851 		{
852 			props_impl_t *impl = &props->impls[i];
853 
854 			if(impl->access == props->urid.patch_readable)
855 				continue; // skip read-only, as it makes no sense to restore them
856 
857 			_props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
858 
859 			// create temporary copy of value, store() may well be blocking
860 			const uint32_t size = impl->stash.size;
861 			memcpy(body, impl->stash.body, size);
862 
863 			_props_impl_unlock(impl, PROP_STATE_NONE);
864 
865 			if( map_path && (impl->type == props->urid.atom_path) )
866 			{
867 				const char *path = strstr(body, "file://")
868 					? body + 7 // skip "file://"
869 					: body;
870 				char *abstract = map_path->abstract_path(map_path->handle, path);
871 				if(abstract)
872 				{
873 					const uint32_t sz = strlen(abstract) + 1;
874 					store(state, impl->property, abstract, sz, impl->type, flags);
875 
876 					free(abstract);
877 				}
878 			}
879 			else // !Path
880 			{
881 				store(state, impl->property, body, size, impl->type, flags);
882 			}
883 		}
884 
885 		free(body);
886 	}
887 
888 	return LV2_STATE_SUCCESS;
889 }
890 
891 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)892 props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
893 	LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features)
894 {
895 	const LV2_State_Map_Path *map_path = NULL;
896 
897 	for(unsigned i = 0; features[i]; i++)
898 	{
899 		if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
900 			map_path = features[i]->data;
901 	}
902 
903 	for(unsigned i = 0; i < props->nimpls; i++)
904 	{
905 		props_impl_t *impl = &props->impls[i];
906 
907 		if(impl->access == props->urid.patch_readable)
908 			continue; // skip read-only, as it makes no sense to restore them
909 
910 		size_t size;
911 		uint32_t type;
912 		uint32_t _flags;
913 		const void *body = retrieve(state, impl->property, &size, &type, &_flags);
914 
915 		if(  body
916 			&& (type == impl->type)
917 			&& ( (impl->def->max_size == 0) || (size <= impl->def->max_size) ) )
918 		{
919 			if(map_path && (type == props->urid.atom_path) )
920 			{
921 				char *absolute = map_path->absolute_path(map_path->handle, body);
922 				if(absolute)
923 				{
924 					const uint32_t sz = strlen(absolute) + 1;
925 
926 					_props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
927 
928 					impl->stash.size = sz;
929 					memcpy(impl->stash.body, absolute, sz);
930 
931 					_props_impl_unlock(impl, PROP_STATE_RESTORE);
932 
933 					free(absolute);
934 				}
935 			}
936 			else // !Path
937 			{
938 				_props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
939 
940 				impl->stash.size = size;
941 				memcpy(impl->stash.body, body, size);
942 
943 				_props_impl_unlock(impl, PROP_STATE_RESTORE);
944 			}
945 		}
946 	}
947 
948 	_props_restoring_set(props);
949 
950 	return LV2_STATE_SUCCESS;
951 }
952 
953 #ifdef __cplusplus
954 }
955 #endif
956 
957 #endif // _LV2_PROPS_H_
958