From 4711fdf527f416faee8ff19e15f050d4b48dcfb2 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 5 May 2023 09:43:57 -0400 Subject: [WIP] Generalize node construction API --- src/base64.c | 27 -- src/base64.h | 35 -- src/byte_source.c | 3 +- src/env.c | 37 ++- src/node.c | 967 ++++++++++++++++++++++++++++++++++-------------------- src/node.h | 66 +++- src/read_turtle.c | 2 +- src/value.c | 101 ++++++ src/world.c | 4 +- src/writer.c | 7 +- 10 files changed, 788 insertions(+), 461 deletions(-) delete mode 100644 src/base64.c delete mode 100644 src/base64.h create mode 100644 src/value.c (limited to 'src') diff --git a/src/base64.c b/src/base64.c deleted file mode 100644 index 50ec3981..00000000 --- a/src/base64.c +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2011-2020 David Robillard -// SPDX-License-Identifier: ISC - -#include "base64.h" - -#include "exess/exess.h" -#include "serd/string.h" - -#include - -void* -serd_base64_decode(const char* const str, const size_t len, size_t* const size) -{ - const size_t max_size = exess_base64_decoded_size(len); - - void* const buf = malloc(max_size); - const ExessVariableResult r = exess_read_base64(max_size, buf, str); - if (r.status) { - *size = 0; - free(buf); - return NULL; - } - - *size = r.write_count; - - return buf; -} diff --git a/src/base64.h b/src/base64.h deleted file mode 100644 index d3e2b6e1..00000000 --- a/src/base64.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2011-2023 David Robillard -// SPDX-License-Identifier: ISC - -#ifndef SERD_SRC_BASE64_H -#define SERD_SRC_BASE64_H - -#include "serd/attributes.h" - -#include -#include -#include - -/** - Return the number of bytes required to encode `size` bytes in base64. - - @param size The number of input (binary) bytes to encode. - @param wrap_lines Wrap lines at 76 characters to conform to RFC 2045. - @return The length of the base64 encoding, excluding null terminator. -*/ -SERD_CONST_FUNC size_t -serd_base64_get_length(size_t size, bool wrap_lines); - -/** - Encode `size` bytes of `buf` into `str`, which must be large enough. - - @param str Output string buffer. - @param buf Input binary data. - @param size Number of bytes to encode from `buf`. - @param wrap_lines Wrap lines at 76 characters to conform to RFC 2045. - @return True iff `str` contains newlines. -*/ -bool -serd_base64_encode(uint8_t* str, const void* buf, size_t size, bool wrap_lines); - -#endif // SERD_SRC_BASE64_H diff --git a/src/byte_source.c b/src/byte_source.c index cf9a2466..249c0dde 100644 --- a/src/byte_source.c +++ b/src/byte_source.c @@ -7,7 +7,6 @@ #include "system.h" #include "serd/node.h" -#include "serd/string_view.h" #include #include @@ -67,7 +66,7 @@ serd_byte_source_new_input(SerdAllocator* const allocator, SerdNode* const source_name = name ? serd_node_copy(allocator, name) - : serd_new_string(allocator, serd_string("input")); + : serd_node_new(allocator, serd_a_string("input")); if (!source_name) { return NULL; diff --git a/src/env.c b/src/env.c index 90924cd2..dcffb98e 100644 --- a/src/env.c +++ b/src/env.c @@ -159,7 +159,7 @@ serd_env_set_base_uri(SerdEnv* const env, const SerdStringView uri) // Replace the current base URI if ((env->base_uri_node = - serd_new_parsed_uri(env->allocator, new_base_uri))) { + serd_node_new(env->allocator, serd_a_parsed_uri(new_base_uri)))) { env->base_uri = serd_node_uri_view(env->base_uri_node); } else { return SERD_BAD_ALLOC; @@ -209,7 +209,7 @@ serd_env_add(SerdEnv* const env, if (prefix) { if (!!strcmp(serd_node_string(prefix->uri), uri.data)) { serd_node_free(env->allocator, prefix->uri); - prefix->uri = serd_new_uri(env->allocator, uri); + prefix->uri = serd_node_new(env->allocator, serd_a_uri(uri)); } } else { SerdPrefix* const new_prefixes = @@ -222,8 +222,9 @@ serd_env_add(SerdEnv* const env, env->prefixes = new_prefixes; - SerdNode* const name_node = serd_new_string(env->allocator, name); - SerdNode* const uri_node = serd_new_uri(env->allocator, uri); + SerdNode* const name_node = + serd_node_new(env->allocator, serd_a_string_view(name)); + SerdNode* const uri_node = serd_node_new(env->allocator, serd_a_uri(uri)); if (!name_node || !uri_node) { serd_node_free(env->allocator, uri_node); serd_node_free(env->allocator, name_node); @@ -260,7 +261,8 @@ serd_env_set_prefix(SerdEnv* const env, assert(abs_uri_view.scheme.length); // Create a new node for the absolute URI - SerdNode* const abs_uri = serd_new_parsed_uri(env->allocator, abs_uri_view); + SerdNode* const abs_uri = + serd_node_new(env->allocator, serd_a_parsed_uri(abs_uri_view)); if (!abs_uri) { return SERD_BAD_ALLOC; } @@ -317,7 +319,7 @@ serd_env_expand_in_place(const SerdEnv* const env, return SERD_BAD_CURIE; } - uri_prefix->data = serd_node_string(prefix->uri); + uri_prefix->data = prefix->uri ? serd_node_string(prefix->uri) : ""; uri_prefix->length = prefix->uri ? prefix->uri->length : 0; uri_suffix->data = colon + 1; uri_suffix->length = curie.length - name_len - 1; @@ -338,15 +340,23 @@ serd_env_expand_curie(const SerdEnv* const env, const SerdStringView curie) return NULL; } - const size_t len = prefix.length + suffix.length; - SerdNode* const ret = serd_node_malloc(env->allocator, len, 0U, SERD_URI); - if (ret) { - char* const string = serd_node_buffer(ret); + const size_t len = prefix.length + suffix.length; + const size_t real_length = serd_node_pad_length(len); + const size_t node_size = sizeof(SerdNode) + real_length; + SerdNode* node = serd_node_malloc(env->allocator, node_size); + + if (node) { + node->length = len; + node->flags = 0U; + node->type = SERD_URI; + + char* const string = (char*)(node + 1U); + assert(prefix.data); memcpy(string, prefix.data, prefix.length); memcpy(string + prefix.length, suffix.data, suffix.length); } - return ret; + return node; } SerdNode* @@ -359,8 +369,9 @@ serd_env_expand_node(const SerdEnv* const env, const SerdNode* const node) const SerdURIView uri = serd_node_uri_view(node); const SerdURIView abs_uri = serd_resolve_uri(uri, env->base_uri); - return abs_uri.scheme.length ? serd_new_parsed_uri(env->allocator, abs_uri) - : NULL; + return abs_uri.scheme.length + ? serd_node_new(env->allocator, serd_a_parsed_uri(abs_uri)) + : NULL; } SerdStatus diff --git a/src/node.c b/src/node.c index 135ccc22..b4cd14fa 100644 --- a/src/node.c +++ b/src/node.c @@ -8,81 +8,142 @@ #include "string_utils.h" #include "exess/exess.h" -#include "serd/buffer.h" #include "serd/node.h" #include "serd/status.h" #include "serd/string_view.h" #include "serd/uri.h" +#include "serd/value.h" #include "serd/write_result.h" #include "zix/attributes.h" #include -#include #include #include -#include #include -typedef struct { - const void* ZIX_NULLABLE buf; - size_t len; -} SerdConstBuffer; +#ifndef NDEBUG +# define MUST_SUCCEED(status) assert(!(status)) +#else +# define MUST_SUCCEED(status) ((void)(status)) +#endif #define NS_XSD "http://www.w3.org/2001/XMLSchema#" -typedef struct StaticNode { - SerdNode node; - char buf[sizeof(NS_XSD "base64Binary")]; -} StaticNode; +static const SerdNodeFlags meta_mask = (SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE); -#define DEFINE_XSD_NODE(name) \ - static const StaticNode serd_xsd_##name = { \ - {sizeof(NS_XSD #name) - 1, 0, SERD_URI}, NS_XSD #name}; +static const ExessDatatype value_type_datatypes[] = { + EXESS_NOTHING, + EXESS_BOOLEAN, + EXESS_DOUBLE, + EXESS_FLOAT, + EXESS_LONG, + EXESS_INT, + EXESS_SHORT, + EXESS_BYTE, + EXESS_ULONG, + EXESS_UINT, + EXESS_USHORT, + EXESS_UBYTE, +}; -DEFINE_XSD_NODE(base64Binary) -DEFINE_XSD_NODE(boolean) -DEFINE_XSD_NODE(decimal) -DEFINE_XSD_NODE(integer) +// Argument constructors -static const SerdNodeFlags meta_mask = (SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE); +SerdNodeArgs +serd_a_token(const SerdNodeType type, const SerdStringView string) +{ + const SerdNodeArgs args = {SERD_NODE_ARGS_TOKEN, {{type, string}}}; + return args; +} -static size_t -string_sink(const void* const buf, - const size_t size, - const size_t nmemb, - void* const stream) +SerdNodeArgs +serd_a_parsed_uri(const SerdURIView uri) { - char** ptr = (char**)stream; - memcpy(*ptr, buf, size * nmemb); - *ptr += size * nmemb; - return nmemb; + SerdNodeArgs args; + args.type = SERD_NODE_ARGS_PARSED_URI; + args.data.as_parsed_uri.uri = uri; + return args; +} + +SerdNodeArgs +serd_a_file_uri(const SerdStringView path, const SerdStringView hostname) +{ + SerdNodeArgs args; + args.type = SERD_NODE_ARGS_FILE_URI; + args.data.as_file_uri.path = path; + args.data.as_file_uri.hostname = hostname; + return args; } -ZIX_PURE_FUNC static size_t -serd_node_pad_length(const size_t n_bytes) +SerdNodeArgs +serd_a_literal(const SerdStringView string, + const SerdNodeFlags flags, + const SerdStringView meta) { - 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; + SerdNodeArgs args; + args.type = SERD_NODE_ARGS_LITERAL; + args.data.as_literal.string = string; + args.data.as_literal.flags = flags; + args.data.as_literal.meta = meta; + return args; } -static const SerdNode* -serd_node_meta_c(const SerdNode* const node) +SerdNodeArgs +serd_a_primitive(const SerdValue value) { - return node + 1 + (serd_node_pad_length(node->length) / sizeof(SerdNode)); + SerdNodeArgs args; + args.type = SERD_NODE_ARGS_PRIMITIVE; + args.data.as_primitive.value = value; + return args; } -static SerdNode* -serd_node_meta(SerdNode* const node) +SerdNodeArgs +serd_a_decimal(const double value) { - return node + 1 + (serd_node_pad_length(node->length) / sizeof(SerdNode)); + SerdNodeArgs args; + args.type = SERD_NODE_ARGS_DECIMAL; + args.data.as_decimal.value = value; + return args; } -ZIX_PURE_FUNC static const SerdNode* -serd_node_maybe_get_meta_c(const SerdNode* const node) +SerdNodeArgs +serd_a_integer(const int64_t value) +{ + SerdNodeArgs args; + args.type = SERD_NODE_ARGS_INTEGER; + args.data.as_integer.value = value; + return args; +} + +SerdNodeArgs +serd_a_hex(const size_t size, const void* const data) +{ + SerdNodeArgs args; + args.type = SERD_NODE_ARGS_HEX; + args.data.as_blob.size = size; + args.data.as_blob.data = data; + return args; +} + +SerdNodeArgs +serd_a_base64(size_t size, const void* const data) +{ + SerdNodeArgs args; + args.type = SERD_NODE_ARGS_BASE64; + args.data.as_blob.size = size; + args.data.as_blob.data = data; + return args; +} + +// Node functions + +// Round size up to an even multiple of the node alignment +static size_t +serd_node_pad_size(const size_t size) { - return (node->flags & meta_mask) ? serd_node_meta_c(node) : NULL; + const size_t n_trailing = size % serd_node_align; + const size_t n_pad = n_trailing ? (serd_node_align - n_trailing) : 0U; + + return size + n_pad; } static void @@ -96,7 +157,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 } @@ -104,32 +166,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(SerdAllocator* const allocator, - const size_t length, - const SerdNodeFlags flags, - const SerdNodeType type) +serd_node_malloc(SerdAllocator* const allocator, const size_t size) { - const size_t size = sizeof(SerdNode) + serd_node_pad_length(length); - - SerdNode* const node = - (SerdNode*)serd_aaligned_calloc(allocator, serd_node_align, size); - - if (node) { - node->length = 0; - node->flags = flags; - node->type = type; - } + SerdNode* const node = (SerdNode*)serd_aaligned_calloc( + allocator, serd_node_align, serd_node_pad_size(size)); assert((uintptr_t)node % serd_node_align == 0U); return node; } +SerdNode* +serd_node_try_malloc(SerdAllocator* const allocator, const SerdWriteResult r) +{ + return (r.status && r.status != SERD_OVERFLOW) + ? NULL + : serd_node_malloc(allocator, r.count); +} + SerdStatus serd_node_set(SerdAllocator* const allocator, SerdNode** const dst, @@ -179,44 +246,31 @@ result(const SerdStatus status, const size_t count) return result; } -SerdNode* -serd_new_token(SerdAllocator* const allocator, - const SerdNodeType type, - const SerdStringView str) -{ - SerdNodeFlags flags = 0U; - const size_t length = str.data ? str.length : 0U; - SerdNode* node = serd_node_malloc(allocator, length, flags, type); - - if (node) { - if (str.data) { - memcpy(serd_node_buffer(node), str.data, length); - } - - node->length = length; +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.length); + if (!buf || total_size > buf_size) { + return result(SERD_OVERFLOW, total_size); } - serd_node_check_padding(node); + SerdNode* const node = (SerdNode*)buf; - return node; -} - -SerdNode* -serd_new_string(SerdAllocator* const allocator, const SerdStringView str) -{ - SerdNodeFlags flags = 0U; - SerdNode* node = serd_node_malloc(allocator, str.length, flags, SERD_LITERAL); - - if (node) { - if (str.data && str.length) { - memcpy(serd_node_buffer(node), str.data, str.length); - } + node->length = string.length; + node->flags = flags; + node->type = type; - node->length = str.length; - serd_node_check_padding(node); + if (string.data) { + memcpy(serd_node_buffer(node), string.data, string.length); } - return node; + serd_node_zero_pad(node); + return result(SERD_SUCCESS, total_size); } ZIX_PURE_FUNC static bool @@ -248,165 +302,427 @@ is_langtag(const SerdStringView string) return true; } -SerdNode* -serd_new_literal(SerdAllocator* const allocator, - const SerdStringView string, - const SerdNodeFlags flags, - const SerdStringView meta) +static 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(allocator, string.length, flags, SERD_LITERAL); - - memcpy(serd_node_buffer(node), string.data, string.length); - node->length = string.length; - 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_BAD_ARG, 0); } if (!meta.length) { - return NULL; + return result(SERD_BAD_ARG, 0); } if (((flags & SERD_HAS_DATATYPE) && (!serd_uri_string_has_scheme(meta.data) || !strcmp(meta.data, NS_RDF "langString"))) || ((flags & SERD_HAS_LANGUAGE) && !is_langtag(meta))) { - return NULL; + return result(SERD_BAD_ARG, 0); + } + + // Calculate total node size + const size_t padded_len = serd_node_pad_length(string.length); + const size_t meta_size = sizeof(SerdNode) + serd_node_pad_length(meta.length); + const size_t total_size = sizeof(SerdNode) + padded_len + meta_size; + if (!buf || total_size > buf_size) { + return result(SERD_OVERFLOW, total_size); } - const size_t len = serd_node_pad_length(string.length); - const size_t meta_len = serd_node_pad_length(meta.length); - const size_t meta_size = sizeof(SerdNode) + meta_len; + // Write node header + SerdNode* const node = (SerdNode*)buf; + node->length = string.length; + node->flags = flags; + node->type = SERD_LITERAL; - SerdNode* node = - serd_node_malloc(allocator, len + meta_size, flags, SERD_LITERAL); + // Copy string to node body memcpy(serd_node_buffer(node), string.data, string.length); - node->length = string.length; - SerdNode* meta_node = node + 1U + (len / sizeof(SerdNode)); - meta_node->length = meta.length; + // Append datatype or language + SerdNode* meta_node = node + 1 + (padded_len / sizeof(SerdNode)); meta_node->type = (flags & SERD_HAS_DATATYPE) ? SERD_URI : SERD_LITERAL; + meta_node->length = meta.length; memcpy(serd_node_buffer(meta_node), meta.data, meta.length); - serd_node_check_padding(meta_node); - serd_node_check_padding(node); - return node; + serd_node_zero_pad(node); + return result(SERD_SUCCESS, total_size); +} + +static ExessDatatype +value_type_datatype(const SerdValueType value_type) +{ + return (value_type > SERD_UBYTE) ? EXESS_NOTHING + : value_type_datatypes[value_type]; +} + +static const char* +value_type_uri(const SerdValueType value_type) +{ + return exess_datatype_uri(value_type_datatype(value_type)); +} + +static inline SerdValueType +datatype_value_type(const ExessDatatype datatype) +{ + switch (datatype) { + case EXESS_NOTHING: + return SERD_NOTHING; + case EXESS_BOOLEAN: + return SERD_BOOL; + case EXESS_DECIMAL: + case EXESS_DOUBLE: + return SERD_DOUBLE; + case EXESS_FLOAT: + return SERD_FLOAT; + case EXESS_INTEGER: + case EXESS_NON_POSITIVE_INTEGER: + case EXESS_NEGATIVE_INTEGER: + case EXESS_LONG: + return SERD_LONG; + case EXESS_INT: + return SERD_INT; + case EXESS_SHORT: + return SERD_SHORT; + case EXESS_BYTE: + return SERD_BYTE; + case EXESS_NON_NEGATIVE_INTEGER: + case EXESS_ULONG: + return SERD_ULONG; + case EXESS_UINT: + return SERD_UINT; + case EXESS_USHORT: + return SERD_USHORT; + case EXESS_UBYTE: + return SERD_UBYTE; + case EXESS_POSITIVE_INTEGER: + return SERD_ULONG; + + case EXESS_DURATION: + case EXESS_DATETIME: + case EXESS_TIME: + case EXESS_DATE: + case EXESS_HEX: + case EXESS_BASE64: + break; + } + + return SERD_NOTHING; } -SerdNode* -serd_new_blank(SerdAllocator* const allocator, const SerdStringView str) -{ - return serd_new_token(allocator, SERD_BLANK, str); +static SerdWriteResult +serd_node_construct_value(const size_t buf_size, + void* const buf, + const SerdValue value) +{ + char temp[EXESS_MAX_DOUBLE_LENGTH + 1] = {0}; + ExessResult r = {EXESS_UNSUPPORTED, 0U}; + switch (value.type) { + case SERD_NOTHING: + return result(SERD_BAD_ARG, 0U); + case SERD_BOOL: + r = exess_write_boolean(value.data.as_bool, sizeof(temp), temp); + break; + case SERD_DOUBLE: + r = exess_write_double(value.data.as_double, sizeof(temp), temp); + break; + case SERD_FLOAT: + r = exess_write_float(value.data.as_float, sizeof(temp), temp); + break; + case SERD_LONG: + r = exess_write_long(value.data.as_long, sizeof(temp), temp); + break; + case SERD_INT: + r = exess_write_int(value.data.as_int, sizeof(temp), temp); + break; + case SERD_SHORT: + r = exess_write_short(value.data.as_short, sizeof(temp), temp); + break; + case SERD_BYTE: + r = exess_write_byte(value.data.as_byte, sizeof(temp), temp); + break; + case SERD_ULONG: + r = exess_write_ulong(value.data.as_ulong, sizeof(temp), temp); + break; + case SERD_UINT: + r = exess_write_uint(value.data.as_uint, sizeof(temp), temp); + break; + case SERD_USHORT: + r = exess_write_ushort(value.data.as_ushort, sizeof(temp), temp); + break; + case SERD_UBYTE: + r = exess_write_ubyte(value.data.as_ubyte, sizeof(temp), temp); + break; + } + + MUST_SUCCEED(r.status); // The only error is buffer overrun + + const char* const datatype_uri = value_type_uri(value.type); + assert(datatype_uri); + + return serd_node_construct_literal(buf_size, + buf, + serd_substring(temp, r.count), + SERD_HAS_DATATYPE, + serd_string(datatype_uri)); } -ExessResult -serd_node_get_value_as(const SerdNode* const node, - const ExessDatatype value_type, - const size_t value_size, - void* const value) +static SerdWriteResult +serd_node_construct_decimal(const size_t buf_size, + void* const buf, + const double value) { - const SerdNode* const datatype_node = serd_node_datatype(node); + char temp[EXESS_MAX_DECIMAL_LENGTH + 1] = {0}; - const ExessDatatype node_type = - datatype_node ? exess_datatype_from_uri(serd_node_string(datatype_node)) - : EXESS_NOTHING; + 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")); +} - if (node_type == EXESS_NOTHING || - (node_type == EXESS_HEX && value_type == EXESS_BASE64) || - (node_type == EXESS_BASE64 && value_type == EXESS_HEX)) { - // Try to read the large or untyped node string directly into the result - const ExessVariableResult vr = - exess_read_value(value_type, value_size, value, serd_node_string(node)); +static SerdWriteResult +serd_node_construct_integer(const size_t buf_size, + void* const buf, + const int64_t value) +{ + char temp[24] = {0}; + const ExessResult r = exess_write_long(value, sizeof(temp), temp); + MUST_SUCCEED(r.status); // The only error is buffer overrun - const ExessResult r = {vr.status, vr.write_count}; - return r; + return serd_node_construct_literal(buf_size, + buf, + serd_substring(temp, r.count), + SERD_HAS_DATATYPE, + serd_string(NS_XSD "integer")); +} + +static SerdWriteResult +serd_node_construct_binary( + const size_t buf_size, + void* const buf, + const size_t value_size, + const void* const value, + const SerdStringView datatype_uri, + ExessResult (*write_func)(size_t, const void*, size_t, char*)) +{ + // Verify argument sanity + if (!value || !value_size) { + return result(SERD_BAD_ARG, 0); } - // Read the (smallish) value from the node - ExessValue node_value = {false}; - const ExessVariableResult vr = exess_read_value( - node_type, sizeof(node_value), &node_value, serd_node_string(node)); + // Find the size required for the datatype + const size_t type_length = serd_node_pad_length(datatype_uri.length); + const size_t type_size = sizeof(SerdNode) + type_length; - if (vr.status) { - const ExessResult r = {vr.status, 0U}; - return r; + // Find the length of the encoded string (just an O(1) arithmetic expression) + ExessResult r = write_func(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_OVERFLOW, total_size); } - // Coerce value to the desired type if possible - return exess_value_coerce(EXESS_REDUCE_PRECISION, - node_type, - vr.write_count, - &node_value, - value_type, - value_size, - value); + // Write node header + SerdNode* const node = (SerdNode*)buf; + node->length = r.count; + node->flags = SERD_HAS_DATATYPE; + node->type = SERD_LITERAL; + + // Write the encoded string into the node body + r = write_func( + 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 = datatype_uri.length; + meta_node->flags = 0U; + meta_node->type = SERD_URI; + memcpy(serd_node_buffer(meta_node), datatype_uri.data, datatype_uri.length); + + return result(SERD_SUCCESS, total_size); } -bool -serd_get_boolean(const SerdNode* const node) +static size_t +string_sink(const void* const buf, + const size_t size, + const size_t nmemb, + void* const stream) { - assert(node); + char** ptr = (char**)stream; + memcpy(*ptr, buf, size * nmemb); + *ptr += size * nmemb; + return nmemb; +} - bool value = false; - serd_node_get_value_as(node, EXESS_BOOLEAN, sizeof(value), &value); +static 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_OVERFLOW, required_size); + } + + // Write node header + SerdNode* const node = (SerdNode*)buf; + node->length = length; + node->flags = 0U; + node->type = SERD_URI; - return value; + // 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); } -double -serd_get_double(const SerdNode* const node) +SerdNode* +serd_node_new(SerdAllocator* const allocator, const SerdNodeArgs args) { - assert(node); + SerdWriteResult r = serd_node_construct(0, NULL, args); + if (r.status != SERD_OVERFLOW) { + return NULL; + } - double value = (double)NAN; // NOLINT(google-readability-casting) - serd_node_get_value_as(node, EXESS_DOUBLE, sizeof(value), &value); + assert(r.count % sizeof(SerdNode) == 0); - return value; + SerdNode* const node = + serd_node_malloc(allocator, sizeof(SerdNode) + r.count + 1); + + if (node) { + r = serd_node_construct(r.count, node, args); + MUST_SUCCEED(r.status); // Any error should have been reported above + } + + return node; } -float -serd_get_float(const SerdNode* const node) +SerdValue +serd_node_value(const SerdNode* const node) { assert(node); - float value = (float)NAN; // NOLINT(google-readability-casting) - serd_node_get_value_as(node, EXESS_FLOAT, sizeof(value), &value); + const SerdNode* const datatype_node = serd_node_datatype(node); + + const ExessDatatype datatype = + datatype_node ? exess_datatype_from_uri(serd_node_string(datatype_node)) + : EXESS_NOTHING; + + const SerdValueType value_type = datatype_value_type(datatype); + if (value_type == SERD_NOTHING) { + return serd_nothing(); + } + + ExessValue value = {false}; + const ExessVariableResult vr = + exess_read_value(datatype, sizeof(value), &value, serd_node_string(node)); - return value; + if (vr.status) { + return serd_nothing(); + } + + SerdValue result = {value_type, {false}}; + memcpy(&result.data, &value, vr.write_count); + + return result; } -int64_t -serd_get_integer(const SerdNode* const node) +SerdValue +serd_node_value_as(const SerdNode* const node, + const SerdValueType type, + const bool lossy) { - assert(node); + // Get the value as it is + const SerdValue value = serd_node_value(node); + if (!value.type || value.type == type) { + return value; + } - int64_t value = 0; - serd_node_get_value_as(node, EXESS_LONG, sizeof(value), &value); + const ExessCoercions coercions = + lossy ? (EXESS_REDUCE_PRECISION | EXESS_ROUND | EXESS_TRUNCATE) + : EXESS_LOSSLESS; + + const ExessDatatype node_datatype = value_type_datatype(value.type); + const ExessDatatype datatype = value_type_datatype(type); + SerdValue result = {type, {false}}; + + // Coerce to the desired type + const ExessResult r = exess_value_coerce(coercions, + node_datatype, + exess_value_size(node_datatype), + &value.data, + datatype, + exess_value_size(datatype), + &result.data); + + if (r.status) { + result.type = SERD_NOTHING; + } - return value; + return result; } size_t -serd_get_base64_size(const SerdNode* const node) +serd_node_decoded_size(const SerdNode* const node) { - return exess_base64_decoded_size(serd_node_length(node)); + const SerdNode* const datatype = serd_node_datatype(node); + if (!datatype) { + return 0U; + } + + if (!strcmp(serd_node_string(datatype), NS_XSD "hexBinary")) { + return exess_hex_decoded_size(serd_node_length(node)); + } + + if (!strcmp(serd_node_string(datatype), NS_XSD "base64Binary")) { + return exess_base64_decoded_size(serd_node_length(node)); + } + + return 0U; } SerdWriteResult -serd_get_base64(const SerdNode* const node, - const size_t buf_size, - void* const buf) +serd_node_decode(const SerdNode* const node, + const size_t buf_size, + void* const buf) { - const size_t max_size = serd_get_base64_size(node); - const ExessVariableResult r = - exess_read_base64(buf_size, buf, serd_node_string(node)); + const SerdNode* const datatype = serd_node_datatype(node); + if (!datatype) { + return result(SERD_BAD_ARG, 0U); + } + + ExessVariableResult r = {EXESS_UNSUPPORTED, 0U, 0U}; - return r.status == EXESS_NO_SPACE ? result(SERD_OVERFLOW, max_size) + if (!strcmp(serd_node_string(datatype), NS_XSD "hexBinary")) { + r = exess_read_hex(buf_size, buf, serd_node_string(node)); + } else if (!strcmp(serd_node_string(datatype), NS_XSD "base64Binary")) { + r = exess_read_base64(buf_size, buf, serd_node_string(node)); + } else { + return result(SERD_BAD_ARG, 0U); + } + + return r.status == EXESS_NO_SPACE ? result(SERD_OVERFLOW, r.write_count) : r.status ? result(SERD_BAD_SYNTAX, 0U) : result(SERD_SUCCESS, r.write_count); } @@ -485,197 +801,120 @@ serd_node_compare(const SerdNode* const a, const SerdNode* const b) return strcmp(serd_node_string_i(ma), serd_node_string_i(mb)); } -SerdNode* -serd_new_uri(SerdAllocator* const allocator, const SerdStringView string) -{ - return serd_new_token(allocator, SERD_URI, string); -} +typedef struct { + char* buf; + size_t len; + size_t offset; +} ConstructWriteHead; -SerdNode* -serd_new_parsed_uri(SerdAllocator* const allocator, const SerdURIView uri) +static size_t +construct_write(const void* const buf, + const size_t size, + const size_t nmemb, + void* const stream) { - const size_t len = serd_uri_string_length(uri); - SerdNode* const node = serd_node_malloc(allocator, len, 0, SERD_URI); - - if (node) { - char* ptr = serd_node_buffer(node); - const size_t actual_len = serd_write_uri(uri, string_sink, &ptr); - - assert(actual_len == len); + const size_t n_bytes = size * nmemb; + ConstructWriteHead* const head = (ConstructWriteHead*)stream; - serd_node_buffer(node)[actual_len] = '\0'; - node->length = actual_len; + if (head->buf && head->offset + n_bytes <= head->len) { + memcpy(head->buf + head->offset, buf, n_bytes); } - serd_node_check_padding(node); - return node; -} - -SerdNode* -serd_new_file_uri(SerdAllocator* const allocator, - const SerdStringView path, - const SerdStringView hostname) -{ - SerdBuffer buffer = {NULL, NULL, 0U}; - - serd_write_file_uri(path, hostname, serd_buffer_write, &buffer); - serd_buffer_close(&buffer); - - const size_t length = buffer.len; - const char* const string = (char*)buffer.buf; - SerdNode* const node = - serd_new_string(allocator, serd_substring(string, length)); - - 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); - -static SerdNode* -serd_new_custom_literal(SerdAllocator* const allocator, - const void* const user_data, - const size_t len, - const SerdWriteLiteralFunc write, - const SerdNode* const datatype) -{ - if (len == 0 || !write) { - return NULL; +static 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, sizeof(header), 1, &head); + + // Write URI string node body + const size_t length = + serd_write_file_uri(path, hostname, construct_write, &head); + + // Terminate string and pad with at least 1 additional null byte + const size_t padded_length = serd_node_pad_length(length); + count += length; + for (size_t p = 0U; p < padded_length - length; ++p) { + count += construct_write("", 1, 1, &head); } - const size_t datatype_size = serd_node_total_size(datatype); - const size_t total_size = serd_node_pad_length(len) + datatype_size; - - SerdNode* const node = serd_node_malloc( - allocator, total_size, datatype ? SERD_HAS_DATATYPE : 0U, SERD_LITERAL); - - node->length = write(user_data, len + 1, serd_node_buffer(node)); - - if (datatype) { - memcpy(serd_node_meta(node), datatype, datatype_size); + if (!buf || count > buf_size) { + return result(SERD_OVERFLOW, count); } - return node; -} - -SerdNode* -serd_new_double(SerdAllocator* const allocator, const double d) -{ - char buf[EXESS_MAX_DOUBLE_LENGTH + 1] = {0}; - - const ExessResult r = exess_write_double(d, sizeof(buf), buf); - - return r.status ? NULL - : serd_new_literal(allocator, - serd_substring(buf, r.count), - SERD_HAS_DATATYPE, - serd_string(EXESS_XSD_URI "double")); -} - -SerdNode* -serd_new_float(SerdAllocator* const allocator, const float f) -{ - char buf[EXESS_MAX_FLOAT_LENGTH + 1] = {0}; - - const ExessResult r = exess_write_float(f, sizeof(buf), buf); + node->length = length; + assert(node->length == strlen(serd_node_string(node))); - return r.status ? NULL - : serd_new_literal(allocator, - serd_substring(buf, r.count), - SERD_HAS_DATATYPE, - serd_string(EXESS_XSD_URI "float")); + return result(SERD_SUCCESS, count); } -SerdNode* -serd_new_boolean(SerdAllocator* const allocator, bool b) -{ - return serd_new_literal(allocator, - b ? serd_string("true") : serd_string("false"), - SERD_HAS_DATATYPE, - serd_node_string_view(&serd_xsd_boolean.node)); -} - -SerdNode* -serd_new_decimal(SerdAllocator* const allocator, - 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); - - // Allocate node with enough space for value and datatype URI - SerdNode* const node = - serd_node_malloc(allocator, - 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); - - node->length = r.count; - memcpy(serd_node_meta(node), type, type_size); - serd_node_check_padding(node); - return node; -} - -SerdNode* -serd_new_integer(SerdAllocator* const allocator, const int64_t i) -{ - // Use given datatype, or xsd:integer as a default if it is null - const SerdNode* datatype = &serd_xsd_integer.node; - const size_t datatype_size = serd_node_total_size(datatype); - - // 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(allocator, - serd_node_pad_length(r.count) + datatype_size, - SERD_HAS_DATATYPE, - SERD_LITERAL); - - // Write string directly into node - r = exess_write_long(i, r.count + 1U, serd_node_buffer(node)); - assert(!r.status); - - node->length = r.count; - memcpy(serd_node_meta(node), datatype, datatype_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) -{ - const SerdConstBuffer blob = *(const SerdConstBuffer*)user_data; - - const ExessResult r = exess_write_base64(blob.len, blob.buf, buf_size, buf); - - return r.status ? 0 : r.count; -} - -SerdNode* -serd_new_base64(SerdAllocator* const allocator, const void* buf, size_t size) -{ - const size_t len = exess_write_base64(size, buf, 0, NULL).count; - SerdConstBuffer blob = {buf, size}; +SerdWriteResult +serd_node_construct(const size_t buf_size, + void* const buf, + const SerdNodeArgs args) +{ + switch (args.type) { + case SERD_NODE_ARGS_TOKEN: + return serd_node_construct_simple( + buf_size, buf, args.data.as_token.type, 0U, args.data.as_token.string); + + case SERD_NODE_ARGS_PARSED_URI: + return serd_node_construct_uri(buf_size, buf, args.data.as_parsed_uri.uri); + + case SERD_NODE_ARGS_FILE_URI: + return serd_node_construct_file_uri(buf_size, + buf, + args.data.as_file_uri.path, + args.data.as_file_uri.hostname); + + case SERD_NODE_ARGS_LITERAL: + return serd_node_construct_literal(buf_size, + buf, + args.data.as_literal.string, + args.data.as_literal.flags, + args.data.as_literal.meta); + + case SERD_NODE_ARGS_PRIMITIVE: + return serd_node_construct_value( + buf_size, buf, args.data.as_primitive.value); + + case SERD_NODE_ARGS_DECIMAL: + return serd_node_construct_decimal( + buf_size, buf, args.data.as_decimal.value); + + case SERD_NODE_ARGS_INTEGER: + return serd_node_construct_integer( + buf_size, buf, args.data.as_integer.value); + + case SERD_NODE_ARGS_HEX: + return serd_node_construct_binary(buf_size, + buf, + args.data.as_blob.size, + args.data.as_blob.data, + serd_string(NS_XSD "hexBinary"), + exess_write_hex); + + case SERD_NODE_ARGS_BASE64: + return serd_node_construct_binary(buf_size, + buf, + args.data.as_blob.size, + args.data.as_blob.data, + serd_string(NS_XSD "base64Binary"), + exess_write_base64); + } - return serd_new_custom_literal( - allocator, &blob, len, write_base64_literal, &serd_xsd_base64Binary.node); + return result(SERD_BAD_ARG, 0U); } SerdNodeType @@ -760,3 +999,5 @@ serd_node_free(SerdAllocator* const allocator, SerdNode* const node) { serd_aaligned_free(allocator, node); } + +#undef MUST_SUCCEED diff --git a/src/node.h b/src/node.h index 43368367..0dd15d0c 100644 --- a/src/node.h +++ b/src/node.h @@ -4,12 +4,13 @@ #ifndef SERD_SRC_NODE_H #define SERD_SRC_NODE_H -#include "exess/exess.h" #include "serd/memory.h" #include "serd/node.h" #include "serd/status.h" +#include "serd/write_result.h" #include "zix/attributes.h" +#include #include #include #include @@ -22,19 +23,57 @@ struct SerdNodeImpl { static const size_t serd_node_align = 2 * sizeof(uint64_t); -static inline char* ZIX_NONNULL +#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 + +ZIX_CONST_FUNC static inline char* ZIX_NONNULL serd_node_buffer(SerdNode* ZIX_NONNULL node) { return (char*)(node + 1); } -static inline const char* ZIX_NONNULL +ZIX_PURE_FUNC static inline const char* ZIX_NONNULL serd_node_buffer_c(const SerdNode* ZIX_NONNULL node) { return (const char*)(node + 1); } -static inline const char* ZIX_NONNULL +ZIX_PURE_FUNC static inline SerdNode* ZIX_NONNULL +serd_node_meta(SerdNode* const ZIX_NONNULL node) +{ + return node + 1 + (serd_node_pad_length(node->length) / sizeof(SerdNode)); +} + +ZIX_PURE_FUNC static inline const SerdNode* ZIX_NONNULL +serd_node_meta_c(const SerdNode* const ZIX_NONNULL node) +{ + assert(node->flags & (SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE)); + return node + 1 + (serd_node_pad_length(node->length) / sizeof(SerdNode)); +} + +ZIX_CONST_FUNC static inline const char* ZIX_NONNULL serd_node_string_i(const SerdNode* const ZIX_NONNULL node) { return (const char*)(node + 1); @@ -47,11 +86,12 @@ serd_node_pattern_match(const SerdNode* ZIX_NULLABLE a, return !a || !b || serd_node_equals(a, b); } -SerdNode* ZIX_ALLOCATED -serd_node_malloc(SerdAllocator* ZIX_NULLABLE allocator, - size_t length, - SerdNodeFlags flags, - SerdNodeType type); +ZIX_MALLOC_FUNC SerdNode* ZIX_ALLOCATED +serd_node_malloc(SerdAllocator* ZIX_NULLABLE allocator, size_t size); + +ZIX_MALLOC_FUNC SerdNode* ZIX_ALLOCATED +serd_node_try_malloc(SerdAllocator* ZIX_NULLABLE allocator, + SerdWriteResult result); SerdStatus serd_node_set(SerdAllocator* ZIX_NULLABLE allocator, @@ -59,15 +99,9 @@ serd_node_set(SerdAllocator* ZIX_NULLABLE allocator, const SerdNode* ZIX_NONNULL src); ZIX_PURE_FUNC size_t -serd_node_total_size(const SerdNode* ZIX_NULLABLE node); +serd_node_total_size(const SerdNode* ZIX_NONNULL node); void serd_node_zero_pad(SerdNode* ZIX_NONNULL node); -ExessResult -serd_node_get_value_as(const SerdNode* ZIX_NONNULL node, - ExessDatatype value_type, - size_t value_size, - void* ZIX_NONNULL value); - #endif // SERD_SRC_NODE_H diff --git a/src/read_turtle.c b/src/read_turtle.c index 613b33d2..f85c956a 100644 --- a/src/read_turtle.c +++ b/src/read_turtle.c @@ -348,7 +348,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); } diff --git a/src/value.c b/src/value.c new file mode 100644 index 00000000..ab6b1b43 --- /dev/null +++ b/src/value.c @@ -0,0 +1,101 @@ +// Copyright 2022 David Robillard +// SPDX-License-Identifier: ISC + +#include "serd/value.h" + +#include +#include + +SerdValue +serd_nothing(void) +{ + static const SerdValue value = {SERD_NOTHING, {0}}; + return value; +} + +SerdValue +serd_bool(const bool v) +{ + const SerdValue value = {SERD_BOOL, {v}}; + return value; +} + +SerdValue +serd_double(const double v) +{ + SerdValue value = {SERD_DOUBLE, {0}}; + value.data.as_double = v; + return value; +} + +SerdValue +serd_float(const float v) +{ + SerdValue value = {SERD_FLOAT, {0}}; + value.data.as_float = v; + return value; +} + +SerdValue +serd_long(const int64_t v) +{ + SerdValue value = {SERD_LONG, {0}}; + value.data.as_long = v; + return value; +} + +SerdValue +serd_int(const int32_t v) +{ + SerdValue value = {SERD_INT, {0}}; + value.data.as_int = v; + return value; +} + +SerdValue +serd_short(const int16_t v) +{ + SerdValue value = {SERD_SHORT, {0}}; + value.data.as_short = v; + return value; +} + +SerdValue +serd_byte(const int8_t v) +{ + SerdValue value = {SERD_BYTE, {0}}; + value.data.as_byte = v; + return value; +} + +SerdValue +serd_ulong(const uint64_t v) +{ + SerdValue value = {SERD_ULONG, {0}}; + value.data.as_ulong = v; + return value; +} + +SerdValue +serd_uint(const uint32_t v) +{ + SerdValue value = {SERD_UINT, {0}}; + value.data.as_uint = v; + return value; +} + +SerdValue +serd_ushort(const uint16_t v) +{ + SerdValue value = {SERD_USHORT, {0}}; + value.data.as_ushort = v; + return value; +} + +SerdValue +serd_ubyte(const uint8_t v) +{ + SerdValue value = {SERD_UBYTE, {0}}; + value.data.as_ubyte = v; + return value; +} diff --git a/src/world.c b/src/world.c index 2c563f15..406956b1 100644 --- a/src/world.c +++ b/src/world.c @@ -9,7 +9,6 @@ #include "serd/node.h" #include "serd/status.h" -#include "serd/string_view.h" #include "serd/world.h" #include @@ -23,7 +22,8 @@ serd_world_new(SerdAllocator* const allocator) allocator ? allocator : serd_default_allocator(); SerdWorld* world = (SerdWorld*)serd_acalloc(actual, 1, sizeof(SerdWorld)); - SerdNode* blank_node = serd_new_blank(actual, serd_string("b00000000000")); + SerdNode* blank_node = + serd_node_new(actual, serd_a_blank_string("b00000000000")); if (!world || !blank_node) { serd_node_free(actual, blank_node); diff --git a/src/writer.c b/src/writer.c index 6d52b4e6..1de5e055 100644 --- a/src/writer.c +++ b/src/writer.c @@ -18,6 +18,7 @@ #include "serd/env.h" #include "serd/event.h" #include "serd/log.h" +#include "serd/memory.h" #include "serd/node.h" #include "serd/output_stream.h" #include "serd/sink.h" @@ -1430,12 +1431,14 @@ serd_writer_set_root_uri(SerdWriter* writer, const SerdStringView uri) { assert(writer); - serd_node_free(writer->world->allocator, writer->root_node); + SerdAllocator* const allocator = writer->world->allocator; + + serd_node_free(allocator, writer->root_node); writer->root_node = NULL; writer->root_uri = SERD_URI_NULL; if (uri.length) { - writer->root_node = serd_new_uri(writer->world->allocator, uri); + writer->root_node = serd_node_new(allocator, serd_a_uri(uri)); writer->root_uri = serd_node_uri_view(writer->root_node); } -- cgit v1.2.1