props.h 22.4 KB
Newer Older
Hanspeter Portner's avatar
Hanspeter Portner committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
 *
 * This is free software: you can redistribute it and/or modify
 * it under the terms of the Artistic License 2.0 as published by
 * The Perl Foundation.
 *
 * This source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Artistic License 2.0 for more details.
 *
 * You should have received a copy of the Artistic License 2.0
 * along the source as a COPYING file. If not, obtain it from
 * http://www.perlfoundation.org/artistic_license_2_0.
 */

#ifndef _LV2_PROPS_H_
#define _LV2_PROPS_H_

Hanspeter Portner's avatar
Hanspeter Portner committed
21 22 23 24
#ifdef __cplusplus
extern "C" {
#endif

Hanspeter Portner's avatar
Hanspeter Portner committed
25
#include <stdlib.h>
26
#include <stdatomic.h>
27
#include <stdio.h>
Hanspeter Portner's avatar
Hanspeter Portner committed
28 29 30 31 32 33 34 35

#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
#include <lv2/lv2plug.in/ns/ext/atom/forge.h>
#include <lv2/lv2plug.in/ns/ext/patch/patch.h>
#include <lv2/lv2plug.in/ns/ext/state/state.h>

Hanspeter Portner's avatar
Hanspeter Portner committed
36 37 38 39
/*****************************************************************************
 * API START
 *****************************************************************************/

40
// structures
41 42
typedef struct _props_def_t props_def_t;
typedef struct _props_impl_t props_impl_t;
Hanspeter Portner's avatar
Hanspeter Portner committed
43 44
typedef struct _props_t props_t;

45
// function callbacks
46
typedef void (*props_event_cb_t)(
47 48 49 50
	void *data,
	int64_t frames,
	props_impl_t *impl);

51
struct _props_def_t {
Hanspeter Portner's avatar
Hanspeter Portner committed
52
	const char *property;
53
	const char *type;
Hanspeter Portner's avatar
Hanspeter Portner committed
54
	const char *access;
55
	size_t offset;
Hanspeter Portner's avatar
Hanspeter Portner committed
56

57 58
	uint32_t max_size;
	props_event_cb_t event_cb;
59 60
};

61
struct _props_impl_t {
Hanspeter Portner's avatar
Hanspeter Portner committed
62
	LV2_URID property;
63
	LV2_URID type;
Hanspeter Portner's avatar
Hanspeter Portner committed
64
	LV2_URID access;
65 66 67 68 69 70 71 72 73 74

	struct {
		uint32_t size;
		void *body;
	} value;
	struct {
		uint32_t size;
		void *body;
	} stash;

75
	const props_def_t *def;
76 77

	atomic_int state;
78
	bool stashing;
Hanspeter Portner's avatar
Hanspeter Portner committed
79 80 81 82 83 84 85 86
};

struct _props_t {
	struct {
		LV2_URID subject;

		LV2_URID patch_get;
		LV2_URID patch_set;
87
		LV2_URID patch_put;
Hanspeter Portner's avatar
Hanspeter Portner committed
88 89 90 91 92
		LV2_URID patch_patch;
		LV2_URID patch_wildcard;
		LV2_URID patch_add;
		LV2_URID patch_remove;
		LV2_URID patch_subject;
93
		LV2_URID patch_body;
Hanspeter Portner's avatar
Hanspeter Portner committed
94 95 96 97
		LV2_URID patch_property;
		LV2_URID patch_value;
		LV2_URID patch_writable;
		LV2_URID patch_readable;
98 99 100
		LV2_URID patch_sequence;
		LV2_URID patch_error;
		LV2_URID patch_ack;
101 102 103 104 105 106 107 108

		LV2_URID atom_int;
		LV2_URID atom_long;
		LV2_URID atom_float;
		LV2_URID atom_double;
		LV2_URID atom_bool;
		LV2_URID atom_urid;
		LV2_URID atom_path;
109 110 111 112
		LV2_URID atom_literal;
		LV2_URID atom_vector;
		LV2_URID atom_object;
		LV2_URID atom_sequence;
Hanspeter Portner's avatar
Hanspeter Portner committed
113 114
	} urid;

115 116
	void *data;

117
	bool stashing;
118 119 120
	atomic_bool restoring;

	uint32_t max_size;
121

Hanspeter Portner's avatar
Hanspeter Portner committed
122
	unsigned nimpls;
123
	props_impl_t impls [0];
Hanspeter Portner's avatar
Hanspeter Portner committed
124 125
};

126 127
#define PROPS_T(PROPS, MAX_NIMPLS) \
	props_t (PROPS); \
128
	props_impl_t _impls [(MAX_NIMPLS)]
Hanspeter Portner's avatar
Hanspeter Portner committed
129

130 131
// rt-safe
static inline int
132 133 134
props_init(props_t *props, const char *subject,
	const props_def_t *defs, int nimpls,
	void *value_base, void *stash_base,
135
	LV2_URID_Map *map, void *data);
Hanspeter Portner's avatar
Hanspeter Portner committed
136 137

// rt-safe
138 139 140
static inline void
props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
	LV2_Atom_Forge_Ref *ref);
