aboutsummaryrefslogtreecommitdiffstats
path: root/include/lv2/atom
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2022-07-07 18:59:32 -0400
committerDavid Robillard <d@drobilla.net>2022-07-17 18:14:00 -0400
commit1eccbe4355685b322194df72b5de2382d5290b3b (patch)
tree0677b5c2f577a5024c351a164527f4bdd91a639b /include/lv2/atom
parentd4a970f6962dda28133290194832b726b566ddab (diff)
downloadlv2-1eccbe4355685b322194df72b5de2382d5290b3b.tar.xz
Rearrange source tree to be directly usable by dependants
This allows the LV2 source distribution to be used as an include path for compilers and an LV2_PATH for applications, at the expense of self-contained bundles. That's a nice idea, but it made LV2 itself weird and annoying to depend on. This rearranges things so that directories in the source tree correspond more closely to installation directories. To make this possible, the "aux" directory in the documentation output has been changed to "style", to avoid the reserved name "aux" on Windows.
Diffstat (limited to 'include/lv2/atom')
-rw-r--r--include/lv2/atom/atom.h260
-rw-r--r--include/lv2/atom/forge.h683
-rw-r--r--include/lv2/atom/util.h523
3 files changed, 1466 insertions, 0 deletions
diff --git a/include/lv2/atom/atom.h b/include/lv2/atom/atom.h
new file mode 100644
index 0000000..b090c1e
--- /dev/null
+++ b/include/lv2/atom/atom.h
@@ -0,0 +1,260 @@
+/*
+ Copyright 2008-2016 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef LV2_ATOM_H
+#define LV2_ATOM_H
+
+/**
+ @defgroup atom Atom
+ @ingroup lv2
+
+ A generic value container and several data types.
+
+ See <http://lv2plug.in/ns/ext/atom> for details.
+
+ @{
+*/
+
+#include <stdint.h>
+
+// clang-format off
+
+#define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom" ///< http://lv2plug.in/ns/ext/atom
+#define LV2_ATOM_PREFIX LV2_ATOM_URI "#" ///< http://lv2plug.in/ns/ext/atom#
+
+#define LV2_ATOM__Atom LV2_ATOM_PREFIX "Atom" ///< http://lv2plug.in/ns/ext/atom#Atom
+#define LV2_ATOM__AtomPort LV2_ATOM_PREFIX "AtomPort" ///< http://lv2plug.in/ns/ext/atom#AtomPort
+#define LV2_ATOM__Blank LV2_ATOM_PREFIX "Blank" ///< http://lv2plug.in/ns/ext/atom#Blank
+#define LV2_ATOM__Bool LV2_ATOM_PREFIX "Bool" ///< http://lv2plug.in/ns/ext/atom#Bool
+#define LV2_ATOM__Chunk LV2_ATOM_PREFIX "Chunk" ///< http://lv2plug.in/ns/ext/atom#Chunk
+#define LV2_ATOM__Double LV2_ATOM_PREFIX "Double" ///< http://lv2plug.in/ns/ext/atom#Double
+#define LV2_ATOM__Event LV2_ATOM_PREFIX "Event" ///< http://lv2plug.in/ns/ext/atom#Event
+#define LV2_ATOM__Float LV2_ATOM_PREFIX "Float" ///< http://lv2plug.in/ns/ext/atom#Float
+#define LV2_ATOM__Int LV2_ATOM_PREFIX "Int" ///< http://lv2plug.in/ns/ext/atom#Int
+#define LV2_ATOM__Literal LV2_ATOM_PREFIX "Literal" ///< http://lv2plug.in/ns/ext/atom#Literal
+#define LV2_ATOM__Long LV2_ATOM_PREFIX "Long" ///< http://lv2plug.in/ns/ext/atom#Long
+#define LV2_ATOM__Number LV2_ATOM_PREFIX "Number" ///< http://lv2plug.in/ns/ext/atom#Number
+#define LV2_ATOM__Object LV2_ATOM_PREFIX "Object" ///< http://lv2plug.in/ns/ext/atom#Object
+#define LV2_ATOM__Path LV2_ATOM_PREFIX "Path" ///< http://lv2plug.in/ns/ext/atom#Path
+#define LV2_ATOM__Property LV2_ATOM_PREFIX "Property" ///< http://lv2plug.in/ns/ext/atom#Property
+#define LV2_ATOM__Resource LV2_ATOM_PREFIX "Resource" ///< http://lv2plug.in/ns/ext/atom#Resource
+#define LV2_ATOM__Sequence LV2_ATOM_PREFIX "Sequence" ///< http://lv2plug.in/ns/ext/atom#Sequence
+#define LV2_ATOM__Sound LV2_ATOM_PREFIX "Sound" ///< http://lv2plug.in/ns/ext/atom#Sound
+#define LV2_ATOM__String LV2_ATOM_PREFIX "String" ///< http://lv2plug.in/ns/ext/atom#String
+#define LV2_ATOM__Tuple LV2_ATOM_PREFIX "Tuple" ///< http://lv2plug.in/ns/ext/atom#Tuple
+#define LV2_ATOM__URI LV2_ATOM_PREFIX "URI" ///< http://lv2plug.in/ns/ext/atom#URI
+#define LV2_ATOM__URID LV2_ATOM_PREFIX "URID" ///< http://lv2plug.in/ns/ext/atom#URID
+#define LV2_ATOM__Vector LV2_ATOM_PREFIX "Vector" ///< http://lv2plug.in/ns/ext/atom#Vector
+#define LV2_ATOM__atomTransfer LV2_ATOM_PREFIX "atomTransfer" ///< http://lv2plug.in/ns/ext/atom#atomTransfer
+#define LV2_ATOM__beatTime LV2_ATOM_PREFIX "beatTime" ///< http://lv2plug.in/ns/ext/atom#beatTime
+#define LV2_ATOM__bufferType LV2_ATOM_PREFIX "bufferType" ///< http://lv2plug.in/ns/ext/atom#bufferType
+#define LV2_ATOM__childType LV2_ATOM_PREFIX "childType" ///< http://lv2plug.in/ns/ext/atom#childType
+#define LV2_ATOM__eventTransfer LV2_ATOM_PREFIX "eventTransfer" ///< http://lv2plug.in/ns/ext/atom#eventTransfer
+#define LV2_ATOM__frameTime LV2_ATOM_PREFIX "frameTime" ///< http://lv2plug.in/ns/ext/atom#frameTime
+#define LV2_ATOM__supports LV2_ATOM_PREFIX "supports" ///< http://lv2plug.in/ns/ext/atom#supports
+#define LV2_ATOM__timeUnit LV2_ATOM_PREFIX "timeUnit" ///< http://lv2plug.in/ns/ext/atom#timeUnit
+
+// clang-format on
+
+#define LV2_ATOM_REFERENCE_TYPE 0 ///< The special type for a reference atom
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @cond */
+/** This expression will fail to compile if double does not fit in 64 bits. */
+typedef char lv2_atom_assert_double_fits_in_64_bits
+ [((sizeof(double) <= sizeof(uint64_t)) * 2) - 1];
+/** @endcond */
+
+/**
+ Return a pointer to the contents of an Atom. The "contents" of an atom
+ is the data past the complete type-specific header.
+ @param type The type of the atom, for example LV2_Atom_String.
+ @param atom A variable-sized atom.
+*/
+#define LV2_ATOM_CONTENTS(type, atom) ((void*)((uint8_t*)(atom) + sizeof(type)))
+
+/**
+ Const version of LV2_ATOM_CONTENTS.
+*/
+#define LV2_ATOM_CONTENTS_CONST(type, atom) \
+ ((const void*)((const uint8_t*)(atom) + sizeof(type)))
+
+/**
+ Return a pointer to the body of an Atom. The "body" of an atom is the
+ data just past the LV2_Atom head (i.e. the same offset for all types).
+*/
+#define LV2_ATOM_BODY(atom) LV2_ATOM_CONTENTS(LV2_Atom, atom)
+
+/**
+ Const version of LV2_ATOM_BODY.
+*/
+#define LV2_ATOM_BODY_CONST(atom) LV2_ATOM_CONTENTS_CONST(LV2_Atom, atom)
+
+/** The header of an atom:Atom. */
+typedef struct {
+ uint32_t size; /**< Size in bytes, not including type and size. */
+ uint32_t type; /**< Type of this atom (mapped URI). */
+} LV2_Atom;
+
+/** An atom:Int or atom:Bool. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ int32_t body; /**< Integer value. */
+} LV2_Atom_Int;
+
+/** An atom:Long. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ int64_t body; /**< Integer value. */
+} LV2_Atom_Long;
+
+/** An atom:Float. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ float body; /**< Floating point value. */
+} LV2_Atom_Float;
+
+/** An atom:Double. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ double body; /**< Floating point value. */
+} LV2_Atom_Double;
+
+/** An atom:Bool. May be cast to LV2_Atom. */
+typedef LV2_Atom_Int LV2_Atom_Bool;
+
+/** An atom:URID. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ uint32_t body; /**< URID. */
+} LV2_Atom_URID;
+
+/** An atom:String. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ /* Contents (a null-terminated UTF-8 string) follow here. */
+} LV2_Atom_String;
+
+/** The body of an atom:Literal. */
+typedef struct {
+ uint32_t datatype; /**< Datatype URID. */
+ uint32_t lang; /**< Language URID. */
+ /* Contents (a null-terminated UTF-8 string) follow here. */
+} LV2_Atom_Literal_Body;
+
+/** An atom:Literal. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ LV2_Atom_Literal_Body body; /**< Body. */
+} LV2_Atom_Literal;
+
+/** An atom:Tuple. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ /* Contents (a series of complete atoms) follow here. */
+} LV2_Atom_Tuple;
+
+/** The body of an atom:Vector. */
+typedef struct {
+ uint32_t child_size; /**< The size of each element in the vector. */
+ uint32_t child_type; /**< The type of each element in the vector. */
+ /* Contents (a series of packed atom bodies) follow here. */
+} LV2_Atom_Vector_Body;
+
+/** An atom:Vector. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ LV2_Atom_Vector_Body body; /**< Body. */
+} LV2_Atom_Vector;
+
+/** The body of an atom:Property (typically in an atom:Object). */
+typedef struct {
+ uint32_t key; /**< Key (predicate) (mapped URI). */
+ uint32_t context; /**< Context URID (may be, and generally is, 0). */
+ LV2_Atom value; /**< Value atom header. */
+ /* Value atom body follows here. */
+} LV2_Atom_Property_Body;
+
+/** An atom:Property. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ LV2_Atom_Property_Body body; /**< Body. */
+} LV2_Atom_Property;
+
+/** The body of an atom:Object. May be cast to LV2_Atom. */
+typedef struct {
+ uint32_t id; /**< URID, or 0 for blank. */
+ uint32_t otype; /**< Type URID (same as rdf:type, for fast dispatch). */
+ /* Contents (a series of property bodies) follow here. */
+} LV2_Atom_Object_Body;
+
+/** An atom:Object. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ LV2_Atom_Object_Body body; /**< Body. */
+} LV2_Atom_Object;
+
+/** The header of an atom:Event. Note this type is NOT an LV2_Atom. */
+typedef struct {
+ /** Time stamp. Which type is valid is determined by context. */
+ union {
+ int64_t frames; /**< Time in audio frames. */
+ double beats; /**< Time in beats. */
+ } time;
+ LV2_Atom body; /**< Event body atom header. */
+ /* Body atom contents follow here. */
+} LV2_Atom_Event;
+
+/**
+ The body of an atom:Sequence (a sequence of events).
+
+ The unit field is either a URID that described an appropriate time stamp
+ type, or may be 0 where a default stamp type is known. For
+ LV2_Descriptor::run(), the default stamp type is audio frames.
+
+ The contents of a sequence is a series of LV2_Atom_Event, each aligned
+ to 64-bits, for example:
+ <pre>
+ | Event 1 (size 6) | Event 2
+ | | | | | | | | |
+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ |FRAMES |TYPE |SIZE |DATADATADATAPAD|FRAMES |...
+ </pre>
+*/
+typedef struct {
+ uint32_t unit; /**< URID of unit of event time stamps. */
+ uint32_t pad; /**< Currently unused. */
+ /* Contents (a series of events) follow here. */
+} LV2_Atom_Sequence_Body;
+
+/** An atom:Sequence. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ LV2_Atom_Sequence_Body body; /**< Body. */
+} LV2_Atom_Sequence;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+/**
+ @}
+*/
+
+#endif /* LV2_ATOM_H */
diff --git a/include/lv2/atom/forge.h b/include/lv2/atom/forge.h
new file mode 100644
index 0000000..280bd53
--- /dev/null
+++ b/include/lv2/atom/forge.h
@@ -0,0 +1,683 @@
+/*
+ Copyright 2008-2016 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file forge.h An API for constructing LV2 atoms.
+
+ This file provides an API for constructing Atoms which makes it relatively
+ simple to build nested atoms of arbitrary complexity without requiring
+ dynamic memory allocation.
+
+ The API is based on successively appending the appropriate pieces to build a
+ complete Atom. The size of containers is automatically updated. Functions
+ that begin a container return (via their frame argument) a stack frame which
+ must be popped when the container is finished.
+
+ All output is written to a user-provided buffer or sink function. This
+ makes it possible to create atoms on the stack, on the heap, in LV2 port
+ buffers, in a ringbuffer, or elsewhere, all using the same API.
+
+ This entire API is realtime safe if used with a buffer or a realtime safe
+ sink, except lv2_atom_forge_init() which is only realtime safe if the URI
+ map function is.
+
+ Note these functions are all static inline, do not take their address.
+
+ This header is non-normative, it is provided for convenience.
+*/
+
+#ifndef LV2_ATOM_FORGE_H
+#define LV2_ATOM_FORGE_H
+
+/**
+ @defgroup forge Forge
+ @ingroup atom
+
+ An API for constructing LV2 atoms.
+
+ @{
+*/
+
+#include "lv2/atom/atom.h"
+#include "lv2/atom/util.h"
+#include "lv2/core/attributes.h"
+#include "lv2/urid/urid.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Disable deprecation warnings for Blank and Resource
+LV2_DISABLE_DEPRECATION_WARNINGS
+
+/** Handle for LV2_Atom_Forge_Sink. */
+typedef void* LV2_Atom_Forge_Sink_Handle;
+
+/** A reference to a chunk of written output. */
+typedef intptr_t LV2_Atom_Forge_Ref;
+
+/** Sink function for writing output. See lv2_atom_forge_set_sink(). */
+typedef LV2_Atom_Forge_Ref (*LV2_Atom_Forge_Sink)(
+ LV2_Atom_Forge_Sink_Handle handle,
+ const void* buf,
+ uint32_t size);
+
+/** Function for resolving a reference. See lv2_atom_forge_set_sink(). */
+typedef LV2_Atom* (*LV2_Atom_Forge_Deref_Func)(
+ LV2_Atom_Forge_Sink_Handle handle,
+ LV2_Atom_Forge_Ref ref);
+
+/** A stack frame used for keeping track of nested Atom containers. */
+typedef struct LV2_Atom_Forge_Frame {
+ struct LV2_Atom_Forge_Frame* parent;
+ LV2_Atom_Forge_Ref ref;
+} LV2_Atom_Forge_Frame;
+
+/** A "forge" for creating atoms by appending to a buffer. */
+typedef struct {
+ uint8_t* buf;
+ uint32_t offset;
+ uint32_t size;
+
+ LV2_Atom_Forge_Sink sink;
+ LV2_Atom_Forge_Deref_Func deref;
+ LV2_Atom_Forge_Sink_Handle handle;
+
+ LV2_Atom_Forge_Frame* stack;
+
+ LV2_URID Blank LV2_DEPRECATED;
+ LV2_URID Bool;
+ LV2_URID Chunk;
+ LV2_URID Double;
+ LV2_URID Float;
+ LV2_URID Int;
+ LV2_URID Long;
+ LV2_URID Literal;
+ LV2_URID Object;
+ LV2_URID Path;
+ LV2_URID Property;
+ LV2_URID Resource LV2_DEPRECATED;
+ LV2_URID Sequence;
+ LV2_URID String;
+ LV2_URID Tuple;
+ LV2_URID URI;
+ LV2_URID URID;
+ LV2_URID Vector;
+} LV2_Atom_Forge;
+
+static inline void
+lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size);
+
+/**
+ Initialise `forge`.
+
+ URIs will be mapped using `map` and stored, a reference to `map` itself is
+ not held.
+*/
+static inline void
+lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map)
+{
+ lv2_atom_forge_set_buffer(forge, NULL, 0);
+ forge->Blank = map->map(map->handle, LV2_ATOM__Blank);
+ forge->Bool = map->map(map->handle, LV2_ATOM__Bool);
+ forge->Chunk = map->map(map->handle, LV2_ATOM__Chunk);
+ forge->Double = map->map(map->handle, LV2_ATOM__Double);
+ forge->Float = map->map(map->handle, LV2_ATOM__Float);
+ forge->Int = map->map(map->handle, LV2_ATOM__Int);
+ forge->Long = map->map(map->handle, LV2_ATOM__Long);
+ forge->Literal = map->map(map->handle, LV2_ATOM__Literal);
+ forge->Object = map->map(map->handle, LV2_ATOM__Object);
+ forge->Path = map->map(map->handle, LV2_ATOM__Path);
+ forge->Property = map->map(map->handle, LV2_ATOM__Property);
+ forge->Resource = map->map(map->handle, LV2_ATOM__Resource);
+ forge->Sequence = map->map(map->handle, LV2_ATOM__Sequence);
+ forge->String = map->map(map->handle, LV2_ATOM__String);
+ forge->Tuple = map->map(map->handle, LV2_ATOM__Tuple);
+ forge->URI = map->map(map->handle, LV2_ATOM__URI);
+ forge->URID = map->map(map->handle, LV2_ATOM__URID);
+ forge->Vector = map->map(map->handle, LV2_ATOM__Vector);
+}
+
+/** Access the Atom pointed to by a reference. */
+static inline LV2_Atom*
+lv2_atom_forge_deref(LV2_Atom_Forge* forge, LV2_Atom_Forge_Ref ref)
+{
+ return forge->buf ? (LV2_Atom*)ref : forge->deref(forge->handle, ref);
+}
+
+/**
+ @name Object Stack
+ @{
+*/
+
+/**
+ Push a stack frame.
+ This is done automatically by container functions (which take a stack frame
+ pointer), but may be called by the user to push the top level container when
+ writing to an existing Atom.
+*/
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_push(LV2_Atom_Forge* forge,
+ LV2_Atom_Forge_Frame* frame,
+ LV2_Atom_Forge_Ref ref)
+{
+ frame->parent = forge->stack;
+ frame->ref = ref;
+
+ if (ref) {
+ forge->stack = frame; // Don't push, so walking the stack is always safe
+ }
+
+ return ref;
+}
+
+/** Pop a stack frame. This must be called when a container is finished. */
+static inline void
+lv2_atom_forge_pop(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame)
+{
+ if (frame->ref) {
+ // If frame has a valid ref, it must be the top of the stack
+ assert(frame == forge->stack);
+ forge->stack = frame->parent;
+ }
+ // Otherwise, frame was not pushed because of overflow, do nothing
+}
+
+/** Return true iff the top of the stack has the given type. */
+static inline bool
+lv2_atom_forge_top_is(LV2_Atom_Forge* forge, uint32_t type)
+{
+ return forge->stack && forge->stack->ref &&
+ (lv2_atom_forge_deref(forge, forge->stack->ref)->type == type);
+}
+
+/** Return true iff `type` is an atom:Object. */
+static inline bool
+lv2_atom_forge_is_object_type(const LV2_Atom_Forge* forge, uint32_t type)
+{
+ return (type == forge->Object || type == forge->Blank ||
+ type == forge->Resource);
+}
+
+/** Return true iff `type` is an atom:Object with a blank ID. */
+static inline bool
+lv2_atom_forge_is_blank(const LV2_Atom_Forge* forge,
+ uint32_t type,
+ const LV2_Atom_Object_Body* body)
+{
+ return (type == forge->Blank || (type == forge->Object && body->id == 0));
+}
+
+/**
+ @}
+ @name Output Configuration
+ @{
+*/
+
+/** Set the output buffer where `forge` will write atoms. */
+static inline void
+lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size)
+{
+ forge->buf = buf;
+ forge->size = (uint32_t)size;
+ forge->offset = 0;
+ forge->deref = NULL;
+ forge->sink = NULL;
+ forge->handle = NULL;
+ forge->stack = NULL;
+}
+
+/**
+ Set the sink function where `forge` will write output.
+
+ The return value of forge functions is an LV2_Atom_Forge_Ref which is an
+ integer type safe to use as a pointer but is otherwise opaque. The sink
+ function must return a ref that can be dereferenced to access as least
+ sizeof(LV2_Atom) bytes of the written data, so sizes can be updated. For
+ ringbuffers, this should be possible as long as the size of the buffer is a
+ multiple of sizeof(LV2_Atom), since atoms are always aligned.
+
+ Note that 0 is an invalid reference, so if you are using a buffer offset be
+ sure to offset it such that 0 is never a valid reference. You will get
+ confusing errors otherwise.
+*/
+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)
+{
+ forge->buf = NULL;
+ forge->size = forge->offset = 0;
+ forge->deref = deref;
+ forge->sink = sink;
+ forge->handle = handle;
+ forge->stack = NULL;
+}
+
+/**
+ @}
+ @name Low Level Output
+ @{
+*/
+
+/**
+ Write raw output. This is used internally, but is also useful for writing
+ atom types not explicitly supported by the forge API. Note the caller is
+ responsible for ensuring the output is appropriately padded.
+*/
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_raw(LV2_Atom_Forge* forge, const void* data, uint32_t size)
+{
+ LV2_Atom_Forge_Ref out = 0;
+ if (forge->sink) {
+ out = forge->sink(forge->handle, data, size);
+ } else {
+ out = (LV2_Atom_Forge_Ref)forge->buf + forge->offset;
+ uint8_t* mem = forge->buf + forge->offset;
+ if (forge->offset + size > forge->size) {
+ return 0;
+ }
+ forge->offset += size;
+ memcpy(mem, data, size);
+ }
+ for (LV2_Atom_Forge_Frame* f = forge->stack; f; f = f->parent) {
+ lv2_atom_forge_deref(forge, f->ref)->size += size;
+ }
+ return out;
+}
+
+/** Pad output accordingly so next write is 64-bit aligned. */
+static inline void
+lv2_atom_forge_pad(LV2_Atom_Forge* forge, uint32_t written)
+{
+ const uint64_t pad = 0;
+ const uint32_t pad_size = lv2_atom_pad_size(written) - written;
+ lv2_atom_forge_raw(forge, &pad, pad_size);
+}
+
+/** Write raw output, padding to 64-bits as necessary. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size)
+{
+ LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, data, size);
+ if (out) {
+ lv2_atom_forge_pad(forge, size);
+ }
+ return out;
+}
+
+/** Write a null-terminated string body. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_string_body(LV2_Atom_Forge* forge, const char* str, uint32_t len)
+{
+ LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, str, len);
+ if (out && (out = lv2_atom_forge_raw(forge, "", 1))) {
+ lv2_atom_forge_pad(forge, len + 1);
+ }
+ return out;
+}
+
+/**
+ @}
+ @name Atom Output
+ @{
+*/
+
+/** Write an atom:Atom header. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_atom(LV2_Atom_Forge* forge, uint32_t size, uint32_t type)
+{
+ const LV2_Atom a = {size, type};
+ return lv2_atom_forge_raw(forge, &a, sizeof(a));
+}
+
+/** Write a primitive (fixed-size) atom. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_primitive(LV2_Atom_Forge* forge, const LV2_Atom* a)
+{
+ return (
+ lv2_atom_forge_top_is(forge, forge->Vector)
+ ? lv2_atom_forge_raw(forge, LV2_ATOM_BODY_CONST(a), a->size)
+ : lv2_atom_forge_write(forge, a, (uint32_t)sizeof(LV2_Atom) + a->size));
+}
+
+/** Write an atom:Int. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_int(LV2_Atom_Forge* forge, int32_t val)
+{
+ const LV2_Atom_Int a = {{sizeof(val), forge->Int}, val};
+ return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a);
+}
+
+/** Write an atom:Long. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_long(LV2_Atom_Forge* forge, int64_t val)
+{
+ const LV2_Atom_Long a = {{sizeof(val), forge->Long}, val};
+ return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a);
+}
+
+/** Write an atom:Float. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_float(LV2_Atom_Forge* forge, float val)
+{
+ const LV2_Atom_Float a = {{sizeof(val), forge->Float}, val};
+ return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a);
+}
+
+/** Write an atom:Double. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_double(LV2_Atom_Forge* forge, double val)
+{
+ const LV2_Atom_Double a = {{sizeof(val), forge->Double}, val};
+ return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a);
+}
+
+/** Write an atom:Bool. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_bool(LV2_Atom_Forge* forge, bool val)
+{
+ const LV2_Atom_Bool a = {{sizeof(int32_t), forge->Bool}, val ? 1 : 0};
+ return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a);
+}
+
+/** Write an atom:URID. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_urid(LV2_Atom_Forge* forge, LV2_URID id)
+{
+ const LV2_Atom_URID a = {{sizeof(id), forge->URID}, id};
+ return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a);
+}
+
+/** Write an atom compatible with atom:String. Used internally. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_typed_string(LV2_Atom_Forge* forge,
+ uint32_t type,
+ const char* str,
+ uint32_t len)
+{
+ const LV2_Atom_String a = {{len + 1, type}};
+ LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a));
+ if (out) {
+ if (!lv2_atom_forge_string_body(forge, str, len)) {
+ LV2_Atom* atom = lv2_atom_forge_deref(forge, out);
+ atom->size = atom->type = 0;
+ out = 0;
+ }
+ }
+ return out;
+}
+
+/** Write an atom:String. Note that `str` need not be NULL terminated. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_string(LV2_Atom_Forge* forge, const char* str, uint32_t len)
+{
+ return lv2_atom_forge_typed_string(forge, forge->String, str, len);
+}
+
+/**
+ Write an atom:URI. Note that `uri` need not be NULL terminated.
+ This does not map the URI, but writes the complete URI string. To write
+ a mapped URI, use lv2_atom_forge_urid().
+*/
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_uri(LV2_Atom_Forge* forge, const char* uri, uint32_t len)
+{
+ return lv2_atom_forge_typed_string(forge, forge->URI, uri, len);
+}
+
+/** Write an atom:Path. Note that `path` need not be NULL terminated. */
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_path(LV2_Atom_Forge* forge, const char* path, uint32_t len)
+{
+ return lv2_atom_forge_typed_string(forge, forge->Path, path, len);
+}
+
+/** Write an atom:Literal. */
+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)
+{
+ const LV2_Atom_Literal a = {
+ {(uint32_t)(sizeof(LV2_Atom_Literal) - sizeof(LV2_Atom) + len + 1),
+ forge->Literal},
+ {datatype, lang}};
+ LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a));
+ if (out) {
+ if (!lv2_atom_forge_string_body(forge, str, len)) {
+ LV2_Atom* atom = lv2_atom_forge_deref(forge, out);
+ atom->size = atom->type = 0;
+ out = 0;
+ }
+ }
+ return out;
+}
+
+/** Start an atom:Vector. */
+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)
+{
+ const LV2_Atom_Vector a = {{sizeof(LV2_Atom_Vector_Body), forge->Vector},
+ {child_size, child_type}};
+ return lv2_atom_forge_push(
+ forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
+}
+
+/** Write a complete atom:Vector. */
+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)
+{
+ const LV2_Atom_Vector a = {
+ {(uint32_t)sizeof(LV2_Atom_Vector_Body) + n_elems * child_size,
+ forge->Vector},
+ {child_size, child_type}};
+ LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a));
+ if (out) {
+ lv2_atom_forge_write(forge, elems, child_size * n_elems);
+ }
+ return out;
+}
+
+/**
+ Write the header of an atom:Tuple.
+
+ The passed frame will be initialised to represent this tuple. To complete
+ the tuple, write a sequence of atoms, then pop the frame with
+ lv2_atom_forge_pop().
+
+ For example:
+ @code
+ // Write tuple (1, 2.0)
+ LV2_Atom_Forge_Frame frame;
+ LV2_Atom* tup = (LV2_Atom*)lv2_atom_forge_tuple(forge, &frame);
+ lv2_atom_forge_int(forge, 1);
+ lv2_atom_forge_float(forge, 2.0);
+ lv2_atom_forge_pop(forge, &frame);
+ @endcode
+*/
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_tuple(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame)
+{
+ const LV2_Atom_Tuple a = {{0, forge->Tuple}};
+ return lv2_atom_forge_push(
+ forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
+}
+
+/**
+ Write the header of an atom:Object.
+
+ The passed frame will be initialised to represent this object. To complete
+ the object, write a sequence of properties, then pop the frame with
+ lv2_atom_forge_pop().
+
+ For example:
+ @code
+ LV2_URID eg_Cat = map("http://example.org/Cat");
+ LV2_URID eg_name = map("http://example.org/name");
+
+ // Start object with type eg_Cat and blank ID
+ LV2_Atom_Forge_Frame frame;
+ lv2_atom_forge_object(forge, &frame, 0, eg_Cat);
+
+ // Append property eg:name = "Hobbes"
+ lv2_atom_forge_key(forge, eg_name);
+ lv2_atom_forge_string(forge, "Hobbes", strlen("Hobbes"));
+
+ // Finish object
+ lv2_atom_forge_pop(forge, &frame);
+ @endcode
+*/
+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)
+{
+ const LV2_Atom_Object a = {
+ {(uint32_t)sizeof(LV2_Atom_Object_Body), forge->Object}, {id, otype}};
+ return lv2_atom_forge_push(
+ forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
+}
+
+/**
+ The same as lv2_atom_forge_object(), but for object:Resource.
+
+ This function is deprecated and should not be used in new code.
+ Use lv2_atom_forge_object() directly instead.
+*/
+LV2_DEPRECATED
+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)
+{
+ const LV2_Atom_Object a = {
+ {(uint32_t)sizeof(LV2_Atom_Object_Body), forge->Resource}, {id, otype}};
+ return lv2_atom_forge_push(
+ forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
+}
+
+/**
+ The same as lv2_atom_forge_object(), but for object:Blank.
+
+ This function is deprecated and should not be used in new code.
+ Use lv2_atom_forge_object() directly instead.
+*/
+LV2_DEPRECATED
+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)
+{
+ const LV2_Atom_Object a = {
+ {(uint32_t)sizeof(LV2_Atom_Object_Body), forge->Blank}, {id, otype}};
+ return lv2_atom_forge_push(
+ forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
+}
+
+/**
+ Write a property key in an Object, to be followed by the value.
+
+ See lv2_atom_forge_object() documentation for an example.
+*/
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_key(LV2_Atom_Forge* forge, LV2_URID key)
+{
+ const LV2_Atom_Property_Body a = {key, 0, {0, 0}};
+ return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t));
+}
+
+/**
+ Write the header for a property body in an object, with context.
+
+ If you do not need the context, which is almost certainly the case,
+ use the simpler lv2_atom_forge_key() instead.
+*/
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_property_head(LV2_Atom_Forge* forge,
+ LV2_URID key,
+ LV2_URID context)
+{
+ const LV2_Atom_Property_Body a = {key, context, {0, 0}};
+ return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t));
+}
+
+/**
+ Write the header for a Sequence.
+*/
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_sequence_head(LV2_Atom_Forge* forge,
+ LV2_Atom_Forge_Frame* frame,
+ uint32_t unit)
+{
+ const LV2_Atom_Sequence a = {
+ {(uint32_t)sizeof(LV2_Atom_Sequence_Body), forge->Sequence}, {unit, 0}};
+ return lv2_atom_forge_push(
+ forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a)));
+}
+
+/**
+ Write the time stamp header of an Event (in a Sequence) in audio frames.
+ After this, call the appropriate forge method(s) to write the body. Note
+ the returned reference is to an LV2_Event which is NOT an Atom.
+*/
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_frame_time(LV2_Atom_Forge* forge, int64_t frames)
+{
+ return lv2_atom_forge_write(forge, &frames, sizeof(frames));
+}
+
+/**
+ Write the time stamp header of an Event (in a Sequence) in beats. After
+ this, call the appropriate forge method(s) to write the body. Note the
+ returned reference is to an LV2_Event which is NOT an Atom.
+*/
+static inline LV2_Atom_Forge_Ref
+lv2_atom_forge_beat_time(LV2_Atom_Forge* forge, double beats)
+{
+ return lv2_atom_forge_write(forge, &beats, sizeof(beats));
+}
+
+LV2_RESTORE_WARNINGS
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+/**
+ @}
+ @}
+*/
+
+#endif /* LV2_ATOM_FORGE_H */
diff --git a/include/lv2/atom/util.h b/include/lv2/atom/util.h
new file mode 100644
index 0000000..16d2c00
--- /dev/null
+++ b/include/lv2/atom/util.h
@@ -0,0 +1,523 @@
+/*
+ Copyright 2008-2015 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef LV2_ATOM_UTIL_H
+#define LV2_ATOM_UTIL_H
+
+/**
+ @file util.h Helper functions for the LV2 Atom extension.
+
+ Note these functions are all static inline, do not take their address.
+
+ This header is non-normative, it is provided for convenience.
+*/
+
+/**
+ @defgroup util Utilities
+ @ingroup atom
+
+ Utilities for working with atoms.
+
+ @{
+*/
+
+#include "lv2/atom/atom.h"
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Pad a size to 64 bits. */
+static inline uint32_t
+lv2_atom_pad_size(uint32_t size)
+{
+ return (size + 7U) & (~7U);
+}
+
+/** Return the total size of `atom`, including the header. */
+static inline uint32_t
+lv2_atom_total_size(const LV2_Atom* atom)
+{
+ return (uint32_t)sizeof(LV2_Atom) + atom->size;
+}
+
+/** Return true iff `atom` is null. */
+static inline bool
+lv2_atom_is_null(const LV2_Atom* atom)
+{
+ return !atom || (atom->type == 0 && atom->size == 0);
+}
+
+/** Return true iff `a` is equal to `b`. */
+static inline bool
+lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b)
+{
+ return (a == b) || ((a->type == b->type) && (a->size == b->size) &&
+ !memcmp(a + 1, b + 1, a->size));
+}
+
+/**
+ @name Sequence Iterator
+ @{
+*/
+
+/** Get an iterator pointing to the first event in a Sequence body. */
+static inline LV2_Atom_Event*
+lv2_atom_sequence_begin(const LV2_Atom_Sequence_Body* body)
+{
+ return (LV2_Atom_Event*)(body + 1);
+}
+
+/** Get an iterator pointing to the end of a Sequence body. */
+static inline LV2_Atom_Event*
+lv2_atom_sequence_end(const LV2_Atom_Sequence_Body* body, uint32_t size)
+{
+ return (LV2_Atom_Event*)((const uint8_t*)body + lv2_atom_pad_size(size));
+}
+
+/** Return true iff `i` has reached the end of `body`. */
+static inline bool
+lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body,
+ uint32_t size,
+ const LV2_Atom_Event* i)
+{
+ return (const uint8_t*)i >= ((const uint8_t*)body + size);
+}
+
+/** Return an iterator to the element following `i`. */
+static inline LV2_Atom_Event*
+lv2_atom_sequence_next(const LV2_Atom_Event* i)
+{
+ return (LV2_Atom_Event*)((const uint8_t*)i + sizeof(LV2_Atom_Event) +
+ lv2_atom_pad_size(i->body.size));
+}
+
+/**
+ A macro for iterating over all events in a Sequence.
+ @param seq The sequence to iterate over
+ @param iter The name of the iterator
+
+ This macro is used similarly to a for loop (which it expands to), for
+ example:
+
+ @code
+ LV2_ATOM_SEQUENCE_FOREACH(sequence, ev) {
+ // Do something with ev (an LV2_Atom_Event*) here...
+ }
+ @endcode
+*/
+#define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \
+ for (LV2_Atom_Event * iter = lv2_atom_sequence_begin(&(seq)->body); \
+ !lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \
+ (iter) = lv2_atom_sequence_next(iter))
+
+/** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */
+#define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \
+ for (LV2_Atom_Event * iter = lv2_atom_sequence_begin(body); \
+ !lv2_atom_sequence_is_end(body, size, (iter)); \
+ (iter) = lv2_atom_sequence_next(iter))
+
+/**
+ @}
+ @name Sequence Utilities
+ @{
+*/
+
+/**
+ Clear all events from `sequence`.
+
+ This simply resets the size field, the other fields are left untouched.
+*/
+static inline void
+lv2_atom_sequence_clear(LV2_Atom_Sequence* seq)
+{
+ seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
+}
+
+/**
+ Append an event at the end of `sequence`.
+
+ @param seq Sequence to append to.
+ @param capacity Total capacity of the sequence atom
+ (as set by the host for sequence output ports).
+ @param event Event to write.
+
+ @return A pointer to the newly written event in `seq`,
+ or NULL on failure (insufficient space).
+*/
+static inline LV2_Atom_Event*
+lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq,
+ uint32_t capacity,
+ const LV2_Atom_Event* event)
+{
+ const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size;
+ if (capacity - seq->atom.size < total_size) {
+ return NULL;
+ }
+
+ LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size);
+ memcpy(e, event, total_size);
+
+ seq->atom.size += lv2_atom_pad_size(total_size);
+
+ return e;
+}
+
+/**
+ @}
+ @name Tuple Iterator
+ @{
+*/
+
+/** Get an iterator pointing to the first element in `tup`. */
+static inline LV2_Atom*
+lv2_atom_tuple_begin(const LV2_Atom_Tuple* tup)
+{
+ return (LV2_Atom*)(LV2_ATOM_BODY(tup));
+}
+
+/** Return true iff `i` has reached the end of `body`. */
+static inline bool
+lv2_atom_tuple_is_end(const void* body, uint32_t size, const LV2_Atom* i)
+{
+ return (const uint8_t*)i >= ((const uint8_t*)body + size);
+}
+
+/** Return an iterator to the element following `i`. */
+static inline LV2_Atom*
+lv2_atom_tuple_next(const LV2_Atom* i)
+{
+ return (LV2_Atom*)((const uint8_t*)i + sizeof(LV2_Atom) +
+ lv2_atom_pad_size(i->size));
+}
+
+/**
+ A macro for iterating over all properties of a Tuple.
+ @param tuple The tuple to iterate over
+ @param iter The name of the iterator
+
+ This macro is used similarly to a for loop (which it expands to), for
+ example:
+
+ @code
+ LV2_ATOM_TUPLE_FOREACH(tuple, elem) {
+ // Do something with elem (an LV2_Atom*) here...
+ }
+ @endcode
+*/
+#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \
+ for (LV2_Atom * iter = lv2_atom_tuple_begin(tuple); \
+ !lv2_atom_tuple_is_end( \
+ LV2_ATOM_BODY(tuple), (tuple)->atom.size, (iter)); \
+ (iter) = lv2_atom_tuple_next(iter))
+
+/** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */
+#define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \
+ for (LV2_Atom * iter = (LV2_Atom*)(body); \
+ !lv2_atom_tuple_is_end(body, size, (iter)); \
+ (iter) = lv2_atom_tuple_next(iter))
+
+/**
+ @}
+ @name Object Iterator
+ @{
+*/
+
+/** Return a pointer to the first property in `body`. */
+static inline LV2_Atom_Property_Body*
+lv2_atom_object_begin(const LV2_Atom_Object_Body* body)
+{
+ return (LV2_Atom_Property_Body*)(body + 1);
+}
+
+/** Return true iff `i` has reached the end of `obj`. */
+static inline bool
+lv2_atom_object_is_end(const LV2_Atom_Object_Body* body,
+ uint32_t size,
+ const LV2_Atom_Property_Body* i)
+{
+ return (const uint8_t*)i >= ((const uint8_t*)body + size);
+}
+
+/** Return an iterator to the property following `i`. */
+static inline LV2_Atom_Property_Body*
+lv2_atom_object_next(const LV2_Atom_Property_Body* i)
+{
+ const LV2_Atom* const value =
+ (const LV2_Atom*)((const uint8_t*)i + 2 * sizeof(uint32_t));
+ return (LV2_Atom_Property_Body*)((const uint8_t*)i +
+ lv2_atom_pad_size(
+ (uint32_t)sizeof(LV2_Atom_Property_Body) +
+ value->size));
+}
+
+/**
+ A macro for iterating over all properties of an Object.
+ @param obj The object to iterate over
+ @param iter The name of the iterator
+
+ This macro is used similarly to a for loop (which it expands to), for
+ example:
+
+ @code
+ LV2_ATOM_OBJECT_FOREACH(object, i) {
+ // Do something with i (an LV2_Atom_Property_Body*) here...
+ }
+ @endcode
+*/
+#define LV2_ATOM_OBJECT_FOREACH(obj, iter) \
+ for (LV2_Atom_Property_Body * iter = lv2_atom_object_begin(&(obj)->body); \
+ !lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \
+ (iter) = lv2_atom_object_next(iter))
+
+/** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */
+#define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \
+ for (LV2_Atom_Property_Body * iter = lv2_atom_object_begin(body); \
+ !lv2_atom_object_is_end(body, size, (iter)); \
+ (iter) = lv2_atom_object_next(iter))
+
+/**
+ @}
+ @name Object Query
+ @{
+*/
+
+/** A single entry in an Object query. */
+typedef struct {
+ uint32_t key; /**< Key to query (input set by user) */
+ const LV2_Atom** value; /**< Found value (output set by query function) */
+} LV2_Atom_Object_Query;
+
+/** Sentinel for lv2_atom_object_query(). */
+static const LV2_Atom_Object_Query LV2_ATOM_OBJECT_QUERY_END = {0, NULL};
+
+/**
+ Get an object's values for various keys.
+
+ The value pointer of each item in `query` will be set to the location of
+ the corresponding value in `object`. Every value pointer in `query` MUST
+ be initialised to NULL. This function reads `object` in a single linear
+ sweep. By allocating `query` on the stack, objects can be "queried"
+ quickly without allocating any memory. This function is realtime safe.
+
+ This function can only do "flat" queries, it is not smart enough to match
+ variables in nested objects.
+
+ For example:
+ @code
+ const LV2_Atom* name = NULL;
+ const LV2_Atom* age = NULL;
+ LV2_Atom_Object_Query q[] = {
+ { urids.eg_name, &name },
+ { urids.eg_age, &age },
+ LV2_ATOM_OBJECT_QUERY_END
+ };
+ lv2_atom_object_query(obj, q);
+ // name and age are now set to the appropriate values in obj, or NULL.
+ @endcode
+*/
+static inline int
+lv2_atom_object_query(const LV2_Atom_Object* object,
+ LV2_Atom_Object_Query* query)
+{
+ int matches = 0;
+ int n_queries = 0;
+
+ /* Count number of query keys so we can short-circuit when done */
+ for (LV2_Atom_Object_Query* q = query; q->key; ++q) {
+ ++n_queries;
+ }
+
+ LV2_ATOM_OBJECT_FOREACH (object, prop) {
+ for (LV2_Atom_Object_Query* q = query; q->key; ++q) {
+ if (q->key == prop->key && !*q->value) {
+ *q->value = &prop->value;
+ if (++matches == n_queries) {
+ return matches;
+ }
+ break;
+ }
+ }
+ }
+ return matches;
+}
+
+/**
+ Body only version of lv2_atom_object_get().
+*/
+static inline int
+lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...)
+{
+ int matches = 0;
+ int n_queries = 0;
+
+ /* Count number of keys so we can short-circuit when done */
+ va_list args;
+ va_start(args, body);
+ for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) {
+ if (!va_arg(args, const LV2_Atom**)) {
+ va_end(args);
+ return -1;
+ }
+ }
+ va_end(args);
+
+ LV2_ATOM_OBJECT_BODY_FOREACH (body, size, prop) {
+ va_start(args, body);
+ for (int i = 0; i < n_queries; ++i) {
+ uint32_t qkey = va_arg(args, uint32_t);
+ const LV2_Atom** qval = va_arg(args, const LV2_Atom**);
+ if (qkey == prop->key && !*qval) {
+ *qval = &prop->value;
+ if (++matches == n_queries) {
+ va_end(args);
+ return matches;
+ }
+ break;
+ }
+ }
+ va_end(args);
+ }
+ return matches;
+}
+
+/**
+ Variable argument version of lv2_atom_object_query().
+
+ This is nicer-looking in code, but a bit more error-prone since it is not
+ type safe and the argument list must be terminated.
+
+ The arguments should be a series of uint32_t key and const LV2_Atom** value
+ pairs, terminated by a zero key. The value pointers MUST be initialized to
+ NULL. For example:
+
+ @code
+ const LV2_Atom* name = NULL;
+ const LV2_Atom* age = NULL;
+ lv2_atom_object_get(obj,
+ uris.name_key, &name,
+ uris.age_key, &age,
+ 0);
+ @endcode
+*/
+static inline int
+lv2_atom_object_get(const LV2_Atom_Object* object, ...)
+{
+ int matches = 0;
+ int n_queries = 0;
+
+ /* Count number of keys so we can short-circuit when done */
+ va_list args;
+ va_start(args, object);
+ for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) {
+ if (!va_arg(args, const LV2_Atom**)) {
+ va_end(args);
+ return -1;
+ }
+ }
+ va_end(args);
+
+ LV2_ATOM_OBJECT_FOREACH (object, prop) {
+ va_start(args, object);
+ for (int i = 0; i < n_queries; ++i) {
+ uint32_t qkey = va_arg(args, uint32_t);
+ const LV2_Atom** qval = va_arg(args, const LV2_Atom**);
+ if (qkey == prop->key && !*qval) {
+ *qval = &prop->value;
+ if (++matches == n_queries) {
+ va_end(args);
+ return matches;
+ }
+ break;
+ }
+ }
+ va_end(args);
+ }
+ return matches;
+}
+
+/**
+ Variable argument version of lv2_atom_object_query() with types.
+
+ This is like lv2_atom_object_get(), but each entry has an additional
+ parameter to specify the required type. Only atoms with a matching type
+ will be selected.
+
+ The arguments should be a series of uint32_t key, const LV2_Atom**, uint32_t
+ type triples, terminated by a zero key. The value pointers MUST be
+ initialized to NULL. For example:
+
+ @code
+ const LV2_Atom_String* name = NULL;
+ const LV2_Atom_Int* age = NULL;
+ lv2_atom_object_get(obj,
+ uris.name_key, &name, uris.atom_String,
+ uris.age_key, &age, uris.atom_Int
+ 0);
+ @endcode
+*/
+static inline int
+lv2_atom_object_get_typed(const LV2_Atom_Object* object, ...)
+{
+ int matches = 0;
+ int n_queries = 0;
+
+ /* Count number of keys so we can short-circuit when done */
+ va_list args;
+ va_start(args, object);
+ for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) {
+ if (!va_arg(args, const LV2_Atom**) || !va_arg(args, uint32_t)) {
+ va_end(args);
+ return -1;
+ }
+ }
+ va_end(args);
+
+ LV2_ATOM_OBJECT_FOREACH (object, prop) {
+ va_start(args, object);
+ for (int i = 0; i < n_queries; ++i) {
+ const uint32_t qkey = va_arg(args, uint32_t);
+ const LV2_Atom** qval = va_arg(args, const LV2_Atom**);
+ const uint32_t qtype = va_arg(args, uint32_t);
+ if (!*qval && qkey == prop->key && qtype == prop->value.type) {
+ *qval = &prop->value;
+ if (++matches == n_queries) {
+ va_end(args);
+ return matches;
+ }
+ break;
+ }
+ }
+ va_end(args);
+ }
+ return matches;
+}
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+/**
+ @}
+ @}
+*/
+
+#endif /* LV2_ATOM_UTIL_H */