atom-util.h (14566B)
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 */