Hanspeter Portner's avatar
Hanspeter Portner committed
141 142 143 144 145 146 147

// rt-safe
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);

// rt-safe
148
static inline void
149 150
props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
	LV2_URID property, LV2_Atom_Forge_Ref *ref);
151 152 153 154

// rt-safe
static inline void
props_stash(props_t *props, LV2_URID property);
Hanspeter Portner's avatar
Hanspeter Portner committed
155

156 157 158 159 160 161 162 163
// rt-safe
static inline LV2_URID
props_map(props_t *props, const char *property);

// rt-safe
static inline const char *
props_unmap(props_t *props, LV2_URID property);

164
// non-rt
Hanspeter Portner's avatar
Hanspeter Portner committed
165
static inline LV2_State_Status
166
props_save(props_t *props, LV2_State_Store_Function store,
Hanspeter Portner's avatar
Hanspeter Portner committed
167 168
	LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);

169
// non-rt
Hanspeter Portner's avatar
Hanspeter Portner committed
170
static inline LV2_State_Status
171
props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
Hanspeter Portner's avatar
Hanspeter Portner committed
172 173 174 175 176 177
	LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);

/*****************************************************************************
 * API END
 *****************************************************************************/

178
// enumerations
179
typedef enum _props_state_t {
180 181 182
	PROP_STATE_NONE    = 0,
	PROP_STATE_LOCK    = 1,
	PROP_STATE_RESTORE = 2
183
} props_state_t;
184

185
static inline void
186
_props_impl_spin_lock(props_impl_t *impl, int from, int to)
187
{
188 189 190 191 192
	int expected = from;
	const int desired = to;

	while(!atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
		memory_order_acquire, memory_order_acquire))
193 194 195 196 197
	{
		// spin
	}
}

198
static inline bool
199
_props_impl_try_lock(props_impl_t *impl, int from, int to)
200
{
201 202
	int expected = from;
	const int desired = to;
Hanspeter Portner's avatar
Hanspeter Portner committed
203

204 205
	return atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
		memory_order_acquire, memory_order_acquire);
206
}
Hanspeter Portner's avatar
Hanspeter Portner committed
207

208 209
static inline void
_props_impl_unlock(props_impl_t *impl, int to)
210
{
211
	atomic_store_explicit(&impl->state, to, memory_order_release);
212 213
}

214 215
static inline bool
_props_restoring_get(props_t *props)
216
{
217
	return atomic_exchange_explicit(&props->restoring, false, memory_order_acquire);
218 219
}

220
static inline void
221
_props_restoring_set(props_t *props)
Hanspeter Portner's avatar
Hanspeter Portner committed
222
{
223
	atomic_store_explicit(&props->restoring, true, memory_order_release);
Hanspeter Portner's avatar
Hanspeter Portner committed
224 225
}

226
static inline void
227
_props_qsort(props_impl_t *A, int n)
228
{
229 230
	if(n < 2)
		return;
231

232 233 234 235
	const props_impl_t *p = A;

	int i = -1;
	int j = n;
236

237
	while(true)
238
	{
239 240 241
		do {
			i += 1;
		} while(A[i].property < p->property);
242

243 244 245
		do {
			j -= 1;
		} while(A[j].property > p->property);
246 247 248 249

		if(i >= j)
			break;

250 251 252
		const props_impl_t tmp = A[i];
		A[i] = A[j];
		A[j] = tmp;
253 254
	}

255 256
	_props_qsort(A, j + 1);
	_props_qsort(A + j + 1, n - j - 1);
257 258
}

