aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-07-22 23:39:39 -0400
committerDavid Robillard <d@drobilla.net>2022-01-14 19:37:51 -0500
commita63a8f19c54dfee75e092819d6622b8d36fe1d39 (patch)
tree3c2fe49c257e27d369365a0c08c1524baaf74a4c
parent64e81dfd6ec04995fd396269deb6b32fe2d1192d (diff)
downloadserd-a63a8f19c54dfee75e092819d6622b8d36fe1d39.tar.gz
serd-a63a8f19c54dfee75e092819d6622b8d36fe1d39.tar.bz2
serd-a63a8f19c54dfee75e092819d6622b8d36fe1d39.zip
Expose low-level node construction API
-rw-r--r--include/serd/serd.h387
-rw-r--r--src/env.c28
-rw-r--r--src/n3.c5
-rw-r--r--src/node.c660
-rw-r--r--src/node.h52
-rw-r--r--src/nodes.c2
-rw-r--r--src/static_nodes.h41
-rw-r--r--src/world.c2
-rw-r--r--test/test_env.c2
-rw-r--r--test/test_node.c65
-rw-r--r--test/test_node_syntax.c36
11 files changed, 915 insertions, 365 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 2bda9829..fe610fc5 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -600,81 +600,337 @@ typedef enum {
} SerdNodeType;
/**
- Create a new "token" node that is just a string.
+ @defgroup serd_node_construction Construction
+
+ This is the low-level node construction API, which can be used to construct
+ nodes into existing buffers. Advanced applications can use this to
+ specially manage node memory, for example by allocating nodes on the stack,
+ or with a special allocator.
+
+ Note that nodes are "plain old data", so there is no need to destroy a
+ constructed node, and nodes may be trivially copied, for example with
+ memcpy().
+
+ @{
+*/
+
+/**
+ Construct a node into an existing buffer.
+
+ This is the universal node constructor which can construct any node. An
+ error will be returned if the parameters do not make sense. In particular,
+ SERD_HAS_DATATYPE or SERD_HAS_LANGUAGE (but not both) may only be given if
+ `type` is `SERD_LITERAL`, and `meta` must be syntactically valid based on
+ that flag.
+
+ This function may also be used to determine the size of buffer required by
+ passing a null buffer with zero size.
+
+ @param buf_size The size of `buf` in bytes, or zero to only measure.
+
+ @param buf Buffer where the node will be written, or null to only measure.
+
+ @param type The type of the node to construct.
+
+ @param string The string body of the node.
+
+ @param flags Flags that describe the details of the node.
+
+ @param meta The string value of the literal's metadata. If
+ #SERD_HAS_DATATYPE is set, then this must be an absolute datatype URI. If
+ #SERD_HAS_LANGUAGE is set, then this must be a language tag like "en-ca".
+ Otherwise, it is ignored.
+
+ @return A result with a `status` and a `count` of bytes written. If the
+ buffer is too small for the node, then `status` will be #SERD_ERR_OVERFLOW,
+ and `count` will be set to the number of bytes required to successfully
+ construct the node.
+*/
+SERD_API
+SerdWriteResult
+serd_node_construct(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ SerdNodeType type,
+ SerdStringView string,
+ SerdNodeFlags flags,
+ SerdStringView meta);
+
+/**
+ Construct a simple "token" node.
"Token" is just a shorthand used in this API to refer to a node that is not
- a typed or tagged literal. This can be used to create URIs, blank nodes,
- variables, and simple string literals.
+ a typed or tagged literal, that is, a node that is just one string. This
+ can be used to create URIs, blank nodes, variables, and simple string
+ literals.
Note that string literals constructed with this function will have no flags
set, and so will be written as "short" literals (not triple-quoted). To
- construct long literals, use the more advanced serd_new_literal() with
- #SERD_IS_LONG.
+ construct long literals, use the more advanced serd_construct_literal() with
+ the #SERD_IS_LONG flag.
+
+ See the serd_node_construct() documentation for details on buffer usage and
+ the return value.
*/
SERD_API
-SerdNode* SERD_ALLOCATED
-serd_new_token(SerdNodeType type, SerdStringView string);
+SerdWriteResult
+serd_node_construct_token(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ SerdNodeType type,
+ SerdStringView string);
+
+/**
+ Construct a URI node from a parsed URI.
+
+ This is similar to serd_node_construct_token(), but will serialise a parsed
+ URI into the new node. This can be used to resolve a relative URI reference
+ or expand a CURIE directly into a node without needing to allocate the URI
+ string separately.
+*/
+SerdWriteResult
+serd_node_construct_uri(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ SerdURIView uri);
+
+/**
+ Construct a file URI node from a path and optional hostname.
-/// Create a new plain literal string node from `str`
+ This is similar to serd_node_construct_token(), but will create a new file
+ URI from a file path and optional hostname, performing any necessary
+ escaping.
+*/
+SerdWriteResult
+serd_node_construct_file_uri(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ SerdStringView path,
+ SerdStringView hostname);
+
+/**
+ Construct a literal node with an optional datatype or language.
+
+ Either a datatype (which must be an absolute URI) or a language (which must
+ be an RFC5646 language tag) may be given, but not both.
+
+ This is the most general literal constructor, which can be used to construct
+ any literal node. This works like serd_node_construct(), see its
+ documentation for details.
+*/
SERD_API
-SerdNode* SERD_ALLOCATED
-serd_new_string(SerdStringView string);
+SerdWriteResult
+serd_node_construct_literal(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ SerdStringView string,
+ SerdNodeFlags flags,
+ SerdStringView meta);
/**
- Create a new literal node with optional datatype or language.
+ Construct a canonical xsd:boolean literal.
- This can create more complex literals than serd_new_string() with an
- associated datatype URI or language tag, as well as control whether a
- literal should be written as a short or long (triple-quoted) string.
+ The constructed node will be either "true" or "false", with datatype
+ xsd:boolean.
- @param string The string value of the literal.
+ This is a convenience wrapper for serd_node_construct_literal() that
+ constructs a node directly from a `bool`.
+*/
+SerdWriteResult
+serd_node_construct_boolean(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ bool value);
- @param flags Flags to describe the literal and its metadata. This must be a
- valid combination of flags, in particular, at most one of #SERD_HAS_DATATYPE
- and #SERD_HAS_LANGUAGE may be set.
+/**
+ Construct a canonical xsd:decimal literal.
- @param meta The string value of the literal's metadata. If
- #SERD_HAS_DATATYPE is set, then this must be an absolute datatype URI. If
- #SERD_HAS_LANGUAGE is set, then this must be a language tag like "en-ca".
- Otherwise, it is ignored.
+ The constructed node will be an xsd:decimal literal, like "12.34", with
+ datatype xsd:decimal.
- @return A newly allocated literal node that must be freed with
- serd_node_free(), or null if the arguments are invalid or allocation failed.
+ The node will always contain a '.', start with a digit, and end with a digit
+ (a leading and/or trailing '0' will be added if necessary), for example,
+ "1.0". It will never be in scientific notation.
+
+ This is a convenience wrapper for serd_node_construct_literal() that
+ constructs a node directly from a `double`.
+*/
+SerdWriteResult
+serd_node_construct_decimal(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ double value);
+
+/**
+ Construct a canonical xsd:double literal.
+
+ The constructed node will be an xsd:double literal, like "1.23E45", with
+ datatype xsd:double. A canonical xsd:double is always in scientific
+ notation.
+
+ This is a convenience wrapper for serd_node_construct_literal() that
+ constructs a node directly from a `double`.
+*/
+SerdWriteResult
+serd_node_construct_double(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ double value);
+
+/**
+ Construct a canonical xsd:float literal.
+
+ The constructed node will be an xsd:float literal, like "1.23E45", with
+ datatype xsd:float. A canonical xsd:float is always in scientific notation.
+
+ Uses identical formatting to serd_node_construct_double(), except with at
+ most 9 significant digits (under 14 characters total).
+
+ This is a convenience wrapper for serd_node_construct_literal() that
+ constructs a node directly from a `float`.
+*/
+SerdWriteResult
+serd_node_construct_float(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ float value);
+
+/**
+ Construct a canonical xsd:integer literal.
+
+ The constructed node will be an xsd:integer literal like "1234", with the
+ given datatype, or datatype xsd:integer if none is given. It is the
+ caller's responsibility to ensure that the value is within the range of the
+ given datatype.
+*/
+SerdWriteResult
+serd_node_construct_integer(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ int64_t value,
+ SerdStringView datatype);
+
+/**
+ Construct a canonical xsd:base64Binary literal.
+
+ The constructed node will be an xsd:base64Binary literal like "Zm9vYmFy",
+ with datatype xsd:base64Binary.
+*/
+SerdWriteResult
+serd_node_construct_base64(size_t buf_size,
+ void* SERD_NULLABLE buf,
+ size_t value_size,
+ const void* SERD_NONNULL value,
+ SerdStringView datatype);
+
+/**
+ @}
+ @defgroup serd_node_allocation Dynamic Allocation
+
+ This is a convenient higher-level node construction API which allocates
+ nodes on the heap. The returned nodes must be freed with serd_node_free().
+
+ Note that in most cases it is better to use a #SerdNodes instead of managing
+ individual node allocations.
+
+ @{
+*/
+
+/**
+ Create a new node of any type.
+
+ This is a wrapper for serd_node_construct() that allocates a new node on the
+ heap.
+
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_literal(SerdStringView string,
- SerdNodeFlags flags,
- SerdStringView meta);
+serd_node_new(SerdNodeType type,
+ SerdStringView string,
+ SerdNodeFlags flags,
+ SerdStringView meta);
+
+/**
+ Create a new simple "token" node.
+
+ This is a wrapper for serd_node_construct_token() that allocates a new node
+ on the heap.
+
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
+*/
+SERD_API
+SerdNode* SERD_ALLOCATED
+serd_new_token(SerdNodeType type, SerdStringView string);
+
+/**
+ Create a new string literal node.
+
+ This is a trivial wrapper for serd_new_token() that passes `SERD_LITERAL`
+ for the type.
-/// Create a new blank node
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
+*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_blank(SerdStringView string);
+serd_new_string(SerdStringView string);
-/// Create a new URI node
+/**
+ Create a new URI node from a string.
+
+ This is a wrapper for serd_node_construct_uri() that allocates a new
+ node on the heap.
+
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
+*/
SERD_API
SerdNode* SERD_ALLOCATED
serd_new_uri(SerdStringView string);
-/// Create a new URI from a URI view
+/**
+ Create a new URI node from a parsed URI.
+
+ This is a wrapper for serd_node_construct_uri() that allocates a new
+ node on the heap.
+
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
+*/
SERD_API
SerdNode* SERD_ALLOCATED
serd_new_parsed_uri(SerdURIView uri);
/**
- Create a new file URI node from a file system path and optional hostname.
+ Create a new file URI node from a path and optional hostname.
- Backslashes in Windows paths will be converted, and other characters will be
- percent encoded as necessary.
+ This is a wrapper for serd_node_construct_file_uri() that allocates a new
+ node on the heap.
- If `path` is relative, `hostname` is ignored.
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
*/
SERD_API
SerdNode* SERD_ALLOCATED
serd_new_file_uri(SerdStringView path, SerdStringView hostname);
-/// Create a new node by serialising `b` into an xsd:boolean string
+/**
+ Create a new literal node.
+
+ This is a wrapper for serd_node_construct_literal() that allocates a new
+ node on the heap.
+
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
+*/
+SERD_API
+SerdNode* SERD_ALLOCATED
+serd_new_literal(SerdStringView string,
+ SerdNodeFlags flags,
+ SerdStringView meta);
+
+/**
+ Create a new canonical xsd:boolean node.
+
+ This is a wrapper for serd_node_construct_boolean() that allocates a new
+ node on the heap.
+
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
+*/
SERD_API
SerdNode* SERD_ALLOCATED
serd_new_boolean(bool b);
@@ -682,29 +938,24 @@ serd_new_boolean(bool b);
/**
Create a new canonical xsd:decimal literal.
- The resulting node will always contain a '.', start with a digit, and end
- with a digit (a leading and/or trailing '0' will be added if necessary), for
- example, "1.0". It will never be in scientific notation.
+ This is a wrapper for serd_node_construct_decimal() that allocates a new
+ node on the heap.
- @param d The value for the new node.
- @param datatype Datatype of node, or NULL for xsd:decimal.
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_decimal(double d, const SerdNode* SERD_NULLABLE datatype);
+serd_new_decimal(double d);
/**
Create a new canonical xsd:double literal.
- The returned node will always be in scientific notation, like "1.23E4",
- except for NaN and negative/positive infinity, which are "NaN", "-INF", and
- "INF", respectively.
+ This is a wrapper for serd_node_construct_double() that allocates a new
+ node on the heap.
- Uses the shortest possible representation that precisely describes `d`,
- which has at most 17 significant digits (under 24 characters total).
-
- @param d Double value to write.
- @return A literal node with datatype xsd:double.
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
*/
SERD_API
SerdNode* SERD_ALLOCATED
@@ -713,11 +964,11 @@ serd_new_double(double d);
/**
Create a new canonical xsd:float literal.
- Uses identical formatting to serd_new_double(), except with at most 9
- significant digits (under 14 characters total).
+ This is a wrapper for serd_node_construct_float() that allocates a new
+ node on the heap.
- @param f Float value of literal.
- @return A literal node with datatype xsd:float.
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
*/
SERD_API
SerdNode* SERD_ALLOCATED
@@ -726,28 +977,34 @@ serd_new_float(float f);
/**
Create a new canonical xsd:integer literal.
- @param i Integer value of literal.
- @param datatype Datatype of node, or NULL for xsd:integer.
+ This is a wrapper for serd_node_construct_integer() that allocates a new
+ node on the heap.
+
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_integer(int64_t i, const SerdNode* SERD_NULLABLE datatype);
+serd_new_integer(int64_t i, SerdStringView datatype);
/**
Create a new canonical xsd:base64Binary literal.
- This function can be used to make a node out of arbitrary binary data, which
- can be decoded using serd_base64_decode().
+ This is a wrapper for serd_node_construct_base64() that allocates a new
+ node on the heap.
- @param buf Raw binary data to encode in node.
- @param size Size of `buf` in bytes.
- @param datatype Datatype of node, or null for xsd:base64Binary.
+ @return A newly allocated node that must be freed with serd_node_free(), or
+ null.
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_base64(const void* SERD_NONNULL buf,
- size_t size,
- const SerdNode* SERD_NULLABLE datatype);
+serd_new_base64(const void* SERD_NONNULL buf,
+ size_t size,
+ SerdStringView datatype);
+
+/**
+ @}
+*/
/**
Return the value of `node` as a boolean.
diff --git a/src/env.c b/src/env.c
index d4ae1937..434d1a3b 100644
--- a/src/env.c
+++ b/src/env.c
@@ -258,7 +258,7 @@ serd_env_expand_in_place(const SerdEnv* const env,
return SERD_ERR_BAD_CURIE;
}
- uri_prefix->buf = serd_node_string(prefix->uri);
+ uri_prefix->buf = prefix->uri ? serd_node_string(prefix->uri) : "";
uri_prefix->len = prefix->uri ? prefix->uri->length : 0;
uri_suffix->buf = colon + 1;
uri_suffix->len = curie.len - name_len - 1;
@@ -279,20 +279,24 @@ serd_env_expand_curie(const SerdEnv* const env, const SerdStringView curie)
return NULL;
}
- const size_t len = prefix.len + suffix.len;
- SerdNode* const ret = serd_node_malloc(len, 0u, SERD_URI);
- if (!ret) {
- return NULL;
- }
+ const size_t len = prefix.len + suffix.len;
- char* const string = serd_node_buffer(ret);
- assert(string);
- assert(prefix.buf);
+ const size_t real_length = serd_node_pad_length(len);
+ const size_t node_size = sizeof(SerdNode) + real_length;
+ SerdNode* node = serd_node_malloc(node_size);
- memcpy(string, prefix.buf, prefix.len);
- memcpy(string + prefix.len, suffix.buf, suffix.len);
+ if (node) {
+ node->length = len;
+ node->flags = 0u;
+ node->type = SERD_URI;
+
+ char* const string = (char*)(node + 1u);
+ assert(prefix.buf);
+ memcpy(string, prefix.buf, prefix.len);
+ memcpy(string + prefix.len, suffix.buf, suffix.len);
+ }
- return ret;
+ return node;
}
SerdNode*
diff --git a/src/n3.c b/src/n3.c
index 6813bccc..79a48a93 100644
--- a/src/n3.c
+++ b/src/n3.c
@@ -369,7 +369,7 @@ resolve_IRIREF(SerdReader* const reader,
temp->length = serd_write_uri(uri, write_to_stack, &ctx);
if (!ctx.status) {
// Replace the destination with the new expanded node
- memmove(dest, temp, serd_node_total_size(temp));
+ memmove(dest, temp, sizeof(SerdNode) + serd_node_pad_length(temp->length));
serd_stack_pop_to(&reader->stack, string_start_offset + dest->length);
}
@@ -438,7 +438,8 @@ read_PrefixedName(SerdReader* const reader,
}
// Replace the destination with the new expanded node
- const size_t total_size = serd_node_total_size(temp);
+ const size_t total_size =
+ sizeof(SerdNode) + serd_node_pad_length(temp->length);
memmove(dest, temp, total_size);
diff --git a/src/node.c b/src/node.c
index d29a6119..6958ea5e 100644
--- a/src/node.c
+++ b/src/node.c
@@ -17,7 +17,6 @@
#include "node.h"
#include "namespaces.h"
-#include "static_nodes.h"
#include "string_utils.h"
#include "system.h"
@@ -28,53 +27,24 @@
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdlib.h>
#include <string.h>
-typedef struct {
- const void* SERD_NULLABLE buf;
- size_t len;
-} SerdConstBuffer;
+#ifndef NDEBUG
+# define MUST_SUCCEED(status) assert(!(status))
+#else
+# define MUST_SUCCEED(status) ((void)(status))
+#endif
static const SerdNodeFlags meta_mask = (SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE);
+// Round size up to an even multiple of the node alignment
static size_t
-string_sink(const void* const buf,
- const size_t size,
- const size_t nmemb,
- void* const stream)
+serd_node_pad_size(const size_t size)
{
- char** ptr = (char**)stream;
- memcpy(*ptr, buf, size * nmemb);
- *ptr += size * nmemb;
- return nmemb;
-}
+ const size_t n_trailing = size % serd_node_align;
+ const size_t n_pad = n_trailing ? (serd_node_align - n_trailing) : 0u;
-static size_t
-serd_node_pad_length(const size_t n_bytes)
-{
- const size_t pad = sizeof(SerdNode) - (n_bytes + 2) % sizeof(SerdNode);
- const size_t size = n_bytes + 2 + pad;
- assert(size % sizeof(SerdNode) == 0);
- return size;
-}
-
-static const SerdNode*
-serd_node_meta_c(const SerdNode* const node)
-{
- return node + 1 + (serd_node_pad_length(node->length) / sizeof(SerdNode));
-}
-
-static SerdNode*
-serd_node_meta(SerdNode* const node)
-{
- return node + 1 + (serd_node_pad_length(node->length) / sizeof(SerdNode));
-}
-
-static const SerdNode*
-serd_node_maybe_get_meta_c(const SerdNode* const node)
-{
- return (node->flags & meta_mask) ? serd_node_meta_c(node) : NULL;
+ return size + n_pad;
}
static void
@@ -88,7 +58,8 @@ serd_node_check_padding(const SerdNode* node)
assert(serd_node_buffer_c(node)[node->length + i] == '\0');
}
- serd_node_check_padding(serd_node_maybe_get_meta_c(node));
+ serd_node_check_padding(serd_node_datatype(node));
+ serd_node_check_padding(serd_node_language(node));
}
#endif
}
@@ -96,27 +67,37 @@ serd_node_check_padding(const SerdNode* node)
size_t
serd_node_total_size(const SerdNode* const node)
{
- return node ? (sizeof(SerdNode) + serd_node_pad_length(node->length) +
- serd_node_total_size(serd_node_maybe_get_meta_c(node)))
- : 0;
+ const size_t real_length = serd_node_pad_length(node->length);
+ const size_t base_size = sizeof(SerdNode) + real_length;
+
+ if (!(node->flags & meta_mask)) {
+ return base_size;
+ }
+
+ const SerdNode* const meta = serd_node_meta_c(node);
+ const size_t meta_real_length = serd_node_pad_length(meta->length);
+
+ return base_size + sizeof(SerdNode) + meta_real_length;
}
SerdNode*
-serd_node_malloc(const size_t length,
- const SerdNodeFlags flags,
- const SerdNodeType type)
+serd_node_malloc(const size_t size)
{
- const size_t size = sizeof(SerdNode) + serd_node_pad_length(length);
- SerdNode* node = (SerdNode*)serd_calloc_aligned(serd_node_align, size);
-
- node->length = 0;
- node->flags = flags;
- node->type = type;
+ SerdNode* const node =
+ (SerdNode*)serd_calloc_aligned(serd_node_align, serd_node_pad_size(size));
assert((uintptr_t)node % serd_node_align == 0);
return node;
}
+SerdNode*
+serd_node_try_malloc(const SerdWriteResult r)
+{
+ return (r.status && r.status != SERD_ERR_OVERFLOW)
+ ? NULL
+ : serd_node_malloc(r.count);
+}
+
void
serd_node_set(SerdNode** const dst, const SerdNode* const src)
{
@@ -163,46 +144,42 @@ result(const SerdStatus status, const size_t count)
return result;
}
-SerdNode*
-serd_new_token(const SerdNodeType type, const SerdStringView str)
-{
- SerdNodeFlags flags = 0u;
- const size_t length = str.buf ? str.len : 0u;
- SerdNode* node = serd_node_malloc(length, flags, type);
+static SerdWriteResult
+serd_node_construct_simple(const size_t buf_size,
+ void* const buf,
+ const SerdNodeType type,
+ const SerdNodeFlags flags,
+ const SerdStringView string)
+{
+ const size_t total_size = sizeof(SerdNode) + serd_node_pad_length(string.len);
+ if (!buf || total_size > buf_size) {
+ return result(SERD_ERR_OVERFLOW, total_size);
+ }
- if (node) {
- if (str.buf) {
- memcpy(serd_node_buffer(node), str.buf, length);
- }
+ SerdNode* const node = (SerdNode*)buf;
- node->length = length;
- }
+ node->length = string.len;
+ node->flags = flags;
+ node->type = type;
- serd_node_check_padding(node);
+ if (string.buf) {
+ memcpy(serd_node_buffer(node), string.buf, string.len);
+ }
- return node;
+ serd_node_zero_pad(node);
+ return result(SERD_SUCCESS, total_size);
}
-SerdNode*
-serd_new_string(const SerdStringView str)
+SerdWriteResult
+serd_node_construct_token(const size_t buf_size,
+ void* const buf,
+ const SerdNodeType type,
+ const SerdStringView string)
{
- SerdNodeFlags flags = 0u;
- SerdNode* node = serd_node_malloc(str.len, flags, SERD_LITERAL);
-
- if (node) {
- if (str.buf && str.len) {
- memcpy(serd_node_buffer(node), str.buf, str.len);
- }
-
- node->length = str.len;
- serd_node_check_padding(node);
- }
-
- return node;
+ return serd_node_construct_simple(buf_size, buf, type, 0u, string);
}
-SERD_PURE_FUNC
-static bool
+bool
is_langtag(const SerdStringView string)
{
// First character must be a letter
@@ -231,57 +208,296 @@ is_langtag(const SerdStringView string)
return true;
}
-SerdNode*
-serd_new_literal(const SerdStringView string,
- const SerdNodeFlags flags,
- const SerdStringView meta)
+SerdWriteResult
+serd_node_construct_literal(const size_t buf_size,
+ void* const buf,
+ const SerdStringView string,
+ const SerdNodeFlags flags,
+ const SerdStringView meta)
{
if (!(flags & (SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE))) {
- SerdNode* node = serd_node_malloc(string.len, flags, SERD_LITERAL);
-
- memcpy(serd_node_buffer(node), string.buf, string.len);
- node->length = string.len;
- serd_node_check_padding(node);
- return node;
+ return serd_node_construct_simple(
+ buf_size, buf, SERD_LITERAL, flags, string);
}
if ((flags & SERD_HAS_DATATYPE) && (flags & SERD_HAS_LANGUAGE)) {
- return NULL;
+ return result(SERD_ERR_BAD_ARG, 0);
}
if (!meta.len) {
- return NULL;
+ return result(SERD_ERR_BAD_ARG, 0);
}
if (((flags & SERD_HAS_DATATYPE) &&
(!serd_uri_string_has_scheme(meta.buf) ||
!strcmp(meta.buf, NS_RDF "langString"))) ||
((flags & SERD_HAS_LANGUAGE) && !is_langtag(meta))) {
- return NULL;
+ return result(SERD_ERR_BAD_ARG, 0);
}
- const size_t len = serd_node_pad_length(string.len);
- const size_t meta_len = serd_node_pad_length(meta.len);
- const size_t meta_size = sizeof(SerdNode) + meta_len;
+ const size_t padded_length = serd_node_pad_length(string.len);
+
+ const size_t meta_size = sizeof(SerdNode) + serd_node_pad_length(meta.len);
+ const size_t total_size = sizeof(SerdNode) + padded_length + meta_size;
+ if (!buf || total_size > buf_size) {
+ return result(SERD_ERR_OVERFLOW, total_size);
+ }
+
+ SerdNode* const node = (SerdNode*)buf;
- SerdNode* node = serd_node_malloc(len + meta_size, flags, SERD_LITERAL);
- memcpy(serd_node_buffer(node), string.buf, string.len);
node->length = string.len;
+ node->flags = flags;
+ node->type = SERD_LITERAL;
- SerdNode* meta_node = node + 1u + (len / sizeof(SerdNode));
- meta_node->length = meta.len;
+ memcpy(serd_node_buffer(node), string.buf, string.len);
+
+ SerdNode* meta_node = node + 1 + (padded_length / sizeof(SerdNode));
meta_node->type = (flags & SERD_HAS_DATATYPE) ? SERD_URI : SERD_LITERAL;
+ meta_node->length = meta.len;
memcpy(serd_node_buffer(meta_node), meta.buf, meta.len);
- serd_node_check_padding(meta_node);
+ serd_node_zero_pad(node);
+ return result(SERD_SUCCESS, total_size);
+}
+
+SerdWriteResult
+serd_node_construct(const size_t buf_size,
+ void* const buf,
+ const SerdNodeType type,
+ const SerdStringView string,
+ const SerdNodeFlags flags,
+ const SerdStringView meta)
+{
+ return ((type == SERD_LITERAL)
+ ? serd_node_construct_literal(buf_size, buf, string, flags, meta)
+ : (meta.len > 0u)
+ ? result(SERD_ERR_BAD_ARG, 0)
+ : serd_node_construct_token(buf_size, buf, type, string));
+}
+
+SerdWriteResult
+serd_node_construct_boolean(const size_t buf_size,
+ void* const buf,
+ const bool value)
+{
+ char temp[EXESS_MAX_BOOLEAN_LENGTH + 1] = {0};
+
+ const ExessResult r = exess_write_boolean(value, sizeof(temp), temp);
+ MUST_SUCCEED(r.status); // The only error is buffer overrun
+
+ return serd_node_construct_literal(buf_size,
+ buf,
+ SERD_SUBSTRING(temp, r.count),
+ SERD_HAS_DATATYPE,
+ SERD_STRING(EXESS_XSD_URI "boolean"));
+}
+
+SerdWriteResult
+serd_node_construct_decimal(const size_t buf_size,
+ void* const buf,
+ const double value)
+{
+ char temp[EXESS_MAX_DECIMAL_LENGTH + 1] = {0};
+
+ const ExessResult r = exess_write_decimal(value, sizeof(temp), temp);
+ MUST_SUCCEED(r.status); // The only error is buffer overrun
+
+ return serd_node_construct_literal(buf_size,
+ buf,
+ SERD_SUBSTRING(temp, r.count),
+ SERD_HAS_DATATYPE,
+ SERD_STRING(EXESS_XSD_URI "decimal"));
+}
+
+SerdWriteResult
+serd_node_construct_double(const size_t buf_size,
+ void* const buf,
+ const double value)
+{
+ char temp[EXESS_MAX_DOUBLE_LENGTH + 1] = {0};
+
+ const ExessResult r = exess_write_double(value, sizeof(temp), temp);
+ MUST_SUCCEED(r.status); // The only error is buffer overrun
+
+ return serd_node_construct_literal(buf_size,
+ buf,
+ SERD_SUBSTRING(temp, r.count),
+ SERD_HAS_DATATYPE,
+ SERD_STRING(EXESS_XSD_URI "double"));
+}
+
+SerdWriteResult
+serd_node_construct_float(const size_t buf_size,
+ void* const buf,
+ const float value)
+{
+ char temp[EXESS_MAX_FLOAT_LENGTH + 1] = {0};
+
+ const ExessResult r = exess_write_float(value, sizeof(temp), temp);
+ MUST_SUCCEED(r.status); // The only error is buffer overrun
+
+ return serd_node_construct_literal(buf_size,
+ buf,
+ SERD_SUBSTRING(temp, r.count),
+ SERD_HAS_DATATYPE,
+ SERD_STRING(EXESS_XSD_URI "float"));
+}
+
+SerdWriteResult
+serd_node_construct_integer(const size_t buf_size,
+ void* const buf,
+ const int64_t value,
+ const SerdStringView datatype)
+{
+ if (datatype.len && !serd_uri_string_has_scheme(datatype.buf)) {
+ return result(SERD_ERR_BAD_ARG, 0);
+ }
+
+ char temp[24] = {0};
+ const ExessResult r = exess_write_long(value, sizeof(temp), temp);
+ MUST_SUCCEED(r.status); // The only error is buffer overrun
+
+ return serd_node_construct_literal(
+ buf_size,
+ buf,
+ SERD_SUBSTRING(temp, r.count),
+ SERD_HAS_DATATYPE,
+ datatype.len ? datatype : SERD_STRING(NS_XSD "integer"));
+}
+
+SerdWriteResult
+serd_node_construct_base64(const size_t buf_size,
+ void* const buf,
+ const size_t value_size,
+ const void* const value,
+ const SerdStringView datatype)
+{
+ static const SerdStringView xsd_base64Binary =
+ SERD_STRING(NS_XSD "base64Binary");
+
+ // Verify argument sanity
+ if (!value || !value_size ||
+ (datatype.len && !serd_uri_string_has_scheme(datatype.buf))) {
+ return result(SERD_ERR_BAD_ARG, 0);
+ }
+
+ // Determine the type to use (default to xsd:base64Binary)
+ const SerdStringView type = datatype.len ? datatype : xsd_base64Binary;
+ const size_t type_length = serd_node_pad_length(type.len);
+ const size_t type_size = sizeof(SerdNode) + type_length;
+
+ // Find the length of the encoded string (just an O(1) arithmetic expression)
+ ExessResult r = exess_write_base64(value_size, value, 0, NULL);
+
+ // Check that the provided buffer is large enough
+ const size_t padded_length = serd_node_pad_length(r.count);
+ const size_t total_size = sizeof(SerdNode) + padded_length + type_size;
+ if (!buf || total_size > buf_size) {
+ return result(SERD_ERR_OVERFLOW, total_size);
+ }
+
+ SerdNode* const node = (SerdNode*)buf;
+ node->length = r.count;
+ node->flags = SERD_HAS_DATATYPE;
+ node->type = SERD_LITERAL;
+
+ // Write the encoded base64 into the node body
+ r = exess_write_base64(
+ value_size, value, total_size - sizeof(SerdNode), serd_node_buffer(node));
+
+ MUST_SUCCEED(r.status);
+ (void)r;
+
+ // Append datatype
+ SerdNode* meta_node = node + 1 + (padded_length / sizeof(SerdNode));
+ meta_node->length = type.len;
+ meta_node->flags = 0u;
+ meta_node->type = SERD_URI;
+ memcpy(serd_node_buffer(meta_node), type.buf, type.len);
+
+ return result(SERD_SUCCESS, total_size);
+}
+
+static size_t
+string_sink(const void* const buf,
+ const size_t size,
+ const size_t nmemb,
+ void* const stream)
+{
+ char** ptr = (char**)stream;
+ memcpy(*ptr, buf, size * nmemb);
+ *ptr += size * nmemb;
+ return nmemb;
+}
+
+SerdWriteResult
+serd_node_construct_uri(const size_t buf_size,
+ void* const buf,
+ const SerdURIView uri)
+{
+ const size_t length = serd_uri_string_length(uri);
+ const size_t required_size = sizeof(SerdNode) + serd_node_pad_length(length);
+ if (!buf || buf_size < required_size) {
+ return result(SERD_ERR_OVERFLOW, required_size);
+ }
+
+ // Write node header
+ SerdNode* const node = (SerdNode*)buf;
+ node->length = length;
+ node->flags = 0u;
+ node->type = SERD_URI;
+
+ // Serialise URI to node body
+ char* ptr = serd_node_buffer(node);
+ const size_t actual_length = serd_write_uri(uri, string_sink, &ptr);
+ assert(actual_length == length);
+
+ serd_node_buffer(node)[actual_length] = '\0';
serd_node_check_padding(node);
+ return result(SERD_SUCCESS, required_size);
+}
+
+SerdNode*
+serd_node_new(const SerdNodeType type,
+ const SerdStringView string,
+ const SerdNodeFlags flags,
+ const SerdStringView meta)
+{
+ SerdWriteResult r = serd_node_construct(0, NULL, type, string, flags, meta);
+ if (r.status != SERD_ERR_OVERFLOW) {
+ return NULL;
+ }
+
+ assert(r.count % sizeof(SerdNode) == 0);
+
+ SerdNode* const node = serd_node_malloc(sizeof(SerdNode) + r.count + 1);
+
+ if (node) {
+ r = serd_node_construct(r.count, node, type, string, flags, meta);
+ MUST_SUCCEED(r.status); // Any error should have been reported above
+ }
+
return node;
}
SerdNode*
-serd_new_blank(const SerdStringView str)
+serd_new_token(const SerdNodeType type, const SerdStringView string)
+{
+ return serd_node_new(type, string, 0u, SERD_EMPTY_STRING());
+}
+
+SerdNode*
+serd_new_string(const SerdStringView str)
+{
+ return serd_node_new(SERD_LITERAL, str, 0u, SERD_EMPTY_STRING());
+}
+
+SerdNode*
+serd_new_literal(const SerdStringView str,
+ const SerdNodeFlags flags,
+ const SerdStringView meta)
{
- return serd_new_token(SERD_BLANK, str);
+ return serd_node_new(SERD_LITERAL, str, flags, meta);
}
ExessResult
@@ -454,68 +670,96 @@ serd_node_compare(const SerdNode* const a, const SerdNode* const b)
}
SerdNode*
-serd_new_uri(const SerdStringView str)
+serd_new_uri(const SerdStringView string)
{
- return serd_new_token(SERD_URI, str);
+ return serd_new_token(SERD_URI, string);
}
SerdNode*
serd_new_parsed_uri(const SerdURIView uri)
{
- const size_t len = serd_uri_string_length(uri);
- SerdNode* const node = serd_node_malloc(len, 0, SERD_URI);
- char* ptr = serd_node_buffer(node);
- const size_t actual_len = serd_write_uri(uri, string_sink, &ptr);
-
- assert(actual_len == len);
+ SerdWriteResult r = serd_node_construct_uri(0u, NULL, uri);
+ SerdNode* const node = serd_node_try_malloc(r);
- serd_node_buffer(node)[actual_len] = '\0';
- node->length = actual_len;
+ if (node) {
+ r = serd_node_construct_uri(r.count, node, uri);
+ MUST_SUCCEED(r.status);
+ }
- serd_node_check_padding(node);
return node;
}
-SerdNode*
-serd_new_file_uri(const SerdStringView path, const SerdStringView hostname)
-{
- SerdBuffer buffer = {NULL, 0u};
+typedef struct {
+ char* buf;
+ size_t len;
+ size_t offset;
+} ConstructWriteHead;
- serd_write_file_uri(path, hostname, serd_buffer_sink, &buffer);
- serd_buffer_sink_finish(&buffer);
+static size_t
+construct_write(const void* const buf,
+ const size_t size,
+ const size_t nmemb,
+ void* const stream)
+{
+ const size_t n_bytes = size * nmemb;
+ ConstructWriteHead* const head = (ConstructWriteHead*)stream;
- SerdNode* node =
- serd_new_uri(SERD_SUBSTRING((const char*)buffer.buf, buffer.len - 1));
+ if (head->buf && head->offset + n_bytes <= head->len) {
+ memcpy(head->buf + head->offset, buf, n_bytes);
+ }
- free(buffer.buf);
- serd_node_check_padding(node);
- return node;
+ head->offset += n_bytes;
+ return n_bytes;
}
-typedef size_t (*SerdWriteLiteralFunc)(const void* user_data,
- size_t buf_size,
- char* buf);
+SerdWriteResult
+serd_node_construct_file_uri(const size_t buf_size,
+ void* const buf,
+ const SerdStringView path,
+ const SerdStringView hostname)
+{
+ SerdNode* const node = (SerdNode*)buf;
+ ConstructWriteHead head = {(char*)buf, buf_size, 0u};
+ size_t count = 0u;
+
+ // Write node header
+ SerdNode header = {0u, 0u, SERD_URI};
+ count += construct_write(&header, 1, sizeof(header), &head);
+
+ // Write URI string node body
+ const size_t length =
+ serd_write_file_uri(path, hostname, construct_write, &head);
+
+ // Terminate string
+ count += length;
+ count += construct_write("", 1, 1, &head);
+
+ // Write any additional null bytes needed for padding
+ const size_t padded_length = serd_node_pad_length(length);
+ for (size_t p = 0u; p < padded_length - length; ++p) {
+ count += construct_write("", 1, 1, &head);
+ }
-static SerdNode*
-serd_new_custom_literal(const void* const user_data,
- const size_t len,
- const SerdWriteLiteralFunc write,
- const SerdNode* const datatype)
-{
- if (len == 0 || !write) {
- return NULL;
+ if (!buf || count > buf_size) {
+ return result(SERD_ERR_OVERFLOW, count);
}
- const size_t datatype_size = serd_node_total_size(datatype);
- const size_t total_size = serd_node_pad_length(len) + datatype_size;
+ node->length = length;
+ assert(node->length == strlen(serd_node_string(node)));
- SerdNode* const node = serd_node_malloc(
- total_size, datatype ? SERD_HAS_DATATYPE : 0u, SERD_LITERAL);
+ return result(SERD_SUCCESS, count);
+}
- node->length = write(user_data, len + 1, serd_node_buffer(node));
+SerdNode*
+serd_new_file_uri(const SerdStringView path, const SerdStringView hostname)
+{
+ SerdWriteResult r = serd_node_construct_file_uri(0, NULL, path, hostname);
+ SerdNode* const node = serd_node_try_malloc(r);
- if (datatype) {
- memcpy(serd_node_meta(node), datatype, datatype_size);
+ if (node) {
+ r = serd_node_construct_file_uri(r.count, node, path, hostname);
+ MUST_SUCCEED(r.status);
+ assert(serd_node_length(node) == strlen(serd_node_string(node)));
}
serd_node_check_padding(node);
@@ -525,107 +769,97 @@ serd_new_custom_literal(const void* const user_data,
SerdNode*
serd_new_double(const double d)
{
- char buf[EXESS_MAX_DOUBLE_LENGTH + 1] = {0};
+ SerdWriteResult r = serd_node_construct_double(0, NULL, d);
+ SerdNode* const node = serd_node_try_malloc(r);
- const ExessResult r = exess_write_double(d, sizeof(buf), buf);
+ if (node) {
+ r = serd_node_construct_double(r.count, node, d);
+ MUST_SUCCEED(r.status);
+ assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ }
- return r.status ? NULL
- : serd_new_literal(SERD_SUBSTRING(buf, r.count),
- SERD_HAS_DATATYPE,
- SERD_STRING(EXESS_XSD_URI "double"));
+ serd_node_check_padding(node);
+ return node;
}
SerdNode*
serd_new_float(const float f)
{
- char buf[EXESS_MAX_FLOAT_LENGTH + 1] = {0};
+ SerdWriteResult r = serd_node_construct_float(0, NULL, f);
+ SerdNode* const node = serd_node_try_malloc(r);
- const ExessResult r = exess_write_float(f, sizeof(buf), buf);
+ if (node) {
+ r = serd_node_construct_float(r.count, node, f);
+ MUST_SUCCEED(r.status);
+ assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ }
- return r.status ? NULL
- : serd_new_literal(SERD_SUBSTRING(buf, r.count),
- SERD_HAS_DATATYPE,
- SERD_STRING(EXESS_XSD_URI "float"));
+ serd_node_check_padding(node);
+ return node;
}
SerdNode*
serd_new_boolean(bool b)
{
- return serd_new_literal(b ? SERD_STRING("true") : SERD_STRING("false"),
- SERD_HAS_DATATYPE,
- serd_node_string_view(&serd_xsd_boolean.node));
-}
-
-SerdNode*
-serd_new_decimal(const double d, const SerdNode* const datatype)
-{
- // Use given datatype, or xsd:decimal as a default if it is null
- const SerdNode* type = datatype ? datatype : &serd_xsd_decimal.node;
- const size_t type_size = serd_node_total_size(type);
-
- // Measure integer string to know how much space the node will need
- ExessResult r = exess_write_decimal(d, 0, NULL);
- assert(!r.status);
+ SerdWriteResult r = serd_node_construct_boolean(0, NULL, b);
+ SerdNode* const node = serd_node_try_malloc(r);
- // Allocate node with enough space for value and datatype URI
- SerdNode* const node = serd_node_malloc(
- serd_node_pad_length(r.count) + type_size, SERD_HAS_DATATYPE, SERD_LITERAL);
-
- // Write string directly into node
- r = exess_write_decimal(d, r.count + 1, serd_node_buffer(node));
- assert(!r.status);
+ if (node) {
+ r = serd_node_construct_boolean(r.count, node, b);
+ MUST_SUCCEED(r.status);
+ assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ }
- node->length = r.count;
- memcpy(serd_node_meta(node), type, type_size);
serd_node_check_padding(node);
return node;
}
SerdNode*
-serd_new_integer(const int64_t i, const SerdNode* const datatype)
+serd_new_decimal(const double d)
{
- // Use given datatype, or xsd:integer as a default if it is null
- const SerdNode* type = datatype ? datatype : &serd_xsd_integer.node;
- const size_t type_size = serd_node_total_size(type);
-
- // Measure integer string to know how much space the node will need
- ExessResult r = exess_write_long(i, 0, NULL);
- assert(!r.status);
-
- // Allocate node with enough space for value and datatype URI
- SerdNode* const node = serd_node_malloc(
- serd_node_pad_length(r.count) + type_size, SERD_HAS_DATATYPE, SERD_LITERAL);
+ SerdWriteResult r = serd_node_construct_decimal(0, NULL, d);
+ SerdNode* const node = serd_node_try_malloc(r);
- // Write string directly into node
- r = exess_write_long(i, r.count + 1, serd_node_buffer(node));
- assert(!r.status);
+ if (node) {
+ r = serd_node_construct_decimal(r.count, node, d);
+ MUST_SUCCEED(r.status);
+ assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ }
- node->length = r.count;
- memcpy(serd_node_meta(node), type, type_size);
serd_node_check_padding(node);
return node;
}
-static size_t
-write_base64_literal(const void* const user_data,
- const size_t buf_size,
- char* const buf)
+SerdNode*
+serd_new_integer(const int64_t i, const SerdStringView datatype)
{
- const SerdConstBuffer blob = *(const SerdConstBuffer*)user_data;
+ SerdWriteResult r = serd_node_construct_integer(0, NULL, i, datatype);
+ SerdNode* const node = serd_node_try_malloc(r);
- const ExessResult r = exess_write_base64(blob.len, blob.buf, buf_size, buf);
+ if (node) {
+ r = serd_node_construct_integer(r.count, node, i, datatype);
+ MUST_SUCCEED(r.status);
+ assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ }
- return r.status ? 0 : r.count;
+ serd_node_check_padding(node);
+ return node;
}
SerdNode*
-serd_new_base64(const void* buf, size_t size, const SerdNode* datatype)
+serd_new_base64(const void* buf, size_t size, const SerdStringView datatype)
{
- const size_t len = exess_write_base64(size, buf, 0, NULL).count;
- const SerdNode* type = datatype ? datatype : &serd_xsd_base64Binary.node;
- SerdConstBuffer blob = {buf, size};
+ SerdWriteResult r = serd_node_construct_base64(0, NULL, size, buf, datatype);
+ SerdNode* const node = serd_node_try_malloc(r);
+
+ if (node) {
+ r = serd_node_construct_base64(r.count, node, size, buf, datatype);
+ MUST_SUCCEED(r.status);
+ assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ }
- return serd_new_custom_literal(&blob, len, write_base64_literal, type);
+ serd_node_check_padding(node);
+ return node;
}
SerdNodeType
@@ -696,3 +930,5 @@ serd_node_free(SerdNode* const node)
{
serd_free_aligned(node);
}
+
+#undef MUST_SUCCEED
diff --git a/src/node.h b/src/node.h
index affa2efc..ab10a3e7 100644
--- a/src/node.h
+++ b/src/node.h
@@ -20,6 +20,7 @@
#include "exess/exess.h"
#include "serd/serd.h"
+#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -32,6 +33,31 @@ struct SerdNodeImpl {
static const size_t serd_node_align = 2 * sizeof(uint64_t);
+#if SIZE_MAX == UINT64_MAX
+
+static inline size_t
+serd_node_pad_length(const size_t n_bytes)
+{
+ const size_t align = sizeof(SerdNode);
+
+ assert((align & (align - 1u)) == 0u);
+
+ return (n_bytes + align + 2u) & ~(align - 1u);
+}
+
+#else
+
+static inline size_t
+serd_node_pad_length(const size_t n_bytes)
+{
+ const size_t pad = sizeof(SerdNode) - (n_bytes + 2) % sizeof(SerdNode);
+ const size_t size = n_bytes + 2 + pad;
+ assert(size % sizeof(SerdNode) == 0);
+ return size;
+}
+
+#endif
+
static inline char* SERD_NONNULL
serd_node_buffer(SerdNode* SERD_NONNULL node)
{
@@ -44,6 +70,19 @@ serd_node_buffer_c(const SerdNode* SERD_NONNULL node)
return (const char*)(node + 1);
}
+static inline SerdNode* SERD_NONNULL
+serd_node_meta(SerdNode* const SERD_NONNULL node)
+{
+ return node + 1 + (serd_node_pad_length(node->length) / sizeof(SerdNode));
+}
+
+static inline const SerdNode* SERD_NONNULL
+serd_node_meta_c(const SerdNode* const SERD_NONNULL node)
+{
+ assert(node->flags & (SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE));
+ return node + 1 + (serd_node_pad_length(node->length) / sizeof(SerdNode));
+}
+
static inline const char* SERD_NONNULL
serd_node_string_i(const SerdNode* const SERD_NONNULL node)
{
@@ -57,8 +96,17 @@ serd_node_pattern_match(const SerdNode* SERD_NULLABLE a,
return !a || !b || serd_node_equals(a, b);
}
+SERD_PURE_FUNC
+bool
+is_langtag(SerdStringView string);
+
+SERD_MALLOC_FUNC
+SerdNode* SERD_ALLOCATED
+serd_node_malloc(size_t size);
+
+SERD_MALLOC_FUNC
SerdNode* SERD_ALLOCATED
-serd_node_malloc(size_t length, SerdNodeFlags flags, SerdNodeType type);
+serd_node_try_malloc(SerdWriteResult result);
void
serd_node_set(SerdNode* SERD_NULLABLE* SERD_NONNULL dst,
@@ -66,7 +114,7 @@ serd_node_set(SerdNode* SERD_NULLABLE* SERD_NONNULL dst,
SERD_PURE_FUNC
size_t
-serd_node_total_size(const SerdNode* SERD_NULLABLE node);
+serd_node_total_size(const SerdNode* SERD_NONNULL node);
void
serd_node_zero_pad(SerdNode* SERD_NONNULL node);
diff --git a/src/nodes.c b/src/nodes.c
index 51f354bb..885731cc 100644
--- a/src/nodes.c
+++ b/src/nodes.c
@@ -174,7 +174,7 @@ serd_nodes_uri(SerdNodes* const nodes, const SerdStringView string)
const SerdNode*
serd_nodes_blank(SerdNodes* const nodes, const SerdStringView string)
{
- return serd_nodes_manage(nodes, serd_new_blank(string));
+ return serd_nodes_manage(nodes, serd_new_token(SERD_BLANK, string));
}
void
diff --git a/src/static_nodes.h b/src/static_nodes.h
deleted file mode 100644
index 467d846f..00000000
--- a/src/static_nodes.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- Copyright 2019-2020 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#ifndef SERD_STATIC_NODES_H
-#define SERD_STATIC_NODES_H
-
-#include "serd/serd.h"
-
-#include "namespaces.h"
-#include "node.h"
-
-typedef struct StaticNode {
- SerdNode node;
- char buf[sizeof(NS_XSD "base64Binary") + sizeof(SerdNode)];
-} StaticNode;
-
-#define DEFINE_XSD_NODE(name) \
- static const StaticNode serd_xsd_##name = { \
- {sizeof(NS_XSD #name) - 1, 0, SERD_URI}, NS_XSD #name};
-
-DEFINE_XSD_NODE(base64Binary)
-DEFINE_XSD_NODE(boolean)
-DEFINE_XSD_NODE(decimal)
-DEFINE_XSD_NODE(double)
-DEFINE_XSD_NODE(float)
-DEFINE_XSD_NODE(integer)
-
-#endif // SERD_STATIC_NODES_H
diff --git a/src/world.c b/src/world.c
index d0ad60da..5af8c3d4 100644
--- a/src/world.c
+++ b/src/world.c
@@ -87,7 +87,7 @@ serd_world_new(void)
world->xsd_decimal = serd_nodes_uri(nodes, xsd_decimal);
world->xsd_integer = serd_nodes_uri(nodes, xsd_integer);
- world->blank_node = serd_new_blank(SERD_STRING("b00000000000"));
+ world->blank_node = serd_new_token(SERD_BLANK, SERD_STRING("b00000000000"));
world->nodes = nodes;
world->stderr_color = terminal_supports_color(stderr);
diff --git a/test/test_env.c b/test/test_env.c
index 1b924ae9..9ab13ef2 100644
--- a/test/test_env.c
+++ b/test/test_env.c
@@ -260,7 +260,7 @@ test_expand_bad_curie(void)
static void
test_expand_blank(void)
{
- SerdNode* const blank = serd_new_blank(SERD_STRING("b1"));
+ SerdNode* const blank = serd_new_token(SERD_BLANK, SERD_STRING("b1"));
SerdEnv* const env = serd_env_new(SERD_EMPTY_STRING());
assert(!serd_env_expand_node(env, blank));
diff --git a/test/test_node.c b/test/test_node.c
index efd93ddd..200791af 100644
--- a/test/test_node.c
+++ b/test/test_node.c
@@ -123,7 +123,7 @@ test_decimal(void)
"0.0000000001"};
for (size_t i = 0; i < sizeof(test_values) / sizeof(double); ++i) {
- SerdNode* node = serd_new_decimal(test_values[i], NULL);
+ SerdNode* node = serd_new_decimal(test_values[i]);
const char* node_str = serd_node_string(node);
assert(!strcmp(node_str, test_strings[i]));
@@ -205,7 +205,8 @@ test_get_double(void)
assert(isnan(serd_get_double(invalid)));
serd_node_free(invalid);
- SerdNode* const base64 = serd_new_base64(blob, sizeof(blob), NULL);
+ SerdNode* const base64 =
+ serd_new_base64(blob, sizeof(blob), SERD_EMPTY_STRING());
assert(isnan(serd_get_double(base64)));
serd_node_free(base64);
@@ -283,12 +284,14 @@ test_get_float(void)
static void
test_integer(void)
{
+ assert(!serd_new_integer(42, SERD_STRING("notauri")));
+
const int64_t test_values[] = {0, -0, -23, 23, -12340, 1000, -1000};
const char* test_strings[] = {
"0", "0", "-23", "23", "-12340", "1000", "-1000"};
for (size_t i = 0; i < sizeof(test_values) / sizeof(double); ++i) {
- SerdNode* node = serd_new_integer(test_values[i], NULL);
+ SerdNode* node = serd_new_integer(test_values[i], SERD_EMPTY_STRING());
const char* node_str = serd_node_string(node);
assert(!strcmp(node_str, test_strings[i]));
const size_t len = strlen(node_str);
@@ -333,7 +336,8 @@ test_get_integer(void)
static void
test_base64(void)
{
- assert(!serd_new_base64(&SERD_URI_NULL, 0, NULL));
+ assert(!serd_new_base64(&SERD_URI_NULL, 1, SERD_STRING("notauri")));
+ assert(!serd_new_base64(&SERD_URI_NULL, 0, SERD_EMPTY_STRING()));
// Test valid base64 blobs with a range of sizes
for (size_t size = 1; size < 256; ++size) {
@@ -342,7 +346,7 @@ test_base64(void)
data[i] = (uint8_t)((size + i) % 256);
}
- SerdNode* blob = serd_new_base64(data, size, NULL);
+ SerdNode* blob = serd_new_base64(data, size, SERD_EMPTY_STRING());
const char* blob_str = serd_node_string(blob);
const size_t max_size = serd_get_base64_size(blob);
uint8_t* out = (uint8_t*)calloc(1, max_size);
@@ -460,6 +464,28 @@ check_copy_equals(const SerdNode* const node)
}
static void
+test_new(void)
+{
+ assert(!serd_node_new(SERD_URI,
+ SERD_STRING("http://example.org/"),
+ SERD_HAS_DATATYPE,
+ SERD_STRING("http://example.org/uris/cant/have/me")));
+
+ assert(!serd_node_new(SERD_URI,
+ SERD_STRING("http://example.org/"),
+ SERD_HAS_LANGUAGE,
+ SERD_STRING("in-valid")));
+
+ assert(!serd_node_new(SERD_BLANK,
+ SERD_STRING("b0"),
+ SERD_HAS_DATATYPE,
+ SERD_STRING("http://example.org/uris/cant/have/me")));
+
+ assert(!serd_node_new(
+ SERD_BLANK, SERD_STRING("b0"), SERD_HAS_LANGUAGE, SERD_STRING("in-valid")));
+}
+
+static void
test_literal(void)
{
static const SerdStringView hello_str = SERD_STRING("hello");
@@ -522,7 +548,7 @@ test_literal(void)
static void
test_blank(void)
{
- SerdNode* blank = serd_new_blank(SERD_STRING("b0"));
+ SerdNode* blank = serd_new_token(SERD_BLANK, SERD_STRING("b0"));
assert(serd_node_length(blank) == 2);
assert(serd_node_flags(blank) == 0);
assert(!strcmp(serd_node_string(blank), "b0"));
@@ -532,15 +558,14 @@ test_blank(void)
static void
test_compare(void)
{
- SerdNode* xsd_short =
- serd_new_uri(SERD_STRING("http://www.w3.org/2001/XMLSchema#short"));
+ SerdNode* xsd_short = serd_new_token(
+ SERD_URI, SERD_STRING("http://www.w3.org/2001/XMLSchema#short"));
SerdNode* angst = serd_new_string(SERD_STRING("angst"));
SerdNode* angst_de = serd_new_literal(
SERD_STRING("angst"), SERD_HAS_LANGUAGE, SERD_STRING("de"));
- assert(angst_de);
SerdNode* angst_en = serd_new_literal(
SERD_STRING("angst"), SERD_HAS_LANGUAGE, SERD_STRING("en"));
@@ -549,10 +574,20 @@ test_compare(void)
SerdNode* hello = serd_new_string(SERD_STRING("Hello"));
SerdNode* universe = serd_new_string(SERD_STRING("Universe"));
- SerdNode* integer = serd_new_integer(4, NULL);
- SerdNode* short_integer = serd_new_integer(4, xsd_short);
- SerdNode* blank = serd_new_blank(SERD_STRING("b1"));
- SerdNode* uri = serd_new_uri(SERD_STRING("http://example.org/"));
+ SerdNode* integer = serd_new_integer(4, SERD_EMPTY_STRING());
+ SerdNode* short_integer = serd_new_integer(4, SERD_STRING(NS_XSD "short"));
+ SerdNode* blank = serd_new_token(SERD_BLANK, SERD_STRING("b1"));
+
+ SerdNode* uri = serd_new_uri(SERD_STRING("http://example.org/"));
+
+ SerdNode* aardvark =
+ serd_new_literal(SERD_STRING("alex"),
+ SERD_HAS_DATATYPE,
+ SERD_STRING("http://example.org/Aardvark"));
+
+ SerdNode* badger = serd_new_literal(SERD_STRING("bobby"),
+ SERD_HAS_DATATYPE,
+ SERD_STRING("http://example.org/Badger"));
// Types are ordered according to their SerdNodeType (more or less arbitrary)
assert(serd_node_compare(hello, uri) < 0);
@@ -565,11 +600,14 @@ test_compare(void)
assert(serd_node_compare(angst, angst_de) < 0);
assert(serd_node_compare(angst_de, angst_en) < 0);
assert(serd_node_compare(integer, short_integer) < 0);
+ assert(serd_node_compare(aardvark, badger) < 0);
serd_node_free(uri);
serd_node_free(blank);
serd_node_free(short_integer);
serd_node_free(integer);
+ serd_node_free(badger);
+ serd_node_free(aardvark);
serd_node_free(universe);
serd_node_free(hello);
serd_node_free(hallo);
@@ -596,6 +634,7 @@ main(void)
test_node_equals();
test_node_from_syntax();
test_node_from_substring();
+ test_new();
test_literal();
test_blank();
test_compare();
diff --git a/test/test_node_syntax.c b/test/test_node_syntax.c
index 26b57ff1..a9829688 100644
--- a/test/test_node_syntax.c
+++ b/test/test_node_syntax.c
@@ -47,8 +47,8 @@ test_common(const SerdSyntax syntax)
static const SerdStringView datatype =
SERD_STRING("http://example.org/Datatype");
- SerdNode* const num_type =
- serd_new_uri(SERD_STRING("http://example.org/Decimal"));
+ static const SerdStringView num_type =
+ SERD_STRING("http://example.org/Decimal");
assert(test(syntax, serd_new_string(SERD_STRING("node")), "\"node\""));
@@ -61,18 +61,16 @@ test_common(const SerdSyntax syntax)
serd_new_literal(SERD_STRING("X"), SERD_HAS_DATATYPE, datatype),
"\"X\"^^<http://example.org/Datatype>"));
- assert(test(syntax, serd_new_blank(SERD_STRING("blank")), "_:blank"));
- assert(test(syntax, serd_new_blank(SERD_STRING("b0")), "_:b0"));
+ assert(
+ test(syntax, serd_new_token(SERD_BLANK, SERD_STRING("blank")), "_:blank"));
+
+ assert(test(syntax, serd_new_token(SERD_BLANK, SERD_STRING("b0")), "_:b0"));
assert(test(syntax,
serd_new_uri(SERD_STRING("http://example.org/")),
"<http://example.org/>"));
assert(test(syntax,
- serd_new_decimal(1.25, num_type),
- "\"1.25\"^^<http://example.org/Decimal>"));
-
- assert(test(syntax,
serd_new_double(1.25),
"\"1.25E0\"^^<http://www.w3.org/2001/XMLSchema#double>"));
@@ -86,10 +84,8 @@ test_common(const SerdSyntax syntax)
assert(
test(syntax,
- serd_new_base64(data, sizeof(data), NULL),
+ serd_new_base64(data, sizeof(data), SERD_EMPTY_STRING()),
"\"BAAAAAIAAAA=\"^^<http://www.w3.org/2001/XMLSchema#base64Binary>"));
-
- serd_node_free(num_type);
}
static void
@@ -118,11 +114,11 @@ test_ntriples(void)
}
assert(test(SERD_NTRIPLES,
- serd_new_decimal(1.25, NULL),
+ serd_new_decimal(1.25),
"\"1.25\"^^<http://www.w3.org/2001/XMLSchema#decimal>"));
assert(test(SERD_NTRIPLES,
- serd_new_integer(1234, NULL),
+ serd_new_integer(1234, SERD_EMPTY_STRING()),
"\"1234\"^^<http://www.w3.org/2001/XMLSchema#integer>"));
assert(test(SERD_NTRIPLES,
@@ -137,10 +133,20 @@ test_ntriples(void)
static void
test_turtle(void)
{
+ static const SerdStringView xsd_integer =
+ SERD_STRING("http://www.w3.org/2001/XMLSchema#integer");
+
test_common(SERD_TURTLE);
+
test(SERD_TURTLE, serd_new_uri(SERD_STRING("rel/uri")), "<rel/uri>");
- assert(test(SERD_TURTLE, serd_new_decimal(1.25, NULL), "1.25"));
- assert(test(SERD_TURTLE, serd_new_integer(1234, NULL), "1234"));
+
+ assert(test(SERD_TURTLE, serd_new_decimal(1.25), "1.25"));
+
+ assert(
+ test(SERD_TURTLE, serd_new_integer(1234, SERD_EMPTY_STRING()), "1234"));
+
+ assert(test(SERD_TURTLE, serd_new_integer(1234, xsd_integer), "1234"));
+
assert(test(SERD_TURTLE, serd_new_boolean(true), "true"));
assert(test(SERD_TURTLE, serd_new_boolean(false), "false"));
}