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