259
static inline props_impl_t *
260
_props_bsearch(props_t *props, LV2_URID property)
261
{
262
	props_impl_t *base = props->impls;
263

264
	for(int N = props->nimpls, half; N > 1; N -= half)
265
	{
266 267 268
		half = N/2;
		props_impl_t *dst = &base[half];
		base = (dst->property > property) ? base : dst;
269
	}
270

271
	return (base->property == property) ? base : NULL;
Hanspeter Portner's avatar
Hanspeter Portner committed
272 273 274
}

static inline LV2_Atom_Forge_Ref
275 276
_props_patch_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
	props_impl_t *impl, int32_t sequence_num)
Hanspeter Portner's avatar
Hanspeter Portner committed
277
{
278 279 280 281 282 283 284
	LV2_Atom_Forge_Frame obj_frame;

	LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);

	if(ref)
		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_set);
	{
Hanspeter Portner's avatar
Hanspeter Portner committed
285 286 287 288 289 290 291
		if(props->urid.subject) // is optional
		{
			if(ref)
				ref = lv2_atom_forge_key(forge, props->urid.patch_subject);
			if(ref)
				ref = lv2_atom_forge_urid(forge, props->urid.subject);
		}
292

293 294 295 296 297 298 299 300
		if(sequence_num) // is optional
		{
			if(ref)
				ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
			if(ref)
				ref = lv2_atom_forge_int(forge, sequence_num);
		}

301 302 303 304 305 306 307 308
		if(ref)
			ref = lv2_atom_forge_key(forge, props->urid.patch_property);
		if(ref)
			ref = lv2_atom_forge_urid(forge, impl->property);

		if(ref)
			lv2_atom_forge_key(forge, props->urid.patch_value);
		if(ref)
309 310 311
			ref = lv2_atom_forge_atom(forge, impl->value.size, impl->type);
		if(ref)
			ref = lv2_atom_forge_write(forge, impl->value.body, impl->value.size);
312 313 314 315 316
	}
	if(ref)
		lv2_atom_forge_pop(forge, &obj_frame);

	return ref;
Hanspeter Portner's avatar
Hanspeter Portner committed
317 318
}

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
{
323
	LV2_Atom_Forge_Frame obj_frame;
324

325 326 327 328
	LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);

	if(ref)
		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_error);
329
	{
330 331 332 333
		if(ref)
			ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
		if(ref)
			ref = lv2_atom_forge_int(forge, sequence_num);
334
	}
335 336
	if(ref)
		lv2_atom_forge_pop(forge, &obj_frame);
337

338
	return ref;
Hanspeter Portner's avatar
Hanspeter Portner committed
339 340 341
}

static inline LV2_Atom_Forge_Ref
342 343
_props_patch_ack(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
	int32_t sequence_num)
Hanspeter Portner's avatar
Hanspeter Portner committed
344 345 346 347
{
	LV2_Atom_Forge_Frame obj_frame;

	LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
348

Hanspeter Portner's avatar
Hanspeter Portner committed
349
	if(ref)
350
		ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_ack);
Hanspeter Portner's avatar
Hanspeter Portner committed
351 352
	{
		if(ref)
353
			ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
Hanspeter Portner's avatar
Hanspeter Portner committed
354
		if(ref)
355
			ref = lv2_atom_forge_int(forge, sequence_num);
Hanspeter Portner's avatar
Hanspeter Portner committed
356 357 358 359
	}
	if(ref)
		lv2_atom_forge_pop(forge, &obj_frame);

360 361 362 363 364 365 366
	return ref;
}

static inline void
_props_impl_stash(props_t *props, props_impl_t *impl)
{
	if(_props_impl_try_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK))
Hanspeter Portner's avatar
Hanspeter Portner committed
367
	{
368 369 370
		impl->stashing = false;
		impl->stash.size = impl->value.size;
		memcpy(impl->stash.body, impl->value.body, impl->value.size);
Hanspeter Portner's avatar
Hanspeter Portner committed
371

372 373 374 375 376 377 378 379
		_props_impl_unlock(impl, PROP_STATE_NONE);
	}
	else
	{
		impl->stashing = true; // try again later
		props->stashing = true;
	}
}
Hanspeter Portner's avatar
Hanspeter Portner committed
380

