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