summaryrefslogtreecommitdiffstats
path: root/src/serialisation
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-03-19 20:16:46 +0000
committerDavid Robillard <d@drobilla.net>2012-03-19 20:16:46 +0000
commit254b434f0a79fea54bd963e8ff2e845a5b0cd3a6 (patch)
treeddf849fc5b64d1096846c28c1f1a742f54c3adff /src/serialisation
parentbc3afd8380d59c750c8f8e9bf1ed1b8d4a6826e9 (diff)
downloadingen-254b434f0a79fea54bd963e8ff2e845a5b0cd3a6.tar.gz
ingen-254b434f0a79fea54bd963e8ff2e845a5b0cd3a6.tar.bz2
ingen-254b434f0a79fea54bd963e8ff2e845a5b0cd3a6.zip
Partially functioning communication between Ingen LV2 plugin and UI.
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@4078 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/serialisation')
-rw-r--r--src/serialisation/Parser.cpp87
-rw-r--r--src/serialisation/Serialiser.cpp92
-rw-r--r--src/serialisation/sratom/sratom.c731
-rw-r--r--src/serialisation/sratom/sratom.h176
-rw-r--r--src/serialisation/wscript9
5 files changed, 1002 insertions, 93 deletions
diff --git a/src/serialisation/Parser.cpp b/src/serialisation/Parser.cpp
index 9acd460c..19c1f957 100644
--- a/src/serialisation/Parser.cpp
+++ b/src/serialisation/Parser.cpp
@@ -15,30 +15,23 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <locale.h>
-
#include <set>
-#include <boost/format.hpp>
-
#include <glibmm/convert.h>
#include <glibmm/fileutils.h>
#include <glibmm/miscutils.h>
#include <glibmm/ustring.h>
+#include "ingen/Interface.hpp"
+#include "ingen/serialisation/Parser.hpp"
+#include "ingen/shared/LV2URIMap.hpp"
+#include "ingen/shared/URIs.hpp"
+#include "ingen/shared/World.hpp"
#include "raul/Atom.hpp"
-#include "raul/AtomRDF.hpp"
#include "raul/log.hpp"
-
#include "serd/serd.h"
#include "sord/sordmm.hpp"
-
-#include "ingen/Interface.hpp"
-#include "ingen/shared/World.hpp"
-#include "ingen/shared/LV2URIMap.hpp"
-#include "ingen/shared/URIs.hpp"
-
-#include "ingen/serialisation/Parser.hpp"
+#include "sratom/sratom.h"
#define LOG(s) s << "[Parser] "
@@ -103,18 +96,46 @@ skip_property(const Sord::Node& predicate)
}
static Resource::Properties
-get_properties(Raul::Forge& forge,
- Sord::Model& model,
- const Sord::Node& subject)
+get_properties(Ingen::Shared::World* world,
+ Sord::Model& model,
+ const Sord::Node& subject)
{
+ SerdChunk out = { NULL, 0 };
+ LV2_URID_Map* map = &world->lv2_uri_map()->urid_map_feature()->urid_map;
+ LV2_URID_Unmap* unmap = &world->lv2_uri_map()->urid_unmap_feature()->urid_unmap;
+ Sratom* sratom = sratom_new(map);
+
+ LV2_Atom_Forge forge;
+ lv2_atom_forge_init(&forge, map);
+ lv2_atom_forge_set_sink(&forge, sratom_forge_sink, sratom_forge_deref, &out);
+
Resource::Properties props;
for (Sord::Iter i = model.find(subject, nil, nil); !i.end(); ++i) {
if (!skip_property(i.get_predicate())) {
- props.insert(
- make_pair(i.get_predicate().to_string(),
- AtomRDF::node_to_atom(forge, model, i.get_object())));
+ out.len = 0;
+ sratom_read(sratom, &forge, world->rdf_world()->c_obj(),
+ model.c_obj(), i.get_object().c_obj());
+ LV2_Atom* atom = (LV2_Atom*)out.buf;
+ Atom atomm;
+ // FIXME: Don't bloat out all URIs
+ if (atom->type == forge.URID) {
+ atomm = world->forge().alloc_uri(
+ unmap->unmap(unmap->handle, *(uint32_t*)LV2_ATOM_BODY(atom)));
+ } else {
+ atomm = world->forge().alloc(
+ atom->size, atom->type, LV2_ATOM_BODY(atom));
+ }
+ /*
+ std::cerr << "READ PROPERTY " << i.get_predicate()
+ << " = " << world->forge().str(atomm)
+ << " :: " << unmap->unmap(unmap->handle, atom->type)
+ << std::endl;
+ */
+ props.insert(make_pair(i.get_predicate().to_string(), atomm));
}
}
+
+ sratom_free(sratom);
return props;
}
@@ -130,12 +151,12 @@ get_port(Ingen::Shared::World* world,
const URIs& uris = *world->uris().get();
// Get all properties
- Resource::Properties props = get_properties(world->forge(), model, subject);
+ Resource::Properties props = get_properties(world, model, subject);
// Get index
Resource::Properties::const_iterator i = props.find(uris.lv2_index);
if (i == props.end()
- || i->second.type() != Atom::INT
+ || i->second.type() != world->forge().Int
|| i->second.get_int32() < 0) {
LOG(error) << "Port " << subject << " has no valid lv2:index" << endl;
return -1;
@@ -254,9 +275,9 @@ parse_node(Ingen::Shared::World* world,
parse_patch(world, target, model, subject,
path.parent(), Raul::Symbol(path.symbol()));
} else {
- Resource::Properties props = get_properties(world->forge(), model, subject);
+ Resource::Properties props = get_properties(world, model, subject);
props.insert(make_pair(uris.rdf_type,
- Raul::URI(uris.ingen_Node)));
+ uris.forge.alloc_uri(uris.ingen_Node.str())));
target->put(path, props);
}
return path;
@@ -275,7 +296,6 @@ parse_patch(Ingen::Shared::World* world,
const Sord::URI ingen_polyphony(*world->rdf_world(), NS_INGEN "polyphony");
const Sord::URI lv2_port(*world->rdf_world(), NS_LV2 "port");
- Raul::Forge& forge = world->forge();
const URIs& uris = *world->uris().get();
const Sord::Node& patch = subject_node;
@@ -284,8 +304,10 @@ parse_patch(Ingen::Shared::World* world,
// Use parameter overridden polyphony, if given
if (data) {
GraphObject::Properties::iterator poly_param = data.get().find(uris.ingen_polyphony);
- if (poly_param != data.get().end() && poly_param->second.type() == Atom::INT)
+ if (poly_param != data.get().end() &&
+ poly_param->second.type() == world->forge().Int) {
patch_poly = poly_param->second.get_int32();
+ }
}
// Load polyphony from file if necessary
@@ -324,7 +346,7 @@ parse_patch(Ingen::Shared::World* world,
// Create patch
Path patch_path(patch_path_str);
- Resource::Properties props = get_properties(forge, model, subject_node);
+ Resource::Properties props = get_properties(world, model, subject_node);
target->put(patch_path, props);
// For each node in this patch
@@ -438,16 +460,7 @@ parse_properties(Ingen::Shared::World* world,
const Raul::URI& uri,
boost::optional<GraphObject::Properties> data)
{
- Resource::Properties properties;
- for (Sord::Iter i = model.find(subject, nil, nil); !i.end(); ++i) {
- const Sord::Node& key = i.get_predicate();
- const Sord::Node& val = i.get_object();
- if (!skip_property(key)) {
- properties.insert(
- make_pair(key.to_string(),
- AtomRDF::node_to_atom(world->forge(), model, val)));
- }
- }
+ Resource::Properties properties = get_properties(world, model, subject);
target->put(uri, properties);
@@ -588,7 +601,7 @@ Parser::parse_file(Ingen::Shared::World* world,
if (parsed_path) {
target->set_property(*parsed_path, "http://drobilla.net/ns/ingen#document",
- world->forge().alloc(Atom::URI, uri.c_str()));
+ world->forge().alloc_uri(uri));
} else {
LOG(warn) << "Document URI lost" << endl;
}
diff --git a/src/serialisation/Serialiser.cpp b/src/serialisation/Serialiser.cpp
index 2ea07af3..224785b6 100644
--- a/src/serialisation/Serialiser.cpp
+++ b/src/serialisation/Serialiser.cpp
@@ -15,19 +15,12 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <locale.h>
-
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstring>
-#include <fstream>
-#include <map>
-#include <stdexcept>
#include <string>
-#include <utility>
-#include <vector>
#include <glib.h>
#include <glib/gstdio.h>
@@ -36,26 +29,23 @@
#include <glibmm/miscutils.h>
#include <glibmm/module.h>
-#include "raul/Atom.hpp"
-#include "raul/AtomRDF.hpp"
-#include "raul/Path.hpp"
-#include "raul/log.hpp"
-
-#include "sord/sordmm.hpp"
-
#include "ingen/Connection.hpp"
+#include "ingen/Interface.hpp"
#include "ingen/Node.hpp"
#include "ingen/Patch.hpp"
#include "ingen/Plugin.hpp"
#include "ingen/Port.hpp"
-#include "ingen/Interface.hpp"
+#include "ingen/serialisation/Serialiser.hpp"
#include "ingen/shared/LV2URIMap.hpp"
#include "ingen/shared/ResourceImpl.hpp"
#include "ingen/shared/Store.hpp"
#include "ingen/shared/URIs.hpp"
#include "ingen/shared/World.hpp"
-
-#include "ingen/serialisation/Serialiser.hpp"
+#include "raul/Atom.hpp"
+#include "raul/Path.hpp"
+#include "raul/log.hpp"
+#include "sord/sordmm.hpp"
+#include "sratom/sratom.h"
#define LOG(s) s << "[Serialiser] "
@@ -227,21 +217,11 @@ Serialiser::Impl::write_bundle(SharedPtr<const Patch> patch,
}
string
-Serialiser::to_string(SharedPtr<const GraphObject> object,
- const string& base_uri,
- const GraphObject::Properties& extra_rdf)
+Serialiser::to_string(SharedPtr<const GraphObject> object,
+ const string& base_uri)
{
start_to_string(object->path(), base_uri);
serialise(object);
-
- Sord::URI base_rdf_node(me->_model->world(), base_uri);
- for (GraphObject::Properties::const_iterator v = extra_rdf.begin();
- v != extra_rdf.end(); ++v) {
- me->_model->add_statement(base_rdf_node,
- AtomRDF::atom_to_node(*me->_model, v->first),
- AtomRDF::atom_to_node(*me->_model, v->second));
- }
-
return finish();
}
@@ -252,8 +232,6 @@ Serialiser::to_string(SharedPtr<const GraphObject> object,
void
Serialiser::Impl::start_to_filename(const string& filename)
{
- setlocale(LC_NUMERIC, "C");
-
assert(filename.find(":") == string::npos || filename.substr(0, 5) == "file:");
if (filename.find(":") == string::npos) {
_base_uri = "file://" + filename;
@@ -278,8 +256,6 @@ Serialiser::Impl::start_to_filename(const string& filename)
void
Serialiser::start_to_string(const Raul::Path& root, const string& base_uri)
{
- setlocale(LC_NUMERIC, "C");
-
me->_root_path = root;
me->_base_uri = base_uri;
me->_model = new Sord::Model(*me->_world.rdf_world(), base_uri);
@@ -328,7 +304,7 @@ void
Serialiser::serialise(SharedPtr<const GraphObject> object) throw (std::logic_error)
{
if (!me->_model)
- throw std::logic_error("serialise called without serialization in progress");
+ throw std::logic_error("serialise called without serialisation in progress");
SharedPtr<const Patch> patch = PtrCast<const Patch>(object);
if (patch) {
@@ -372,13 +348,17 @@ Serialiser::Impl::serialise_patch(SharedPtr<const Patch> patch,
Sord::Curie(world, "lv2:extensionData"),
Sord::URI(world, "http://lv2plug.in/ns/ext/state#Interface"));
+ _model->add_statement(patch_id,
+ Sord::URI(world, "http://lv2plug.in/ns/extensions/ui#ui"),
+ Sord::URI(world, "http://drobilla.net/ns/ingen#ui"));
+
const URIs& uris = *_world.uris().get();
// Always write a symbol (required by Ingen)
string symbol;
GraphObject::Properties::const_iterator s = patch->properties().find(uris.lv2_symbol);
if (s == patch->properties().end()
- || !s->second.type() == Atom::STRING
+ || !s->second.type() == _world.forge().String
|| !Symbol::is_valid(s->second.get_string())) {
symbol = Glib::path_get_basename(_model->base_uri().to_c_string());
symbol = Symbol::symbolify(symbol.substr(0, symbol.find('.')));
@@ -393,7 +373,7 @@ Serialiser::Impl::serialise_patch(SharedPtr<const Patch> patch,
// If the patch has no doap:name (required by LV2), use the symbol
if (patch->properties().find(uris.doap_name) == patch->properties().end())
_model->add_statement(patch_id,
- AtomRDF::atom_to_node(*_model, uris.doap_name),
+ Sord::URI(world, uris.doap_name.str()),
Sord::Literal(world, symbol));
serialise_properties(patch.get(), Resource::INTERNAL, patch_id);
@@ -455,7 +435,7 @@ Serialiser::Impl::serialise_patch(SharedPtr<const Patch> patch,
// Ensure lv2:name always exists so Patch is a valid LV2 plugin
if (p->properties().find(NS_LV2 "name") == p->properties().end())
p->set_property(NS_LV2 "name",
- _world.forge().make(p->symbol().c_str()));
+ _world.forge().alloc(p->symbol().c_str()));
_model->add_statement(patch_id,
Sord::URI(world, NS_LV2 "port"),
@@ -518,13 +498,6 @@ Serialiser::Impl::serialise_port(const Port* port,
Sord::Literal(world, port->path().symbol()));
serialise_properties(port, context, port_id);
-
- if (context == Resource::INTERNAL) {
- _model->add_statement(
- port_id,
- Sord::Curie(world, "lv2:index"),
- AtomRDF::atom_to_node(*_model, _world.forge().make((int)port->index())));
- }
}
void
@@ -542,7 +515,7 @@ Serialiser::Impl::serialise_connection(const Sord::Node& parent,
{
if (!_model)
throw std::logic_error(
- "serialise_connection called without serialization in progress");
+ "serialise_connection called without serialisation in progress");
Sord::World& world = _model->world();
@@ -575,19 +548,32 @@ Serialiser::Impl::serialise_properties(const GraphObject* o,
{
const GraphObject::Properties props = o->properties(context);
+ LV2_URID_Map* map = &_world.lv2_uri_map()->urid_map_feature()->urid_map;
+ LV2_URID_Unmap* unmap = &_world.lv2_uri_map()->urid_unmap_feature()->urid_unmap;
+ Sratom* sratom = sratom_new(map);
+ SerdNode base = serd_node_from_string(SERD_URI,
+ (const uint8_t*)_base_uri.c_str());
+ SerdEnv* env = serd_env_new(&base);
+ SordInserter* inserter = sord_inserter_new(_model->c_obj(), env);
+
+ sratom_set_sink(sratom, _base_uri.c_str(),
+ (SerdStatementSink)sord_inserter_write_statement, NULL,
+ inserter, true);
+
typedef GraphObject::Properties::const_iterator iterator;
for (iterator v = props.begin(); v != props.end(); ++v) {
- const Sord::URI key(_model->world(), v->first.str());
- const Sord::Node value(AtomRDF::atom_to_node(*_model, v->second));
+ const Sord::URI key(_model->world(), v->first.str());
if (!skip_property(key)) {
- if (value.is_valid()) {
- _model->add_statement(id, key, value);
- } else {
- LOG(warn) << "Can not serialise variable '" << v->first << "' :: "
- << (int)v->second.type() << endl;
- }
+ sratom_write(sratom, unmap, 0,
+ sord_node_to_serd_node(id.c_obj()),
+ sord_node_to_serd_node(key.c_obj()),
+ v->second.type(), v->second.size(), v->second.get_body());
}
}
+
+ sord_inserter_free(inserter);
+ serd_env_free(env);
+ sratom_free(sratom);
}
} // namespace Serialisation
diff --git a/src/serialisation/sratom/sratom.c b/src/serialisation/sratom/sratom.c
new file mode 100644
index 00000000..646d4623
--- /dev/null
+++ b/src/serialisation/sratom/sratom.c
@@ -0,0 +1,731 @@
+/*
+ Copyright 2012 David Robillard <http://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.
+*/
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
+#include "lv2/lv2plug.in/ns/ext/atom/util.h"
+
+#include "sratom/sratom.h"
+
+#define NS_MIDI (const uint8_t*)"http://lv2plug.in/ns/ext/midi#"
+#define NS_RDF (const uint8_t*)"http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define NS_XSD (const uint8_t*)"http://www.w3.org/2001/XMLSchema#"
+
+#define USTR(str) ((const uint8_t*)(str))
+
+typedef enum {
+ MODE_NORMAL,
+ MODE_SEQUENCE,
+} ReadMode;
+
+struct SratomImpl {
+ LV2_URID_Map* map;
+ LV2_Atom_Forge forge;
+ LV2_URID atom_Event;
+ LV2_URID midi_MidiEvent;
+ unsigned next_id;
+ SerdNode base_uri;
+ SerdStatementSink write_statement;
+ SerdEndSink end_anon;
+ void* handle;
+ bool pretty_numbers;
+ struct {
+ SordNode* atom_childType;
+ SordNode* atom_frameTime;
+ SordNode* rdf_first;
+ SordNode* rdf_rest;
+ SordNode* rdf_type;
+ SordNode* rdf_value;
+ SordNode* xsd_base64Binary;
+ } nodes;
+};
+
+static void
+read_node(Sratom* sratom,
+ LV2_Atom_Forge* forge,
+ SordWorld* world,
+ SordModel* model,
+ const SordNode* node,
+ ReadMode mode);
+
+SRATOM_API
+Sratom*
+sratom_new(LV2_URID_Map* map)
+{
+ Sratom* sratom = (Sratom*)malloc(sizeof(Sratom));
+ sratom->map = map;
+ sratom->atom_Event = map->map(map->handle, LV2_ATOM__Event);
+ sratom->midi_MidiEvent = map->map(map->handle,
+ (const char*)NS_MIDI "MidiEvent");
+ sratom->next_id = 0;
+ sratom->base_uri = SERD_NODE_NULL;
+ sratom->pretty_numbers = false;
+ memset(&sratom->nodes, 0, sizeof(sratom->nodes));
+ lv2_atom_forge_init(&sratom->forge, map);
+ return sratom;
+}
+
+SRATOM_API
+void
+sratom_free(Sratom* sratom)
+{
+ free(sratom);
+}
+
+SRATOM_API
+void
+sratom_set_sink(Sratom* sratom,
+ const char* base_uri,
+ SerdStatementSink write_statement,
+ SerdEndSink end_anon,
+ void* handle,
+ bool pretty_numbers)
+{
+ if (base_uri) {
+ serd_node_free(&sratom->base_uri);
+ sratom->base_uri = serd_node_new_uri_from_string(
+ USTR(base_uri), NULL, NULL);
+ }
+ sratom->write_statement = write_statement;
+ sratom->end_anon = end_anon;
+ sratom->handle = handle;
+ sratom->pretty_numbers = pretty_numbers;
+}
+
+static void
+gensym(SerdNode* out, char c, unsigned num)
+{
+ out->n_bytes = out->n_chars = snprintf(
+ (char*)out->buf, 10, "%c%u", c, num);
+}
+
+static void
+list_append(Sratom* sratom,
+ LV2_URID_Unmap* unmap,
+ unsigned* flags,
+ SerdNode* s,
+ SerdNode* p,
+ SerdNode* node,
+ uint32_t size,
+ uint32_t type,
+ void* body)
+{
+ // Generate a list node
+ gensym(node, 'l', sratom->next_id);
+ sratom->write_statement(sratom->handle, *flags, NULL,
+ s, p, node, NULL, NULL);
+
+ // _:node rdf:first value
+ *flags = SERD_LIST_CONT;
+ *p = serd_node_from_string(SERD_URI, NS_RDF "first");
+ sratom_write(sratom, unmap, *flags, node, p, type, size, body);
+
+ // Set subject to node and predicate to rdf:rest for next time
+ gensym(node, 'l', ++sratom->next_id);
+ *s = *node;
+ *p = serd_node_from_string(SERD_URI, NS_RDF "rest");
+}
+
+static void
+list_end(SerdStatementSink sink,
+ void* handle,
+ unsigned* flags,
+ SerdNode* s,
+ SerdNode* p)
+{
+ // _:node rdf:rest rdf:nil
+ const SerdNode nil = serd_node_from_string(SERD_URI, NS_RDF "nil");
+ sink(handle, *flags, NULL, s, p, &nil, NULL, NULL);
+}
+
+static void
+start_object(Sratom* sratom,
+ uint32_t flags,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ const SerdNode* node,
+ const char* type)
+{
+ sratom->write_statement(sratom->handle, flags|SERD_ANON_O_BEGIN, NULL,
+ subject, predicate, node, NULL, NULL);
+ if (type) {
+ SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "type");
+ SerdNode o = serd_node_from_string(SERD_URI, USTR(type));
+ sratom->write_statement(sratom->handle, SERD_ANON_CONT, NULL,
+ node, &p, &o, NULL, NULL);
+ }
+}
+
+static bool
+path_is_absolute(const char* path)
+{
+ return (path[0] == '/'
+ || (isalpha(path[0]) && path[1] == ':'
+ && (path[2] == '/' || path[2] == '\\')));
+}
+
+SRATOM_API
+int
+sratom_write(Sratom* sratom,
+ LV2_URID_Unmap* unmap,
+ uint32_t flags,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ uint32_t type_urid,
+ uint32_t size,
+ const void* body)
+{
+ const char* const type = unmap->unmap(unmap->handle, type_urid);
+ uint8_t idbuf[12] = "b0000000000";
+ SerdNode id = serd_node_from_string(SERD_BLANK, idbuf);
+ uint8_t nodebuf[12] = "b0000000000";
+ SerdNode node = serd_node_from_string(SERD_BLANK, nodebuf);
+ SerdNode object = SERD_NODE_NULL;
+ SerdNode datatype = SERD_NODE_NULL;
+ SerdNode language = SERD_NODE_NULL;
+ bool new_node = false;
+ if (type_urid == 0 && size == 0) {
+ object = serd_node_from_string(SERD_URI, USTR(NS_RDF "nil"));
+ } else if (type_urid == sratom->forge.String) {
+ object = serd_node_from_string(SERD_LITERAL, (const uint8_t*)body);
+ } else if (type_urid == sratom->forge.Literal) {
+ LV2_Atom_Literal_Body* lit = (LV2_Atom_Literal_Body*)body;
+ const uint8_t* str = USTR(lit + 1);
+ object = serd_node_from_string(SERD_LITERAL, str);
+ if (lit->datatype) {
+ datatype = serd_node_from_string(
+ SERD_URI, USTR(unmap->unmap(unmap->handle, lit->datatype)));
+ } else if (lit->lang) {
+ const char* lang = unmap->unmap(unmap->handle, lit->lang);
+ const char* prefix = "http://lexvo.org/id/iso639-3/";
+ const size_t prefix_len = strlen(prefix);
+ if (lang && !strncmp(lang, prefix, prefix_len)) {
+ language = serd_node_from_string(
+ SERD_LITERAL, USTR(lang + prefix_len));
+ } else {
+ fprintf(stderr, "Unknown language URI <%s>\n", lang);
+ }
+ }
+ } else if (type_urid == sratom->forge.URID) {
+ const uint32_t id = *(const uint32_t*)body;
+ const uint8_t* str = USTR(unmap->unmap(unmap->handle, id));
+ object = serd_node_from_string(SERD_URI, str);
+ } else if (type_urid == sratom->forge.Path) {
+ const uint8_t* str = USTR(body);
+ if (path_is_absolute((const char*)str)) {
+ new_node = true;
+ object = serd_node_new_file_uri(str, NULL, NULL, false);
+ } else {
+ SerdURI base_uri = SERD_URI_NULL;
+ if (!sratom->base_uri.buf ||
+ strncmp((const char*)sratom->base_uri.buf, "file://", 7)) {
+ fprintf(stderr, "warning: Relative path but base is not a file URI.\n");
+ fprintf(stderr, "warning: Writing ambiguous atom:Path literal.\n");
+ object = serd_node_from_string(SERD_LITERAL, str);
+ datatype = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__Path));
+ } else {
+ if (sratom->base_uri.buf) {
+ serd_uri_parse(sratom->base_uri.buf, &base_uri);
+ }
+ new_node = true;
+ SerdNode rel = serd_node_new_file_uri(str, NULL, NULL, false);
+ object = serd_node_new_uri_from_node(&rel, &base_uri, NULL);
+ serd_node_free(&rel);
+ }
+ }
+ } else if (type_urid == sratom->forge.URI) {
+ const uint8_t* str = USTR(body);
+ object = serd_node_from_string(SERD_URI, str);
+ } else if (type_urid == sratom->forge.Int) {
+ new_node = true;
+ object = serd_node_new_integer(*(int32_t*)body);
+ datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers)
+ ? NS_XSD "integer" : NS_XSD "int");
+ } else if (type_urid == sratom->forge.Long) {
+ new_node = true;
+ object = serd_node_new_integer(*(int64_t*)body);
+ datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers)
+ ? NS_XSD "integer" : NS_XSD "long");
+ } else if (type_urid == sratom->forge.Float) {
+ new_node = true;
+ object = serd_node_new_decimal(*(float*)body, 8);
+ datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers)
+ ? NS_XSD "decimal" : NS_XSD "float");
+ } else if (type_urid == sratom->forge.Double) {
+ new_node = true;
+ object = serd_node_new_decimal(*(double*)body, 16);
+ datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers)
+ ? NS_XSD "decimal" : NS_XSD "double");
+ } else if (type_urid == sratom->forge.Bool) {
+ const int32_t val = *(const int32_t*)body;
+ datatype = serd_node_from_string(SERD_URI, NS_XSD "boolean");
+ object = serd_node_from_string(SERD_LITERAL,
+ USTR(val ? "true" : "false"));
+ } else if (type_urid == sratom->midi_MidiEvent) {
+ new_node = true;
+ datatype = serd_node_from_string(SERD_URI, NS_MIDI "MidiEvent");
+ uint8_t* str = calloc(size * 2 + 1, 1);
+ for (uint32_t i = 0; i < size; ++i) {
+ snprintf((char*)str + (2 * i), size * 2 + 1, "%02X",
+ (unsigned)(uint8_t)*((uint8_t*)body + i));
+ }
+ object = serd_node_from_string(SERD_LITERAL, USTR(str));
+ } else if (type_urid == sratom->atom_Event) {
+ const LV2_Atom_Event* ev = (const LV2_Atom_Event*)body;
+ gensym(&id, 'e', sratom->next_id++);
+ start_object(sratom, flags, subject, predicate, &id, NULL);
+ // TODO: beat time
+ SerdNode time = serd_node_new_integer(ev->time.frames);
+ SerdNode p = serd_node_from_string(SERD_URI,
+ USTR(LV2_ATOM__frameTime));
+ datatype = serd_node_from_string(SERD_URI, NS_XSD "decimal");
+ sratom->write_statement(sratom->handle, SERD_ANON_CONT, NULL,
+ &id, &p, &time, &datatype, &language);
+ serd_node_free(&time);
+
+ p = serd_node_from_string(SERD_URI, NS_RDF "value");
+ sratom_write(sratom, unmap, SERD_ANON_CONT, &id, &p,
+ ev->body.type, ev->body.size, LV2_ATOM_BODY(&ev->body));
+ if (sratom->end_anon) {
+ sratom->end_anon(sratom->handle, &id);
+ }
+ } else if (type_urid == sratom->forge.Tuple) {
+ gensym(&id, 't', sratom->next_id++);
+ start_object(sratom, flags, subject, predicate, &id, type);
+ SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value");
+ flags |= SERD_LIST_O_BEGIN;
+ LV2_TUPLE_BODY_FOREACH(body, size, i) {
+ list_append(sratom, unmap, &flags, &id, &p, &node,
+ i->size, i->type, LV2_ATOM_BODY(i));
+ }
+ list_end(sratom->write_statement, sratom->handle, &flags, &id, &p);
+ if (sratom->end_anon) {
+ sratom->end_anon(sratom->handle, &id);
+ }
+ } else if (type_urid == sratom->forge.Vector) {
+ const LV2_Atom_Vector_Body* vec = (const LV2_Atom_Vector_Body*)body;
+ gensym(&id, 'v', sratom->next_id++);
+ start_object(sratom, flags, subject, predicate, &id, type);
+ SerdNode p = serd_node_from_string(SERD_URI, (const uint8_t*)LV2_ATOM__childType);
+ SerdNode child_type = serd_node_from_string(
+ SERD_URI, (const uint8_t*)unmap->unmap(unmap->handle, vec->child_type));
+ sratom->write_statement(sratom->handle, flags, NULL, &id, &p, &child_type, NULL, NULL);
+ p = serd_node_from_string(SERD_URI, NS_RDF "value");
+ flags |= SERD_LIST_O_BEGIN;
+ for (char* i = (char*)(vec + 1);
+ i < (char*)vec + size;
+ i += vec->child_size) {
+ list_append(sratom, unmap, &flags, &id, &p, &node,
+ vec->child_size, vec->child_type, i);
+ }
+ list_end(sratom->write_statement, sratom->handle, &flags, &id, &p);
+ if (sratom->end_anon) {
+ sratom->end_anon(sratom->handle, &id);
+ }
+ } else if (type_urid == sratom->forge.Blank) {
+ const LV2_Atom_Object_Body* obj = (const LV2_Atom_Object_Body*)body;
+ const char* otype = unmap->unmap(unmap->handle,
+ obj->otype);
+ gensym(&id, 'b', sratom->next_id++);
+ start_object(sratom, flags, subject, predicate, &id, otype);
+ LV2_OBJECT_BODY_FOREACH(obj, size, i) {
+ const LV2_Atom_Property_Body* prop = lv2_object_iter_get(i);
+ const char* const key = unmap->unmap(unmap->handle, prop->key);
+ SerdNode pred = serd_node_from_string(SERD_URI, USTR(key));
+ sratom_write(sratom, unmap, flags|SERD_ANON_CONT, &id, &pred,
+ prop->value.type, prop->value.size,
+ LV2_ATOM_BODY(&prop->value));
+ }
+ if (sratom->end_anon) {
+ sratom->end_anon(sratom->handle, &id);
+ }
+ } else if (type_urid == sratom->forge.Sequence) {
+ const LV2_Atom_Sequence_Body* seq = (const LV2_Atom_Sequence_Body*)body;
+ gensym(&id, 'v', sratom->next_id++);
+ start_object(sratom, flags, subject, predicate, &id, type);
+ SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value");
+ flags |= SERD_LIST_O_BEGIN;
+ LV2_SEQUENCE_BODY_FOREACH(seq, size, i) {
+ LV2_Atom_Event* ev = lv2_sequence_iter_get(i);
+ list_append(sratom, unmap, &flags, &id, &p, &node,
+ sizeof(LV2_Atom_Event) + ev->body.size,
+ sratom->atom_Event,
+ ev);
+ }
+ list_end(sratom->write_statement, sratom->handle, &flags, &id, &p);
+ if (sratom->end_anon) {
+ sratom->end_anon(sratom->handle, &id);
+ }
+ } else {
+ gensym(&id, 'b', sratom->next_id++);
+ start_object(sratom, flags, subject, predicate, &id, type);
+ SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value");
+ SerdNode o = serd_node_new_blob(body, size, true);
+ datatype = serd_node_from_string(SERD_URI, NS_XSD "base64Binary");
+ sratom->write_statement(sratom->handle, flags, NULL, &id, &p, &o, &datatype, NULL);
+ if (sratom->end_anon) {
+ sratom->end_anon(sratom->handle, &id);
+ }
+ serd_node_free(&o);
+ }
+
+ if (object.buf) {
+ sratom->write_statement(sratom->handle, flags, NULL,
+ subject, predicate, &object, &datatype, &language);
+ }
+
+ if (new_node) {
+ serd_node_free(&object);
+ }
+
+ return 0;
+}
+
+SRATOM_API
+char*
+sratom_to_turtle(Sratom* sratom,
+ LV2_URID_Unmap* unmap,
+ const char* base_uri,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ uint32_t type,
+ uint32_t size,
+ const void* body)
+{
+ SerdURI buri = SERD_URI_NULL;
+ SerdNode base = serd_node_new_uri_from_string(USTR(base_uri), NULL, &buri);
+ SerdEnv* env = serd_env_new(&base);
+ SerdChunk str = { NULL, 0 };
+
+ serd_env_set_prefix_from_strings(env, USTR("midi"), NS_MIDI);
+ serd_env_set_prefix_from_strings(env, USTR("atom"),
+ USTR(LV2_ATOM_URI "#"));
+ serd_env_set_prefix_from_strings(env, USTR("rdf"), NS_RDF);
+ serd_env_set_prefix_from_strings(env, USTR("xsd"), NS_XSD);
+ serd_env_set_prefix_from_strings(env, USTR("eg"),
+ USTR("http://example.org/"));
+
+ SerdWriter* writer = serd_writer_new(
+ SERD_TURTLE,
+ SERD_STYLE_ABBREVIATED|SERD_STYLE_RESOLVED|SERD_STYLE_CURIED,
+ env, &buri, serd_chunk_sink, &str);
+
+ // Write @prefix directives
+ serd_env_foreach(env,
+ (SerdPrefixSink)serd_writer_set_prefix,
+ writer);
+
+ sratom_set_sink(sratom, base_uri,
+ (SerdStatementSink)serd_writer_write_statement,
+ (SerdEndSink)serd_writer_end_anon,
+ writer,
+ false);
+ sratom_write(sratom, unmap, SERD_EMPTY_S,
+ subject, predicate, type, size, body);
+ serd_writer_finish(writer);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+ return (char*)serd_chunk_sink_finish(&str);
+}
+
+static const SordNode*
+get_object(SordModel* model,
+ const SordNode* subject,
+ const SordNode* predicate)
+{
+ const SordNode* object = NULL;
+ SordQuad q = { subject, predicate, 0, 0 };
+ SordIter* i = sord_find(model, q);
+ if (!sord_iter_end(i)) {
+ SordQuad quad;
+ sord_iter_get(i, quad);
+ object = quad[SORD_OBJECT];
+ }
+ sord_iter_free(i);
+ return object;
+}
+
+static void
+read_list_value(Sratom* sratom,
+ LV2_Atom_Forge* forge,
+ SordWorld* world,
+ SordModel* model,
+ const SordNode* node,
+ ReadMode mode)
+{
+ const SordNode* first = get_object(model, node, sratom->nodes.rdf_first);
+ const SordNode* rest = get_object(model, node, sratom->nodes.rdf_rest);
+ if (first && rest) {
+ read_node(sratom, forge, world, model, first, mode);
+ read_list_value(sratom, forge, world, model, rest, mode);
+ }
+}
+
+static uint32_t
+atom_size(Sratom* sratom, uint32_t type_urid)
+{
+ if (type_urid == sratom->forge.Int) {
+ return sizeof(int32_t);
+ } else if (type_urid == sratom->forge.Long) {
+ return sizeof(int64_t);
+ } else if (type_urid == sratom->forge.Float) {
+ return sizeof(float);
+ } else if (type_urid == sratom->forge.Double) {
+ return sizeof(double);
+ } else if (type_urid == sratom->forge.Bool) {
+ return sizeof(int32_t);
+ } else if (type_urid == sratom->forge.URID) {
+ return sizeof(uint32_t);
+ } else {
+ return 0;
+ }
+}
+
+static void
+read_node(Sratom* sratom,
+ LV2_Atom_Forge* forge,
+ SordWorld* world,
+ SordModel* model,
+ const SordNode* node,
+ ReadMode mode)
+{
+ LV2_URID_Map* map = sratom->map;
+ size_t len = 0;
+ const char* str = (const char*)sord_node_get_string_counted(node, &len);
+ if (sord_node_get_type(node) == SORD_LITERAL) {
+ char* endptr;
+ SordNode* datatype = sord_node_get_datatype(node);
+ const char* language = sord_node_get_language(node);
+ if (datatype) {
+ const char* type_uri = (const char*)sord_node_get_string(datatype);
+ if (!strcmp(type_uri, (char*)NS_XSD "int") ||
+ !strcmp(type_uri, (char*)NS_XSD "integer")) {
+ lv2_atom_forge_int(forge, strtol(str, &endptr, 10));
+ } else if (!strcmp(type_uri, (char*)NS_XSD "long")) {
+ lv2_atom_forge_long(forge, strtol(str, &endptr, 10));
+ } else if (!strcmp(type_uri, (char*)NS_XSD "float") ||
+ !strcmp(type_uri, (char*)NS_XSD "decimal")) {
+ lv2_atom_forge_float(forge, serd_strtod(str, &endptr));
+ } else if (!strcmp(type_uri, (char*)NS_XSD "double")) {
+ lv2_atom_forge_double(forge, serd_strtod(str, &endptr));
+ } else if (!strcmp(type_uri, (char*)NS_XSD "boolean")) {
+ lv2_atom_forge_bool(forge, !strcmp(str, "true"));
+ } else if (!strcmp(type_uri, LV2_ATOM__Path)) {
+ lv2_atom_forge_path(forge, str, len);
+ } else if (!strcmp(type_uri, (char*)NS_MIDI "MidiEvent")) {
+ lv2_atom_forge_atom(forge, len / 2, sratom->midi_MidiEvent);
+ for (const char* s = str; s < str + len; s += 2) {
+ unsigned num;
+ sscanf(s, "%2X", &num);
+ assert(num < UINT8_MAX);
+ const uint8_t c = num;
+ lv2_atom_forge_raw(forge, &c, 1);
+ }
+ lv2_atom_forge_pad(forge, len / 2);
+ } else {
+ lv2_atom_forge_literal(
+ forge, str, len,
+ sratom->map->map(sratom->map->handle, type_uri),
+ 0);
+ }
+ } else if (language) {
+ const char* prefix = "http://lexvo.org/id/iso639-3/";
+ const size_t lang_len = strlen(prefix) + strlen(language);
+ char* lang_uri = calloc(lang_len + 1, 1);
+ snprintf(lang_uri, lang_len + 1, "%s%s", prefix, language);
+ lv2_atom_forge_literal(
+ forge, str, len, 0,
+ sratom->map->map(sratom->map->handle, lang_uri));
+ free(lang_uri);
+ } else {
+ lv2_atom_forge_string(forge, str, len);
+ }
+ } else if (sord_node_get_type(node) == SORD_URI) {
+ if (!strcmp(str, (const char*)NS_RDF "nil")) {
+ lv2_atom_forge_atom(forge, 0, 0);
+ } else if (!strncmp(str, "file://", 7)) {
+ uint8_t* path = serd_file_uri_parse((const uint8_t*)str, NULL);
+ lv2_atom_forge_path(forge, (const char*)path, strlen((const char*)path));
+ free(path);
+ } else {
+ lv2_atom_forge_urid(forge, map->map(map->handle, str));
+ }
+ } else {
+ const SordNode* type = get_object(model, node, sratom->nodes.rdf_type);
+ const SordNode* value = get_object(model, node, sratom->nodes.rdf_value);
+
+ const uint8_t* type_uri = NULL;
+ uint32_t type_urid = 0;
+ if (type) {
+ type_uri = sord_node_get_string(type);
+ type_urid = map->map(map->handle, (const char*)type_uri);
+ }
+
+ LV2_Atom_Forge_Frame frame = { 0, 0 };
+ if (mode == MODE_SEQUENCE) {
+ const SordNode* frame_time = get_object(
+ model, node, sratom->nodes.atom_frameTime);
+ const char* frame_time_str = frame_time
+ ? (const char*)sord_node_get_string(frame_time)
+ : "";
+ lv2_atom_forge_frame_time(forge, serd_strtod(frame_time_str, NULL));
+ read_node(sratom, forge, world, model, value, MODE_NORMAL);
+ } else if (type_urid == sratom->forge.Tuple) {
+ lv2_atom_forge_tuple(forge, &frame);
+ read_list_value(sratom, forge, world, model, value, MODE_NORMAL);
+ } else if (type_urid == sratom->forge.Sequence) {
+ lv2_atom_forge_sequence_head(forge, &frame, 0);
+ read_list_value(sratom, forge, world, model, value, MODE_SEQUENCE);
+ } else if (type_urid == sratom->forge.Vector) {
+ const SordNode* child_type_node = get_object(
+ model, node, sratom->nodes.atom_childType);
+ uint32_t child_type = map->map(
+ map->handle, (const char*)sord_node_get_string(child_type_node));
+ uint32_t child_size = atom_size(sratom, child_type);
+ if (child_size > 0) {
+ lv2_atom_forge_vector_head(forge, &frame, child_size, child_type);
+ read_list_value(sratom, forge, world, model, value, MODE_NORMAL);
+ }
+ } else if (value && sord_node_equals(sord_node_get_datatype(value),
+ sratom->nodes.xsd_base64Binary)) {
+ size_t vlen = 0;
+ const uint8_t* vstr = sord_node_get_string_counted(value, &vlen);
+ size_t size = 0;
+ void* body = serd_base64_decode(vstr, vlen, &size);
+ lv2_atom_forge_atom(forge, size, type_urid);
+ lv2_atom_forge_write(forge, body, size);
+ free(body);
+ } else {
+ lv2_atom_forge_blank(forge, &frame, sratom->next_id++, type_urid);
+ SordQuad match;
+
+ SordQuad q2 = { node, 0, 0, 0 };
+ SordIter* i = sord_find(model, q2);
+ for (;!sord_iter_end(i); sord_iter_next(i)) {
+ sord_iter_get(i, match);
+ const SordNode* p = match[SORD_PREDICATE];
+ const char* p_uri = (const char*)sord_node_get_string(p);
+ uint32_t p_urid = map->map(map->handle, p_uri);
+ if (!sord_node_equals(p, sratom->nodes.rdf_type)) {
+ // TODO: This will lose multiple rdf:type properties
+ lv2_atom_forge_property_head(forge, p_urid, 0);
+ read_node(sratom, forge, world, model,
+ match[SORD_OBJECT], MODE_NORMAL);
+ }
+ }
+ sord_iter_free(i);
+ }
+ if (frame.ref) {
+ lv2_atom_forge_pop(forge, &frame);
+ }
+ }
+}
+
+SRATOM_API
+void
+sratom_read(Sratom* sratom,
+ LV2_Atom_Forge* forge,
+ SordWorld* world,
+ SordModel* model,
+ const SordNode* node)
+{
+ sratom->nodes.atom_childType = sord_new_uri(world, USTR(LV2_ATOM__childType));
+ sratom->nodes.atom_frameTime = sord_new_uri(world, USTR(LV2_ATOM__frameTime));
+ sratom->nodes.rdf_first = sord_new_uri(world, NS_RDF "first");
+ sratom->nodes.rdf_rest = sord_new_uri(world, NS_RDF "rest");
+ sratom->nodes.rdf_type = sord_new_uri(world, NS_RDF "type");
+ sratom->nodes.rdf_value = sord_new_uri(world, NS_RDF "value");
+ sratom->nodes.xsd_base64Binary = sord_new_uri(world, NS_XSD "base64Binary");
+
+ sratom->next_id = 1;
+ read_node(sratom, forge, world, model, node, MODE_NORMAL);
+
+ sord_node_free(world, sratom->nodes.xsd_base64Binary);
+ sord_node_free(world, sratom->nodes.rdf_value);
+ sord_node_free(world, sratom->nodes.rdf_type);
+ sord_node_free(world, sratom->nodes.rdf_rest);
+ sord_node_free(world, sratom->nodes.rdf_first);
+ sord_node_free(world, sratom->nodes.atom_frameTime);
+ sord_node_free(world, sratom->nodes.atom_childType);
+ memset(&sratom->nodes, 0, sizeof(sratom->nodes));
+}
+
+SRATOM_API
+LV2_Atom_Forge_Ref
+sratom_forge_sink(LV2_Atom_Forge_Sink_Handle handle,
+ const void* buf,
+ uint32_t size)
+{
+ SerdChunk* chunk = (SerdChunk*)handle;
+ const LV2_Atom_Forge_Ref ref = chunk->len + 1;
+ serd_chunk_sink(buf, size, chunk);
+ return ref;
+}
+
+SRATOM_API
+LV2_Atom*
+sratom_forge_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
+{
+ SerdChunk* chunk = (SerdChunk*)handle;
+ return (LV2_Atom*)(chunk->buf + ref - 1);
+}
+
+SRATOM_API
+LV2_Atom*
+sratom_from_turtle(Sratom* sratom,
+ const char* base_uri,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ const char* str)
+{
+ SerdChunk out = { NULL, 0 };
+ SerdNode base = serd_node_new_uri_from_string(USTR(base_uri), NULL, NULL);
+ SordWorld* world = sord_world_new();
+ SordModel* model = sord_new(world, SORD_SPO, false);
+ SerdEnv* env = serd_env_new(&base);
+ SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
+
+ if (!serd_reader_read_string(reader, (const uint8_t*)str)) {
+ SordNode* s = sord_node_from_serd_node(world, env, subject, 0, 0);
+ SordNode* p = sord_node_from_serd_node(world, env, predicate, 0, 0);
+ SordQuad q = { s, p, 0, 0 };
+ SordIter* i = sord_find(model, q);
+ if (!sord_iter_end(i)) {
+ SordQuad result;
+ sord_iter_get(i, result);
+ lv2_atom_forge_set_sink(
+ &sratom->forge, sratom_forge_sink, sratom_forge_deref, &out);
+ sratom_read(sratom, &sratom->forge, world, model, result[SORD_OBJECT]);
+ } else {
+ fprintf(stderr, "Failed to find node\n");
+ }
+ sord_iter_free(i);
+ } else {
+ fprintf(stderr, "Failed to read Turtle\n");
+ }
+
+ serd_reader_free(reader);
+ serd_env_free(env);
+ sord_free(model);
+ sord_world_free(world);
+
+ return (LV2_Atom*)out.buf;
+}
diff --git a/src/serialisation/sratom/sratom.h b/src/serialisation/sratom/sratom.h
new file mode 100644
index 00000000..bd4a5721
--- /dev/null
+++ b/src/serialisation/sratom/sratom.h
@@ -0,0 +1,176 @@
+/*
+ Copyright 2012 David Robillard <http://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 sratom.h API for Sratom, an LV2 Atom RDF serialisation library.
+*/
+
+#ifndef SRATOM_SRATOM_H
+#define SRATOM_SRATOM_H
+
+#include <stdint.h>
+
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
+#include "serd/serd.h"
+#include "sord/sord.h"
+
+#ifdef SRATOM_SHARED
+# ifdef _WIN32
+# define SRATOM_LIB_IMPORT __declspec(dllimport)
+# define SRATOM_LIB_EXPORT __declspec(dllexport)
+# else
+# define SRATOM_LIB_IMPORT __attribute__((visibility("default")))
+# define SRATOM_LIB_EXPORT __attribute__((visibility("default")))
+# endif
+# ifdef SRATOM_INTERNAL
+# define SRATOM_API SRATOM_LIB_EXPORT
+# else
+# define SRATOM_API SRATOM_LIB_IMPORT
+# endif
+#else
+# define SRATOM_API
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ @defgroup sratom Sratom
+ An LV2 Atom RDF serialisation library.
+ @{
+*/
+
+/**
+ Atom serialiser.
+*/
+typedef struct SratomImpl Sratom;
+
+/**
+ Create a new Atom serialiser.
+*/
+SRATOM_API
+Sratom*
+sratom_new(LV2_URID_Map* map);
+
+/**
+ Free an Atom serialisation.
+*/
+SRATOM_API
+void
+sratom_free(Sratom* sratom);
+
+/**
+ Set the sink(s) where sratom will write its output.
+
+ This must be called before calling sratom_write(). If @p pretty_numbers is
+ true, numbers will be written as pretty Turtle literals, rather than string
+ literals with precise types. The cost of this is the types might get
+ fudged on a round-trip to RDF and back.
+*/
+SRATOM_API
+void
+sratom_set_sink(Sratom* sratom,
+ const char* base_uri,
+ SerdStatementSink sink,
+ SerdEndSink end_sink,
+ void* handle,
+ bool pretty_numbers);
+
+/**
+ Write an Atom to RDF.
+ The serialised atom is written to the sink set by sratom_set_sink().
+ @return 0 on success, or a non-zero error code otherwise.
+*/
+SRATOM_API
+int
+sratom_write(Sratom* sratom,
+ LV2_URID_Unmap* unmap,
+ uint32_t flags,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ uint32_t type,
+ uint32_t size,
+ const void* body);
+
+/**
+ Read an Atom from RDF.
+ The resulting atom will be written to @p forge.
+*/
+SRATOM_API
+void
+sratom_read(Sratom* sratom,
+ LV2_Atom_Forge* forge,
+ SordWorld* world,
+ SordModel* model,
+ const SordNode* node);
+
+/**
+ Serialise an Atom to a Turtle string.
+ The returned string must be free()'d by the caller.
+*/
+SRATOM_API
+char*
+sratom_to_turtle(Sratom* sratom,
+ LV2_URID_Unmap* unmap,
+ const char* base_uri,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ uint32_t type,
+ uint32_t size,
+ const void* body);
+
+/**
+ Read an Atom from a Turtle string.
+ The returned atom must be free()'d by the caller.
+*/
+SRATOM_API
+LV2_Atom*
+sratom_from_turtle(Sratom* sratom,
+ const char* base_uri,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ const char* str);
+
+/**
+ A convenient resizing sink for LV2_Atom_Forge.
+ The handle must point to an initialized SerdChunk.
+*/
+SRATOM_API
+LV2_Atom_Forge_Ref
+sratom_forge_sink(LV2_Atom_Forge_Sink_Handle handle,
+ const void* buf,
+ uint32_t size);
+
+/**
+ The corresponding deref function for sratom_forge_sink.
+*/
+SRATOM_API
+LV2_Atom*
+sratom_forge_deref(LV2_Atom_Forge_Sink_Handle handle,
+ LV2_Atom_Forge_Ref ref);
+
+/**
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SRATOM_SRATOM_H */
diff --git a/src/serialisation/wscript b/src/serialisation/wscript
index abc1dd46..58c56f60 100644
--- a/src/serialisation/wscript
+++ b/src/serialisation/wscript
@@ -2,10 +2,13 @@
from waflib.extras import autowaf as autowaf
def build(bld):
- obj = bld(features = 'cxx cxxshlib',
- source = 'Parser.cpp Serialiser.cpp serialisation.cpp',
+ obj = bld(features = 'c cshlib cxx cxxshlib',
+ source = ['Parser.cpp',
+ 'Serialiser.cpp',
+ 'serialisation.cpp',
+ 'sratom/sratom.c'],
export_includes = ['../..'],
- includes = ['../..'],
+ includes = ['.', '../..'],
name = 'libingen_serialisation',
target = 'ingen_serialisation',
install_path = '${LIBDIR}',