381 382 383 384 385 386 387 388 389
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)
{
	if(_props_impl_try_lock(impl, PROP_STATE_RESTORE, PROP_STATE_LOCK))
	{
		impl->stashing = false; // makes no sense to stash a recently restored value
		impl->value.size = impl->stash.size;
		memcpy(impl->value.body, impl->stash.body, impl->stash.size);
Hanspeter Portner's avatar
Hanspeter Portner committed
390

391
		_props_impl_unlock(impl, PROP_STATE_NONE);
392

393 394
		if(*ref)
			*ref = _props_patch_set(props, forge, frames, impl, 0);
Hanspeter Portner's avatar
Hanspeter Portner committed
395

396 397 398 399 400
		const props_def_t *def = impl->def;
		if(def->event_cb)
			def->event_cb(props->data, 0, impl);
	}
}
Hanspeter Portner's avatar
Hanspeter Portner committed
401

402 403 404 405 406 407 408 409 410
static inline void
_props_impl_set(props_t *props, props_impl_t *impl, LV2_URID type,
	uint32_t size, const void *body)
{
	if(  (impl->type == type)
		&& ( (impl->def->max_size == 0) || (size <= impl->def->max_size)) )
	{
		impl->value.size = size;
		memcpy(impl->value.body, body, size);
411

412 413 414
		_props_impl_stash(props, impl);
	}
}
Hanspeter Portner's avatar
Hanspeter Portner committed
415

416 417 418 419 420 421
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)
{
	if(!def->property || !def->type)
		return 0;
Hanspeter Portner's avatar
Hanspeter Portner committed
422

423 424 425 426 427
	const LV2_URID type = map->map(map->handle, def->type);
	const LV2_URID property = map->map(map->handle, def->property);
	const LV2_URID access = def->access
		? map->map(map->handle, def->access)
		: map->map(map->handle, LV2_PATCH__writable);
Hanspeter Portner's avatar
Hanspeter Portner committed
428

429 430
	if(!type || !property || !access)
		return 0;
431

432 433 434
	impl->property = property;
	impl->access = access;
	impl->def = def;
435 436
	impl->value.body = (uint8_t *)value_base + def->offset;
	impl->stash.body = (uint8_t *)stash_base + def->offset;
Hanspeter Portner's avatar
Hanspeter Portner committed
437

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
	uint32_t size;
	if(  (type == props->urid.atom_int)
		|| (type == props->urid.atom_float)
		|| (type == props->urid.atom_bool)
		|| (type == props->urid.atom_urid) )
	{
		size = 4;
	}
	else if((type == props->urid.atom_long)
		|| (type == props->urid.atom_double) )
	{
		size = 8;
	}
	else if(type == props->urid.atom_literal)
	{
		size = sizeof(LV2_Atom_Literal_Body);
	}
	else if(type == props->urid.atom_vector)
	{
		size = sizeof(LV2_Atom_Vector_Body);
	}
	else if(type == props->urid.atom_object)
	{
		size = sizeof(LV2_Atom_Object_Body);
	}
	else if(type == props->urid.atom_sequence)
	{
		size = sizeof(LV2_Atom_Sequence_Body);
	}
	else
	{
		size = 0; // assume everything else as having size 0
	}
Hanspeter Portner's avatar
Hanspeter Portner committed
471

472 473 474
	impl->type = type;
	impl->value.size = size;
	impl->stash.size = size;
475

476 477 478 479 480 481 482 483 484 485
	atomic_init(&impl->state, PROP_STATE_NONE);

	// update maximal value size
	const uint32_t max_size = def->max_size
		? def->max_size
		: size;

	if(max_size > props->max_size)
	{
		props->max_size = max_size;
Hanspeter Portner's avatar
Hanspeter Portner committed
486 487
	}

488
	return 1;
Hanspeter Portner's avatar
Hanspeter Portner committed
489 490
}

491
static inline int
492 493 494
props_init(props_t *props, const char *subject,
	const props_def_t *defs, int nimpls,
	void *value_base, void *stash_base,
495
	LV2_URID_Map *map, void *data)
