diff options
author | David Robillard <d@drobilla.net> | 2023-05-05 09:43:57 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2023-12-02 18:49:08 -0500 |
commit | 4711fdf527f416faee8ff19e15f050d4b48dcfb2 (patch) | |
tree | 6b18712ec44cce5713ddef1a21aec5f12651d901 /src/node.c | |
parent | 248a874d7425749d29cf900a1c3783c624ea8d8c (diff) | |
download | serd-4711fdf527f416faee8ff19e15f050d4b48dcfb2.tar.gz serd-4711fdf527f416faee8ff19e15f050d4b48dcfb2.tar.bz2 serd-4711fdf527f416faee8ff19e15f050d4b48dcfb2.zip |
[WIP] Generalize node construction API
Diffstat (limited to 'src/node.c')
-rw-r--r-- | src/node.c | 967 |
1 files changed, 604 insertions, 363 deletions
@@ -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 <assert.h> -#include <math.h> #include <stdbool.h> #include <stdint.h> -#include <stdlib.h> #include <string.h> -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 |