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