Hanspeter Portner's avatar
Hanspeter Portner committed
496
{
497
	if(!props || !defs || !value_base || !stash_base || !map)
498
		return 0;
Hanspeter Portner's avatar
Hanspeter Portner committed
499

500
	props->nimpls = nimpls;
501
	props->data = data;
502

Hanspeter Portner's avatar
Hanspeter Portner committed
503
	props->urid.subject = subject ? map->map(map->handle, subject) : 0;
504

505 506
	props->urid.patch_get = map->map(map->handle, LV2_PATCH__Get);
	props->urid.patch_set = map->map(map->handle, LV2_PATCH__Set);
507
	props->urid.patch_put = map->map(map->handle, LV2_PATCH__Put);
508 509 510 511 512
	props->urid.patch_patch = map->map(map->handle, LV2_PATCH__Patch);
	props->urid.patch_wildcard = map->map(map->handle, LV2_PATCH__wildcard);
	props->urid.patch_add = map->map(map->handle, LV2_PATCH__add);
	props->urid.patch_remove = map->map(map->handle, LV2_PATCH__remove);
	props->urid.patch_subject = map->map(map->handle, LV2_PATCH__subject);
513
	props->urid.patch_body = map->map(map->handle, LV2_PATCH__body);
514 515 516 517
	props->urid.patch_property = map->map(map->handle, LV2_PATCH__property);
	props->urid.patch_value = map->map(map->handle, LV2_PATCH__value);
	props->urid.patch_writable = map->map(map->handle, LV2_PATCH__writable);
	props->urid.patch_readable = map->map(map->handle, LV2_PATCH__readable);
518 519 520
	props->urid.patch_sequence = map->map(map->handle, LV2_PATCH__sequenceNumber);
	props->urid.patch_ack = map->map(map->handle, LV2_PATCH__Ack);
	props->urid.patch_error = map->map(map->handle, LV2_PATCH__Error);
Hanspeter Portner's avatar
Hanspeter Portner committed
521

522 523 524 525 526 527 528
	props->urid.atom_int = map->map(map->handle, LV2_ATOM__Int);
	props->urid.atom_long = map->map(map->handle, LV2_ATOM__Long);
	props->urid.atom_float = map->map(map->handle, LV2_ATOM__Float);
	props->urid.atom_double = map->map(map->handle, LV2_ATOM__Double);
	props->urid.atom_bool = map->map(map->handle, LV2_ATOM__Bool);
	props->urid.atom_urid = map->map(map->handle, LV2_ATOM__URID);
	props->urid.atom_path = map->map(map->handle, LV2_ATOM__Path);
529 530 531 532
	props->urid.atom_literal = map->map(map->handle, LV2_ATOM__Literal);
	props->urid.atom_vector = map->map(map->handle, LV2_ATOM__Vector);
	props->urid.atom_object = map->map(map->handle, LV2_ATOM__Object);
	props->urid.atom_sequence = map->map(map->handle, LV2_ATOM__Sequence);
Hanspeter Portner's avatar
Hanspeter Portner committed
533

534
	atomic_init(&props->restoring, false);
Hanspeter Portner's avatar
Hanspeter Portner committed
535

536 537 538 539
	int status = 1;
	for(unsigned i = 0; i < props->nimpls; i++)
	{
		props_impl_t *impl = &props->impls[i];
540

541 542 543
		status = status
			&& _props_impl_init(props, impl, &defs[i], value_base, stash_base, map);
	}
Hanspeter Portner's avatar
Hanspeter Portner committed
544

545
	_props_qsort(props->impls, props->nimpls);
Hanspeter Portner's avatar
Hanspeter Portner committed
546

547
	return status;
548 549
}

550 551 552
static inline void
props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
	LV2_Atom_Forge_Ref *ref)
Hanspeter Portner's avatar
Hanspeter Portner committed
553
{
554 555 556 557 558 559 560 561 562 563
	if(_props_restoring_get(props))
	{
		for(unsigned i = 0; i < props->nimpls; i++)
		{
			props_impl_t *impl = &props->impls[i];

			_props_impl_restore(props, forge, frames, impl, ref);
		}
	}

564
	if(props->stashing)
565
	{
566
		props->stashing = false;
567

568
		for(unsigned i = 0; i < props->nimpls; i++)
569
		{
570
			props_impl_t *impl = &props->impls[i];
571

572
			if(impl->stashing)
573
				_props_impl_stash(props, impl);
574 575
		}
	}
576
}
577

