summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/conf.py.in12
-rw-r--r--include/sratom/sratom.h276
-rw-r--r--meson.build42
-rw-r--r--src/dumper.c546
-rw-r--r--src/loader.c519
-rw-r--r--src/sratom.c924
-rw-r--r--test/meson.build2
-rw-r--r--test/test_sratom.c190
8 files changed, 1350 insertions, 1161 deletions
diff --git a/doc/conf.py.in b/doc/conf.py.in
index c3600d2..41ec55f 100644
--- a/doc/conf.py.in
+++ b/doc/conf.py.in
@@ -18,16 +18,16 @@ _opaque = [
"LV2_Atom_Forge",
"LV2_Atom_Forge_Ref",
"LV2_Atom_Forge_Sink_Handle",
+ "LV2_URID",
"LV2_URID_Map",
"LV2_URID_Unmap",
- "SerdEndSink",
"SerdEnv",
+ "SerdModel",
"SerdNode",
- "SerdStatementSink",
- "SordModel",
- "SordNode",
- "SordWorld",
- "SratomImpl",
+ "SerdSink",
+ "SerdWorld",
+ "SratomDumperImpl",
+ "SratomLoaderImpl",
"uint32_t",
]
diff --git a/include/sratom/sratom.h b/include/sratom/sratom.h
index 077adff..68bff33 100644
--- a/include/sratom/sratom.h
+++ b/include/sratom/sratom.h
@@ -21,11 +21,10 @@
#include "lv2/atom/atom.h"
#include "lv2/atom/forge.h"
+#include "lv2/atom/util.h"
#include "lv2/urid/urid.h"
#include "serd/serd.h"
-#include "sord/sord.h"
-#include <stdbool.h>
#include <stdint.h>
#if defined(_WIN32) && !defined(SRATOM_STATIC) && defined(SRATOM_INTERNAL)
@@ -44,165 +43,240 @@ extern "C" {
/**
@defgroup sratom Sratom C API
- This is the public C API of Sratom.
+ This is the complete public C API of sratom.
@{
*/
-/// Atom serializer
-typedef struct SratomImpl Sratom;
+/// Free memory allocated in the sratom library
+SRATOM_API
+void
+sratom_free(void* ptr);
/**
- Mode for reading resources to LV2 Objects.
-
- This affects how resources (which are either blank nodes or have URIs) are
- read by sratom_read(), since they may be read as simple references (a URI or
- blank node ID) or a complete description (an atom "Object").
-
- Currently, blank nodes are always read as Objects, but support for reading
- blank node IDs may be added in the future.
+ @defgroup sratom_dumping Dumping Atoms
+ Writing atoms to a statement stream or a string.
+ @{
*/
+
+/// Flags to control how atoms are written as statements
typedef enum {
- /// Read blank nodes as Objects, and named resources as URIs
- SRATOM_OBJECT_MODE_BLANK,
+ /**
+ Write the main subject with a label.
+
+ If set, the main subject will be written using its blank node label,
+ instead of as an anonymous node.
+ */
+ SRATOM_NAMED_SUBJECT = 1u << 1u,
/**
- Read blank nodes and the main subject as Objects.
+ Write pretty numeric literals in Turtle or TriG.
- Any other named resources are read as URIs. The "main subject" is the
- subject parameter passed to sratom_read(); if this is a resource it will
- be read as an Object, but all other named resources encountered will be
- read as URIs.
+ If set, numbers may be written as pretty literals instead of string
+ literals with explicit data types. Note that enabling this means that
+ types will not survive a round trip.
*/
- SRATOM_OBJECT_MODE_BLANK_SUBJECT
-} SratomObjectMode;
+ SRATOM_PRETTY_NUMBERS = 1u << 2u,
-/// Create a new Atom serializer
-SRATOM_API
-Sratom*
-sratom_new(LV2_URID_Map* map);
+ /**
+ Write terse output without newlines.
-/// Free an Atom serializer
-SRATOM_API
-void
-sratom_free(Sratom* sratom);
+ If set, top level atoms will be written as a single long line.
+ */
+ SRATOM_TERSE = 1u << 3u,
+} SratomDumperFlag;
-/**
- Set the environment for reading or writing Turtle.
+/// Bitwise OR of SratomDumperFlag values
+typedef uint32_t SratomDumperFlags;
- This can be used to set namespace prefixes and a base URI for
- sratom_to_turtle() and sratom_from_turtle().
-*/
-SRATOM_API
-void
-sratom_set_env(Sratom* sratom, SerdEnv* env);
+/// Dumper that writes atoms to a statement sink
+typedef struct SratomDumperImpl SratomDumper;
/**
- Set the sink(s) where sratom will write its output.
+ Create a new atom dumper.
+
+ @param world Serd world.
+ @param map URID mapper, only used during construction.
+ @param unmap URID unmapper, used while dumping to write URI strings.
- This must be called before calling sratom_write().
+ @return A newly allocated object which must be freed with
+ sratom_dumper_free().
*/
SRATOM_API
+SratomDumper*
+sratom_dumper_new(SerdWorld* world, LV2_URID_Map* map, LV2_URID_Unmap* unmap);
+
+/// Free an atom dumper created with sratom_dumper_new()
+SRATOM_API
void
-sratom_set_sink(Sratom* sratom,
- const char* base_uri,
- SerdStatementSink sink,
- SerdEndSink end_sink,
- void* handle);
+sratom_dumper_free(SratomDumper* dumper);
/**
- Write pretty numeric literals.
-
- If `pretty_numbers` is true, numbers will be written as pretty Turtle
- literals, rather than string literals with precise types. The cost of this
- is that the types might get fudged on a round-trip to RDF and back.
+ Write an atom body to a statement sink.
+
+ This is the fundamental write function that writes an atom to a sink as a
+ series of statements. It can be used with any sink, such as a Turtle
+ writer, model inserter, or a custom sink provided by the application.
+
+ This function takes the `type`, `size`, and `body` separately to allow
+ writing atoms from data in memory that does not have an atom header. For
+ true atom pointers, the simpler sratom_dump_atom() can be used instead.
+
+ Since all statements must be triples, a subject and predicate can be
+ provided to serialise literals like `subject predicate "literal"`. If
+ `subject` and `predicate` are null, resources will be written as the
+ subject, but literals will be written as the only element of an anonymous
+ list. A subject and predicate should always be given for lossless
+ round-tripping. This corresponds to using atoms as property values.
+
+ @param dumper Dumper instance
+ @param env Environment defining the base URI and any prefixes
+ @param sink Sink which receives the serialised statements
+ @param subject Subject of first statement, or NULL
+ @param predicate Predicate of first statement, or NULL
+ @param type Type of the atom
+ @param size Size of the atom body in bytes
+ @param body Atom body
+ @param flags Option flags
+ @return Zero on success
*/
SRATOM_API
-void
-sratom_set_pretty_numbers(Sratom* sratom, bool pretty_numbers);
+int
+sratom_dump(SratomDumper* dumper,
+ const SerdEnv* env,
+ const SerdSink* sink,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ LV2_URID type,
+ uint32_t size,
+ const void* body,
+ SratomDumperFlags flags);
-/// Configure how resources will be read to form LV2 Objects
+/**
+ Write an atom to a statement sink
+
+ Convenience wrapper that takes a pointer to a complete atom, see the
+ sratom_dump() documentation for details.
+
+ @param dumper Dumper instance
+ @param env Environment defining the base URI and any prefixes
+ @param sink Sink which receives the serialised statements
+ @param subject Subject of first statement, or NULL
+ @param predicate Predicate of first statement, or NULL
+ @param atom Atom to serialise
+ @param flags Option flags
+ @return Zero on success
+*/
SRATOM_API
-void
-sratom_set_object_mode(Sratom* sratom, SratomObjectMode object_mode);
+int
+sratom_dump_atom(SratomDumper* dumper,
+ const SerdEnv* env,
+ const SerdSink* sink,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ const LV2_Atom* atom,
+ SratomDumperFlags flags);
/**
- Write an Atom to RDF.
+ Write an atom as a string.
- The serialized atom is written to the sink set by sratom_set_sink().
+ The returned string can be forged back into an atom using
+ sratom_from_string().
- @return 0 on success, or a non-zero error code otherwise.
+ @param dumper Dumper instance
+ @param env Environment for namespaces and relative URIs
+ @param atom Atom to serialise
+ @param flags Option flags
+ @return A string that must be freed using sratom_free(), or NULL on error.
*/
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);
+char*
+sratom_to_string(SratomDumper* dumper,
+ const SerdEnv* env,
+ const LV2_Atom* atom,
+ SratomDumperFlags flags);
/**
- Read an Atom from RDF.
+ @}
+ @defgroup sratom_loading Loading Atoms
+ @{
+*/
+
+/// Atom loader that reads atoms from a document
+typedef struct SratomLoaderImpl SratomLoader;
+
+/**
+ Create a new loader for forging atoms from a document.
- The resulting atom will be written to `forge`.
+ @param world RDF world
+ @param map URID mapper
+ @return A new object that must be freed with sratom_loader_free()
*/
SRATOM_API
+SratomLoader*
+sratom_loader_new(SerdWorld* world, LV2_URID_Map* map);
+
+/// Free an atom loader created with sratom_loader_new()
+SRATOM_API
void
-sratom_read(Sratom* sratom,
- LV2_Atom_Forge* forge,
- SordWorld* world,
- SordModel* model,
- const SordNode* node);
+sratom_loader_free(SratomLoader* loader);
/**
- Serialize an Atom to a Turtle string.
+ Load an atom from a model by forging the binary representation.
- The returned string must be free()'d by the caller.
+ This reads `node` in `model` as an atom, constructing the result with
+ `forge`.
+
+ @param loader Loader set up with the appropriate world and URID map.
+
+ @param base_uri Base URI for making relative URI references. This is
+ typically used with file URIs to construct atoms with relative paths. Any
+ URI within the given base URI will be written to `forge` as a relative URI
+ reference. For example, with base URI <file:///my/data/>, the URI
+ <file:///my/data/dir/file.txt> will be written to the forge as the path
+ "dir/file.txt".
+
+ @param forge Atom forge where the result is written.
+
+ @param model Model that contains a description of the atom.
+
+ @param node Node for the atom. This can be a simple literal node for
+ primitive atoms, or the subject resource for more complex structures like
+ objects and vectors.
+
+ @return Zero on success.
*/
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);
+int
+sratom_load(SratomLoader* loader,
+ const SerdNode* base_uri,
+ LV2_Atom_Forge* forge,
+ const SerdModel* model,
+ const SerdNode* node);
/**
- Read an Atom from a Turtle string.
+ Load 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);
+sratom_from_string(SratomLoader* loader, SerdEnv* env, const char* str);
/**
- A convenient resizing sink for LV2_Atom_Forge.
+ Load an Atom from a model.
- The handle must point to an initialized SerdChunk.
+ The returned atom must be free()'d by the caller.
*/
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);
+sratom_from_model(SratomLoader* loader,
+ const SerdNode* base_uri,
+ const SerdModel* model,
+ const SerdNode* subject);
/**
@}
+ @}
*/
#ifdef __cplusplus
diff --git a/meson.build b/meson.build
index d18853b..c85b966 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
project('sratom', ['c'],
- version: '0.6.9',
+ version: '1.0.0',
license: 'ISC',
meson_version: '>= 0.49.2',
default_options: [
@@ -7,7 +7,7 @@ project('sratom', ['c'],
'buildtype=release',
'c_std=c99',
'default_library=shared',
- 'warning_level=2',
+ 'warning_level=3',
])
sratom_src_root = meson.current_source_dir()
@@ -19,7 +19,12 @@ versioned_name = 'sratom' + version_suffix
pkg = import('pkgconfig')
cc = meson.get_compiler('c')
+if add_languages('cpp', required: get_option('bindings_cpp'))
+ cpp = meson.get_compiler('cpp')
+endif
+
# Set ultra strict warnings for developers, if requested
+c_warnings = []
if get_option('strict')
subdir('meson')
@@ -55,10 +60,17 @@ if get_option('strict')
]
endif
- add_project_arguments(cc.get_supported_arguments(c_warnings),
- language: ['c'])
+else
+ if cc.get_id() == 'clang'
+ c_warnings += [
+ '-Wno-nullability-extension',
+ ]
+ endif
endif
+add_project_arguments(cc.get_supported_arguments(c_warnings),
+ language: ['c'])
+
# Add special arguments for MSVC
if cc.get_id() == 'msvc'
msvc_args = [
@@ -76,7 +88,8 @@ c_headers = ['include/sratom/sratom.h']
c_header_files = files(c_headers)
sources = [
- 'src/sratom.c',
+ 'src/dumper.c',
+ 'src/loader.c',
]
# System libraries
@@ -85,17 +98,13 @@ m_dep = cc.find_library('m', required: false)
# Dependencies
lv2_dep = dependency('lv2',
- version: '>= 1.18.3',
+ version: '>= 1.18.2',
fallback: ['lv2', 'lv2_dep'])
-serd_dep = dependency('serd-0',
- version: '>= 0.30.9',
+serd_dep = dependency('serd-1',
+ version: '>= 1.0.0',
fallback: ['serd', 'serd_dep'])
-sord_dep = dependency('sord-0',
- version: '>= 0.16.9',
- fallback: ['sord', 'sord_dep'])
-
# Determine library type and the flags needed to build it
if get_option('default_library') == 'both'
if host_machine.system() == 'windows'
@@ -122,14 +131,14 @@ libsratom = build_target(
version: meson.project_version(),
include_directories: include_directories(['include']),
c_args: library_args,
- dependencies: [m_dep, lv2_dep, serd_dep, sord_dep],
+ dependencies: [m_dep, lv2_dep, serd_dep],
gnu_symbol_visibility: 'hidden',
install: true,
target_type: library_type)
sratom_dep = declare_dependency(
include_directories: include_directories(['include']),
- dependencies: [m_dep, lv2_dep, serd_dep, sord_dep],
+ dependencies: [m_dep, lv2_dep, serd_dep],
link_with: libsratom)
pkg.generate(
@@ -151,6 +160,11 @@ if get_option('tests')
subdir('test')
endif
+# C++ bindings
+if is_variable('cpp')
+ subdir('bindings/cpp')
+endif
+
if meson.version().version_compare('>=0.53.0')
summary('Tests', get_option('tests'), bool_yn: true)
diff --git a/src/dumper.c b/src/dumper.c
new file mode 100644
index 0000000..b2bb852
--- /dev/null
+++ b/src/dumper.c
@@ -0,0 +1,546 @@
+/*
+ Copyright 2012-2021 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.
+*/
+
+#include "sratom/sratom.h"
+
+#include "lv2/atom/atom.h"
+#include "lv2/atom/forge.h"
+#include "lv2/atom/util.h"
+#include "lv2/midi/midi.h"
+#include "lv2/urid/urid.h"
+#include "serd/serd.h"
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define NS_XSD "http://www.w3.org/2001/XMLSchema#"
+
+#define STREAM_WARN(msg) \
+ serd_world_logf( \
+ writer->world, "sratom", SERD_LOG_LEVEL_WARNING, 0, NULL, msg);
+
+#define STREAM_ERRORF(msg, ...) \
+ serd_world_logf( \
+ writer->world, "sratom", SERD_LOG_LEVEL_ERROR, 0, NULL, msg, __VA_ARGS__);
+
+struct SratomDumperImpl {
+ LV2_URID_Unmap* unmap;
+ LV2_Atom_Forge forge;
+ SerdWorld* world;
+ LV2_URID atom_Event;
+ LV2_URID atom_beatTime;
+ LV2_URID midi_MidiEvent;
+ struct {
+ const SerdNode* atom_Path;
+ const SerdNode* atom_beatTime;
+ const SerdNode* atom_childType;
+ const SerdNode* atom_frameTime;
+ const SerdNode* midi_MidiEvent;
+ const SerdNode* rdf_first;
+ const SerdNode* rdf_nil;
+ const SerdNode* rdf_rest;
+ const SerdNode* rdf_type;
+ const SerdNode* rdf_value;
+ const SerdNode* xsd_base64Binary;
+ const SerdNode* xsd_boolean;
+ const SerdNode* xsd_decimal;
+ const SerdNode* xsd_double;
+ const SerdNode* xsd_float;
+ const SerdNode* xsd_int;
+ const SerdNode* xsd_integer;
+ const SerdNode* xsd_long;
+ } nodes;
+};
+
+typedef struct {
+ SratomDumper* writer;
+ const SerdEnv* env;
+ const SerdSink* sink;
+ const SratomDumperFlags flags;
+ SerdStatementFlags sflags;
+ LV2_URID seq_unit;
+} StreamContext;
+
+void
+sratom_free(void* ptr)
+{
+ /* The only sratom memory the user frees comes from sratom_from_model(),
+ which is allocated by serd_buffer_sink. */
+ serd_free(ptr);
+}
+
+SratomDumper*
+sratom_dumper_new(SerdWorld* const world,
+ LV2_URID_Map* const map,
+ LV2_URID_Unmap* const unmap)
+{
+ SratomDumper* writer = (SratomDumper*)calloc(1, sizeof(SratomDumper));
+ if (!writer) {
+ return NULL;
+ }
+
+ writer->world = world;
+ writer->unmap = unmap;
+ writer->atom_Event = map->map(map->handle, LV2_ATOM__Event);
+ writer->atom_beatTime = map->map(map->handle, LV2_ATOM__beatTime);
+ writer->midi_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent);
+ lv2_atom_forge_init(&writer->forge, map);
+
+#define MANAGE_URI(uri) \
+ serd_nodes_manage(serd_world_nodes(world), \
+ serd_new_uri(SERD_STATIC_STRING(uri)))
+
+ writer->nodes.atom_Path = MANAGE_URI(LV2_ATOM__Path);
+ writer->nodes.atom_beatTime = MANAGE_URI(LV2_ATOM__beatTime);
+ writer->nodes.atom_childType = MANAGE_URI(LV2_ATOM__childType);
+ writer->nodes.atom_frameTime = MANAGE_URI(LV2_ATOM__frameTime);
+ writer->nodes.midi_MidiEvent = MANAGE_URI(LV2_MIDI__MidiEvent);
+ writer->nodes.rdf_first = MANAGE_URI(NS_RDF "first");
+ writer->nodes.rdf_nil = MANAGE_URI(NS_RDF "nil");
+ writer->nodes.rdf_rest = MANAGE_URI(NS_RDF "rest");
+ writer->nodes.rdf_type = MANAGE_URI(NS_RDF "type");
+ writer->nodes.rdf_value = MANAGE_URI(NS_RDF "value");
+ writer->nodes.xsd_base64Binary = MANAGE_URI(NS_XSD "base64Binary");
+ writer->nodes.xsd_boolean = MANAGE_URI(NS_XSD "boolean");
+ writer->nodes.xsd_decimal = MANAGE_URI(NS_XSD "decimal");
+ writer->nodes.xsd_double = MANAGE_URI(NS_XSD "double");
+ writer->nodes.xsd_float = MANAGE_URI(NS_XSD "float");
+ writer->nodes.xsd_int = MANAGE_URI(NS_XSD "int");
+ writer->nodes.xsd_integer = MANAGE_URI(NS_XSD "integer");
+ writer->nodes.xsd_long = MANAGE_URI(NS_XSD "long");
+
+#undef MANAGE_URI
+
+ return writer;
+}
+
+void
+sratom_dumper_free(SratomDumper* writer)
+{
+ free(writer);
+}
+
+static int
+write_atom(StreamContext* ctx,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ LV2_URID type,
+ uint32_t size,
+ const void* body);
+
+static void
+list_append(StreamContext* ctx,
+ SerdNode** s,
+ const SerdNode** p,
+ uint32_t size,
+ uint32_t type,
+ const void* body)
+{
+ // Generate a list node
+ SerdNode* node = serd_node_copy(serd_world_get_blank(ctx->writer->world));
+ serd_sink_write(ctx->sink, ctx->sflags, *s, *p, node, NULL);
+
+ // _:node rdf:first value
+ ctx->sflags = 0;
+ *p = ctx->writer->nodes.rdf_first;
+ write_atom(ctx, node, *p, type, size, body);
+
+ // Set subject to node and predicate to rdf:rest for next time
+ serd_node_free(*s);
+ *s = node;
+ *p = ctx->writer->nodes.rdf_rest;
+}
+
+static void
+list_end(StreamContext* ctx, const SerdNode* s, const SerdNode* p)
+{
+ // _:node rdf:rest rdf:nil
+ serd_sink_write(
+ ctx->sink, ctx->sflags, s, p, ctx->writer->nodes.rdf_nil, NULL);
+}
+
+static void
+start_object(StreamContext* ctx,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ const SerdNode* node,
+ const char* type)
+{
+ if (subject && predicate) {
+ serd_sink_write(
+ ctx->sink, ctx->sflags | SERD_ANON_O, subject, predicate, node, NULL);
+ } else {
+ ctx->sflags |= SERD_EMPTY_S;
+ }
+
+ if (type) {
+ SerdNode* o = serd_new_uri(SERD_MEASURE_STRING(type));
+
+ serd_sink_write(
+ ctx->sink, ctx->sflags, node, ctx->writer->nodes.rdf_type, o, NULL);
+
+ serd_node_free(o);
+ }
+}
+
+static void
+end_object(StreamContext* ctx,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ const SerdNode* node)
+{
+ if (subject && predicate) {
+ serd_sink_write_end(ctx->sink, node);
+ }
+}
+
+static bool
+path_is_absolute(const char* path)
+{
+ return (path[0] == '/' || (isalpha(path[0]) && path[1] == ':' &&
+ (path[2] == '/' || path[2] == '\\')));
+}
+
+static const SerdNode*
+number_type(StreamContext* ctx, const SerdNode* type)
+{
+ SratomDumper* const writer = ctx->writer;
+ const bool pretty = (ctx->flags & SRATOM_PRETTY_NUMBERS);
+ if (pretty) {
+ if (type == writer->nodes.xsd_int || type == writer->nodes.xsd_long) {
+ return writer->nodes.xsd_integer;
+ } else if (type == writer->nodes.xsd_float ||
+ type == writer->nodes.xsd_double) {
+ return writer->nodes.xsd_decimal;
+ }
+ }
+ return type;
+}
+
+static bool
+is_primitive_type(StreamContext* ctx, const LV2_URID type)
+{
+ SratomDumper* const writer = ctx->writer;
+ return (!type || type == writer->forge.Bool || type == writer->forge.Double ||
+ type == writer->forge.Float || type == writer->forge.Int ||
+ type == writer->forge.Literal || type == writer->forge.Long ||
+ type == writer->forge.Path || type == writer->forge.String ||
+ type == writer->forge.URI || type == writer->forge.URID);
+}
+
+static int
+write_atom(StreamContext* const ctx,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ const LV2_URID type,
+ const uint32_t size,
+ const void* const body)
+{
+ SratomDumper* writer = ctx->writer;
+ LV2_URID_Unmap* unmap = writer->unmap;
+ const SerdSink* sink = ctx->sink;
+ const SerdEnv* env = ctx->env;
+ const char* const type_uri = unmap->unmap(unmap->handle, type);
+ SerdNode* object = NULL;
+ if (type == 0 && size == 0) {
+ object = serd_node_copy(writer->nodes.rdf_nil);
+ } else if (type == writer->forge.String) {
+ object = serd_new_string(SERD_MEASURE_STRING((const char*)body));
+ } else if (type == writer->forge.Chunk) {
+ object = serd_new_blob(body, size, NULL);
+ } else if (type == writer->forge.Literal) {
+ const LV2_Atom_Literal_Body* lit = (const LV2_Atom_Literal_Body*)body;
+ const char* str = (const char*)(lit + 1);
+ if (lit->datatype) {
+ const SerdStringView datatype_uri =
+ SERD_MEASURE_STRING(unmap->unmap(unmap->handle, lit->datatype));
+ object = serd_new_typed_literal(SERD_MEASURE_STRING(str), datatype_uri);
+ } 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)) {
+ object = serd_new_plain_literal(SERD_MEASURE_STRING(str),
+ SERD_MEASURE_STRING(lang + prefix_len));
+ } else {
+ STREAM_ERRORF("Unknown language URID %u\n", lit->lang);
+ }
+ }
+ } else if (type == writer->forge.URID) {
+ const uint32_t urid = *(const uint32_t*)body;
+ const char* str = unmap->unmap(unmap->handle, urid);
+
+ object = serd_new_uri(SERD_MEASURE_STRING(str));
+ } else if (type == writer->forge.Path) {
+ const SerdStringView str = SERD_MEASURE_STRING((const char*)body);
+ if (path_is_absolute(str.buf)) {
+ object = serd_new_file_uri(str, SERD_EMPTY_STRING());
+ } else {
+ const SerdNode* base_uri = serd_env_base_uri(env);
+ if (!base_uri || strncmp(serd_node_string(base_uri), "file://", 7)) {
+ STREAM_WARN("Relative path but base is not a file URI.\n");
+ STREAM_WARN("Writing ambiguous atom:Path literal.\n");
+ object = serd_new_typed_literal(
+ str, serd_node_string_view(writer->nodes.atom_Path));
+ } else {
+ SerdNode* const rel = serd_new_file_uri(str, SERD_EMPTY_STRING());
+
+ object = serd_new_parsed_uri(serd_resolve_uri(
+ serd_node_uri_view(rel), serd_node_uri_view(base_uri)));
+
+ serd_node_free(rel);
+ }
+ }
+ } else if (type == writer->forge.URI) {
+ object = serd_new_uri(SERD_MEASURE_STRING((const char*)body));
+ } else if (type == writer->forge.Int) {
+ object = serd_new_integer(*(const int32_t*)body,
+ number_type(ctx, writer->nodes.xsd_int));
+ } else if (type == writer->forge.Long) {
+ object = serd_new_integer(*(const int64_t*)body,
+ number_type(ctx, writer->nodes.xsd_long));
+ } else if (type == writer->forge.Float) {
+ object = serd_new_decimal(*(const float*)body,
+ number_type(ctx, writer->nodes.xsd_float));
+ } else if (type == writer->forge.Double) {
+ object = serd_new_decimal(*(const double*)body,
+ number_type(ctx, writer->nodes.xsd_double));
+ } else if (type == writer->forge.Bool) {
+ object = serd_new_boolean(*(const int32_t*)body);
+ } else if (type == writer->midi_MidiEvent) {
+ const size_t len = 2 * size;
+ char* const str = (char*)calloc(len + 1, 1);
+ for (uint32_t i = 0; i < size; ++i) {
+ snprintf(str + (2 * i),
+ size * 2 + 1,
+ "%02X",
+ (unsigned)*((const uint8_t*)body + i));
+ }
+
+ object = serd_new_typed_literal(
+ SERD_STRING_VIEW(str, len),
+ serd_node_string_view(writer->nodes.midi_MidiEvent));
+
+ free(str);
+ } else if (type == writer->atom_Event) {
+ const LV2_Atom_Event* ev = (const LV2_Atom_Event*)body;
+ const SerdNode* id = serd_world_get_blank(writer->world);
+ start_object(ctx, subject, predicate, id, NULL);
+ SerdNode* time = NULL;
+ const SerdNode* p = NULL;
+ if (ctx->seq_unit == writer->atom_beatTime) {
+ p = writer->nodes.atom_beatTime;
+ time = serd_new_double(ev->time.beats);
+ } else {
+ p = writer->nodes.atom_frameTime;
+ time = serd_new_integer(ev->time.frames,
+ number_type(ctx, writer->nodes.xsd_long));
+ }
+ serd_sink_write(sink, 0, id, p, time, NULL);
+ serd_node_free(time);
+
+ p = writer->nodes.rdf_value;
+ write_atom(
+ ctx, id, p, ev->body.type, ev->body.size, LV2_ATOM_BODY_CONST(&ev->body));
+ end_object(ctx, subject, predicate, id);
+ } else if (type == writer->forge.Tuple) {
+ SerdNode* id = serd_node_copy(serd_world_get_blank(writer->world));
+ start_object(ctx, subject, predicate, id, type_uri);
+ const SerdNode* p = writer->nodes.rdf_value;
+ ctx->sflags |= SERD_LIST_O | SERD_TERSE_O;
+ LV2_ATOM_TUPLE_BODY_FOREACH (body, size, i) {
+ if (!is_primitive_type(ctx, i->type)) {
+ ctx->sflags &= ~SERD_TERSE_O;
+ }
+ }
+ LV2_ATOM_TUPLE_BODY_FOREACH (body, size, i) {
+ list_append(ctx, &id, &p, i->size, i->type, LV2_ATOM_BODY(i));
+ }
+ list_end(ctx, id, p);
+ end_object(ctx, subject, predicate, id);
+ serd_node_free(id);
+ } else if (type == writer->forge.Vector) {
+ const LV2_Atom_Vector_Body* vec = (const LV2_Atom_Vector_Body*)body;
+ SerdNode* id = serd_node_copy(serd_world_get_blank(writer->world));
+ start_object(ctx, subject, predicate, id, type_uri);
+ const SerdNode* p = writer->nodes.atom_childType;
+ SerdNode* child_type = serd_new_uri(
+ SERD_MEASURE_STRING(unmap->unmap(unmap->handle, vec->child_type)));
+ serd_sink_write(sink, ctx->sflags, id, p, child_type, NULL);
+ p = writer->nodes.rdf_value;
+ serd_node_free(child_type);
+ ctx->sflags |= SERD_LIST_O;
+ if (is_primitive_type(ctx, vec->child_type)) {
+ ctx->sflags |= SERD_TERSE_O;
+ }
+ for (const char* i = (const char*)(vec + 1); i < (const char*)vec + size;
+ i += vec->child_size) {
+ list_append(ctx, &id, &p, vec->child_size, vec->child_type, i);
+ }
+ list_end(ctx, id, p);
+ end_object(ctx, subject, predicate, id);
+ serd_node_free(id);
+ } else if (lv2_atom_forge_is_object_type(&writer->forge, type)) {
+ const LV2_Atom_Object_Body* obj = (const LV2_Atom_Object_Body*)body;
+ const char* otype = unmap->unmap(unmap->handle, obj->otype);
+
+ SerdNode* id = NULL;
+ if (lv2_atom_forge_is_blank(&writer->forge, type, obj)) {
+ id = serd_node_copy(serd_world_get_blank(writer->world));
+ start_object(ctx, subject, predicate, id, otype);
+ } else {
+ id =
+ serd_new_uri(SERD_MEASURE_STRING(unmap->unmap(unmap->handle, obj->id)));
+ ctx->sflags = 0;
+ start_object(ctx, NULL, NULL, id, otype);
+ }
+ LV2_ATOM_OBJECT_BODY_FOREACH (obj, size, prop) {
+ const char* const key = unmap->unmap(unmap->handle, prop->key);
+ SerdNode* pred = serd_new_uri(SERD_MEASURE_STRING(key));
+ write_atom(ctx,
+ id,
+ pred,
+ prop->value.type,
+ prop->value.size,
+ LV2_ATOM_BODY(&prop->value));
+ serd_node_free(pred);
+ }
+ end_object(ctx, subject, predicate, id);
+ serd_node_free(id);
+ } else if (type == writer->forge.Sequence) {
+ const LV2_Atom_Sequence_Body* seq = (const LV2_Atom_Sequence_Body*)body;
+ SerdNode* id = serd_node_copy(serd_world_get_blank(writer->world));
+ start_object(ctx, subject, predicate, id, type_uri);
+ const SerdNode* p = writer->nodes.rdf_value;
+ ctx->sflags |= SERD_LIST_O;
+ LV2_ATOM_SEQUENCE_BODY_FOREACH (seq, size, ev) {
+ ctx->seq_unit = seq->unit;
+ list_append(ctx,
+ &id,
+ &p,
+ sizeof(LV2_Atom_Event) + ev->body.size,
+ writer->atom_Event,
+ ev);
+ }
+ list_end(ctx, id, p);
+ end_object(ctx, subject, predicate, id);
+ serd_node_free(id);
+ } else {
+ const SerdNode* id = serd_world_get_blank(writer->world);
+ start_object(ctx, subject, predicate, id, type_uri);
+ const SerdNode* p = writer->nodes.rdf_value;
+ SerdNode* o = serd_new_blob(body, size, NULL);
+ serd_sink_write(sink, ctx->sflags, id, p, o, NULL);
+ end_object(ctx, subject, predicate, id);
+ serd_node_free(o);
+ }
+
+ if (object) {
+ if (!subject && !predicate) {
+ subject = serd_world_get_blank(writer->world);
+ predicate = writer->nodes.rdf_first;
+ serd_sink_write(sink,
+ ctx->sflags | SERD_LIST_S | SERD_TERSE_S,
+ subject,
+ predicate,
+ object,
+ NULL);
+ serd_sink_write(sink,
+ SERD_TERSE_S,
+ subject,
+ writer->nodes.rdf_rest,
+ writer->nodes.rdf_nil,
+ NULL);
+ } else {
+ serd_sink_write(sink, ctx->sflags, subject, predicate, object, NULL);
+ }
+ }
+
+ serd_node_free(object);
+
+ return 0;
+}
+
+int
+sratom_dump(SratomDumper* const writer,
+ const SerdEnv* const env,
+ const SerdSink* const sink,
+ const SerdNode* const subject,
+ const SerdNode* const predicate,
+ const LV2_URID type,
+ const uint32_t size,
+ const void* const body,
+ const SratomDumperFlags flags)
+{
+ StreamContext ctx = {writer,
+ env,
+ sink,
+ flags,
+ (flags & SRATOM_NAMED_SUBJECT) ? 0 : SERD_EMPTY_S,
+ 0};
+
+ return write_atom(&ctx, subject, predicate, type, size, body);
+}
+
+int
+sratom_dump_atom(SratomDumper* const writer,
+ const SerdEnv* const env,
+ const SerdSink* const sink,
+ const SerdNode* const subject,
+ const SerdNode* const predicate,
+ const LV2_Atom* const atom,
+ const SratomDumperFlags flags)
+{
+ return sratom_dump(writer,
+ env,
+ sink,
+ subject,
+ predicate,
+ atom->type,
+ atom->size,
+ atom + 1,
+ flags);
+}
+
+char*
+sratom_to_string(SratomDumper* const writer,
+ const SerdEnv* const env,
+ const LV2_Atom* const atom,
+ const SratomDumperFlags flags)
+{
+ SerdBuffer buffer = {NULL, 0};
+ SerdByteSink* const out = serd_byte_sink_new_buffer(&buffer);
+
+ SerdWriter* const ttl_writer =
+ serd_writer_new(writer->world,
+ SERD_TURTLE,
+ flags & SRATOM_TERSE ? SERD_WRITE_TERSE : 0,
+ env,
+ out);
+
+ const SerdSink* const sink = serd_writer_sink(ttl_writer);
+
+ sratom_dump_atom(writer, env, sink, NULL, NULL, atom, flags);
+ serd_writer_finish(ttl_writer);
+ serd_writer_free(ttl_writer);
+
+ return serd_buffer_sink_finish(&buffer);
+}
diff --git a/src/loader.c b/src/loader.c
new file mode 100644
index 0000000..46e7c51
--- /dev/null
+++ b/src/loader.c
@@ -0,0 +1,519 @@
+/*
+ Copyright 2012-2021 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.
+*/
+
+#include "sratom/sratom.h"
+
+#include "lv2/atom/atom.h"
+#include "lv2/atom/forge.h"
+#include "lv2/midi/midi.h"
+#include "lv2/urid/urid.h"
+#include "serd/serd.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define NS_XSD "http://www.w3.org/2001/XMLSchema#"
+
+#define LOAD_ERROR(msg) \
+ serd_world_logf(loader->world, "sratom", SERD_LOG_LEVEL_ERROR, 0, NULL, msg);
+
+typedef enum { MODE_SUBJECT, MODE_BODY, MODE_SEQUENCE } ReadMode;
+
+struct SratomLoaderImpl {
+ LV2_URID_Map* map;
+ LV2_Atom_Forge forge;
+ SerdWorld* world;
+ LV2_URID atom_frameTime;
+ LV2_URID atom_beatTime;
+ LV2_URID midi_MidiEvent;
+ struct {
+ const SerdNode* atom_beatTime;
+ const SerdNode* atom_childType;
+ const SerdNode* atom_frameTime;
+ const SerdNode* rdf_first;
+ const SerdNode* rdf_rest;
+ const SerdNode* rdf_type;
+ const SerdNode* rdf_value;
+ const SerdNode* xsd_base64Binary;
+ } nodes;
+};
+
+typedef struct {
+ SratomLoader* loader;
+ const SerdNode* base_uri;
+ LV2_URID seq_unit;
+} LoadContext;
+
+static void
+read_node(LoadContext* ctx,
+ LV2_Atom_Forge* forge,
+ const SerdModel* model,
+ const SerdNode* node,
+ ReadMode mode);
+
+SratomLoader*
+sratom_loader_new(SerdWorld* world, LV2_URID_Map* map)
+{
+ SratomLoader* sratom = (SratomLoader*)calloc(1, sizeof(SratomLoader));
+ if (!sratom) {
+ return NULL;
+ }
+
+ sratom->world = world;
+ sratom->map = map;
+ sratom->atom_frameTime = map->map(map->handle, LV2_ATOM__frameTime);
+ sratom->atom_beatTime = map->map(map->handle, LV2_ATOM__beatTime);
+ sratom->midi_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent);
+ lv2_atom_forge_init(&sratom->forge, map);
+
+#define MANAGE_URI(uri) \
+ serd_nodes_manage(serd_world_nodes(world), \
+ serd_new_uri(SERD_STATIC_STRING(uri)))
+
+ sratom->nodes.atom_beatTime = MANAGE_URI(LV2_ATOM__beatTime);
+ sratom->nodes.atom_childType = MANAGE_URI(LV2_ATOM__childType);
+ sratom->nodes.atom_frameTime = MANAGE_URI(LV2_ATOM__frameTime);
+ sratom->nodes.rdf_first = MANAGE_URI(NS_RDF "first");
+ sratom->nodes.rdf_rest = MANAGE_URI(NS_RDF "rest");
+ sratom->nodes.rdf_type = MANAGE_URI(NS_RDF "type");
+ sratom->nodes.rdf_value = MANAGE_URI(NS_RDF "value");
+ sratom->nodes.xsd_base64Binary = MANAGE_URI(NS_XSD "base64Binary");
+
+#undef INTERN_URI
+
+ return sratom;
+}
+
+void
+sratom_loader_free(SratomLoader* loader)
+{
+ free(loader);
+}
+
+static void
+read_list_value(LoadContext* ctx,
+ LV2_Atom_Forge* forge,
+ const SerdModel* model,
+ const SerdNode* node,
+ ReadMode mode)
+{
+ const SerdNode* fst =
+ serd_model_get(model, node, ctx->loader->nodes.rdf_first, NULL, NULL);
+ const SerdNode* rst =
+ serd_model_get(model, node, ctx->loader->nodes.rdf_rest, NULL, NULL);
+ if (fst && rst) {
+ read_node(ctx, forge, model, fst, mode);
+ read_list_value(ctx, forge, model, rst, mode);
+ }
+}
+
+static void
+read_resource(LoadContext* ctx,
+ LV2_Atom_Forge* forge,
+ const SerdModel* model,
+ const SerdNode* node,
+ LV2_URID otype)
+{
+ LV2_URID_Map* map = ctx->loader->map;
+ SerdRange* r = serd_model_range(model, node, NULL, NULL, NULL);
+ for (; !serd_range_empty(r); serd_range_next(r)) {
+ const SerdStatement* match = serd_range_front(r);
+ const SerdNode* p = serd_statement_predicate(match);
+ const SerdNode* o = serd_statement_object(match);
+ if (p) {
+ const char* p_uri = serd_node_string(p);
+ uint32_t p_urid = map->map(map->handle, p_uri);
+ if (!(serd_node_equals(p, ctx->loader->nodes.rdf_type) &&
+ serd_node_type(o) == SERD_URI &&
+ map->map(map->handle, serd_node_string(o)) == otype)) {
+ lv2_atom_forge_key(forge, p_urid);
+ read_node(ctx, forge, model, o, MODE_BODY);
+ }
+ }
+ }
+ serd_range_free(r);
+}
+
+static uint32_t
+atom_size(SratomLoader* loader, uint32_t type_urid)
+{
+ if (type_urid == loader->forge.Int) {
+ return sizeof(int32_t);
+ } else if (type_urid == loader->forge.Long) {
+ return sizeof(int64_t);
+ } else if (type_urid == loader->forge.Float) {
+ return sizeof(float);
+ } else if (type_urid == loader->forge.Double) {
+ return sizeof(double);
+ } else if (type_urid == loader->forge.Bool) {
+ return sizeof(int32_t);
+ } else if (type_urid == loader->forge.URID) {
+ return sizeof(uint32_t);
+ }
+ return 0;
+}
+
+static void
+read_uri(LoadContext* ctx, LV2_Atom_Forge* forge, const SerdNode* node)
+{
+ SratomLoader* const loader = ctx->loader;
+ LV2_URID_Map* const map = loader->map;
+ const char* const str = serd_node_string(node);
+
+ if (!strcmp(str, NS_RDF "nil")) {
+ lv2_atom_forge_atom(forge, 0, 0);
+ } else if (!strncmp(str, "file://", 7)) {
+ const SerdURIView base_uri = serd_node_uri_view(ctx->base_uri);
+ const SerdURIView uri = serd_parse_uri(str);
+ if (serd_uri_is_within(uri, base_uri)) {
+ SerdNode* const rel = serd_new_parsed_uri(serd_relative_uri(
+ serd_parse_uri(str), serd_node_uri_view(ctx->base_uri)));
+
+ char* path = serd_parse_file_uri(serd_node_string(rel), NULL);
+ lv2_atom_forge_path(forge, path, strlen(path));
+ serd_free(path);
+ serd_node_free(rel);
+ } else {
+ char* const path = serd_parse_file_uri(str, NULL);
+ lv2_atom_forge_path(forge, path, strlen(path));
+ serd_free(path);
+ }
+ } else {
+ lv2_atom_forge_urid(forge, map->map(map->handle, str));
+ }
+}
+
+static void
+read_typed_literal(SratomLoader* const loader,
+ LV2_Atom_Forge* const forge,
+ const SerdNode* const node,
+ const SerdNode* const datatype)
+{
+ const char* const str = serd_node_string(node);
+ const size_t len = serd_node_length(node);
+ const char* const type_uri = serd_node_string(datatype);
+
+ if (!strcmp(type_uri, NS_XSD "int") || !strcmp(type_uri, NS_XSD "integer")) {
+ lv2_atom_forge_int(forge, strtol(str, NULL, 10));
+ } else if (!strcmp(type_uri, NS_XSD "long")) {
+ lv2_atom_forge_long(forge, strtol(str, NULL, 10));
+ } else if (!strcmp(type_uri, NS_XSD "float") ||
+ !strcmp(type_uri, NS_XSD "decimal")) {
+ lv2_atom_forge_float(forge, serd_get_float(node));
+ } else if (!strcmp(type_uri, NS_XSD "double")) {
+ lv2_atom_forge_double(forge, serd_get_double(node));
+ } else if (!strcmp(type_uri, NS_XSD "boolean")) {
+ lv2_atom_forge_bool(forge, serd_get_boolean(node));
+ } else if (!strcmp(type_uri, NS_XSD "base64Binary")) {
+ size_t size = 0;
+ void* body = serd_base64_decode(str, len, &size);
+ lv2_atom_forge_atom(forge, size, forge->Chunk);
+ lv2_atom_forge_write(forge, body, size);
+ free(body);
+ } else if (!strcmp(type_uri, LV2_ATOM__Path)) {
+ lv2_atom_forge_path(forge, str, len);
+ } else if (!strcmp(type_uri, LV2_MIDI__MidiEvent)) {
+ lv2_atom_forge_atom(forge, len / 2, loader->midi_MidiEvent);
+ for (const char* s = str; s < str + len; s += 2) {
+ unsigned num;
+ sscanf(s, "%2X", &num);
+ 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, loader->map->map(loader->map->handle, type_uri), 0);
+ }
+}
+
+static void
+read_plain_literal(SratomLoader* const loader,
+ LV2_Atom_Forge* const forge,
+ const SerdNode* const node,
+ const SerdNode* const language)
+{
+ const char* const str = serd_node_string(node);
+ const size_t len = serd_node_length(node);
+ const char* lang_str = serd_node_string(language);
+ const char* prefix = "http://lexvo.org/id/iso639-3/";
+ const size_t lang_len = strlen(prefix) + strlen(lang_str);
+ char* const lang_uri = (char*)calloc(lang_len + 1, 1);
+
+ snprintf(lang_uri, lang_len + 1, "%s%s", prefix, lang_str);
+
+ lv2_atom_forge_literal(
+ forge, str, len, 0, loader->map->map(loader->map->handle, lang_uri));
+
+ free(lang_uri);
+}
+
+static void
+read_literal(SratomLoader* loader, LV2_Atom_Forge* forge, const SerdNode* node)
+{
+ assert(serd_node_type(node) == SERD_LITERAL);
+
+ const SerdNode* const datatype = serd_node_datatype(node);
+ const SerdNode* const language = serd_node_language(node);
+ if (datatype) {
+ read_typed_literal(loader, forge, node, datatype);
+ } else if (language) {
+ read_plain_literal(loader, forge, node, language);
+ } else {
+ lv2_atom_forge_string(
+ forge, serd_node_string(node), serd_node_length(node));
+ }
+}
+
+static void
+read_object(LoadContext* ctx,
+ LV2_Atom_Forge* forge,
+ const SerdModel* model,
+ const SerdNode* node,
+ ReadMode mode)
+{
+ SratomLoader* const loader = ctx->loader;
+ LV2_URID_Map* const map = loader->map;
+ const char* const str = serd_node_string(node);
+
+ const SerdNode* const type =
+ serd_model_get(model, node, loader->nodes.rdf_type, NULL, NULL);
+
+ const SerdNode* const value =
+ serd_model_get(model, node, loader->nodes.rdf_value, NULL, NULL);
+
+ const char* type_uri = NULL;
+ uint32_t type_urid = 0;
+ if (type) {
+ type_uri = serd_node_string(type);
+ type_urid = map->map(map->handle, type_uri);
+ }
+
+ LV2_Atom_Forge_Frame frame = {0, 0};
+ if (mode == MODE_SEQUENCE) {
+ LV2_URID seq_unit = 0u;
+ const SerdNode* time = NULL;
+
+ if ((time = serd_model_get(
+ model, node, loader->nodes.atom_frameTime, NULL, NULL))) {
+ lv2_atom_forge_frame_time(forge, time ? serd_get_integer(time) : 0);
+ seq_unit = loader->atom_frameTime;
+
+ } else if ((time = serd_model_get(
+ model, node, loader->nodes.atom_beatTime, NULL, NULL))) {
+ lv2_atom_forge_beat_time(forge, serd_get_double(time));
+ seq_unit = loader->atom_beatTime;
+ }
+
+ read_node(ctx, forge, model, value, MODE_BODY);
+ ctx->seq_unit = seq_unit;
+
+ } else if (type_urid == loader->forge.Tuple) {
+ lv2_atom_forge_tuple(forge, &frame);
+ read_list_value(ctx, forge, model, value, MODE_BODY);
+
+ } else if (type_urid == loader->forge.Sequence) {
+ const LV2_Atom_Forge_Ref ref =
+ lv2_atom_forge_sequence_head(forge, &frame, 0);
+ ctx->seq_unit = 0;
+ read_list_value(ctx, forge, model, value, MODE_SEQUENCE);
+
+ LV2_Atom_Sequence* seq =
+ (LV2_Atom_Sequence*)lv2_atom_forge_deref(forge, ref);
+ seq->body.unit =
+ ((ctx->seq_unit == loader->atom_frameTime) ? 0 : ctx->seq_unit);
+
+ } else if (type_urid == loader->forge.Vector) {
+ const SerdNode* child_type_node =
+ serd_model_get(model, node, loader->nodes.atom_childType, NULL, NULL);
+ uint32_t child_type =
+ map->map(map->handle, serd_node_string(child_type_node));
+ uint32_t child_size = atom_size(loader, child_type);
+ if (child_size > 0) {
+ LV2_Atom_Forge_Ref ref =
+ lv2_atom_forge_vector_head(forge, &frame, child_size, child_type);
+ read_list_value(ctx, forge, model, value, MODE_BODY);
+ lv2_atom_forge_pop(forge, &frame);
+ frame.ref = 0;
+ lv2_atom_forge_pad(forge, lv2_atom_forge_deref(forge, ref)->size);
+ }
+
+ } else if (value && serd_node_equals(serd_node_datatype(value),
+ loader->nodes.xsd_base64Binary)) {
+ const char* vstr = serd_node_string(value);
+ const size_t vlen = serd_node_length(value);
+ 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 if (serd_node_type(node) == SERD_URI) {
+ const LV2_URID urid = map->map(map->handle, str);
+ if (serd_model_count(model, node, NULL, NULL, NULL)) {
+ lv2_atom_forge_object(forge, &frame, urid, type_urid);
+ read_resource(ctx, forge, model, node, type_urid);
+ } else {
+ read_uri(ctx, forge, node);
+ }
+
+ } else {
+ lv2_atom_forge_object(forge, &frame, 0, type_urid);
+ read_resource(ctx, forge, model, node, type_urid);
+ }
+
+ if (frame.ref) {
+ lv2_atom_forge_pop(forge, &frame);
+ }
+}
+
+static void
+read_node(LoadContext* ctx,
+ LV2_Atom_Forge* forge,
+ const SerdModel* model,
+ const SerdNode* node,
+ ReadMode mode)
+{
+ if (serd_node_type(node) == SERD_LITERAL) {
+ read_literal(ctx->loader, forge, node);
+ } else if (serd_node_type(node) == SERD_URI && mode != MODE_SUBJECT) {
+ read_uri(ctx, forge, node);
+ } else {
+ read_object(ctx, forge, model, node, mode);
+ }
+}
+
+int
+sratom_load(SratomLoader* loader,
+ const SerdNode* base_uri,
+ LV2_Atom_Forge* forge,
+ const SerdModel* model,
+ const SerdNode* node)
+{
+ LoadContext ctx = {loader, base_uri, 0};
+ read_node(&ctx, forge, model, node, MODE_SUBJECT);
+ return 0;
+}
+
+static SerdModel*
+model_from_string(SratomLoader* loader, SerdEnv* env, const char* str)
+{
+ SerdModel* const model = serd_model_new(loader->world, SERD_INDEX_SPO);
+ SerdSink* const inserter = serd_inserter_new(model, env, NULL);
+
+ SerdNode* const name = serd_new_string(SERD_STATIC_STRING("string"));
+ SerdByteSource* const source = serd_byte_source_new_string(str, name);
+ SerdReader* const reader = serd_reader_new(
+ loader->world, SERD_TURTLE, SERD_READ_LAX, env, inserter, 4096);
+
+ serd_reader_start(reader, source);
+
+ const SerdStatus st = serd_reader_read_document(reader);
+
+ serd_reader_free(reader);
+ serd_byte_source_free(source);
+ serd_node_free(name);
+ serd_sink_free(inserter);
+
+ if (st) {
+ serd_model_free(model);
+ return NULL;
+ }
+
+ return model;
+}
+
+LV2_Atom*
+sratom_from_string(SratomLoader* const loader,
+ SerdEnv* const env,
+ const char* const str)
+{
+ SerdModel* model = model_from_string(loader, env, str);
+ if (!model || serd_model_empty(model)) {
+ LOAD_ERROR("Failed to read string into model");
+ return NULL;
+ }
+
+ const SerdNode* node = NULL;
+ SerdIter* begin = serd_model_begin(model);
+ const SerdStatement* s = serd_iter_get(begin);
+ if (serd_model_size(model) == 2 &&
+ serd_node_type(serd_statement_subject(s)) == SERD_BLANK &&
+ serd_node_equals(serd_statement_predicate(s), loader->nodes.rdf_first)) {
+ // Special single-element list syntax for literals
+ node = serd_statement_object(s);
+ } else {
+ node = serd_statement_subject(s);
+ }
+
+ if (!node) {
+ LOAD_ERROR("Failed to find a node to parse");
+ return NULL;
+ }
+
+ LV2_Atom* atom =
+ sratom_from_model(loader, serd_env_base_uri(env), model, node);
+
+ serd_iter_free(begin);
+ serd_model_free(model);
+ return atom;
+}
+
+static LV2_Atom_Forge_Ref
+sratom_forge_sink(LV2_Atom_Forge_Sink_Handle handle,
+ const void* buf,
+ uint32_t size)
+{
+ SerdBuffer* chunk = (SerdBuffer*)handle;
+ const LV2_Atom_Forge_Ref ref = chunk->len + 1;
+ serd_buffer_sink(buf, 1, size, chunk);
+ return ref;
+}
+
+static LV2_Atom*
+sratom_forge_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
+{
+ SerdBuffer* chunk = (SerdBuffer*)handle;
+ return (LV2_Atom*)((char*)chunk->buf + ref - 1);
+}
+
+LV2_Atom*
+sratom_from_model(SratomLoader* loader,
+ const SerdNode* base_uri,
+ const SerdModel* model,
+ const SerdNode* subject)
+{
+ if (!subject) {
+ return NULL;
+ }
+
+ SerdBuffer out = {NULL, 0};
+ lv2_atom_forge_set_sink(
+ &loader->forge, sratom_forge_sink, sratom_forge_deref, &out);
+
+ int st = sratom_load(loader, base_uri, &loader->forge, model, subject);
+ if (st) {
+ sratom_free(out.buf);
+ out.buf = NULL;
+ }
+
+ return (LV2_Atom*)out.buf;
+}
diff --git a/src/sratom.c b/src/sratom.c
deleted file mode 100644
index 692257d..0000000
--- a/src/sratom.c
+++ /dev/null
@@ -1,924 +0,0 @@
-/*
- Copyright 2012-2021 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.
-*/
-
-#include "sratom/sratom.h"
-
-#include "lv2/atom/atom.h"
-#include "lv2/atom/forge.h"
-#include "lv2/atom/util.h"
-#include "lv2/midi/midi.h"
-#include "lv2/urid/urid.h"
-
-#include <assert.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#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))
-
-static const SerdStyle style =
- (SerdStyle)(SERD_STYLE_ABBREVIATED | SERD_STYLE_RESOLVED | SERD_STYLE_CURIED);
-
-typedef enum { MODE_SUBJECT, MODE_BODY, MODE_SEQUENCE } ReadMode;
-
-struct SratomImpl {
- LV2_URID_Map* map;
- LV2_Atom_Forge forge;
- SerdEnv* env;
- SerdNode base_uri;
- SerdURI base;
- SerdStatementSink write_statement;
- SerdEndSink end_anon;
- void* handle;
- LV2_URID atom_Event;
- LV2_URID atom_frameTime;
- LV2_URID atom_beatTime;
- LV2_URID midi_MidiEvent;
- unsigned next_id;
- SratomObjectMode object_mode;
- uint32_t seq_unit;
- struct {
- SordNode* atom_childType;
- SordNode* atom_frameTime;
- SordNode* atom_beatTime;
- SordNode* rdf_first;
- SordNode* rdf_rest;
- SordNode* rdf_type;
- SordNode* rdf_value;
- SordNode* xsd_base64Binary;
- } nodes;
-
- bool pretty_numbers;
-};
-
-static void
-read_node(Sratom* sratom,
- LV2_Atom_Forge* forge,
- SordWorld* world,
- SordModel* model,
- const SordNode* node,
- ReadMode mode);
-
-Sratom*
-sratom_new(LV2_URID_Map* map)
-{
- Sratom* sratom = (Sratom*)calloc(1, sizeof(Sratom));
- if (sratom) {
- sratom->map = map;
- sratom->atom_Event = map->map(map->handle, LV2_ATOM__Event);
- sratom->atom_frameTime = map->map(map->handle, LV2_ATOM__frameTime);
- sratom->atom_beatTime = map->map(map->handle, LV2_ATOM__beatTime);
- sratom->midi_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent);
- sratom->object_mode = SRATOM_OBJECT_MODE_BLANK;
- lv2_atom_forge_init(&sratom->forge, map);
- }
- return sratom;
-}
-
-void
-sratom_free(Sratom* sratom)
-{
- if (sratom) {
- serd_node_free(&sratom->base_uri);
- free(sratom);
- }
-}
-
-void
-sratom_set_env(Sratom* sratom, SerdEnv* env)
-{
- sratom->env = env;
-}
-
-void
-sratom_set_sink(Sratom* sratom,
- const char* base_uri,
- SerdStatementSink sink,
- SerdEndSink end_sink,
- void* handle)
-{
- if (base_uri) {
- serd_node_free(&sratom->base_uri);
- sratom->base_uri =
- serd_node_new_uri_from_string(USTR(base_uri), NULL, NULL);
- serd_uri_parse(sratom->base_uri.buf, &sratom->base);
- }
- sratom->write_statement = sink;
- sratom->end_anon = end_sink;
- sratom->handle = handle;
-}
-
-void
-sratom_set_pretty_numbers(Sratom* sratom, bool pretty_numbers)
-{
- sratom->pretty_numbers = pretty_numbers;
-}
-
-void
-sratom_set_object_mode(Sratom* sratom, SratomObjectMode object_mode)
-{
- sratom->object_mode = object_mode;
-}
-
-static void
-gensym(SerdNode* out, char c, unsigned num)
-{
- out->n_bytes = out->n_chars = snprintf((char*)out->buf, 12, "%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,
- const 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,
- const 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)
-{
- if (subject && predicate) {
- sratom->write_statement(sratom->handle,
- *flags | SERD_ANON_O_BEGIN,
- NULL,
- subject,
- predicate,
- node,
- NULL,
- NULL);
- // Start abbreviating object properties
- *flags |= SERD_ANON_CONT;
-
- // Object is in a list, stop list abbreviating if necessary
- *flags &= ~SERD_LIST_CONT;
- }
-
- 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, *flags, 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] == '\\')));
-}
-
-static SerdNode
-number_type(const Sratom* sratom, const uint8_t* type)
-{
- if (sratom->pretty_numbers &&
- (!strcmp((const char*)type, (const char*)NS_XSD "int") ||
- !strcmp((const char*)type, (const char*)NS_XSD "long"))) {
- return serd_node_from_string(SERD_URI, NS_XSD "integer");
- }
-
- if (sratom->pretty_numbers &&
- (!strcmp((const char*)type, (const char*)NS_XSD "float") ||
- !strcmp((const char*)type, (const char*)NS_XSD "double"))) {
- return serd_node_from_string(SERD_URI, NS_XSD "decimal");
- }
-
- return serd_node_from_string(SERD_URI, type);
-}
-
-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.Chunk) {
- datatype = serd_node_from_string(SERD_URI, NS_XSD "base64Binary");
- object = serd_node_new_blob(body, size, true);
- new_node = true;
- } else if (type_urid == sratom->forge.Literal) {
- const LV2_Atom_Literal_Body* lit = (const 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 URID %u\n", lit->lang);
- }
- }
- } else if (type_urid == sratom->forge.URID) {
- const uint32_t urid = *(const uint32_t*)body;
- const uint8_t* str = USTR(unmap->unmap(unmap->handle, urid));
-
- 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, true);
- } else {
- 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 {
- new_node = true;
- SerdNode rel = serd_node_new_file_uri(str, NULL, NULL, true);
- object = serd_node_new_uri_from_node(&rel, &sratom->base, NULL);
- serd_node_free(&rel);
- }
- }
- } else if (type_urid == sratom->forge.URI) {
- object = serd_node_from_string(SERD_URI, USTR(body));
- } else if (type_urid == sratom->forge.Int) {
- new_node = true;
- object = serd_node_new_integer(*(const int32_t*)body);
- datatype = number_type(sratom, NS_XSD "int");
- } else if (type_urid == sratom->forge.Long) {
- new_node = true;
- object = serd_node_new_integer(*(const int64_t*)body);
- datatype = number_type(sratom, NS_XSD "long");
- } else if (type_urid == sratom->forge.Float) {
- new_node = true;
- object = serd_node_new_decimal(*(const float*)body, 8);
- datatype = number_type(sratom, NS_XSD "float");
- } else if (type_urid == sratom->forge.Double) {
- new_node = true;
- object = serd_node_new_decimal(*(const double*)body, 16);
- datatype = number_type(sratom, 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, USTR(LV2_MIDI__MidiEvent));
-
- uint8_t* str = (uint8_t*)calloc(size * 2 + 1, 1);
- for (uint32_t i = 0; i < size; ++i) {
- snprintf((char*)str + (2 * i),
- size * 2 + 1,
- "%02X",
- (unsigned)*((const 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);
- SerdNode time;
- SerdNode p;
- if (sratom->seq_unit == sratom->atom_beatTime) {
- time = serd_node_new_decimal(ev->time.beats, 16);
- p = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__beatTime));
- datatype = number_type(sratom, NS_XSD "double");
- } else {
- time = serd_node_new_integer(ev->time.frames);
- p = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__frameTime));
- datatype = number_type(sratom, NS_XSD "long");
- }
- 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_ATOM_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 (const char* i = (const char*)(vec + 1); i < (const 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 (lv2_atom_forge_is_object_type(&sratom->forge, type_urid)) {
- const LV2_Atom_Object_Body* obj = (const LV2_Atom_Object_Body*)body;
- const char* otype = unmap->unmap(unmap->handle, obj->otype);
-
- if (lv2_atom_forge_is_blank(&sratom->forge, type_urid, obj)) {
- gensym(&id, 'b', sratom->next_id++);
- start_object(sratom, &flags, subject, predicate, &id, otype);
- } else {
- id = serd_node_from_string(
- SERD_URI, (const uint8_t*)unmap->unmap(unmap->handle, obj->id));
- flags = 0;
- start_object(sratom, &flags, NULL, NULL, &id, otype);
- }
- LV2_ATOM_OBJECT_BODY_FOREACH (obj, size, prop) {
- 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,
- &id,
- &pred,
- prop->value.type,
- prop->value.size,
- LV2_ATOM_BODY(&prop->value));
- }
- if (sratom->end_anon && (flags & SERD_ANON_CONT)) {
- 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_ATOM_SEQUENCE_BODY_FOREACH (seq, size, ev) {
- sratom->seq_unit = seq->unit;
- 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 && subject && predicate) {
- 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 && subject && predicate) {
- sratom->end_anon(sratom->handle, &id);
- }
- serd_node_free(&o);
- }
-
- if (object.buf) {
- SerdNode def_s = serd_node_from_string(SERD_BLANK, USTR("atom"));
- SerdNode def_p = serd_node_from_string(SERD_URI, USTR(NS_RDF "value"));
-
- if (!subject) {
- subject = &def_s;
- }
-
- if (!predicate) {
- predicate = &def_p;
- }
-
- sratom->write_statement(sratom->handle,
- flags,
- NULL,
- subject,
- predicate,
- &object,
- &datatype,
- &language);
- }
-
- if (new_node) {
- serd_node_free(&object);
- }
-
- return 0;
-}
-
-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), &sratom->base, &buri);
- SerdEnv* env = sratom->env ? sratom->env : serd_env_new(NULL);
- SerdChunk str = {NULL, 0};
- SerdWriter* writer =
- serd_writer_new(SERD_TURTLE, style, env, &buri, serd_chunk_sink, &str);
-
- serd_env_set_base_uri(env, &base);
- sratom_set_sink(sratom,
- base_uri,
- (SerdStatementSink)serd_writer_write_statement,
- (SerdEndSink)serd_writer_end_anon,
- writer);
- sratom_write(
- sratom, unmap, SERD_EMPTY_S, subject, predicate, type, size, body);
- serd_writer_finish(writer);
-
- serd_writer_free(writer);
- if (!sratom->env) {
- serd_env_free(env);
- }
-
- serd_node_free(&base);
- return (char*)serd_chunk_sink_finish(&str);
-}
-
-static void
-read_list_value(Sratom* sratom,
- LV2_Atom_Forge* forge,
- SordWorld* world,
- SordModel* model,
- const SordNode* node,
- ReadMode mode)
-{
- SordNode* fst = sord_get(model, node, sratom->nodes.rdf_first, NULL, NULL);
- SordNode* rst = sord_get(model, node, sratom->nodes.rdf_rest, NULL, NULL);
- if (fst && rst) {
- read_node(sratom, forge, world, model, fst, mode);
- read_list_value(sratom, forge, world, model, rst, mode);
- }
-
- sord_node_free(world, rst);
- sord_node_free(world, fst);
-}
-
-static void
-read_resource(Sratom* sratom,
- LV2_Atom_Forge* forge,
- SordWorld* world,
- SordModel* model,
- const SordNode* node,
- LV2_URID otype)
-{
- LV2_URID_Map* map = sratom->map;
- SordQuad q = {node, NULL, NULL, NULL};
- SordIter* i = sord_find(model, q);
- SordQuad match;
- for (; !sord_iter_end(i); sord_iter_next(i)) {
- sord_iter_get(i, match);
- const SordNode* p = match[SORD_PREDICATE];
- const SordNode* o = match[SORD_OBJECT];
- 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) &&
- sord_node_get_type(o) == SORD_URI &&
- map->map(map->handle, (const char*)sord_node_get_string(o)) ==
- otype)) {
- lv2_atom_forge_key(forge, p_urid);
- read_node(sratom, forge, world, model, o, MODE_BODY);
- }
- }
- sord_iter_free(i);
-}
-
-static uint32_t
-atom_size(Sratom* sratom, uint32_t type_urid)
-{
- if (type_urid == sratom->forge.Int || type_urid == sratom->forge.Bool) {
- return sizeof(int32_t);
- }
-
- if (type_urid == sratom->forge.Long) {
- return sizeof(int64_t);
- }
-
- if (type_urid == sratom->forge.Float) {
- return sizeof(float);
- }
-
- if (type_urid == sratom->forge.Double) {
- return sizeof(double);
- }
-
- if (type_urid == sratom->forge.URID) {
- return sizeof(uint32_t);
- }
-
- return 0;
-}
-
-static void
-read_literal(Sratom* sratom, LV2_Atom_Forge* forge, const SordNode* node)
-{
- assert(sord_node_get_type(node) == SORD_LITERAL);
-
- size_t len = 0;
- const char* str = (const char*)sord_node_get_string_counted(node, &len);
- 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, (const char*)NS_XSD "int") ||
- !strcmp(type_uri, (const char*)NS_XSD "integer")) {
- lv2_atom_forge_int(forge, strtol(str, NULL, 10));
- } else if (!strcmp(type_uri, (const char*)NS_XSD "long")) {
- lv2_atom_forge_long(forge, strtol(str, NULL, 10));
- } else if (!strcmp(type_uri, (const char*)NS_XSD "float") ||
- !strcmp(type_uri, (const char*)NS_XSD "decimal")) {
- lv2_atom_forge_float(forge, (float)serd_strtod(str, NULL));
- } else if (!strcmp(type_uri, (const char*)NS_XSD "double")) {
- lv2_atom_forge_double(forge, serd_strtod(str, NULL));
- } else if (!strcmp(type_uri, (const char*)NS_XSD "boolean")) {
- lv2_atom_forge_bool(forge, !strcmp(str, "true"));
- } else if (!strcmp(type_uri, (const char*)NS_XSD "base64Binary")) {
- size_t size = 0;
- void* body = serd_base64_decode(USTR(str), len, &size);
- lv2_atom_forge_atom(forge, size, forge->Chunk);
- lv2_atom_forge_write(forge, body, size);
- free(body);
- } else if (!strcmp(type_uri, LV2_ATOM__Path)) {
- lv2_atom_forge_path(forge, str, len);
- } else if (!strcmp(type_uri, LV2_MIDI__MidiEvent)) {
- lv2_atom_forge_atom(forge, len / 2, sratom->midi_MidiEvent);
- for (const char* s = str; s < str + len; s += 2) {
- unsigned num = 0u;
- sscanf(s, "%2X", &num);
- 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) {
- static const char* const prefix = "http://lexvo.org/id/iso639-3/";
- const size_t prefix_len = strlen(prefix);
- const size_t language_len = strlen(language);
- const size_t lang_uri_len = prefix_len + language_len;
- char* lang_uri = (char*)calloc(lang_uri_len + 1, 1);
-
- memcpy(lang_uri, prefix, prefix_len + 1);
- memcpy(lang_uri + prefix_len, language, language_len + 1);
-
- 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);
- }
-}
-
-static void
-read_object(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);
-
- SordNode* type = sord_get(model, node, sratom->nodes.rdf_type, NULL, NULL);
- SordNode* value = sord_get(model, node, sratom->nodes.rdf_value, NULL, NULL);
-
- 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) {
- SordNode* time =
- sord_get(model, node, sratom->nodes.atom_beatTime, NULL, NULL);
- uint32_t seq_unit = 0u;
- if (time) {
- const char* time_str = (const char*)sord_node_get_string(time);
- lv2_atom_forge_beat_time(forge, serd_strtod(time_str, NULL));
- seq_unit = sratom->atom_beatTime;
- } else {
- time = sord_get(model, node, sratom->nodes.atom_frameTime, NULL, NULL);
- const char* time_str =
- time ? (const char*)sord_node_get_string(time) : "";
- lv2_atom_forge_frame_time(forge, serd_strtod(time_str, NULL));
- seq_unit = sratom->atom_frameTime;
- }
- read_node(sratom, forge, world, model, value, MODE_BODY);
- sord_node_free(world, time);
- sratom->seq_unit = seq_unit;
- } else if (type_urid == sratom->forge.Tuple) {
- lv2_atom_forge_tuple(forge, &frame);
- read_list_value(sratom, forge, world, model, value, MODE_BODY);
- } else if (type_urid == sratom->forge.Sequence) {
- const LV2_Atom_Forge_Ref ref =
- lv2_atom_forge_sequence_head(forge, &frame, 0);
- sratom->seq_unit = 0;
- read_list_value(sratom, forge, world, model, value, MODE_SEQUENCE);
-
- LV2_Atom_Sequence* seq =
- (LV2_Atom_Sequence*)lv2_atom_forge_deref(forge, ref);
- seq->body.unit =
- (sratom->seq_unit == sratom->atom_frameTime) ? 0 : sratom->seq_unit;
- } else if (type_urid == sratom->forge.Vector) {
- SordNode* child_type_node =
- sord_get(model, node, sratom->nodes.atom_childType, NULL, NULL);
- 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_Ref ref =
- lv2_atom_forge_vector_head(forge, &frame, child_size, child_type);
- read_list_value(sratom, forge, world, model, value, MODE_BODY);
- lv2_atom_forge_pop(forge, &frame);
- frame.ref = 0;
- lv2_atom_forge_pad(forge, lv2_atom_forge_deref(forge, ref)->size);
- }
- sord_node_free(world, child_type_node);
- } 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 if (sord_node_get_type(node) == SORD_URI) {
- lv2_atom_forge_object(forge, &frame, map->map(map->handle, str), type_urid);
- read_resource(sratom, forge, world, model, node, type_urid);
- } else {
- lv2_atom_forge_object(forge, &frame, 0, type_urid);
- read_resource(sratom, forge, world, model, node, type_urid);
- }
-
- if (frame.ref) {
- lv2_atom_forge_pop(forge, &frame);
- }
- sord_node_free(world, value);
- sord_node_free(world, type);
-}
-
-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) {
- read_literal(sratom, forge, node);
- } else if (sord_node_get_type(node) == SORD_URI &&
- !(sratom->object_mode == SRATOM_OBJECT_MODE_BLANK_SUBJECT &&
- mode == MODE_SUBJECT)) {
- if (!strcmp(str, (const char*)NS_RDF "nil")) {
- lv2_atom_forge_atom(forge, 0, 0);
- } else if (!strncmp(str, "file://", 7)) {
- SerdURI uri;
- serd_uri_parse((const uint8_t*)str, &uri);
-
- SerdNode rel =
- serd_node_new_relative_uri(&uri, &sratom->base, NULL, NULL);
- uint8_t* path = serd_file_uri_parse(rel.buf, NULL);
- if (path) {
- lv2_atom_forge_path(
- forge, (const char*)path, strlen((const char*)path));
- serd_free(path);
- } else {
- // FIXME: Report errors (required API change)
- lv2_atom_forge_atom(forge, 0, 0);
- }
- serd_node_free(&rel);
- } else {
- lv2_atom_forge_urid(forge, map->map(map->handle, str));
- }
- } else {
- read_object(sratom, forge, world, model, node, mode);
- }
-}
-
-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.atom_beatTime = sord_new_uri(world, USTR(LV2_ATOM__beatTime));
- 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_SUBJECT);
-
- 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_beatTime);
- sord_node_free(world, sratom->nodes.atom_childType);
- memset(&sratom->nodes, 0, sizeof(sratom->nodes));
-}
-
-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;
-}
-
-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);
-}
-
-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), &sratom->base, NULL);
- SordWorld* world = sord_world_new();
- SordModel* model = sord_new(world, SORD_SPO, false);
- SerdEnv* env = sratom->env ? sratom->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);
- lv2_atom_forge_set_sink(
- &sratom->forge, sratom_forge_sink, sratom_forge_deref, &out);
- if (subject && predicate) {
- SordNode* p = sord_node_from_serd_node(world, env, predicate, 0, 0);
- SordNode* o = sord_get(model, s, p, NULL, NULL);
- if (o) {
- sratom_read(sratom, &sratom->forge, world, model, o);
- sord_node_free(world, o);
- } else {
- fprintf(stderr, "Failed to find node\n");
- }
- } else {
- sratom_read(sratom, &sratom->forge, world, model, s);
- }
- } else {
- fprintf(stderr, "Failed to read Turtle\n");
- }
-
- serd_reader_free(reader);
- if (!sratom->env) {
- serd_env_free(env);
- }
-
- sord_free(model);
- sord_world_free(world);
- serd_node_free(&base);
-
- return (LV2_Atom*)out.buf;
-}
diff --git a/test/meson.build b/test/meson.build
index fe37bd3..afe704a 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -10,7 +10,7 @@ foreach unit : unit_tests
'test_@0@.c'.format(unit),
c_args: prog_args,
include_directories: include_directories(['../src']),
- dependencies: [sratom_dep, serd_dep, sord_dep]),
+ dependencies: [sratom_dep, serd_dep]),
suite: 'unit')
endforeach
diff --git a/test/test_sratom.c b/test/test_sratom.c
index 95545fc..12daea3 100644
--- a/test/test_sratom.c
+++ b/test/test_sratom.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2016 David Robillard <d@drobilla.net>
+ Copyright 2012-2016 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
@@ -14,35 +14,28 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "sratom/sratom.h"
+
#include "lv2/atom/atom.h"
#include "lv2/atom/forge.h"
-#include "lv2/atom/util.h"
#include "lv2/midi/midi.h"
#include "lv2/urid/urid.h"
#include "serd/serd.h"
-#include "sratom/sratom.h"
-#include <stdarg.h>
+#include <assert.h>
#include <stdbool.h>
-#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#define NS_ATOM "http://lv2plug.in/ns/ext/atom#"
#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-
-#define USTR(s) ((const uint8_t*)(s))
-
-#if defined(__GNUC__)
-# define SRATOM_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
-#else
-# define SRATOM_LOG_FUNC(fmt, arg1)
-#endif
+#define NS_XSD "http://www.w3.org/2001/XMLSchema#"
/// Simple O(n) URI map
typedef struct {
- char** uris;
- size_t n_uris;
+ char** uris;
+ uint32_t n_uris;
} Uris;
static char*
@@ -59,7 +52,7 @@ urid_map(LV2_URID_Map_Handle handle, const char* uri)
{
Uris* const uris = (Uris*)handle;
- for (size_t i = 0; i < uris->n_uris; ++i) {
+ for (uint32_t i = 0; i < uris->n_uris; ++i) {
if (!strcmp(uris->uris[i], uri)) {
return i + 1;
}
@@ -82,20 +75,44 @@ urid_unmap(LV2_URID_Unmap_Handle handle, LV2_URID urid)
return NULL;
}
-SRATOM_LOG_FUNC(1, 2)
static int
-test_fail(const char* fmt, ...)
+check_round_trip(Uris* uris,
+ SerdEnv* env,
+ SratomDumper* dumper,
+ SratomLoader* loader,
+ const LV2_Atom* obj,
+ const SratomDumperFlags flags)
{
- va_list args;
- va_start(args, fmt);
- fprintf(stderr, "error: ");
- vfprintf(stderr, fmt, args);
- va_end(args);
- return 1;
+ (void)uris; // FIXME
+
+ // Serialise atom and print string
+ char* const outstr = sratom_to_string(dumper, env, obj, flags);
+ assert(outstr);
+
+ printf("%s\n", outstr);
+
+ // Parse serialised string back into an atom
+ LV2_Atom* const parsed = sratom_from_string(loader, env, outstr);
+ assert(parsed);
+
+ if (!(flags & SRATOM_PRETTY_NUMBERS)) {
+ // Check that round tripped atom is identical to original
+ assert(obj->type == parsed->type);
+ assert(lv2_atom_equals(obj, parsed));
+
+ /* char* const instr = sratom_to_string(writer, env, parsed, flags); */
+ /* if ( */
+ /* printf("# Turtle => Atom\n\n%s", instr); */
+ }
+
+ sratom_free(parsed);
+ sratom_free(outstr);
+
+ return 0;
}
static int
-test(SerdEnv* env, bool top_level, bool pretty_numbers)
+test(SerdEnv* env, const char* name, const SratomDumperFlags flags)
{
Uris uris = {NULL, 0};
LV2_URID_Map map = {&uris, urid_map};
@@ -103,12 +120,9 @@ test(SerdEnv* env, bool top_level, bool pretty_numbers)
LV2_Atom_Forge forge;
lv2_atom_forge_init(&forge, &map);
- Sratom* sratom = sratom_new(&map);
- sratom_set_env(sratom, env);
- sratom_set_pretty_numbers(sratom, pretty_numbers);
- sratom_set_object_mode(sratom,
- top_level ? SRATOM_OBJECT_MODE_BLANK_SUBJECT
- : SRATOM_OBJECT_MODE_BLANK);
+ SerdWorld* world = serd_world_new();
+ SratomDumper* dumper = sratom_dumper_new(world, &map, &unmap);
+ SratomLoader* loader = sratom_loader_new(world, &map);
LV2_URID eg_Object = urid_map(&uris, "http://example.org/Object");
LV2_URID eg_one = urid_map(&uris, "http://example.org/a-one");
@@ -141,15 +155,8 @@ test(SerdEnv* env, bool top_level, bool pretty_numbers)
uint8_t buf[1024];
lv2_atom_forge_set_buffer(&forge, buf, sizeof(buf));
- const char* obj_uri = "http://example.org/obj";
- LV2_URID obj_id = urid_map(&uris, obj_uri);
LV2_Atom_Forge_Frame obj_frame;
- if (top_level) {
- lv2_atom_forge_object(&forge, &obj_frame, obj_id, eg_Object);
- } else {
- lv2_atom_forge_object(&forge, &obj_frame, 0, eg_Object);
- }
-
+ lv2_atom_forge_object(&forge, &obj_frame, 0, eg_Object);
LV2_Atom* obj = lv2_atom_forge_deref(&forge, obj_frame.ref);
// eg_one = (Int32)1
@@ -182,8 +189,8 @@ test(SerdEnv* env, bool top_level, bool pretty_numbers)
lv2_atom_forge_key(&forge, eg_path);
lv2_atom_forge_path(&forge, pstr, pstr_len);
- // eg_winpath = (Path)"C:\Stupid\File System"
- const char* wpstr = "C:/Stupid/File System";
+ // eg_winpath = (Path)"C:/Weird/File System"
+ const char* wpstr = "C:/Weird/File System";
const size_t wpstr_len = strlen(wpstr);
lv2_atom_forge_key(&forge, eg_winpath);
lv2_atom_forge_path(&forge, wpstr, wpstr_len);
@@ -333,96 +340,49 @@ test(SerdEnv* env, bool top_level, bool pretty_numbers)
lv2_atom_forge_pop(&forge, &obj_frame);
- const char* base_uri = "file:///tmp/base/";
-
- SerdNode s = serd_node_from_string(SERD_URI, USTR("http://example.org/obj"));
- SerdNode p = serd_node_from_string(SERD_URI, USTR(NS_RDF "value"));
-
- SerdNode* subj = top_level ? NULL : &s;
- SerdNode* pred = top_level ? NULL : &p;
-
- char* outstr = sratom_to_turtle(sratom,
- &unmap,
- base_uri,
- subj,
- pred,
- obj->type,
- obj->size,
- LV2_ATOM_BODY(obj));
-
- printf("# Atom => Turtle\n\n%s", outstr);
-
- LV2_Atom* parsed = NULL;
- if (top_level) {
- SerdNode o = serd_node_from_string(SERD_URI, (const uint8_t*)obj_uri);
- parsed = sratom_from_turtle(sratom, base_uri, &o, NULL, outstr);
- } else {
- parsed = sratom_from_turtle(sratom, base_uri, subj, pred, outstr);
- }
-
- if (!pretty_numbers) {
- if (!lv2_atom_equals(obj, parsed)) {
- return test_fail("Parsed atom does not match original\n");
- }
-
- char* instr = sratom_to_turtle(sratom,
- &unmap,
- base_uri,
- subj,
- pred,
- parsed->type,
- parsed->size,
- LV2_ATOM_BODY(parsed));
- printf("# Turtle => Atom\n\n%s", instr);
-
- if (strcmp(outstr, instr)) {
- return test_fail("Re-serialised string differs from original\n");
- }
- free(instr);
+ printf("\n# %s\n\n", name);
+ check_round_trip(&uris, env, dumper, loader, obj, flags);
+ printf("\n");
+ LV2_ATOM_OBJECT_FOREACH ((LV2_Atom_Object*)obj, prop) {
+ check_round_trip(&uris, env, dumper, loader, &prop->value, flags);
}
- printf("All tests passed.\n");
-
- free(parsed);
- free(outstr);
- sratom_free(sratom);
+ sratom_dumper_free(dumper);
+ sratom_loader_free(loader);
for (uint32_t i = 0; i < uris.n_uris; ++i) {
free(uris.uris[i]);
}
+ serd_world_free(world);
free(uris.uris);
return 0;
}
-static int
-test_env(SerdEnv* env)
-{
- if (test(env, false, false) || //
- test(env, true, false) || //
- test(env, false, true) || //
- test(env, true, true)) {
- return 1;
- }
-
- return 0;
-}
-
int
main(void)
{
- // Test with no environment
- if (test_env(NULL)) {
- return 1;
- }
+ SerdEnv* const env = serd_env_new(SERD_STATIC_STRING("file:///tmp/base/"));
- // Test with a prefix defined
- SerdEnv* env = serd_env_new(NULL);
- serd_env_set_prefix_from_strings(
- env, (const uint8_t*)"eg", (const uint8_t*)"http://example.org/");
+ serd_env_set_prefix(
+ env, SERD_STATIC_STRING("eg"), SERD_STATIC_STRING("http://example.org/"));
+
+ serd_env_set_prefix(
+ env, SERD_STATIC_STRING("atom"), SERD_STATIC_STRING(NS_ATOM));
+
+ serd_env_set_prefix(
+ env, SERD_STATIC_STRING("rdf"), SERD_STATIC_STRING(NS_RDF));
+
+ serd_env_set_prefix(
+ env, SERD_STATIC_STRING("xsd"), SERD_STATIC_STRING(NS_XSD));
+
+ const int st =
+ (test(env, "Default", 0) || //
+ test(env, "Pretty", SRATOM_PRETTY_NUMBERS) ||
+ test(env, "Terse", SRATOM_TERSE) ||
+ test(env, "Pretty + Terse", SRATOM_PRETTY_NUMBERS | SRATOM_TERSE));
- test_env(env);
serd_env_free(env);
- return 0;
+ return st;
}