Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/src/lv2/atom-util.h @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 /* | |
2 Copyright 2008-2015 David Robillard <http://drobilla.net> | |
3 | |
4 Permission to use, copy, modify, and/or distribute this software for any | |
5 purpose with or without fee is hereby granted, provided that the above | |
6 copyright notice and this permission notice appear in all copies. | |
7 | |
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 */ | |
16 | |
17 /** | |
18 @file util.h Helper functions for the LV2 Atom extension. | |
19 | |
20 Note these functions are all static inline, do not take their address. | |
21 | |
22 This header is non-normative, it is provided for convenience. | |
23 */ | |
24 | |
25 /** | |
26 @defgroup util Utilities | |
27 @ingroup atom | |
28 @{ | |
29 */ | |
30 | |
31 #ifndef LV2_ATOM_UTIL_H | |
32 #define LV2_ATOM_UTIL_H | |
33 | |
34 #include <stdarg.h> | |
35 #include <stdint.h> | |
36 #include <string.h> | |
37 | |
38 #include "atom.h" | |
39 | |
40 #ifdef __cplusplus | |
41 extern "C" { | |
42 #else | |
43 # include <stdbool.h> | |
44 #endif | |
45 | |
46 /** Pad a size to 64 bits. */ | |
47 static inline uint32_t | |
48 lv2_atom_pad_size(uint32_t size) | |
49 { | |
50 return (size + 7U) & (~7U); | |
51 } | |
52 | |
53 /** Return the total size of `atom`, including the header. */ | |
54 static inline uint32_t | |
55 lv2_atom_total_size(const LV2_Atom* atom) | |
56 { | |
57 return (uint32_t)sizeof(LV2_Atom) + atom->size; | |
58 } | |
59 | |
60 /** Return true iff `atom` is null. */ | |
61 static inline bool | |
62 lv2_atom_is_null(const LV2_Atom* atom) | |
63 { | |
64 return !atom || (atom->type == 0 && atom->size == 0); | |
65 } | |
66 | |
67 /** Return true iff `a` is equal to `b`. */ | |
68 static inline bool | |
69 lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b) | |
70 { | |
71 return (a == b) || ((a->type == b->type) && | |
72 (a->size == b->size) && | |
73 !memcmp(a + 1, b + 1, a->size)); | |
74 } | |
75 | |
76 /** | |
77 @name Sequence Iterator | |
78 @{ | |
79 */ | |
80 | |
81 /** Get an iterator pointing to the first event in a Sequence body. */ | |
82 static inline const LV2_Atom_Event* | |
83 lv2_atom_sequence_begin(const LV2_Atom_Sequence_Body* body) | |
84 { | |
85 return (const LV2_Atom_Event*)(body + 1); | |
86 } | |
87 | |
88 /** Get an iterator pointing to the end of a Sequence body. */ | |
89 static inline const LV2_Atom_Event* | |
90 lv2_atom_sequence_end(const LV2_Atom_Sequence_Body* body, uint32_t size) | |
91 { | |
92 return (const LV2_Atom_Event*)((const uint8_t*)body + lv2_atom_pad_size(size)); | |
93 } | |
94 | |
95 /** Get an iterator pointing to the end of a Sequence body. */ | |
96 static inline LV2_Atom_Event* | |
97 lv2_atom_sequence_end2(LV2_Atom_Sequence_Body* body, uint32_t size) | |
98 { | |
99 return (LV2_Atom_Event*)((uint8_t*)body + lv2_atom_pad_size(size)); | |
100 } | |
101 | |
102 /** Return true iff `i` has reached the end of `body`. */ | |
103 static inline bool | |
104 lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body, | |
105 uint32_t size, | |
106 const LV2_Atom_Event* i) | |
107 { | |
108 return (const uint8_t*)i >= ((const uint8_t*)body + size); | |
109 } | |
110 | |
111 /** Return an iterator to the element following `i`. */ | |
112 static inline const LV2_Atom_Event* | |
113 lv2_atom_sequence_next(const LV2_Atom_Event* i) | |
114 { | |
115 return (const LV2_Atom_Event*)((const uint8_t*)i | |
116 + sizeof(LV2_Atom_Event) | |
117 + lv2_atom_pad_size(i->body.size)); | |
118 } | |
119 | |
120 /** | |
121 A macro for iterating over all events in a Sequence. | |
122 @param seq The sequence to iterate over | |
123 @param iter The name of the iterator | |
124 | |
125 This macro is used similarly to a for loop (which it expands to), e.g.: | |
126 @code | |
127 LV2_ATOM_SEQUENCE_FOREACH(sequence, ev) { | |
128 // Do something with ev (an LV2_Atom_Event*) here... | |
129 } | |
130 @endcode | |
131 */ | |
132 #define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \ | |
133 for (const LV2_Atom_Event* iter = lv2_atom_sequence_begin(&(seq)->body); \ | |
134 !lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \ | |
135 iter = lv2_atom_sequence_next(iter)) | |
136 | |
137 /** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */ | |
138 #define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \ | |
139 for (const LV2_Atom_Event* iter = lv2_atom_sequence_begin(body); \ | |
140 !lv2_atom_sequence_is_end(body, size, (iter)); \ | |
141 iter = lv2_atom_sequence_next(iter)) | |
142 | |
143 /** | |
144 @} | |
145 @name Sequence Utilities | |
146 @{ | |
147 */ | |
148 | |
149 /** | |
150 Clear all events from `sequence`. | |
151 | |
152 This simply resets the size field, the other fields are left untouched. | |
153 */ | |
154 static inline void | |
155 lv2_atom_sequence_clear(LV2_Atom_Sequence* seq) | |
156 { | |
157 seq->atom.size = sizeof(LV2_Atom_Sequence_Body); | |
158 } | |
159 | |
160 /** | |
161 Append an event at the end of `sequence`. | |
162 | |
163 @param seq Sequence to append to. | |
164 @param capacity Total capacity of the sequence atom | |
165 (e.g. as set by the host for sequence output ports). | |
166 @param event Event to write. | |
167 | |
168 @return A pointer to the newly written event in `seq`, | |
169 or NULL on failure (insufficient space). | |
170 */ | |
171 static inline LV2_Atom_Event* | |
172 lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, | |
173 uint32_t capacity, | |
174 const LV2_Atom_Event* event) | |
175 { | |
176 const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size; | |
177 if (capacity - seq->atom.size < total_size) { | |
178 return NULL; | |
179 } | |
180 | |
181 LV2_Atom_Event* e = lv2_atom_sequence_end2(&seq->body, seq->atom.size); | |
182 memcpy(e, event, total_size); | |
183 | |
184 seq->atom.size += lv2_atom_pad_size(total_size); | |
185 | |
186 return e; | |
187 } | |
188 | |
189 /** | |
190 @} | |
191 @name Tuple Iterator | |
192 @{ | |
193 */ | |
194 | |
195 /** Get an iterator pointing to the first element in `tup`. */ | |
196 static inline const LV2_Atom* | |
197 lv2_atom_tuple_begin(const LV2_Atom_Tuple* tup) | |
198 { | |
199 return (const LV2_Atom*)(LV2_ATOM_BODY_CONST(tup)); | |
200 } | |
201 | |
202 /** Return true iff `i` has reached the end of `body`. */ | |
203 static inline bool | |
204 lv2_atom_tuple_is_end(const void* body, uint32_t size, const LV2_Atom* i) | |
205 { | |
206 return (const uint8_t*)i >= ((const uint8_t*)body + size); | |
207 } | |
208 | |
209 /** Return an iterator to the element following `i`. */ | |
210 static inline const LV2_Atom* | |
211 lv2_atom_tuple_next(const LV2_Atom* i) | |
212 { | |
213 return (const LV2_Atom*)( | |
214 (const uint8_t*)i + sizeof(LV2_Atom) + lv2_atom_pad_size(i->size)); | |
215 } | |
216 | |
217 /** | |
218 A macro for iterating over all properties of a Tuple. | |
219 @param tuple The tuple to iterate over | |
220 @param iter The name of the iterator | |
221 | |
222 This macro is used similarly to a for loop (which it expands to), e.g.: | |
223 @code | |
224 LV2_ATOM_TUPLE_FOREACH(tuple, elem) { | |
225 // Do something with elem (an LV2_Atom*) here... | |
226 } | |
227 @endcode | |
228 */ | |
229 #define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \ | |
230 for (const LV2_Atom* iter = lv2_atom_tuple_begin(tuple); \ | |
231 !lv2_atom_tuple_is_end(LV2_ATOM_BODY_CONST(tuple), (tuple)->atom.size, (iter)); \ | |
232 iter = lv2_atom_tuple_next(iter)) | |
233 | |
234 /** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */ | |
235 #define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \ | |
236 for (const LV2_Atom* iter = (const LV2_Atom*)body; \ | |
237 !lv2_atom_tuple_is_end(body, size, (iter)); \ | |
238 iter = lv2_atom_tuple_next(iter)) | |
239 | |
240 /** | |
241 @} | |
242 @name Object Iterator | |
243 @{ | |
244 */ | |
245 | |
246 /** Return a pointer to the first property in `body`. */ | |
247 static inline const LV2_Atom_Property_Body* | |
248 lv2_atom_object_begin(const LV2_Atom_Object_Body* body) | |
249 { | |
250 return (const LV2_Atom_Property_Body*)(body + 1); | |
251 } | |
252 | |
253 /** Return true iff `i` has reached the end of `obj`. */ | |
254 static inline bool | |
255 lv2_atom_object_is_end(const LV2_Atom_Object_Body* body, | |
256 uint32_t size, | |
257 const LV2_Atom_Property_Body* i) | |
258 { | |
259 return (const uint8_t*)i >= ((const uint8_t*)body + size); | |
260 } | |
261 | |
262 /** Return an iterator to the property following `i`. */ | |
263 static inline const LV2_Atom_Property_Body* | |
264 lv2_atom_object_next(const LV2_Atom_Property_Body* i) | |
265 { | |
266 const LV2_Atom* const value = (const LV2_Atom*)( | |
267 (const uint8_t*)i + 2 * sizeof(uint32_t)); | |
268 return (const LV2_Atom_Property_Body*)( | |
269 (const uint8_t*)i + lv2_atom_pad_size( | |
270 (uint32_t)sizeof(LV2_Atom_Property_Body) + value->size)); | |
271 } | |
272 | |
273 /** | |
274 A macro for iterating over all properties of an Object. | |
275 @param obj The object to iterate over | |
276 @param iter The name of the iterator | |
277 | |
278 This macro is used similarly to a for loop (which it expands to), e.g.: | |
279 @code | |
280 LV2_ATOM_OBJECT_FOREACH(object, i) { | |
281 // Do something with prop (an LV2_Atom_Property_Body*) here... | |
282 } | |
283 @endcode | |
284 */ | |
285 #define LV2_ATOM_OBJECT_FOREACH(obj, iter) \ | |
286 for (const LV2_Atom_Property_Body* iter = lv2_atom_object_begin(&(obj)->body); \ | |
287 !lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \ | |
288 iter = lv2_atom_object_next(iter)) | |
289 | |
290 /** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */ | |
291 #define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \ | |
292 for (const LV2_Atom_Property_Body* iter = lv2_atom_object_begin(body); \ | |
293 !lv2_atom_object_is_end(body, size, (iter)); \ | |
294 iter = lv2_atom_object_next(iter)) | |
295 | |
296 /** | |
297 @} | |
298 @name Object Query | |
299 @{ | |
300 */ | |
301 | |
302 /** A single entry in an Object query. */ | |
303 typedef struct { | |
304 uint32_t key; /**< Key to query (input set by user) */ | |
305 const LV2_Atom** value; /**< Found value (output set by query function) */ | |
306 } LV2_Atom_Object_Query; | |
307 | |
308 static const LV2_Atom_Object_Query LV2_ATOM_OBJECT_QUERY_END = { 0, NULL }; | |
309 | |
310 /** | |
311 Get an object's values for various keys. | |
312 | |
313 The value pointer of each item in `query` will be set to the location of | |
314 the corresponding value in `object`. Every value pointer in `query` MUST | |
315 be initialised to NULL. This function reads `object` in a single linear | |
316 sweep. By allocating `query` on the stack, objects can be "queried" | |
317 quickly without allocating any memory. This function is realtime safe. | |
318 | |
319 This function can only do "flat" queries, it is not smart enough to match | |
320 variables in nested objects. | |
321 | |
322 For example: | |
323 @code | |
324 const LV2_Atom* name = NULL; | |
325 const LV2_Atom* age = NULL; | |
326 LV2_Atom_Object_Query q[] = { | |
327 { urids.eg_name, &name }, | |
328 { urids.eg_age, &age }, | |
329 LV2_ATOM_OBJECT_QUERY_END | |
330 }; | |
331 lv2_atom_object_query(obj, q); | |
332 // name and age are now set to the appropriate values in obj, or NULL. | |
333 @endcode | |
334 */ | |
335 static inline int | |
336 lv2_atom_object_query(const LV2_Atom_Object* object, | |
337 LV2_Atom_Object_Query* query) | |
338 { | |
339 int matches = 0; | |
340 int n_queries = 0; | |
341 | |
342 /* Count number of query keys so we can short-circuit when done */ | |
343 for (LV2_Atom_Object_Query* q = query; q->key; ++q) { | |
344 ++n_queries; | |
345 } | |
346 | |
347 LV2_ATOM_OBJECT_FOREACH(object, prop) { | |
348 for (LV2_Atom_Object_Query* q = query; q->key; ++q) { | |
349 if (q->key == prop->key && !*q->value) { | |
350 *q->value = &prop->value; | |
351 if (++matches == n_queries) { | |
352 return matches; | |
353 } | |
354 break; | |
355 } | |
356 } | |
357 } | |
358 return matches; | |
359 } | |
360 | |
361 /** | |
362 Body only version of lv2_atom_object_get(). | |
363 */ | |
364 static inline int | |
365 lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...) | |
366 { | |
367 int matches = 0; | |
368 int n_queries = 0; | |
369 | |
370 /* Count number of keys so we can short-circuit when done */ | |
371 va_list args; | |
372 va_start(args, body); | |
373 for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { | |
374 if (!va_arg(args, const LV2_Atom**)) { | |
375 va_end(args); | |
376 return -1; | |
377 } | |
378 } | |
379 va_end(args); | |
380 | |
381 LV2_ATOM_OBJECT_BODY_FOREACH(body, size, prop) { | |
382 va_start(args, body); | |
383 for (int i = 0; i < n_queries; ++i) { | |
384 uint32_t qkey = va_arg(args, uint32_t); | |
385 const LV2_Atom** qval = va_arg(args, const LV2_Atom**); | |
386 if (qkey == prop->key && !*qval) { | |
387 *qval = &prop->value; | |
388 if (++matches == n_queries) { | |
389 va_end(args); | |
390 return matches; | |
391 } | |
392 break; | |
393 } | |
394 } | |
395 va_end(args); | |
396 } | |
397 return matches; | |
398 } | |
399 | |
400 /** | |
401 Variable argument version of lv2_atom_object_query(). | |
402 | |
403 This is nicer-looking in code, but a bit more error-prone since it is not | |
404 type safe and the argument list must be terminated. | |
405 | |
406 The arguments should be a series of uint32_t key and const LV2_Atom** value | |
407 pairs, terminated by a zero key. The value pointers MUST be initialized to | |
408 NULL. For example: | |
409 | |
410 @code | |
411 const LV2_Atom* name = NULL; | |
412 const LV2_Atom* age = NULL; | |
413 lv2_atom_object_get(obj, | |
414 uris.name_key, &name, | |
415 uris.age_key, &age, | |
416 0); | |
417 @endcode | |
418 */ | |
419 static inline int | |
420 lv2_atom_object_get(const LV2_Atom_Object* object, ...) | |
421 { | |
422 int matches = 0; | |
423 int n_queries = 0; | |
424 | |
425 /* Count number of keys so we can short-circuit when done */ | |
426 va_list args; | |
427 va_start(args, object); | |
428 for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { | |
429 if (!va_arg(args, const LV2_Atom**)) { | |
430 return -1; | |
431 } | |
432 } | |
433 va_end(args); | |
434 | |
435 LV2_ATOM_OBJECT_FOREACH(object, prop) { | |
436 va_start(args, object); | |
437 for (int i = 0; i < n_queries; ++i) { | |
438 uint32_t qkey = va_arg(args, uint32_t); | |
439 const LV2_Atom** qval = va_arg(args, const LV2_Atom**); | |
440 if (qkey == prop->key && !*qval) { | |
441 *qval = &prop->value; | |
442 if (++matches == n_queries) { | |
443 return matches; | |
444 } | |
445 break; | |
446 } | |
447 } | |
448 va_end(args); | |
449 } | |
450 return matches; | |
451 } | |
452 | |
453 /** | |
454 Variable argument version of lv2_atom_object_query() with types. | |
455 | |
456 This is like lv2_atom_object_get(), but each entry has an additional | |
457 parameter to specify the required type. Only atoms with a matching type | |
458 will be selected. | |
459 | |
460 The arguments should be a series of uint32_t key, const LV2_Atom**, uint32_t | |
461 type triples, terminated by a zero key. The value pointers MUST be | |
462 initialized to NULL. For example: | |
463 | |
464 @code | |
465 const LV2_Atom_String* name = NULL; | |
466 const LV2_Atom_Int* age = NULL; | |
467 lv2_atom_object_get(obj, | |
468 uris.name_key, &name, uris.atom_String, | |
469 uris.age_key, &age, uris.atom_Int | |
470 0); | |
471 @endcode | |
472 */ | |
473 static inline int | |
474 lv2_atom_object_get_typed(const LV2_Atom_Object* object, ...) | |
475 { | |
476 int matches = 0; | |
477 int n_queries = 0; | |
478 | |
479 /* Count number of keys so we can short-circuit when done */ | |
480 va_list args; | |
481 va_start(args, object); | |
482 for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { | |
483 if (!va_arg(args, const LV2_Atom**) || | |
484 !va_arg(args, uint32_t)) { | |
485 return -1; | |
486 } | |
487 } | |
488 va_end(args); | |
489 | |
490 LV2_ATOM_OBJECT_FOREACH(object, prop) { | |
491 va_start(args, object); | |
492 for (int i = 0; i < n_queries; ++i) { | |
493 const uint32_t qkey = va_arg(args, uint32_t); | |
494 const LV2_Atom** qval = va_arg(args, const LV2_Atom**); | |
495 const uint32_t qtype = va_arg(args, uint32_t); | |
496 if (!*qval && qkey == prop->key && qtype == prop->value.type) { | |
497 *qval = &prop->value; | |
498 if (++matches == n_queries) { | |
499 return matches; | |
500 } | |
501 break; | |
502 } | |
503 } | |
504 va_end(args); | |
505 } | |
506 return matches; | |
507 } | |
508 | |
509 /** | |
510 @} | |
511 @} | |
512 */ | |
513 | |
514 #ifdef __cplusplus | |
515 } /* extern "C" */ | |
516 #endif | |
517 | |
518 #endif /* LV2_ATOM_UTIL_H */ |