aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-05-12 18:03:13 +0200
committerDavid Robillard <d@drobilla.net>2022-01-13 23:03:21 -0500
commit7a171498eca31ddedb0de1f372c982eca00d3d50 (patch)
tree9e36ee76aef1a6e41fac894c70421bbebf3a6847
parentdb3799cd77f8f4e6525ea31c4d5f9400f47aa228 (diff)
downloadserd-7a171498eca31ddedb0de1f372c982eca00d3d50.tar.gz
serd-7a171498eca31ddedb0de1f372c982eca00d3d50.tar.bz2
serd-7a171498eca31ddedb0de1f372c982eca00d3d50.zip
Set datatypes on integer, decimal, and base64 nodes
-rw-r--r--include/serd/serd.h20
-rw-r--r--src/node.c78
-rw-r--r--src/static_nodes.h38
-rw-r--r--test/test_node.c34
4 files changed, 132 insertions, 38 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h
index cb65ba14..6b455878 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -557,15 +557,23 @@ serd_new_file_uri(SerdStringView path, SerdStringView hostname);
@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, or NULL for xsd:decimal.
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_decimal(double d, unsigned frac_digits);
+serd_new_decimal(double d,
+ unsigned frac_digits,
+ const SerdNode* SERD_NULLABLE datatype);
-/// Create a new node by serialising `i` into an xsd:integer string
+/**
+ Create a new node by serialising `i` into an xsd:integer string.
+
+ @param i Integer value to serialise.
+ @param datatype Datatype of node, or NULL for xsd:integer.
+*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_integer(int64_t i);
+serd_new_integer(int64_t i, const SerdNode* SERD_NULLABLE datatype);
/**
Create a node by serialising `buf` into an xsd:base64Binary string.
@@ -576,10 +584,14 @@ serd_new_integer(int64_t i);
@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, or NULL for xsd:base64Binary.
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_blob(const void* SERD_NONNULL buf, size_t size, bool wrap_lines);
+serd_new_blob(const void* SERD_NONNULL buf,
+ size_t size,
+ bool wrap_lines,
+ const SerdNode* SERD_NULLABLE datatype);
/// Return a deep copy of `node`
SERD_API
diff --git a/src/node.c b/src/node.c
index fcec7336..8f981856 100644
--- a/src/node.c
+++ b/src/node.c
@@ -17,6 +17,7 @@
#include "node.h"
#include "base64.h"
+#include "static_nodes.h"
#include "string_utils.h"
#include "system.h"
@@ -47,6 +48,7 @@ static const SerdNodeFlags meta_mask = (SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE);
static SerdNode*
serd_new_from_uri(SerdURIView uri, SerdURIView base);
+SERD_PURE_FUNC
static size_t
serd_uri_string_length(const SerdURIView* const uri)
{
@@ -78,8 +80,16 @@ string_sink(const void* const buf, const size_t len, void* const stream)
static size_t
serd_node_pad_size(const size_t n_bytes)
{
- const size_t pad = sizeof(SerdNode) - (n_bytes + 2) % sizeof(SerdNode);
- return n_bytes + 2 + pad;
+ 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 SerdNode*
+serd_node_meta(SerdNode* const node)
+{
+ return node + 1 + (serd_node_pad_size(node->length) / sizeof(SerdNode));
}
static const SerdNode*
@@ -409,21 +419,28 @@ serd_digits(const double abs)
}
SerdNode*
-serd_new_decimal(const double d, const unsigned frac_digits)
+serd_new_decimal(const double d,
+ const unsigned frac_digits,
+ const SerdNode* const 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;
@@ -461,19 +478,24 @@ serd_new_decimal(const double d, const unsigned frac_digits)
}
}
+ memcpy(serd_node_meta(node), type, type_len);
return node;
}
SerdNode*
-serd_new_integer(const int64_t i)
+serd_new_integer(const int64_t i, const SerdNode* const datatype)
{
- uint64_t abs_i = (i < 0) ? -i : i;
- const unsigned digits = serd_digits((double)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;
+ uint64_t abs_i = (uint64_t)((i < 0) ? -i : i);
+ const unsigned digits = serd_digits((double)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;
@@ -486,25 +508,35 @@ serd_new_integer(const int64_t i)
*s-- = (char)('0' + (abs_i % 10));
} while ((abs_i /= 10) > 0);
+ memcpy(serd_node_meta(node), type, type_len);
return node;
}
SerdNode*
-serd_new_blob(const void* const buf, const size_t size, const bool wrap_lines)
+serd_new_blob(const void* const buf,
+ const size_t size,
+ const bool wrap_lines,
+ const SerdNode* const datatype)
{
if (!buf || !size) {
return NULL;
}
- const size_t len = serd_base64_get_length(size, wrap_lines);
- SerdNode* node = serd_node_malloc(len + 1, 0, SERD_LITERAL);
- uint8_t* str = (uint8_t*)serd_node_buffer(node);
+ 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);
+ uint8_t* str = (uint8_t*)serd_node_buffer(node);
if (serd_base64_encode(str, buf, size, wrap_lines)) {
node->flags |= SERD_HAS_NEWLINE;
}
node->length = len;
+ memcpy(serd_node_meta(node), type, type_len);
return node;
}
@@ -541,14 +573,6 @@ serd_node_uri_view(const SerdNode* const node)
: SERD_URI_NULL;
}
-SERD_PURE_FUNC
-static const SerdNode*
-serd_node_meta_node(const SerdNode* node)
-{
- const size_t len = serd_node_pad_size(node->length);
- return node + 1 + (len / sizeof(SerdNode));
-}
-
const SerdNode*
serd_node_datatype(const SerdNode* const node)
{
@@ -556,7 +580,7 @@ serd_node_datatype(const SerdNode* const node)
return NULL;
}
- const SerdNode* const datatype = serd_node_meta_node(node);
+ const SerdNode* const datatype = serd_node_meta_c(node);
assert(datatype->type == SERD_URI || datatype->type == SERD_CURIE);
return datatype;
}
@@ -568,7 +592,7 @@ serd_node_language(const SerdNode* const node)
return NULL;
}
- const SerdNode* const lang = serd_node_meta_node(node);
+ const SerdNode* const lang = serd_node_meta_c(node);
assert(lang->type == SERD_LITERAL);
return lang;
}
diff --git a/src/static_nodes.h b/src/static_nodes.h
new file mode 100644
index 00000000..1b1fc062
--- /dev/null
+++ b/src/static_nodes.h
@@ -0,0 +1,38 @@
+/*
+ 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 "node.h"
+#include "serd_internal.h"
+
+typedef struct StaticNode {
+ SerdNode node;
+ char buf[sizeof(NS_XSD "base64Binary")];
+} 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(decimal)
+DEFINE_XSD_NODE(integer)
+
+#endif // SERD_STATIC_NODES_H
diff --git a/test/test_node.c b/test/test_node.c
index 3df678cc..bd3a5506 100644
--- a/test/test_node.c
+++ b/test/test_node.c
@@ -34,6 +34,8 @@
# define NAN (INFINITY - INFINITY)
#endif
+#define NS_XSD "http://www.w3.org/2001/XMLSchema#"
+
static void
test_strtod(double dbl, double max_delta)
{
@@ -91,14 +93,23 @@ test_double_to_node(void)
NULL};
for (size_t i = 0; i < sizeof(dbl_test_nums) / sizeof(double); ++i) {
- SerdNode* node = serd_new_decimal(dbl_test_nums[i], 8);
+ SerdNode* node = serd_new_decimal(dbl_test_nums[i], 8, NULL);
const char* node_str = node ? serd_node_string(node) : NULL;
const bool pass = (node_str && dbl_test_strs[i])
? !strcmp(node_str, dbl_test_strs[i])
: (node_str == dbl_test_strs[i]);
assert(pass);
- assert(!node || serd_node_length(node) == strlen(node_str));
- serd_node_free(node);
+
+ const size_t len = node_str ? strlen(node_str) : 0;
+ assert((!node && len == 0) || serd_node_length(node) == len);
+
+ if (node) {
+ const SerdNode* const datatype = serd_node_datatype(node);
+ assert(datatype);
+ assert(!dbl_test_strs[i] ||
+ !strcmp(serd_node_string(datatype), NS_XSD "decimal"));
+ serd_node_free(node);
+ }
}
}
@@ -111,10 +122,15 @@ test_integer_to_node(void)
"0", "0", "-23", "23", "-12340", "1000", "-1000"};
for (size_t i = 0; i < sizeof(int_test_nums) / sizeof(double); ++i) {
- SerdNode* node = serd_new_integer(int_test_nums[i]);
+ SerdNode* node = serd_new_integer(int_test_nums[i], NULL);
const char* node_str = serd_node_string(node);
assert(!strcmp(node_str, int_test_strs[i]));
- assert(serd_node_length(node) == strlen(node_str));
+ const size_t len = strlen(node_str);
+ assert(serd_node_length(node) == len);
+
+ const SerdNode* const datatype = serd_node_datatype(node);
+ assert(datatype);
+ assert(!strcmp(serd_node_string(datatype), NS_XSD "integer"));
serd_node_free(node);
}
}
@@ -122,7 +138,7 @@ test_integer_to_node(void)
static void
test_blob_to_node(void)
{
- assert(!serd_new_blob(&SERD_URI_NULL, 0, false));
+ assert(!serd_new_blob(&SERD_URI_NULL, 0, false, NULL));
for (size_t size = 1; size < 256; ++size) {
uint8_t* const data = (uint8_t*)malloc(size);
@@ -131,7 +147,7 @@ test_blob_to_node(void)
}
size_t out_size = 0;
- SerdNode* blob = serd_new_blob(data, size, size % 5);
+ SerdNode* blob = serd_new_blob(data, size, size % 5, NULL);
const char* blob_str = serd_node_string(blob);
uint8_t* out =
(uint8_t*)serd_base64_decode(blob_str, serd_node_length(blob), &out_size);
@@ -143,6 +159,10 @@ test_blob_to_node(void)
assert(out[i] == data[i]);
}
+ const SerdNode* const datatype = serd_node_datatype(blob);
+ assert(datatype);
+ assert(!strcmp(serd_node_string(datatype), NS_XSD "base64Binary"));
+
serd_node_free(blob);
serd_free(out);
free(data);