From f48dac14b6533b4cdd4804513216f4f11de36d9a Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 12 May 2018 18:03:13 +0200 Subject: Set datatypes on integer, decimal, and base64 nodes --- serd/serd.h | 24 +++++++++++--- src/node.c | 99 ++++++++++++++++++++++++++++++++++++++++--------------- tests/serd_test.c | 20 +++++++++-- 3 files changed, 110 insertions(+), 33 deletions(-) diff --git a/serd/serd.h b/serd/serd.h index c765f8dd..f148acb4 100644 --- a/serd/serd.h +++ b/serd/serd.h @@ -554,18 +554,27 @@ serd_node_new_relative_uri(const char* str, represent a double and float, respectively. @param d The value for the new node. + @param frac_digits The maximum number of digits after the decimal place. + + @param datatype Datatype of node, may be NULL in which case the node has + type xsd:decimal. */ SERD_API SerdNode* -serd_node_new_decimal(double d, unsigned frac_digits); +serd_node_new_decimal(double d, unsigned frac_digits, const SerdNode* datatype); /** Create a new node by serialising `i` into an xsd:integer string. + + @param i Integer value to serialise. + + @param datatype Datatype of node, may be NULL in which case the node has + type xsd:integer. */ SERD_API SerdNode* -serd_node_new_integer(int64_t i); +serd_node_new_integer(int64_t i, const SerdNode* datatype); /** Create a node by serialising `buf` into an xsd:base64Binary string. @@ -573,12 +582,19 @@ serd_node_new_integer(int64_t i); binary data, which can be decoded using serd_base64_decode(). @param buf Raw binary input data. + @param size Size of `buf`. + @param wrap_lines Wrap lines at 76 characters to conform to RFC 2045. + + @param datatype Datatype of node, may be NULL in which case the node has + type xsd:base64Binary. */ SERD_API -SerdNode* -serd_node_new_blob(const void* buf, size_t size, bool wrap_lines); +SerdNode* serd_node_new_blob(const void* buf, + size_t size, + bool wrap_lines, + const SerdNode* datatype); /** Return the type of a node (SERD_URI, SERD_BLANK, or SERD_LITERAL). diff --git a/src/node.c b/src/node.c index 36c2c8ca..523893df 100644 --- a/src/node.c +++ b/src/node.c @@ -39,6 +39,19 @@ static const size_t serd_node_align = sizeof(SerdNode); +typedef struct StaticNode { + SerdNode node; + char buf[sizeof(NS_XSD) + sizeof("base64Binary") + 1]; +} StaticNode; + +#define DEFINE_XSD_NODE(name) \ + static const StaticNode serd_xsd_ ## name = { \ + { sizeof(NS_XSD #name), 0, SERD_URI }, NS_XSD #name }; + +DEFINE_XSD_NODE(decimal) +DEFINE_XSD_NODE(integer) +DEFINE_XSD_NODE(base64Binary) + static SerdNode* serd_node_new_from_uri(const SerdURI* uri, const SerdURI* base); @@ -46,7 +59,29 @@ static size_t serd_node_pad_size(const size_t n_bytes) { const size_t pad = serd_node_align - (n_bytes + 2) % serd_node_align; - return n_bytes + 2 + pad; + const size_t size = n_bytes + 2 + pad; + assert(size % serd_node_align == 0); + return size; +} + +static const SerdNode* +serd_node_get_meta_c(const SerdNode* node) +{ + return node + 1 + (serd_node_pad_size(node->n_bytes) / serd_node_align); +} + +static const SerdNode* +serd_node_maybe_get_meta_c(const SerdNode* node) +{ + return (node->flags & (SERD_HAS_LANGUAGE | SERD_HAS_DATATYPE)) + ? (node + 1 + (serd_node_pad_size(node->n_bytes) / serd_node_align)) + : NULL; +} + +static SerdNode* +serd_node_get_meta(SerdNode* node) +{ + return node + 1 + (serd_node_pad_size(node->n_bytes) / serd_node_align); } size_t @@ -391,21 +426,26 @@ serd_digits(double abs) } SerdNode* -serd_node_new_decimal(double d, unsigned frac_digits) +serd_node_new_decimal(double d, unsigned frac_digits, const SerdNode* datatype) { if (isnan(d) || isinf(d)) { return NULL; } + const SerdNode* type = datatype ? datatype : &serd_xsd_decimal.node; const double abs_d = fabs(d); const unsigned int_digits = serd_digits(abs_d); const size_t len = int_digits + frac_digits + 3; - SerdNode* const node = serd_node_malloc(len, 0, SERD_LITERAL); - char* const buf = serd_node_buffer(node); - const double int_part = floor(abs_d); + const size_t type_len = serd_node_total_size(type); + const size_t total_len = len + type_len; + + SerdNode* const node = + serd_node_malloc(total_len, SERD_HAS_DATATYPE, SERD_LITERAL); // Point s to decimal point location - char* s = buf + int_digits; + char* const buf = serd_node_buffer(node); + const double int_part = floor(abs_d); + char* s = buf + int_digits; if (d < 0.0) { *buf = '-'; ++s; @@ -443,19 +483,25 @@ serd_node_new_decimal(double d, unsigned frac_digits) } } + memcpy(serd_node_get_meta(node), type, type_len); return node; } SerdNode* -serd_node_new_integer(int64_t i) +serd_node_new_integer(int64_t i, const SerdNode* datatype) { - int64_t abs_i = (i < 0) ? -i : i; - const unsigned digits = serd_digits(abs_i); - SerdNode* node = serd_node_malloc(digits + 2, 0, SERD_LITERAL); - char* buf = serd_node_buffer(node); + const SerdNode* type = datatype ? datatype : &serd_xsd_integer.node; + int64_t abs_i = (i < 0) ? -i : i; + const unsigned digits = serd_digits(abs_i); + const size_t type_len = serd_node_total_size(type); + const size_t total_len = digits + 2 + type_len; + + SerdNode* node = + serd_node_malloc(total_len, SERD_HAS_DATATYPE, SERD_LITERAL); // Point s to the end - char* s = buf + digits - 1; + char* buf = serd_node_buffer(node); + char* s = buf + digits - 1; if (i < 0) { *buf = '-'; ++s; @@ -468,20 +514,30 @@ serd_node_new_integer(int64_t i) *s-- = '0' + (abs_i % 10); } while ((abs_i /= 10) > 0); + memcpy(serd_node_get_meta(node), type, type_len); return node; } SerdNode* -serd_node_new_blob(const void* buf, size_t size, bool wrap_lines) +serd_node_new_blob(const void* buf, + size_t size, + bool wrap_lines, + const SerdNode* datatype) { - const size_t len = serd_base64_get_length(size, wrap_lines); - SerdNode* const node = serd_node_malloc(len + 1, 0, SERD_LITERAL); + const SerdNode* type = datatype ? datatype : &serd_xsd_base64Binary.node; + const size_t len = serd_base64_get_length(size, wrap_lines); + const size_t type_len = serd_node_total_size(type); + const size_t total_len = len + 1 + type_len; + + SerdNode* const node = + serd_node_malloc(total_len, SERD_HAS_DATATYPE, SERD_LITERAL); if (serd_base64_encode(serd_node_buffer(node), buf, size, wrap_lines)) { node->flags |= SERD_HAS_NEWLINE; } node->n_bytes = len + 1; + memcpy(serd_node_get_meta(node), type, type_len); return node; } @@ -503,15 +559,6 @@ serd_node_get_length(const SerdNode* node) return node ? node->n_bytes : 0; } -static const SerdNode* -serd_node_get_meta_node(const SerdNode* node) -{ - const size_t len = serd_node_pad_size(node->n_bytes); - assert((intptr_t)node % serd_node_align == 0); - assert(len % serd_node_align == 0); - return node + 1 + (len / serd_node_align); -} - const SerdNode* serd_node_get_datatype(const SerdNode* node) { @@ -519,7 +566,7 @@ serd_node_get_datatype(const SerdNode* node) return NULL; } - const SerdNode* const datatype = serd_node_get_meta_node(node); + const SerdNode* const datatype = serd_node_get_meta_c(node); assert(datatype->type == SERD_URI || datatype->type == SERD_CURIE); return datatype; } @@ -531,7 +578,7 @@ serd_node_get_language(const SerdNode* node) return NULL; } - const SerdNode* const lang = serd_node_get_meta_node(node); + const SerdNode* const lang = serd_node_get_meta_c(node); assert(lang->type == SERD_LITERAL); return lang; } diff --git a/tests/serd_test.c b/tests/serd_test.c index e5037945..e63e9594 100644 --- a/tests/serd_test.c +++ b/tests/serd_test.c @@ -33,6 +33,8 @@ # define NAN (INFINITY - INFINITY) #endif +#define NS_XSD "http://www.w3.org/2001/XMLSchema#" + static int test_strtod(double dbl, double max_delta) { @@ -172,7 +174,7 @@ main(void) }; for (unsigned i = 0; i < sizeof(dbl_test_nums) / sizeof(double); ++i) { - SerdNode* node = serd_node_new_decimal(dbl_test_nums[i], 8); + SerdNode* node = serd_node_new_decimal(dbl_test_nums[i], 8, NULL); const char* node_str = serd_node_get_string(node); const bool pass = (node_str && dbl_test_strs[i]) ? !strcmp(node_str, dbl_test_strs[i]) @@ -183,6 +185,10 @@ main(void) const size_t len = node_str ? strlen(node_str) : 0; if (serd_node_get_length(node) != len) { FAILF("Length %zu != %zu\n", serd_node_get_length(node), len); + } else if (dbl_test_strs[i] && + strcmp(serd_node_get_string(serd_node_get_datatype(node)), + NS_XSD "decimal")) { + FAIL("Decimal node has incorrect default datatype\n"); } serd_node_free(node); } @@ -198,7 +204,7 @@ main(void) }; for (unsigned i = 0; i < sizeof(int_test_nums) / sizeof(double); ++i) { - SerdNode* node = serd_node_new_integer(int_test_nums[i]); + SerdNode* node = serd_node_new_integer(int_test_nums[i], NULL); const char* node_str = serd_node_get_string(node); if (strcmp(node_str, (const char*)int_test_strs[i])) { FAILF("Serialised `%s' != %s\n", node_str, int_test_strs[i]); @@ -206,6 +212,9 @@ main(void) const size_t len = strlen(node_str); if (serd_node_get_length(node) != len) { FAILF("Length %zu,%zu != %zu\n", serd_node_get_length(node), len); + } else if (strcmp(serd_node_get_string(serd_node_get_datatype(node)), + NS_XSD "integer")) { + FAIL("Integer node has incorrect default datatype\n"); } serd_node_free(node); } @@ -218,7 +227,7 @@ main(void) } size_t out_size; - SerdNode* blob = serd_node_new_blob(data, size, size % 5); + SerdNode* blob = serd_node_new_blob(data, size, size % 5, NULL); const char* blob_str = serd_node_get_string(blob); uint8_t* out = (uint8_t*)serd_base64_decode( blob_str, serd_node_get_length(blob), &out_size); @@ -232,6 +241,11 @@ main(void) } } + if (strcmp(serd_node_get_string(serd_node_get_datatype(blob)), + NS_XSD "base64Binary")) { + FAIL("Blob node has incorrect default datatype\n"); + } + serd_node_free(blob); free(out); free(data); -- cgit v1.2.1