aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2020-08-12 20:54:05 +0200
committerDavid Robillard <d@drobilla.net>2020-10-27 13:13:59 +0100
commit7f1d50b40814db24573b9eb425566ce1d44d2e85 (patch)
tree56c87c5b0ed1b84027957888605e497d4521f0a0
parentaafc82f0bee3b222d395ef959aa4a6917f2ef0b2 (diff)
downloadserd-7f1d50b40814db24573b9eb425566ce1d44d2e85.tar.gz
serd-7f1d50b40814db24573b9eb425566ce1d44d2e85.tar.bz2
serd-7f1d50b40814db24573b9eb425566ce1d44d2e85.zip
Make serd_env_expand() always return a node if it is not relative
The old interface was potentially faster, because it avoided unnecessary copies, but was annoying to use and problematic because nodes that don't need expansion were indistinguishable from nodes that failed to expand (like those that use undefined namespace prefixes). In the grand scheme of things, the potential performance improvement isn't worth it, and unexpected unexpanded nodes causes deep nightmarish problems, so just make serd_env_expand() copy the input if it doesn't need expansion to make the API simpler and safer.
-rw-r--r--serd/serd.h6
-rw-r--r--src/env.c30
-rw-r--r--tests/env_test.c186
3 files changed, 176 insertions, 46 deletions
diff --git a/serd/serd.h b/serd/serd.h
index 4dd7e7bd..d57dc00c 100644
--- a/serd/serd.h
+++ b/serd/serd.h
@@ -1123,11 +1123,13 @@ SerdNode*
serd_env_qualify(const SerdEnv* env, const SerdNode* uri);
/**
- Expand `node`, transforming CURIEs into URIs
+ Expand `node`, transforming CURIEs and URI references into absolute URIs.
If `node` is a literal, its datatype is expanded if necessary.
- Returns null if `node` can not be expanded.
+ For simple nodes that do not require expansion, a copy is returned. Null is
+ returned if `node` is/contains a CURIE or relative URI that can not be
+ expanded.
*/
SERD_API
SerdNode*
diff --git a/src/env.c b/src/env.c
index f7b1b452..a9753069 100644
--- a/src/env.c
+++ b/src/env.c
@@ -295,23 +295,33 @@ expand_literal(const SerdEnv* env, const SerdNode* node)
assert(serd_node_type(node) == SERD_LITERAL);
const SerdNode* const datatype = serd_node_datatype(node);
- if (datatype && serd_node_type(datatype) == SERD_CURIE) {
+ if (!datatype) {
+ return serd_node_copy(node);
+ }
+
+ if (serd_node_type(datatype) == SERD_CURIE) {
SerdStringView prefix;
SerdStringView suffix;
- if (!serd_env_expand_in_place(env, datatype, &prefix, &suffix)) {
- return serd_new_typed_literal_expanded(serd_node_string(node),
- serd_node_length(node),
- serd_node_flags(node),
- SERD_URI,
- prefix,
- suffix);
+ if (serd_env_expand_in_place(env, datatype, &prefix, &suffix)) {
+ return NULL;
}
- } else if (datatype && serd_node_type(datatype) == SERD_URI) {
+
+ return serd_new_typed_literal_expanded(serd_node_string(node),
+ serd_node_length(node),
+ serd_node_flags(node),
+ SERD_URI,
+ prefix,
+ suffix);
+
+ } else if (serd_node_type(datatype) == SERD_URI) {
SerdURI datatype_uri;
serd_uri_parse(serd_node_string(datatype), &datatype_uri);
SerdURI abs_datatype_uri;
serd_uri_resolve(&datatype_uri, &env->base_uri, &abs_datatype_uri);
+ if (abs_datatype_uri.scheme.len == 0) {
+ return NULL;
+ }
return serd_new_typed_literal_uri(serd_node_string(node),
serd_node_length(node),
@@ -361,7 +371,7 @@ serd_env_expand(const SerdEnv* env, const SerdNode* node)
case SERD_CURIE:
return expand_curie(env, node);
case SERD_BLANK:
- return NULL;
+ return serd_node_copy(node);
}
}
diff --git a/tests/env_test.c b/tests/env_test.c
index 1d8b612f..bf222d14 100644
--- a/tests/env_test.c
+++ b/tests/env_test.c
@@ -120,36 +120,27 @@ test_set_prefix(void)
}
static void
-test_expand(void)
-{
- SerdNode* const name = serd_new_string("eg.1");
- SerdNode* const eg = serd_new_uri(NS_EG);
- SerdNode* const blank = serd_new_blank("b1");
- SerdNode* const rel = serd_new_uri("rel");
- SerdNode* const base = serd_new_uri("http://example.org/b/");
- SerdNode* const c1 = serd_new_curie("eg.1:foo");
- SerdNode* const c1_full = serd_new_uri("http://example.org/foo");
- SerdNode* const c2 = serd_new_curie("hm:what");
- SerdNode* const type = serd_new_uri("Type");
- SerdNode* const typed = serd_new_typed_literal("data", type);
- SerdEnv* const env = serd_env_new(base);
-
- assert(!serd_env_set_prefix(env, name, eg));
+test_expand_untyped_literal(void)
+{
+ SerdNode* const untyped = serd_new_string("data");
+ SerdEnv* const env = serd_env_new(NULL);
- assert(!serd_env_expand(env, name));
- assert(!serd_env_expand(env, blank));
+ SerdNode* const untyped_out = serd_env_expand(env, untyped);
+ assert(serd_node_equals(untyped_out, untyped));
+ serd_node_free(untyped_out);
- // Expand CURIE
- SerdNode* const c1_out = serd_env_expand(env, c1);
- assert(serd_node_equals(c1_out, c1_full));
- serd_node_free(c1_out);
+ serd_env_free(env);
+ serd_node_free(untyped);
+}
- // Expand relative URI
- SerdNode* const rel_out = serd_env_expand(env, rel);
- assert(!strcmp(serd_node_string(rel_out), "http://example.org/b/rel"));
- serd_node_free(rel_out);
+static void
+test_expand_uri_datatype(void)
+{
+ SerdNode* const base = serd_new_uri("http://example.org/b/");
+ SerdNode* const type = serd_new_uri("Type");
+ SerdNode* const typed = serd_new_typed_literal("data", type);
+ SerdEnv* const env = serd_env_new(base);
- // Expand literal with URI datatype
SerdNode* const typed_out = serd_env_expand(env, typed);
assert(typed_out);
assert(!strcmp(serd_node_string(typed_out), "data"));
@@ -158,22 +149,142 @@ test_expand(void)
"http://example.org/b/Type"));
serd_node_free(typed_out);
- assert(!serd_env_expand(env, c2));
+ serd_env_free(env);
+ serd_node_free(typed);
+ serd_node_free(type);
+ serd_node_free(base);
+}
+
+static void
+test_expand_bad_uri_datatype(void)
+{
+ SerdNode* const type = serd_new_uri("Type");
+ SerdNode* const typed = serd_new_typed_literal("data", type);
+ SerdEnv* const env = serd_env_new(NULL);
+
+ assert(!serd_env_expand(env, typed));
serd_env_free(env);
serd_node_free(typed);
serd_node_free(type);
- serd_node_free(c2);
- serd_node_free(c1_full);
- serd_node_free(c1);
+}
+
+static void
+test_expand_curie_datatype(void)
+{
+ SerdNode* const name = serd_new_string("eg");
+ SerdNode* const eg = serd_new_uri(NS_EG);
+ SerdNode* const type = serd_new_curie("eg:Type");
+ SerdNode* const typed = serd_new_typed_literal("data", type);
+ SerdEnv* const env = serd_env_new(NULL);
+
+ assert(!serd_env_set_prefix(env, name, eg));
+
+ SerdNode* const typed_out = serd_env_expand(env, typed);
+ assert(typed_out);
+ assert(!strcmp(serd_node_string(typed_out), "data"));
+ assert(serd_node_datatype(typed_out));
+ assert(!strcmp(serd_node_string(serd_node_datatype(typed_out)),
+ "http://example.org/Type"));
+ serd_node_free(typed_out);
+
+ serd_env_free(env);
+ serd_node_free(typed);
+ serd_node_free(type);
+ serd_node_free(eg);
+ serd_node_free(name);
+}
+
+static void
+test_expand_bad_curie_datatype(void)
+{
+ SerdNode* const type = serd_new_curie("eg:Type");
+ SerdNode* const typed = serd_new_typed_literal("data", type);
+ SerdEnv* const env = serd_env_new(NULL);
+
+ assert(!serd_env_expand(env, typed));
+
+ serd_env_free(env);
+ serd_node_free(typed);
+ serd_node_free(type);
+}
+
+static void
+test_expand_uri(void)
+{
+ SerdNode* const rel = serd_new_uri("rel");
+ SerdNode* const base = serd_new_uri("http://example.org/b/");
+ SerdEnv* const env = serd_env_new(base);
+
+ SerdNode* const rel_out = serd_env_expand(env, rel);
+ assert(!strcmp(serd_node_string(rel_out), "http://example.org/b/rel"));
+ serd_node_free(rel_out);
+
+ serd_env_free(env);
serd_node_free(base);
serd_node_free(rel);
- serd_node_free(blank);
+}
+
+static void
+test_expand_bad_uri(void)
+{
+ SerdNode* const bad_uri = serd_new_uri("rel");
+ SerdEnv* const env = serd_env_new(NULL);
+
+ assert(!serd_env_expand(env, bad_uri));
+
+ serd_env_free(env);
+ serd_node_free(bad_uri);
+}
+
+static void
+test_expand_curie(void)
+{
+ SerdNode* const name = serd_new_string("eg.1");
+ SerdNode* const eg = serd_new_uri(NS_EG);
+ SerdNode* const curie = serd_new_curie("eg.1:foo");
+ SerdEnv* const env = serd_env_new(NULL);
+
+ assert(!serd_env_set_prefix(env, name, eg));
+
+ SerdNode* const curie_out = serd_env_expand(env, curie);
+ assert(curie_out);
+ assert(!strcmp(serd_node_string(curie_out), "http://example.org/foo"));
+ serd_node_free(curie_out);
+
+ serd_env_free(env);
+ serd_node_free(curie);
serd_node_free(eg);
serd_node_free(name);
}
static void
+test_expand_bad_curie(void)
+{
+ SerdNode* const curie = serd_new_curie("eg.1:foo");
+ SerdEnv* const env = serd_env_new(NULL);
+
+ assert(!serd_env_expand(env, curie));
+
+ serd_env_free(env);
+ serd_node_free(curie);
+}
+
+static void
+test_expand_blank(void)
+{
+ SerdNode* const blank = serd_new_blank("b1");
+ SerdEnv* const env = serd_env_new(NULL);
+
+ SerdNode* const blank_out = serd_env_expand(env, blank);
+ assert(serd_node_equals(blank_out, blank));
+ serd_node_free(blank_out);
+
+ serd_env_free(env);
+ serd_node_free(blank);
+}
+
+static void
test_qualify(void)
{
SerdNode* const name = serd_new_string("eg");
@@ -185,8 +296,6 @@ test_qualify(void)
assert(!serd_env_set_prefix(env, name, eg));
- assert(!serd_env_expand(env, name));
-
SerdNode* const u1_out = serd_env_qualify(env, u1);
assert(serd_node_equals(u1_out, c1));
serd_node_free(u1_out);
@@ -243,7 +352,16 @@ main(void)
test_null();
test_base_uri();
test_set_prefix();
- test_expand();
+ test_expand_untyped_literal();
+ test_expand_uri_datatype();
+ test_expand_bad_uri_datatype();
+ test_expand_curie_datatype();
+ test_expand_bad_curie_datatype();
+ test_expand_uri();
+ test_expand_bad_uri();
+ test_expand_curie();
+ test_expand_bad_curie();
+ test_expand_blank();
test_qualify();
test_equals();
return 0;