578 579 580 581
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)
{
Hanspeter Portner's avatar
Hanspeter Portner committed
582
	if(!lv2_atom_forge_is_object_type(forge, obj->atom.type))
583
	{
Hanspeter Portner's avatar
Hanspeter Portner committed
584
		return 0;
585
	}
Hanspeter Portner's avatar
Hanspeter Portner committed
586 587 588 589 590

	if(obj->body.otype == props->urid.patch_get)
	{
		const LV2_Atom_URID *subject = NULL;
		const LV2_Atom_URID *property = NULL;
591
		const LV2_Atom_Int *sequence = NULL;
Hanspeter Portner's avatar
Hanspeter Portner committed
592

593 594 595 596 597
		lv2_atom_object_get(obj,
			props->urid.patch_subject, &subject,
			props->urid.patch_property, &property,
			props->urid.patch_sequence, &sequence,
			0);
Hanspeter Portner's avatar
Hanspeter Portner committed
598

Hanspeter Portner's avatar
Hanspeter Portner committed
599
		// check for a matching optional subject
600 601 602 603
		if(  (subject && props->urid.subject)
			&& ( (subject->atom.type != props->urid.atom_urid)
				|| (subject->body != props->urid.subject) ) )
		{
Hanspeter Portner's avatar
Hanspeter Portner committed
604
			return 0;
605
		}
Hanspeter Portner's avatar
Hanspeter Portner committed
606

607 608 609 610 611 612
		int32_t sequence_num = 0;
		if(sequence && (sequence->atom.type == props->urid.atom_int))
		{
			sequence_num = sequence->body;
		}

613
		if(!property)
Hanspeter Portner's avatar
Hanspeter Portner committed
614
		{
615
			for(unsigned i = 0; i < props->nimpls; i++)
Hanspeter Portner's avatar
Hanspeter Portner committed
616
			{
617
				props_impl_t *impl = &props->impls[i];
618 619

				if(*ref)
620
					*ref = _props_patch_set(props, forge, frames, impl, sequence_num);
Hanspeter Portner's avatar
Hanspeter Portner committed
621
			}
622

Hanspeter Portner's avatar
Hanspeter Portner committed
623 624
			return 1;
		}
625
		else if(property->atom.type == props->urid.atom_urid)
Hanspeter Portner's avatar
Hanspeter Portner committed
626
		{
627
			props_impl_t *impl = _props_bsearch(props, property->body);
628

Hanspeter Portner's avatar
Hanspeter Portner committed
629 630
			if(impl)
			{
631
				*ref = _props_patch_set(props, forge, frames, impl, sequence_num);
632

Hanspeter Portner's avatar
Hanspeter Portner committed
633 634
				return 1;
			}
635 636 637 638 639 640 641 642
			else if(sequence_num)
			{
				*ref = _props_patch_error(props, forge, frames, sequence_num);
			}
		}
		else if(sequence_num)
		{
			*ref = _props_patch_error(props, forge, frames, sequence_num);
Hanspeter Portner's avatar
Hanspeter Portner committed
643 644 645 646 647 648
		}
	}
	else if(obj->body.otype == props->urid.patch_set)
	{
		const LV2_Atom_URID *subject = NULL;
		const LV2_Atom_URID *property = NULL;
649
		const LV2_Atom_Int *sequence = NULL;
Hanspeter Portner's avatar
Hanspeter Portner committed
650 651
		const LV2_Atom *value = NULL;

652 653 654 655 656 657
		lv2_atom_object_get(obj,
			props->urid.patch_subject, &subject,
			props->urid.patch_property, &property,
			props->urid.patch_sequence, &sequence,
			props->urid.patch_value, &value,
			0);
Hanspeter Portner's avatar
Hanspeter Portner committed
658

Hanspeter Portner's avatar
Hanspeter Portner committed
659
		// check for a matching optional subject
660 661 662 663
		if(  (subject && props->urid.subject)
			&& ( (subject->atom.type != props->urid.atom_urid)
				|| (subject->body != props->urid.subject) ) )
		{
Hanspeter Portner's avatar
Hanspeter Portner committed
664
			return 0;
665
		}
Hanspeter Portner's avatar
Hanspeter Portner committed
666

667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
		int32_t sequence_num = 0;
		if(sequence && (sequence->atom.type == props->urid.atom_int))
		{
			sequence_num = sequence->body;
		}

		if(!property || (property->atom.type != props->urid.atom_urid) || !value)
		{
			if(sequence_num)
			{
				*ref = _props_patch_error(props, forge, frames, sequence_num);
			}

			return 0;
		}

		props_impl_t *impl = _props_bsearch(props, property->body);
Hanspeter Portner's avatar
Hanspeter Portner committed
684 685
		if(impl && (impl->access == props->urid.patch_writable) )
		{
686 687
			_props_impl_set(props, impl, value->type, value->size,
				LV2_ATOM_BODY_CONST(value));
688

689
			const props_def_t *def = impl->def;
690 691 692 693 694 695 696
			if(def->event_cb)
				def->event_cb(props->data, frames, impl);

			if(sequence_num)
			{
				*ref = _props_patch_ack(props, forge, frames, sequence_num);
			}
697

Hanspeter Portner's avatar
Hanspeter Portner committed
698 699
			return 1;
		}
700 701 702 703
		else if(sequence_num)
		{
			*ref = _props_patch_error(props, forge, frames, sequence_num);
		}
Hanspeter Portner's avatar
Hanspeter Portner committed
704
	}
705 706 707
	else if(obj->body.otype == props->urid.patch_put)
	{
		const LV2_Atom_URID *subject = NULL;
708
		const LV2_Atom_Int *sequence = NULL;
709 710
		const LV2_Atom_Object *body = NULL;

711 712 713 714 715
		lv2_atom_object_get(obj,
			props->urid.patch_subject, &subject,
			props->urid.patch_sequence, &sequence,
			props->urid.patch_body, &body,
			0);
716 717 718 719 720 721 722 723 724

		// check for a matching optional subject
		if(  (subject && props->urid.subject)
			&& ( (subject->atom.type != props->urid.atom_urid)
				|| (subject->body != props->urid.subject) ) )
		{
			return 0;
		}

725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
		int32_t sequence_num = 0;
		if(sequence && (sequence->atom.type == props->urid.atom_int))
		{
			sequence_num = sequence->body;
		}

		if(!body || !lv2_atom_forge_is_object_type(forge, body->atom.type))
		{
			if(sequence_num)
			{
				*ref = _props_patch_error(props, forge, frames, sequence_num);
			}

			return 0;
		}

741 742 743 744 745
		LV2_ATOM_OBJECT_FOREACH(body, prop)
		{
			const LV2_URID property = prop->key;
			const LV2_Atom *value = &prop->value;

746
			props_impl_t *impl = _props_bsearch(props, property);
747 748
			if(impl && (impl->access == props->urid.patch_writable) )
			{
749 750
				_props_impl_set(props, impl, value->type, value->size,
					LV2_ATOM_BODY_CONST(value));
751

752
				const props_def_t *def = impl->def;
753 754
				if(def->event_cb)
					def->event_cb(props->data, frames, impl);
755 756
			}
		}
757 758 759 760 761 762

		if(sequence_num)
		{
			*ref = _props_patch_ack(props, forge, frames, sequence_num);
		}

763 764
		return 1;
	}
Hanspeter Portner's avatar
Hanspeter Portner committed
765 766 767 768

	return 0; // did not handle a patch event
}

