diff options
author | David Robillard <d@drobilla.net> | 2021-07-22 23:39:39 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-01-14 19:37:51 -0500 |
commit | a63a8f19c54dfee75e092819d6622b8d36fe1d39 (patch) | |
tree | 3c2fe49c257e27d369365a0c08c1524baaf74a4c | |
parent | 64e81dfd6ec04995fd396269deb6b32fe2d1192d (diff) | |
download | serd-a63a8f19c54dfee75e092819d6622b8d36fe1d39.tar.gz serd-a63a8f19c54dfee75e092819d6622b8d36fe1d39.tar.bz2 serd-a63a8f19c54dfee75e092819d6622b8d36fe1d39.zip |
Expose low-level node construction API
-rw-r--r-- | include/serd/serd.h | 387 | ||||
-rw-r--r-- | src/env.c | 28 | ||||
-rw-r--r-- | src/n3.c | 5 | ||||
-rw-r--r-- | src/node.c | 660 | ||||
-rw-r--r-- | src/node.h | 52 | ||||
-rw-r--r-- | src/nodes.c | 2 | ||||
-rw-r--r-- | src/static_nodes.h | 41 | ||||
-rw-r--r-- | src/world.c | 2 | ||||
-rw-r--r-- | test/test_env.c | 2 | ||||
-rw-r--r-- | test/test_node.c | 65 | ||||
-rw-r--r-- | test/test_node_syntax.c | 36 |
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. @@ -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* @@ -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); @@ -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 @@ -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")); } |