1 /*
2 Copyright 2008-2013 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 forge.h An API for constructing LV2 atoms.
19
20 This file provides an API for constructing Atoms which makes it relatively
21 simple to build nested atoms of arbitrary complexity without requiring
22 dynamic memory allocation.
23
24 The API is based on successively appending the appropriate pieces to build a
25 complete Atom. The size of containers is automatically updated. Functions
26 that begin a container return (via their frame argument) a stack frame which
27 must be popped when the container is finished.
28
29 All output is written to a user-provided buffer or sink function. This
30 makes it popssible to create create atoms on the stack, on the heap, in LV2
31 port buffers, in a ringbuffer, or elsewhere, all using the same API.
32
33 This entire API is realtime safe if used with a buffer or a realtime safe
34 sink, except lv2_atom_forge_init() which is only realtime safe if the URI
35 map function is.
36
37 Note these functions are all static inline, do not take their address.
38
39 This header is non-normative, it is provided for convenience.
40 */
41
42 #ifndef LV2_ATOM_FORGE_H
43 #define LV2_ATOM_FORGE_H
44
45 #include <assert.h>
46
47 #include "atom.h"
48 #include "atom-util.h"
49 #include "urid.h"
50
51 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
52 # define LV2_ATOM_FORGE_DEPRECATED __attribute__((__deprecated__))
53 #else
54 # define LV2_ATOM_FORGE_DEPRECATED
55 #endif
56
57 #ifdef __cplusplus
58 extern "C" {
59 #else
60 # include <stdbool.h>
61 #endif
62
63 /** Handle for LV2_Atom_Forge_Sink. */
64 typedef void* LV2_Atom_Forge_Sink_Handle;
65
66 /** A reference to a chunk of written output. */
67 typedef intptr_t LV2_Atom_Forge_Ref;
68
69 /** Sink function for writing output. See lv2_atom_forge_set_sink(). */
70 typedef LV2_Atom_Forge_Ref
71 (*LV2_Atom_Forge_Sink)(LV2_Atom_Forge_Sink_Handle handle,
72 const void* buf,
73 uint32_t size);
74
75 /** Function for resolving a reference. See lv2_atom_forge_set_sink(). */
76 typedef LV2_Atom*
77 (*LV2_Atom_Forge_Deref_Func)(LV2_Atom_Forge_Sink_Handle handle,
78 LV2_Atom_Forge_Ref ref);
79
80 /** A stack frame used for keeping track of nested Atom containers. */
81 typedef struct _LV2_Atom_Forge_Frame {
82 struct _LV2_Atom_Forge_Frame* parent;
83 LV2_Atom_Forge_Ref ref;
84 } LV2_Atom_Forge_Frame;
85
86 /** A "forge" for creating atoms by appending to a buffer. */
87 typedef struct {
88 uint8_t* buf;
89 uint32_t offset;
90 uint32_t size;
91
92 LV2_Atom_Forge_Sink sink;
93 LV2_Atom_Forge_Deref_Func deref;
94 LV2_Atom_Forge_Sink_Handle handle;
95
96 LV2_Atom_Forge_Frame* stack;
97
98 LV2_URID Blank LV2_ATOM_FORGE_DEPRECATED;
99 LV2_URID Bool;
100 LV2_URID Chunk;
101 LV2_URID Double;
102 LV2_URID Float;
103 LV2_URID Int;
104 LV2_URID Long;
105 LV2_URID Literal;
106 LV2_URID Object;
107 LV2_URID Path;
108 LV2_URID Property;
109 LV2_URID Resource LV2_ATOM_FORGE_DEPRECATED;
110 LV2_URID Sequence;
111 LV2_URID String;
112 LV2_URID Tuple;
113 LV2_URID URI;
114 LV2_URID URID;
115 LV2_URID Vector;
116 } LV2_Atom_Forge;
117
118 static inline void
119 lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size);
120
121 /**
122 Initialise @p forge.
123
124 URIs will be mapped using @p map and stored, a reference to @p map itself is
125 not held.
126 */
127 static inline void
lv2_atom_forge_init(LV2_Atom_Forge * forge,LV2_URID_Map * map)128 lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map)
129 {
130 #if defined(__clang__)
131 # pragma clang diagnostic push
132 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
133 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
134 # pragma GCC diagnostic push
135 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
136 #endif
137 lv2_atom_forge_set_buffer(forge, NULL, 0);
138 forge->Blank = map->map(map->handle, LV2_ATOM__Blank);
139 forge->Bool = map->map(map->handle, LV2_ATOM__Bool);
140 forge->Chunk = map->map(map->handle, LV2_ATOM__Chunk);
141 forge->Double = map->map(map->handle, LV2_ATOM__Double);
142 forge->Float = map->map(map->handle, LV2_ATOM__Float);
143 forge->Int = map->map(map->handle, LV2_ATOM__Int);
144 forge->Long = map->map(map->handle, LV2_ATOM__Long);
145 forge->Literal = map->map(map->handle, LV2_ATOM__Literal);
146 forge->Object = map->map(map->handle, LV2_ATOM__Object);
147 forge->Path = map->map(map->handle, LV2_ATOM__Path);
148 forge->Property = map->map(map->handle, LV2_ATOM__Property);
149 forge->Resource = map->map(map->handle, LV2_ATOM__Resource);
150 forge->Sequence = map->map(map->handle, LV2_ATOM__Sequence);
151 forge->String = map->map(map->handle, LV2_ATOM__String);
152 forge->Tuple = map->map(map->handle, LV2_ATOM__Tuple);
153 forge->URI = map->map(map->handle, LV2_ATOM__URI);
154 forge->URID = map->map(map->handle, LV2_ATOM__URID);
155 forge->Vector = map->map(map->handle, LV2_ATOM__Vector);
156 #if defined(__clang__)
157 # pragma clang diagnostic pop
158 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
159 # pragma GCC diagnostic pop
160 #endif
161 }
162
163 static inline LV2_Atom*
lv2_atom_forge_deref(LV2_Atom_Forge * forge,LV2_Atom_Forge_Ref ref)164 lv2_atom_forge_deref(LV2_Atom_Forge* forge, LV2_Atom_Forge_Ref ref)
165 {
166 if (forge->buf) {
167 return (LV2_Atom*)ref;
168 } else {
169 return forge->deref(forge->handle, ref);
170 }
171 }
172
173 /**
174 @name Object Stack
175 @{
176 */
177
178 /**
179 Push a stack frame.
180 This is done automatically by container functions (which take a stack frame
181 pointer), but may be called by the user to push the top level container when
182 writing to an existing Atom.
183 */
184 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_push(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,LV2_Atom_Forge_Ref ref)185 lv2_atom_forge_push(LV2_Atom_Forge* forge,
186 LV2_Atom_Forge_Frame* frame,
187 LV2_Atom_Forge_Ref ref)
188 {
189 frame->parent = forge->stack;
190 frame->ref = ref;
191 forge->stack = frame;
192 return ref;
193 }
194
195 /** Pop a stack frame. This must be called when a container is finished. */
196 static inline void
lv2_atom_forge_pop(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame)197 lv2_atom_forge_pop(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame)
198 {
199 assert(frame == forge->stack);
200 forge->stack = frame->parent;
201 }
202
203 /** Return true iff the top of the stack has the given type. */
204 static inline bool
lv2_atom_forge_top_is(LV2_Atom_Forge * forge,uint32_t type)205 lv2_atom_forge_top_is(LV2_Atom_Forge* forge, uint32_t type)
206 {
207 return forge->stack && forge->stack->ref &&
208 (lv2_atom_forge_deref(forge, forge->stack->ref)->type == type);
209 }
210
211 /** Return true iff @p type is an atom:Object. */
212 static inline bool
lv2_atom_forge_is_object_type(const LV2_Atom_Forge * forge,uint32_t type)213 lv2_atom_forge_is_object_type(const LV2_Atom_Forge* forge, uint32_t type)
214 {
215 #if defined(__clang__)
216 # pragma clang diagnostic push
217 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
218 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
219 # pragma GCC diagnostic push
220 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
221 #endif
222 return (type == forge->Object ||
223 type == forge->Blank ||
224 type == forge->Resource);
225 #if defined(__clang__)
226 # pragma clang diagnostic pop
227 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
228 # pragma GCC diagnostic pop
229 #endif
230 }
231
232 /** Return true iff @p type is an atom:Object with a blank ID. */
233 static inline bool
lv2_atom_forge_is_blank(const LV2_Atom_Forge * forge,uint32_t type,const LV2_Atom_Object_Body * body)234 lv2_atom_forge_is_blank(const LV2_Atom_Forge* forge,
235 uint32_t type,
236 const LV2_Atom_Object_Body* body)
237 {
238 #if defined(__clang__)
239 # pragma clang diagnostic push
240 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
241 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
242 # pragma GCC diagnostic push
243 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
244 #endif
245 return (type == forge->Blank ||
246 (type == forge->Object && body->id == 0));
247 #if defined(__clang__)
248 # pragma clang diagnostic pop
249 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
250 # pragma GCC diagnostic pop
251 #endif
252 }
253
254 /**
255 @}
256 @name Output Configuration
257 @{
258 */
259
260 /** Set the output buffer where @p forge will write atoms. */
261 static inline void
lv2_atom_forge_set_buffer(LV2_Atom_Forge * forge,uint8_t * buf,size_t size)262 lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size)
263 {
264 forge->buf = buf;
265 forge->size = (uint32_t)size;
266 forge->offset = 0;
267 forge->deref = NULL;
268 forge->sink = NULL;
269 forge->handle = NULL;
270 forge->stack = NULL;
271 }
272
273 /**
274 Set the sink function where @p forge will write output.
275
276 The return value of forge functions is an LV2_Atom_Forge_Ref which is an
277 integer type safe to use as a pointer but is otherwise opaque. The sink
278 function must return a ref that can be dereferenced to access as least
279 sizeof(LV2_Atom) bytes of the written data, so sizes can be updated. For
280 ringbuffers, this should be possible as long as the size of the buffer is a
281 multiple of sizeof(LV2_Atom), since atoms are always aligned.
282
283 Note that 0 is an invalid reference, so if you are using a buffer offset be
284 sure to offset it such that 0 is never a valid reference. You will get
285 confusing errors otherwise.
286 */
287 static inline void
lv2_atom_forge_set_sink(LV2_Atom_Forge * forge,LV2_Atom_Forge_Sink sink,LV2_Atom_Forge_Deref_Func deref,LV2_Atom_Forge_Sink_Handle handle)288 lv2_atom_forge_set_sink(LV2_Atom_Forge* forge,
289 LV2_Atom_Forge_Sink sink,
290 LV2_Atom_Forge_Deref_Func deref,
291 LV2_Atom_Forge_Sink_Handle handle)
292 {
293 forge->buf = NULL;
294 forge->size = forge->offset = 0;
295 forge->deref = deref;
296 forge->sink = sink;
297 forge->handle = handle;
298 forge->stack = NULL;
299 }
300
301 /**
302 @}
303 @name Low Level Output
304 @{
305 */
306
307 /**
308 Write raw output. This is used internally, but is also useful for writing
309 atom types not explicitly supported by the forge API. Note the caller is
310 responsible for ensuring the output is approriately padded.
311 */
312 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_raw(LV2_Atom_Forge * forge,const void * data,uint32_t size)313 lv2_atom_forge_raw(LV2_Atom_Forge* forge, const void* data, uint32_t size)
314 {
315 LV2_Atom_Forge_Ref out = 0;
316 if (forge->sink) {
317 out = forge->sink(forge->handle, data, size);
318 } else {
319 out = (LV2_Atom_Forge_Ref)forge->buf + (LV2_Atom_Forge_Ref)forge->offset;
320 uint8_t* mem = forge->buf + forge->offset;
321 if (forge->offset + size > forge->size) {
322 return 0;
323 }
324 forge->offset += size;
325 memcpy(mem, data, size);
326 }
327 for (LV2_Atom_Forge_Frame* f = forge->stack; f; f = f->parent) {
328 lv2_atom_forge_deref(forge, f->ref)->size += size;
329 }
330 return out;
331 }
332
333 /** Pad output accordingly so next write is 64-bit aligned. */
334 static inline void
lv2_atom_forge_pad(LV2_Atom_Forge * forge,uint32_t written)335 lv2_atom_forge_pad(LV2_Atom_Forge* forge, uint32_t written)
336 {
337 const uint64_t pad = 0;
338 const uint32_t pad_size = lv2_atom_pad_size(written) - written;
339 lv2_atom_forge_raw(forge, &pad, pad_size);
340 }
341
342 /** Write raw output, padding to 64-bits as necessary. */
343 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_write(LV2_Atom_Forge * forge,const void * data,uint32_t size)344 lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size)
345 {
346 LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, data, size);
347 if (out) {
348 lv2_atom_forge_pad(forge, size);
349 }
350 return out;
351 }
352
353 /** Write a null-terminated string body. */
354 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_string_body(LV2_Atom_Forge * forge,const char * str,uint32_t len)355 lv2_atom_forge_string_body(LV2_Atom_Forge* forge,
356 const char* str,
357 uint32_t len)
358 {
359 LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, str, len);
360 if (out && (out = lv2_atom_forge_raw(forge, "", 1))) {
361 lv2_atom_forge_pad(forge, len + 1);
362 }
363 return out;
364 }
365
366 /**
367 @}
368 @name Atom Output
369 @{
370 */
371
372 /** Write an atom:Atom header. */
373 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_atom(LV2_Atom_Forge * forge,uint32_t size,uint32_t type)374 lv2_atom_forge_atom(LV2_Atom_Forge* forge, uint32_t size, uint32_t type)
375 {
376 const LV2_Atom a = { size, type };
377 return lv2_atom_forge_raw(forge, &a, sizeof(a));
378 }
379
380 /** Write a primitive (fixed-size) atom. */
381 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_primitive(LV2_Atom_Forge * forge,const LV2_Atom * a)382 lv2_atom_forge_primitive(LV2_Atom_Forge* forge, const LV2_Atom* a)
383 {
384 if (lv2_atom_forge_top_is(forge, forge->Vector)) {
385 return lv2_atom_forge_raw(forge, LV2_ATOM_BODY_CONST(a), a->size);
386 } else {
387 return lv2_atom_forge_write(
388 forge, a, (uint32_t)sizeof(LV2_Atom) + a->size);
389 }
390 }
391
392 /** Write an atom:Int. */
393 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_int(LV2_Atom_Forge * forge,int32_t val)394 lv2_atom_forge_int(LV2_Atom_Forge* forge, int32_t val)
395 {
396 const LV2_Atom_Int a = { { sizeof(val), forge->Int }, val };
397 return lv2_atom_forge_primitive(forge, &a.atom);
398 }
399
400 /** Write an atom:Long. */
401 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_long(LV2_Atom_Forge * forge,int64_t val)402 lv2_atom_forge_long(LV2_Atom_Forge* forge, int64_t val)
403 {
404 const LV2_Atom_Long a = { { sizeof(val), forge->Long }, val };
405 return lv2_atom_forge_primitive(forge, &a.atom);
406 }
407
408 /** Write an atom:Float. */
409 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_float(LV2_Atom_Forge * forge,float val)410 lv2_atom_forge_float(LV2_Atom_Forge* forge, float val)
411 {
412 const LV2_Atom_Float a = { { sizeof(val), forge->Float }, val };
413 return lv2_atom_forge_primitive(forge, &a.atom);
414 }
415
416 /** Write an atom:Double. */
417 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_double(LV2_Atom_Forge * forge,double val)418 lv2_atom_forge_double(LV2_Atom_Forge* forge, double val)
419 {
420 const LV2_Atom_Double a = { { sizeof(val), forge->Double }, val };
421 return lv2_atom_forge_primitive(forge, &a.atom);
422 }
423
424 /** Write an atom:Bool. */
425 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_bool(LV2_Atom_Forge * forge,bool val)426 lv2_atom_forge_bool(LV2_Atom_Forge* forge, bool val)
427 {
428 const LV2_Atom_Bool a = { { sizeof(int32_t), forge->Bool }, val ? 1 : 0 };
429 return lv2_atom_forge_primitive(forge, &a.atom);
430 }
431
432 /** Write an atom:URID. */
433 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_urid(LV2_Atom_Forge * forge,LV2_URID id)434 lv2_atom_forge_urid(LV2_Atom_Forge* forge, LV2_URID id)
435 {
436 const LV2_Atom_URID a = { { sizeof(id), forge->URID }, id };
437 return lv2_atom_forge_primitive(forge, &a.atom);
438 }
439
440 /** Write an atom compatible with atom:String. Used internally. */
441 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_typed_string(LV2_Atom_Forge * forge,uint32_t type,const char * str,uint32_t len)442 lv2_atom_forge_typed_string(LV2_Atom_Forge* forge,
443 uint32_t type,
444 const char* str,
445 uint32_t len)
446 {
447 const LV2_Atom_String a = { { len + 1, type } };
448 LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a));
449 if (out) {
450 if (!lv2_atom_forge_string_body(forge, str, len)) {
451 LV2_Atom* atom = lv2_atom_forge_deref(forge, out);
452 atom->size = atom->type = 0;
453 out = 0;
454 }
455 }
456 return out;
457 }
458
459 /** Write an atom:String. Note that @p str need not be NULL terminated. */
460 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_string(LV2_Atom_Forge * forge,const char * str,uint32_t len)461 lv2_atom_forge_string(LV2_Atom_Forge* forge, const char* str, uint32_t len)
462 {
463 return lv2_atom_forge_typed_string(forge, forge->String, str, len);
464 }
465
466 /**
467 Write an atom:URI. Note that @p uri need not be NULL terminated.
468 This does not map the URI, but writes the complete URI string. To write
469 a mapped URI, use lv2_atom_forge_urid().
470 */
471 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_uri(LV2_Atom_Forge * forge,const char * uri,uint32_t len)472 lv2_atom_forge_uri(LV2_Atom_Forge* forge, const char* uri, uint32_t len)
473 {
474 return lv2_atom_forge_typed_string(forge, forge->URI, uri, len);
475 }
476
477 /** Write an atom:Path. Note that @p path need not be NULL terminated. */
478 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_path(LV2_Atom_Forge * forge,const char * path,uint32_t len)479 lv2_atom_forge_path(LV2_Atom_Forge* forge, const char* path, uint32_t len)
480 {
481 return lv2_atom_forge_typed_string(forge, forge->Path, path, len);
482 }
483
484 /** Write an atom:Literal. */
485 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_literal(LV2_Atom_Forge * forge,const char * str,uint32_t len,uint32_t datatype,uint32_t lang)486 lv2_atom_forge_literal(LV2_Atom_Forge* forge,
487 const char* str,
488 uint32_t len,
489 uint32_t datatype,
490 uint32_t lang)
491 {
492 const LV2_Atom_Literal a = {
493 { (uint32_t)(sizeof(LV2_Atom_Literal) - sizeof(LV2_Atom) + len + 1),
494 forge->Literal },
495 { datatype,
496 lang }
497 };
498 LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a));
499 if (out) {
500 if (!lv2_atom_forge_string_body(forge, str, len)) {
501 LV2_Atom* atom = lv2_atom_forge_deref(forge, out);
502 atom->size = atom->type = 0;
503 out = 0;
504 }
505 }
506 return out;
507 }
508
509 /** Start an atom:Vector. */
510 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_vector_head(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,uint32_t child_size,uint32_t child_type)511 lv2_atom_forge_vector_head(LV2_Atom_Forge* forge,
512 LV2_Atom_Forge_Frame* frame,
513 uint32_t child_size,
514 uint32_t child_type)
515 {
516 const LV2_Atom_Vector a = {
517 { sizeof(LV2_Atom_Vector_Body), forge->Vector },
518 { child_size, child_type }
519 };
520 return lv2_atom_forge_push(
521 forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
522 }
523
524 /** Write a complete atom:Vector. */
525 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_vector(LV2_Atom_Forge * forge,uint32_t child_size,uint32_t child_type,uint32_t n_elems,const void * elems)526 lv2_atom_forge_vector(LV2_Atom_Forge* forge,
527 uint32_t child_size,
528 uint32_t child_type,
529 uint32_t n_elems,
530 const void* elems)
531 {
532 const LV2_Atom_Vector a = {
533 { (uint32_t)(sizeof(LV2_Atom_Vector_Body) + n_elems * child_size),
534 forge->Vector },
535 { child_size, child_type }
536 };
537 LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a));
538 if (out) {
539 lv2_atom_forge_write(forge, elems, child_size * n_elems);
540 }
541 return out;
542 }
543
544 /**
545 Write the header of an atom:Tuple.
546
547 The passed frame will be initialised to represent this tuple. To complete
548 the tuple, write a sequence of atoms, then pop the frame with
549 lv2_atom_forge_pop().
550
551 For example:
552 @code
553 // Write tuple (1, 2.0)
554 LV2_Atom_Forge_Frame frame;
555 LV2_Atom* tup = (LV2_Atom*)lv2_atom_forge_tuple(forge, &frame);
556 lv2_atom_forge_int32(forge, 1);
557 lv2_atom_forge_float(forge, 2.0);
558 lv2_atom_forge_pop(forge, &frame);
559 @endcode
560 */
561 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_tuple(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame)562 lv2_atom_forge_tuple(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame)
563 {
564 const LV2_Atom_Tuple a = { { 0, forge->Tuple } };
565 return lv2_atom_forge_push(
566 forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
567 }
568
569 /**
570 Write the header of an atom:Object.
571
572 The passed frame will be initialised to represent this object. To complete
573 the object, write a sequence of properties, then pop the frame with
574 lv2_atom_forge_pop().
575
576 For example:
577 @code
578 LV2_URID eg_Cat = map("http://example.org/Cat");
579 LV2_URID eg_name = map("http://example.org/name");
580
581 // Start object with type eg_Cat and blank ID
582 LV2_Atom_Forge_Frame frame;
583 lv2_atom_forge_object(forge, &frame, 0, eg_Cat);
584
585 // Append property eg:name = "Hobbes"
586 lv2_atom_forge_key(forge, eg_name);
587 lv2_atom_forge_string(forge, "Hobbes", strlen("Hobbes"));
588
589 // Finish object
590 lv2_atom_forge_pop(forge, &frame);
591 @endcode
592 */
593 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_object(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,LV2_URID id,LV2_URID otype)594 lv2_atom_forge_object(LV2_Atom_Forge* forge,
595 LV2_Atom_Forge_Frame* frame,
596 LV2_URID id,
597 LV2_URID otype)
598 {
599 const LV2_Atom_Object a = {
600 { (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Object },
601 { id, otype }
602 };
603 return lv2_atom_forge_push(
604 forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
605 }
606
607 /**
608 The same as lv2_atom_forge_object(), but for object:Resource.
609
610 This function is deprecated and should not be used in new code.
611 Use lv2_atom_forge_object() directly instead.
612 */
613 LV2_ATOM_FORGE_DEPRECATED
614 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_resource(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,LV2_URID id,LV2_URID otype)615 lv2_atom_forge_resource(LV2_Atom_Forge* forge,
616 LV2_Atom_Forge_Frame* frame,
617 LV2_URID id,
618 LV2_URID otype)
619 {
620 #if defined(__clang__)
621 # pragma clang diagnostic push
622 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
623 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
624 # pragma GCC diagnostic push
625 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
626 #endif
627 const LV2_Atom_Object a = {
628 { (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Resource },
629 { id, otype }
630 };
631 return lv2_atom_forge_push(
632 forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
633 #if defined(__clang__)
634 # pragma clang diagnostic pop
635 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
636 # pragma GCC diagnostic pop
637 #endif
638 }
639
640 /**
641 The same as lv2_atom_forge_object(), but for object:Blank.
642
643 This function is deprecated and should not be used in new code.
644 Use lv2_atom_forge_object() directly instead.
645 */
646 LV2_ATOM_FORGE_DEPRECATED
647 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_blank(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,uint32_t id,LV2_URID otype)648 lv2_atom_forge_blank(LV2_Atom_Forge* forge,
649 LV2_Atom_Forge_Frame* frame,
650 uint32_t id,
651 LV2_URID otype)
652 {
653 #if defined(__clang__)
654 # pragma clang diagnostic push
655 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
656 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
657 # pragma GCC diagnostic push
658 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
659 #endif
660 const LV2_Atom_Object a = {
661 { (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Blank },
662 { id, otype }
663 };
664 return lv2_atom_forge_push(
665 forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
666 #if defined(__clang__)
667 # pragma clang diagnostic pop
668 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
669 # pragma GCC diagnostic pop
670 #endif
671 }
672
673 /**
674 Write a property key in an Object, to be followed by the value.
675
676 See lv2_atom_forge_object() documentation for an example.
677 */
678 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_key(LV2_Atom_Forge * forge,LV2_URID key)679 lv2_atom_forge_key(LV2_Atom_Forge* forge,
680 LV2_URID key)
681 {
682 const LV2_Atom_Property_Body a = { key, 0, { 0, 0 } };
683 return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t));
684 }
685
686 /**
687 Write the header for a property body in an object, with context.
688
689 If you do not need the context, which is almost certainly the case,
690 use the simpler lv2_atom_forge_key() instead.
691 */
692 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_property_head(LV2_Atom_Forge * forge,LV2_URID key,LV2_URID context)693 lv2_atom_forge_property_head(LV2_Atom_Forge* forge,
694 LV2_URID key,
695 LV2_URID context)
696 {
697 const LV2_Atom_Property_Body a = { key, context, { 0, 0 } };
698 return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t));
699 }
700
701 /**
702 Write the header for a Sequence.
703 */
704 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_sequence_head(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,uint32_t unit)705 lv2_atom_forge_sequence_head(LV2_Atom_Forge* forge,
706 LV2_Atom_Forge_Frame* frame,
707 uint32_t unit)
708 {
709 const LV2_Atom_Sequence a = {
710 { (uint32_t)sizeof(LV2_Atom_Sequence_Body), forge->Sequence },
711 { unit, 0 }
712 };
713 return lv2_atom_forge_push(
714 forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
715 }
716
717 /**
718 Write the time stamp header of an Event (in a Sequence) in audio frames.
719 After this, call the appropriate forge method(s) to write the body. Note
720 the returned reference is to an LV2_Event which is NOT an Atom.
721 */
722 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_frame_time(LV2_Atom_Forge * forge,int64_t frames)723 lv2_atom_forge_frame_time(LV2_Atom_Forge* forge, int64_t frames)
724 {
725 return lv2_atom_forge_write(forge, &frames, sizeof(frames));
726 }
727
728 /**
729 Write the time stamp header of an Event (in a Sequence) in beats. After
730 this, call the appropriate forge method(s) to write the body. Note the
731 returned reference is to an LV2_Event which is NOT an Atom.
732 */
733 static inline LV2_Atom_Forge_Ref
lv2_atom_forge_beat_time(LV2_Atom_Forge * forge,double beats)734 lv2_atom_forge_beat_time(LV2_Atom_Forge* forge, double beats)
735 {
736 return lv2_atom_forge_write(forge, &beats, sizeof(beats));
737 }
738
739 /**
740 @}
741 */
742
743 #ifdef __cplusplus
744 } /* extern "C" */
745 #endif
746
747 #endif /* LV2_ATOM_FORGE_H */
748