769
static inline void
770 771
props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
	LV2_URID property, LV2_Atom_Forge_Ref *ref)
772
{
773
	props_impl_t *impl = _props_bsearch(props, property);
774

Hanspeter Portner's avatar
Hanspeter Portner committed
775
	if(impl)
776
	{
777 778 779 780
		_props_impl_stash(props, impl);

		if(*ref) //TODO use patch:sequenceNumber
			*ref = _props_patch_set(props, forge, frames, impl, 0);
781
	}
782
}
783

784 785 786
static inline void
props_stash(props_t *props, LV2_URID property)
{
787
	props_impl_t *impl = _props_bsearch(props, property);
788 789

	if(impl)
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
		_props_impl_stash(props, impl);
}

static inline LV2_URID
props_map(props_t *props, const char *uri)
{
	for(unsigned i = 0; i < props->nimpls; i++)
	{
		props_impl_t *impl = &props->impls[i];

		if(!strcmp(impl->def->property, uri))
			return impl->property;
	}

	return 0;
}

static inline const char *
props_unmap(props_t *props, LV2_URID property)
{
	props_impl_t *impl = _props_bsearch(props, property);

	if(impl)
		return impl->def->property;

	return NULL;
816 817
}

Hanspeter Portner's avatar
Hanspeter Portner committed
818
static inline LV2_State_Status
819
props_save(props_t *props, LV2_State_Store_Function store,
Hanspeter Portner's avatar
Hanspeter Portner committed
820 821
	LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features)
{
822 823
	const LV2_State_Map_Path *map_path = NULL;

824 825 826
	// set POD flag if not already set by host
	flags |= LV2_STATE_IS_POD;

827 828 829
	for(unsigned i = 0; features[i]; i++)
	{
		if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
830
		{
831
			map_path = features[i]->data;
832 833
			break;
		}
834 835
	}

836 837
	void *body = malloc(props->max_size); // create memory to store widest value
	if(body)
Hanspeter Portner's avatar
Hanspeter Portner committed
838
	{
839 840 841
		for(unsigned i = 0; i < props->nimpls; i++)
		{
			props_impl_t *impl = &props->impls[i];
Hanspeter Portner's avatar
Hanspeter Portner committed
842

843 844
			if(impl->access == props->urid.patch_readable)
				continue; // skip read-only, as it makes no sense to restore them
845

846
			_props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
847

848 849 850
			// create temporary copy of value, store() may well be blocking
			const uint32_t size = impl->stash.size;
			memcpy(body, impl->stash.body, size);
851

852
			_props_impl_unlock(impl, PROP_STATE_NONE);
853

854
			if( map_path && (impl->type == props->urid.atom_path) )
855
			{
856
				const char *path = strstr(body, "file://")
857 858
					? (char *)body + 7 // skip "file://"
					: (char *)body;
859
				char *abstract = map_path->abstract_path(map_path->handle, path);
860
				if(abstract)
861
				{
862 863 864
					const uint32_t sz = strlen(abstract) + 1;
					store(state, impl->property, abstract, sz, impl->type, flags);

865 866
					free(abstract);
				}
867
			}
868 869
			else // !Path
			{
870
				store(state, impl->property, body, size, impl->type, flags);
871
			}
872
		}
873

874
		free(body);
Hanspeter Portner's avatar
Hanspeter Portner committed
875 876 877 878 879 880
	}

	return LV2_STATE_SUCCESS;
}

