DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

atom-forge.h (21830B)


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