summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2011-12-30 08:23:19 +0000
committerDavid Robillard <d@drobilla.net>2011-12-30 08:23:19 +0000
commit8708ccb858ddbf5d521c4755e137bd04544a6ae5 (patch)
tree2da5030db36592ff19b13f8ff802a5f5c3bfb3b8 /src
parentc5c4a6e935eecc2d2dac7bb9cccd36057b8dc123 (diff)
downloadlilv-8708ccb858ddbf5d521c4755e137bd04544a6ae5.tar.gz
lilv-8708ccb858ddbf5d521c4755e137bd04544a6ae5.tar.bz2
lilv-8708ccb858ddbf5d521c4755e137bd04544a6ae5.zip
Support arbitrary binary data in plugin state via base64 encoding.
Unit testing for plugin instantiation and state. Build without LV2 state available. Support URID values in plugin state nicely. Fix various holes in state implementation exposed by tests. git-svn-id: http://svn.drobilla.net/lad/trunk/lilv@3908 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src')
-rw-r--r--src/instance.c2
-rw-r--r--src/lilv_internal.h8
-rw-r--r--src/node.c26
-rw-r--r--src/state.c184
-rw-r--r--src/world.c2
5 files changed, 176 insertions, 46 deletions
diff --git a/src/instance.c b/src/instance.c
index e9d4591..4823f59 100644
--- a/src/instance.c
+++ b/src/instance.c
@@ -27,6 +27,8 @@ lilv_plugin_instantiate(const LilvPlugin* plugin,
double sample_rate,
const LV2_Feature*const* features)
{
+ lilv_plugin_load_if_necessary(plugin);
+
LilvInstance* result = NULL;
const LV2_Feature** local_features = NULL;
diff --git a/src/lilv_internal.h b/src/lilv_internal.h
index 75e21a2..9f19c1c 100644
--- a/src/lilv_internal.h
+++ b/src/lilv_internal.h
@@ -150,6 +150,7 @@ struct LilvWorldImpl {
SordNode* rdfs_label_node;
SordNode* rdfs_seealso_node;
SordNode* rdfs_subclassof_node;
+ SordNode* xsd_base64Binary_node;
SordNode* xsd_boolean_node;
SordNode* xsd_decimal_node;
SordNode* xsd_double_node;
@@ -168,7 +169,8 @@ typedef enum {
LILV_VALUE_INT,
LILV_VALUE_FLOAT,
LILV_VALUE_BOOL,
- LILV_VALUE_BLANK
+ LILV_VALUE_BLANK,
+ LILV_VALUE_BLOB
} LilvNodeType;
struct LilvNodeImpl {
@@ -179,6 +181,10 @@ struct LilvNodeImpl {
float float_val;
bool bool_val;
SordNode* uri_val;
+ struct {
+ void* buf;
+ size_t size;
+ } blob_val;
} val;
LilvNodeType type;
};
diff --git a/src/node.c b/src/node.c
index 3dc4ace..debf1be 100644
--- a/src/node.c
+++ b/src/node.c
@@ -21,7 +21,7 @@
#include "lilv_internal.h"
static void
-lilv_node_set_numerics_from_string(LilvNode* val)
+lilv_node_set_numerics_from_string(LilvNode* val, size_t len)
{
char* endptr;
@@ -39,6 +39,9 @@ lilv_node_set_numerics_from_string(LilvNode* val)
case LILV_VALUE_BOOL:
val->val.bool_val = (!strcmp(val->str_val, "true"));
break;
+ case LILV_VALUE_BLOB:
+ val->val.blob_val.buf = serd_base64_decode(
+ (const uint8_t*)val->str_val, len, &val->val.blob_val.size);
}
}
@@ -66,6 +69,7 @@ lilv_node_new(LilvWorld* world, LilvNodeType type, const char* str)
case LILV_VALUE_INT:
case LILV_VALUE_FLOAT:
case LILV_VALUE_BOOL:
+ case LILV_VALUE_BLOB:
val->str_val = lilv_strdup(str);
break;
}
@@ -80,6 +84,7 @@ lilv_node_new_from_node(LilvWorld* world, const SordNode* node)
LilvNode* result = NULL;
SordNode* datatype_uri = NULL;
LilvNodeType type = LILV_VALUE_STRING;
+ size_t len = 0;
switch (sord_node_get_type(node)) {
case SORD_URI:
@@ -106,19 +111,15 @@ lilv_node_new_from_node(LilvWorld* world, const SordNode* node)
type = LILV_VALUE_FLOAT;
else if (sord_node_equals(datatype_uri, world->xsd_integer_node))
type = LILV_VALUE_INT;
+ else if (sord_node_equals(datatype_uri, world->xsd_base64Binary_node))
+ type = LILV_VALUE_BLOB;
else
LILV_ERRORF("Unknown datatype `%s'\n",
sord_node_get_string(datatype_uri));
}
- result = lilv_node_new(world, type, (const char*)sord_node_get_string(node));
- switch (result->type) {
- case LILV_VALUE_INT:
- case LILV_VALUE_FLOAT:
- case LILV_VALUE_BOOL:
- lilv_node_set_numerics_from_string(result);
- default:
- break;
- }
+ result = lilv_node_new(
+ world, type, (const char*)sord_node_get_string_counted(node, &len));
+ lilv_node_set_numerics_from_string(result, len);
break;
default:
assert(false);
@@ -237,6 +238,10 @@ lilv_node_equals(const LilvNode* value, const LilvNode* other)
return (value->val.float_val == other->val.float_val);
case LILV_VALUE_BOOL:
return (value->val.bool_val == other->val.bool_val);
+ case LILV_VALUE_BLOB:
+ return (value->val.blob_val.size == other->val.blob_val.size)
+ && !memcmp(value->val.blob_val.buf, other->val.blob_val.buf,
+ value->val.blob_val.size);
}
return false; /* shouldn't get here */
@@ -263,6 +268,7 @@ lilv_node_get_turtle_token(const LilvNode* value)
break;
case LILV_VALUE_STRING:
case LILV_VALUE_BOOL:
+ case LILV_VALUE_BLOB:
result = lilv_strdup(value->str_val);
break;
case LILV_VALUE_INT:
diff --git a/src/state.c b/src/state.c
index aae3fab..f50557f 100644
--- a/src/state.c
+++ b/src/state.c
@@ -73,6 +73,14 @@ property_cmp(const void* a, const void* b)
return pa->key - pb->key;
}
+static int
+value_cmp(const void* a, const void* b)
+{
+ const PortValue* pa = (const PortValue*)a;
+ const PortValue* pb = (const PortValue*)b;
+ return strcmp(pa->symbol, pb->symbol);
+}
+
LILV_API
const LilvNode*
lilv_state_get_plugin_uri(const LilvState* state)
@@ -131,7 +139,7 @@ store_callback(void* handle,
state->props = realloc(state->props,
(++state->num_props) * sizeof(Property));
Property* const prop = &state->props[state->num_props - 1];
-
+
prop->value = malloc(size);
memcpy(prop->value, value, size);
@@ -183,10 +191,12 @@ lilv_state_new_from_instance(const LilvPlugin* plugin,
#endif // HAVE_LV2_STATE
qsort(state->props, state->num_props, sizeof(Property), property_cmp);
+ qsort(state->values, state->num_values, sizeof(PortValue), value_cmp);
return state;
}
+#ifdef HAVE_LV2_STATE
static const void*
retrieve_callback(void* handle,
uint32_t key,
@@ -208,6 +218,7 @@ retrieve_callback(void* handle,
}
return NULL;
}
+#endif // HAVE_LV2_STATE
LILV_API
void
@@ -261,9 +272,6 @@ property_from_node(LilvWorld* world,
prop->type = map->map(map->handle, NS_ATOM "URID");
prop->size = sizeof(uint32_t);
break;
- case LILV_VALUE_BLANK:
- // TODO: Hmm...
- break;
case LILV_VALUE_STRING:
prop->size = strlen(str) + 1;
prop->value = malloc(prop->size);
@@ -288,8 +296,11 @@ property_from_node(LilvWorld* world,
prop->type = map->map(map->handle, NS_ATOM "Float");
prop->size = sizeof(float);
break;
+ case LILV_VALUE_BLANK:
+ case LILV_VALUE_BLOB:
+ // TODO: Blank nodes in state
+ break;
}
- prop->flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE;
}
static LilvState*
@@ -302,9 +313,9 @@ new_state_from_model(LilvWorld* world,
memset(state, '\0', sizeof(LilvState));
// Get the plugin URI this state applies to
- const SordQuad pat1 = {
+ const SordQuad upat = {
node, world->lv2_appliesTo_node, NULL, NULL };
- SordIter* i = sord_find(model, pat1);
+ SordIter* i = sord_find(model, upat);
if (i) {
state->plugin_uri = lilv_node_new_from_node(
world, lilv_match_object(i));
@@ -314,9 +325,19 @@ new_state_from_model(LilvWorld* world,
sord_node_get_string(node));
}
+ // Get the state label
+ const SordQuad lpat = {
+ node, world->rdfs_label_node, NULL, NULL };
+ i = sord_find(model, lpat);
+ if (i) {
+ state->label = lilv_strdup(
+ (const char*)sord_node_get_string(lilv_match_object(i)));
+ sord_iter_free(i);
+ }
+
// Get port values
- const SordQuad pat2 = { node, world->lv2_port_node, NULL, NULL };
- SordIter* ports = sord_find(model, pat2);
+ const SordQuad ppat = { node, world->lv2_port_node, NULL, NULL };
+ SordIter* ports = sord_find(model, ppat);
FOREACH_MATCH(ports) {
const SordNode* port = lilv_match_object(ports);
const SordNode* label = get_one(model, port, world->rdfs_label_node);
@@ -346,31 +367,53 @@ new_state_from_model(LilvWorld* world,
SordNode* statep = sord_new_uri(world->world, USTR(NS_STATE "state"));
const SordNode* state_node = get_one(model, node, statep);
if (state_node) {
- const SordQuad pat3 = { state_node, NULL, NULL };
- SordIter* props = sord_find(model, pat3);
+ const SordQuad spat = { state_node, NULL, NULL };
+ SordIter* props = sord_find(model, spat);
FOREACH_MATCH(props) {
- const SordNode* p = lilv_match_predicate(props);
- const SordNode* o = lilv_match_object(props);
- LilvNode* onode = lilv_node_new_from_node(world, o);
-
- Property prop = { NULL, 0, 0, 0, 0 };
+ const SordNode* p = lilv_match_predicate(props);
+ const SordNode* o = lilv_match_object(props);
+
+ uint32_t flags = 0;
+#ifdef HAVE_LV2_STATE
+ flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE;
+#endif
+ Property prop = { NULL, 0, 0, 0, flags };
prop.key = map->map(map->handle,
(const char*)sord_node_get_string(p));
- property_from_node(world, map, onode, &prop);
+
+ if (sord_node_get_type(o) == SORD_BLANK) {
+ const SordNode* type = get_one(model, o, world->rdf_a_node);
+ const SordNode* value = get_one(model, o, world->rdf_value_node);
+ if (type && value) {
+ size_t len;
+ const uint8_t* b64 = sord_node_get_string_counted(value, &len);
+ prop.value = serd_base64_decode(b64, len, &prop.size);
+ prop.type = map->map(map->handle,
+ (const char*)sord_node_get_string(type));
+ } else {
+ LILV_ERRORF("Unable to parse blank node property <%p>\n",
+ sord_node_get_string(p));
+ }
+ } else {
+ LilvNode* onode = lilv_node_new_from_node(world, o);
+ property_from_node(world, map, onode, &prop);
+ lilv_node_free(onode);
+ }
+
if (prop.value) {
state->props = realloc(
state->props, (++state->num_props) * sizeof(Property));
state->props[state->num_props - 1] = prop;
}
- lilv_node_free(onode);
}
sord_iter_free(props);
}
sord_node_free(world->world, statep);
qsort(state->props, state->num_props, sizeof(Property), property_cmp);
-
+ qsort(state->values, state->num_values, sizeof(PortValue), value_cmp);
+
return state;
}
@@ -425,10 +468,14 @@ lilv_state_new_from_file(LilvWorld* world,
}
static LilvNode*
-node_from_property(LilvWorld* world, const char* type, void* value, size_t size)
+node_from_property(LilvWorld* world, LV2_URID_Unmap* unmap,
+ const char* type, void* value, size_t size)
{
if (!strcmp(type, NS_ATOM "String")) {
return lilv_new_string(world, (const char*)value);
+ } else if (!strcmp(type, NS_ATOM "URID")) {
+ const char* str = unmap->unmap(unmap->handle, *(uint32_t*)value);
+ return lilv_new_uri(world, str);
} else if (!strcmp(type, NS_ATOM "Int32")) {
if (size == sizeof(int32_t)) {
return lilv_new_int(world, *(int32_t*)value);
@@ -491,8 +538,8 @@ add_state_to_manifest(const LilvNode* plugin_uri,
{
FILE* fd = fopen((char*)manifest_path, "a");
if (!fd) {
- fprintf(stderr, "error: Failed to open %s (%s)\n",
- manifest_path, strerror(errno));
+ LILV_ERRORF("Failed to open %s (%s)\n",
+ manifest_path, strerror(errno));
return 4;
}
@@ -508,6 +555,7 @@ add_state_to_manifest(const LilvNode* plugin_uri,
SerdEnv* env = serd_env_new(NULL);
serd_env_set_prefix_from_strings(env, USTR("lv2"), USTR(LILV_NS_LV2));
serd_env_set_prefix_from_strings(env, USTR("pset"), USTR(NS_PSET));
+ serd_env_set_prefix_from_strings(env, USTR("rdf"), USTR(LILV_NS_RDF));
serd_env_set_prefix_from_strings(env, USTR("rdfs"), USTR(LILV_NS_RDFS));
#if defined(HAVE_LOCKF) && defined(HAVE_FILENO)
@@ -607,15 +655,14 @@ lilv_state_save(LilvWorld* world,
const char* const home = getenv("HOME");
if (!home) {
- fprintf(stderr, "error: $HOME is undefined\n");
+ LILV_ERROR("$HOME is undefined\n");
return 2;
}
// Create ~/.lv2/
char* const lv2dir = lilv_strjoin(home, "/.lv2/", NULL);
if (mkdir(lv2dir, 0755) && errno != EEXIST) {
- fprintf(stderr, "error: Unable to create %s (%s)\n",
- lv2dir, strerror(errno));
+ LILV_ERRORF("Unable to create %s (%s)\n", lv2dir, strerror(errno));
free(lv2dir);
return 3;
}
@@ -623,13 +670,12 @@ lilv_state_save(LilvWorld* world,
// Create ~/.lv2/presets.lv2/
char* const bundle = lilv_strjoin(lv2dir, "presets.lv2/", NULL);
if (mkdir(bundle, 0755) && errno != EEXIST) {
- fprintf(stderr, "error: Unable to create %s (%s)\n",
- lv2dir, strerror(errno));
+ LILV_ERRORF("Unable to create %s (%s)\n", lv2dir, strerror(errno));
free(lv2dir);
free(bundle);
return 4;
}
-
+
char* const filename = pathify(state->label);
default_path = lilv_strjoin(bundle, filename, ".ttl", NULL);
default_manifest_path = lilv_strjoin(bundle, "manifest.ttl", NULL);
@@ -648,8 +694,7 @@ lilv_state_save(LilvWorld* world,
FILE* fd = fopen(path, "w");
if (!fd) {
- fprintf(stderr, "error: Failed to open %s (%s)\n",
- path, strerror(errno));
+ LILV_ERRORF("Failed to open %s (%s)\n", path, strerror(errno));
free(default_path);
free(default_manifest_path);
return 4;
@@ -658,6 +703,7 @@ lilv_state_save(LilvWorld* world,
SerdEnv* env = serd_env_new(NULL);
serd_env_set_prefix_from_strings(env, USTR("lv2"), USTR(LILV_NS_LV2));
serd_env_set_prefix_from_strings(env, USTR("pset"), USTR(NS_PSET));
+ serd_env_set_prefix_from_strings(env, USTR("rdf"), USTR(LILV_NS_RDF));
serd_env_set_prefix_from_strings(env, USTR("rdfs"), USTR(LILV_NS_RDFS));
serd_env_set_prefix_from_strings(env, USTR("state"), USTR(NS_STATE));
@@ -744,20 +790,50 @@ lilv_state_save(LilvWorld* world,
LILV_WARNF("Failed to unmap property key `%d'\n", prop->key);
} else if (!type) {
LILV_WARNF("Failed to unmap property type `%d'\n", prop->type);
- } else if (!(prop->flags & LV2_STATE_IS_PORTABLE)) {
+#ifdef HAVE_LV2_STATE
+ } else if (!(prop->flags & LV2_STATE_IS_PORTABLE)
+ || !(prop->flags & LV2_STATE_IS_POD)) {
LILV_WARNF("Unable to save non-portable property <%s>\n", type);
+#endif
} else {
+ SerdNode t;
+ p = serd_node_from_string(SERD_URI, USTR(key));
LilvNode* const node = node_from_property(
- world, type, prop->value, prop->size);
+ world, unmap, type, prop->value, prop->size);
if (node) {
- p = serd_node_from_string(SERD_URI, USTR(key));
- SerdNode t;
node_to_serd(node, &o, &t);
serd_writer_write_statement(
writer, SERD_ANON_CONT, NULL,
&state_node, &p, &o, &t, NULL);
} else {
- LILV_WARNF("Unable to save property type <%s>\n", type);
+ char name[16];
+ snprintf(name, sizeof(name), "b%u", i);
+ const SerdNode blank = serd_node_from_string(
+ SERD_BLANK, (const uint8_t*)name);
+
+ // <state> <key> [
+ serd_writer_write_statement(
+ writer, SERD_ANON_O_BEGIN, NULL,
+ &state_node, &p, &blank, NULL, NULL);
+
+ // rdf:type <type>
+ p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type"));
+ o = serd_node_from_string(SERD_URI, USTR(type));
+ serd_writer_write_statement(
+ writer, SERD_ANON_CONT, NULL,
+ &blank, &p, &o, NULL, NULL);
+
+ // rdf:value "string"^^<xsd:base64Binary>
+ SerdNode blob = serd_node_new_blob(prop->value, prop->size, true);
+ p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "value"));
+ t = serd_node_from_string(SERD_URI,
+ USTR(LILV_NS_XSD "base64Binary"));
+ serd_writer_write_statement(
+ writer, SERD_ANON_CONT, NULL,
+ &blank, &p, &blob, &t, NULL);
+ serd_node_free(&blob);
+
+ serd_writer_end_anon(writer, &blank); // ]
}
}
}
@@ -781,6 +857,44 @@ lilv_state_save(LilvWorld* world,
}
LILV_API
+bool
+lilv_state_equals(const LilvState* a, const LilvState* b)
+{
+ if (!lilv_node_equals(a->plugin_uri, b->plugin_uri)
+ || (a->label && !b->label)
+ || (b->label && !a->label)
+ || (a->label && b->label && strcmp(a->label, b->label))
+ || a->num_props != b->num_props
+ || a->num_values != b->num_values) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < a->num_values; ++i) {
+ PortValue* const av = &a->values[i];
+ PortValue* const bv = &b->values[i];
+ if (strcmp(av->symbol, bv->symbol)) {
+ return false;
+ } else if (!lilv_node_equals(av->value, bv->value)) {
+ return false;
+ }
+ }
+
+ for (uint32_t i = 0; i < a->num_props; ++i) {
+ Property* const ap = &a->props[i];
+ Property* const bp = &b->props[i];
+ if (ap->size != bp->size
+ || ap->key != bp->key
+ || ap->type != bp->type
+ || ap->flags != bp->flags
+ || memcmp(ap->value, bp->value, ap->size)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+LILV_API
void
lilv_state_free(LilvState* state)
{
diff --git a/src/world.c b/src/world.c
index af20bc3..2029136 100644
--- a/src/world.c
+++ b/src/world.c
@@ -77,6 +77,7 @@ lilv_world_new(void)
world->rdfs_label_node = NEW_URI(LILV_NS_RDFS "label");
world->rdfs_seealso_node = NEW_URI(LILV_NS_RDFS "seeAlso");
world->rdfs_subclassof_node = NEW_URI(LILV_NS_RDFS "subClassOf");
+ world->xsd_base64Binary_node = NEW_URI(LILV_NS_XSD "base64Binary");
world->xsd_boolean_node = NEW_URI(LILV_NS_XSD "boolean");
world->xsd_decimal_node = NEW_URI(LILV_NS_XSD "decimal");
world->xsd_double_node = NEW_URI(LILV_NS_XSD "double");
@@ -135,6 +136,7 @@ lilv_world_free(LilvWorld* world)
sord_node_free(world->world, world->rdfs_label_node);
sord_node_free(world->world, world->rdfs_seealso_node);
sord_node_free(world->world, world->rdfs_subclassof_node);
+ sord_node_free(world->world, world->xsd_base64Binary_node);
sord_node_free(world->world, world->xsd_boolean_node);
sord_node_free(world->world, world->xsd_decimal_node);
sord_node_free(world->world, world->xsd_double_node);