static inline LV2_State_Status
881
props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
882 883
	LV2_State_Handle state, uint32_t flags __attribute__((unused)),
	const LV2_Feature *const *features)
Hanspeter Portner's avatar
Hanspeter Portner committed
884
{
885 886 887 888 889 890 891 892
	const LV2_State_Map_Path *map_path = NULL;

	for(unsigned i = 0; features[i]; i++)
	{
		if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
			map_path = features[i]->data;
	}

Hanspeter Portner's avatar
Hanspeter Portner committed
893 894
	for(unsigned i = 0; i < props->nimpls; i++)
	{
895
		props_impl_t *impl = &props->impls[i];
Hanspeter Portner's avatar
Hanspeter Portner committed
896

897 898 899
		if(impl->access == props->urid.patch_readable)
			continue; // skip read-only, as it makes no sense to restore them

Hanspeter Portner's avatar
Hanspeter Portner committed
900 901 902
		size_t size;
		uint32_t type;
		uint32_t _flags;
903
		const void *body = retrieve(state, impl->property, &size, &type, &_flags);
904

905 906 907
		if(  body
			&& (type == impl->type)
			&& ( (impl->def->max_size == 0) || (size <= impl->def->max_size) ) )
Hanspeter Portner's avatar
Hanspeter Portner committed
908
		{
909
			if(map_path && (type == props->urid.atom_path) )
910
			{
911
				char *absolute = map_path->absolute_path(map_path->handle, body);
912 913
				if(absolute)
				{
914 915 916 917 918 919 920 921 922
					const uint32_t sz = strlen(absolute) + 1;

					_props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);

					impl->stash.size = sz;
					memcpy(impl->stash.body, absolute, sz);

					_props_impl_unlock(impl, PROP_STATE_RESTORE);

923 924 925 926 927
					free(absolute);
				}
			}
			else // !Path
			{
928
				_props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
929

930 931 932 933 934
				impl->stash.size = size;
				memcpy(impl->stash.body, body, size);

				_props_impl_unlock(impl, PROP_STATE_RESTORE);
			}
935
		}
Hanspeter Portner's avatar
Hanspeter Portner committed
936 937
	}

938 939
	_props_restoring_set(props);

Hanspeter Portner's avatar
Hanspeter Portner committed
940 941 942
	return LV2_STATE_SUCCESS;
}

Hanspeter Portner's avatar
Hanspeter Portner committed
943 944 945 946
#ifdef __cplusplus
}
#endif

Hanspeter Portner's avatar
Hanspeter Portner committed
947
#endif // _LV2_PROPS_H_