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 | |
parent | 248a874d7425749d29cf900a1c3783c624ea8d8c (diff) | |
download | serd-4711fdf527f416faee8ff19e15f050d4b48dcfb2.tar.gz serd-4711fdf527f416faee8ff19e15f050d4b48dcfb2.tar.bz2 serd-4711fdf527f416faee8ff19e15f050d4b48dcfb2.zip |
[WIP] Generalize node construction API
-rw-r--r-- | .clang-tidy | 1 | ||||
-rw-r--r-- | doc/conf.py.in | 5 | ||||
-rw-r--r-- | include/serd/node.h | 432 | ||||
-rw-r--r-- | include/serd/serd.h | 1 | ||||
-rw-r--r-- | include/serd/string.h | 18 | ||||
-rw-r--r-- | include/serd/value.h | 118 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | src/base64.c | 27 | ||||
-rw-r--r-- | src/base64.h | 35 | ||||
-rw-r--r-- | src/byte_source.c | 3 | ||||
-rw-r--r-- | src/env.c | 37 | ||||
-rw-r--r-- | src/node.c | 967 | ||||
-rw-r--r-- | src/node.h | 66 | ||||
-rw-r--r-- | src/read_turtle.c | 2 | ||||
-rw-r--r-- | src/value.c | 101 | ||||
-rw-r--r-- | src/world.c | 4 | ||||
-rw-r--r-- | src/writer.c | 7 | ||||
-rw-r--r-- | test/test_caret.c | 12 | ||||
-rw-r--r-- | test/test_env.c | 16 | ||||
-rw-r--r-- | test/test_log.c | 3 | ||||
-rw-r--r-- | test/test_node.c | 608 | ||||
-rw-r--r-- | test/test_overflow.c | 2 | ||||
-rw-r--r-- | test/test_reader.c | 2 | ||||
-rw-r--r-- | test/test_reader_writer.c | 23 | ||||
-rw-r--r-- | test/test_sink.c | 9 | ||||
-rw-r--r-- | test/test_statement.c | 27 | ||||
-rw-r--r-- | test/test_terse_write.c | 20 | ||||
-rw-r--r-- | test/test_uri.c | 35 | ||||
-rw-r--r-- | test/test_writer.c | 71 | ||||
-rw-r--r-- | tools/console.c | 4 | ||||
-rw-r--r-- | tools/serd-pipe.c | 7 |
31 files changed, 1741 insertions, 924 deletions
diff --git a/.clang-tidy b/.clang-tidy index a283cc08..ba762f8a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -5,7 +5,6 @@ Checks: > *, -altera-*, -bugprone-assignment-in-if-condition, - -bugprone-suspicious-realloc-usage, -clang-diagnostic-unused-macros, -llvmlibc-*, -modernize-macro-to-enum, diff --git a/doc/conf.py.in b/doc/conf.py.in index 51a09d4e..c6e146f2 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -36,9 +36,14 @@ _opaque = [ "SerdStatementImpl", "SerdWorldImpl", "SerdWriterImpl", + "int16_t", + "int32_t", "int64_t", + "int8_t", "size_t", + "uint16_t", "uint32_t", + "uint64_t", "uint8_t", "va_list", ] diff --git a/include/serd/node.h b/include/serd/node.h index f42f07f1..d140e4c0 100644 --- a/include/serd/node.h +++ b/include/serd/node.h @@ -8,6 +8,7 @@ #include "serd/memory.h" #include "serd/string_view.h" #include "serd/uri.h" +#include "serd/value.h" #include "serd/write_result.h" #include "zix/attributes.h" @@ -107,168 +108,324 @@ typedef uint32_t SerdNodeFlags; /** @} - @defgroup serd_node_dynamic_allocation Dynamic Allocation - @{ -*/ + @defgroup serd_node_construction_arguments Arguments -/** - Create a new simple "token" node. + A unified representation of the arguments needed to specify any node. + + Since there are several types of node, and a node can be constructed in + memory in several ways, the API for specifying node arguments is separate + from the APIs for actually creating nodes. This prevents a combinatorial + API explosion by allowing functions that create or access nodes to have a + single parameter that describes the node. - A "token" is a node that isn't a typed or tagged literal. This can be used - to create URIs, blank nodes, CURIEs, and simple string literals. + Arguments constructors like #serd_a_file_uri return a temporary view of + their arguments, which can be passed (usually inline) to node construction + functions like #serd_node_new, or #serd_node_construct. + + @{ */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_token(SerdAllocator* ZIX_NULLABLE allocator, - SerdNodeType type, - SerdStringView string); -/** - Create a new string literal node. +/// The type of a #SerdNodeArgs +typedef enum { + SERD_NODE_ARGS_TOKEN, ///< A token @see #serd_a_token + SERD_NODE_ARGS_PARSED_URI, ///< A parsed URI @see #serd_a_parsed_uri + SERD_NODE_ARGS_FILE_URI, ///< A file URI @see #serd_a_file_uri + SERD_NODE_ARGS_LITERAL, ///< A literal @see #serd_a_literal + SERD_NODE_ARGS_PRIMITIVE, ///< A "native" primitive @see #serd_a_primitive + SERD_NODE_ARGS_DECIMAL, ///< A decimal number @see #serd_a_decimal + SERD_NODE_ARGS_INTEGER, ///< An integer number @see #serd_a_integer + SERD_NODE_ARGS_HEX, ///< A hex-encoded blob @see #serd_a_hex + SERD_NODE_ARGS_BASE64, ///< A base64-encoded blob @see #serd_a_base64 +} SerdNodeArgsType; + +/// The data for #SERD_NODE_ARGS_TOKEN +typedef struct { + SerdNodeType type; + SerdStringView string; +} SerdNodeTokenArgs; + +/// The data for #SERD_NODE_ARGS_PARSED_URI +typedef struct { + SerdURIView uri; +} SerdNodeParsedURIArgs; + +/// The data for #SERD_NODE_ARGS_FILE_URI +typedef struct { + SerdStringView path; + SerdStringView hostname; +} SerdNodeFileURIArgs; + +/// The data for #SERD_NODE_ARGS_LITERAL +typedef struct { + SerdStringView string; + SerdNodeFlags flags; + SerdStringView meta; +} SerdNodeLiteralArgs; + +/// The data for #SERD_NODE_ARGS_PRIMITIVE +typedef struct { + SerdValue value; +} SerdNodePrimitiveArgs; + +/// The data for #SERD_NODE_ARGS_DECIMAL +typedef struct { + double value; +} SerdNodeDecimalArgs; + +/// The data for #SERD_NODE_ARGS_INTEGER +typedef struct { + int64_t value; +} SerdNodeIntegerArgs; + +/// The data for #SERD_NODE_ARGS_HEX or #SERD_NODE_ARGS_BASE64 +typedef struct { + size_t size; + const void* ZIX_NONNULL data; +} SerdNodeBlobArgs; + +/// The data of a #SerdNodeArgs +typedef union { + SerdNodeTokenArgs as_token; + SerdNodeParsedURIArgs as_parsed_uri; + SerdNodeFileURIArgs as_file_uri; + SerdNodeLiteralArgs as_literal; + SerdNodePrimitiveArgs as_primitive; + SerdNodeDecimalArgs as_decimal; + SerdNodeIntegerArgs as_integer; + SerdNodeBlobArgs as_blob; +} SerdNodeArgsData; + +/// Arguments for constructing a node +typedef struct { + SerdNodeArgsType type; ///< Type of node described and valid field of `data` + SerdNodeArgsData data; ///< Data union +} SerdNodeArgs; + +/** + A simple "token" node. + + "Token" is just a shorthand used in this API to refer to a node that is not + a typed or tagged literal, that is, a node that is just one string. This + can be used to create URIs, blank nodes, variables, and simple string + literals. + + Note that string literals constructed with this function will have no flags + set, and so will be written as "short" literals (not triple-quoted). To + construct long literals, use the more advanced serd_a_literal() with the + #SERD_IS_LONG flag. +*/ +SERD_CONST_API SerdNodeArgs +serd_a_token(SerdNodeType type, SerdStringView string); + +/// A URI node from a parsed URI +SERD_CONST_API SerdNodeArgs +serd_a_parsed_uri(SerdURIView uri); + +/// A file URI node from a path and optional hostname +SERD_CONST_API SerdNodeArgs +serd_a_file_uri(SerdStringView path, SerdStringView hostname); + +/** + A literal node with an optional datatype or language. + + Either a datatype (which must be an absolute URI) or a language (which must + be an RFC5646 language tag) may be given, but not both. + + This is the most general literal constructor, which can be used to construct + any literal node. + + @param string The string body of the node. + + @param flags Flags that describe the details of the node. + + @param meta The string value of the literal's metadata. If + #SERD_HAS_DATATYPE is set, then this must be an absolute datatype URI. If + #SERD_HAS_LANGUAGE is set, then this must be a language tag like "en-ca". + Otherwise, it is ignored. */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_string(SerdAllocator* ZIX_NULLABLE allocator, SerdStringView string); +SERD_CONST_API SerdNodeArgs +serd_a_literal(SerdStringView string, SerdNodeFlags flags, SerdStringView meta); -/** - Create a new literal node with optional datatype or language. +/// A simple string literal node from a string view +ZIX_CONST_FUNC static inline SerdNodeArgs +serd_a_string_view(SerdStringView string) +{ + return serd_a_token(SERD_LITERAL, string); +} - This can create more complex literals than serd_new_string() with an - associated datatype URI or language tag, as well as control whether a - literal should be written as a short or long (triple-quoted) string. +/// A simple string literal node from a C string +ZIX_CONST_FUNC static inline SerdNodeArgs +serd_a_string(const char* ZIX_NONNULL string) +{ + return serd_a_string_view(serd_string(string)); +} - @param allocator Allocator for the returned node. +/// A blank node from a string view +ZIX_CONST_FUNC static inline SerdNodeArgs +serd_a_blank(SerdStringView name) +{ + return serd_a_token(SERD_BLANK, name); +} - @param string The string value of the literal. +/// A blank node from a string +ZIX_CONST_FUNC static inline SerdNodeArgs +serd_a_blank_string(const char* ZIX_NONNULL name) +{ + return serd_a_blank(serd_string(name)); +} - @param flags Flags to describe the literal and its metadata. This must be a - valid combination of flags, in particular, at most one of #SERD_HAS_DATATYPE - and #SERD_HAS_LANGUAGE may be set. +/// A URI node from a string view +ZIX_CONST_FUNC static inline SerdNodeArgs +serd_a_uri(SerdStringView uri) +{ + return serd_a_token(SERD_URI, uri); +} - @param meta The string value of the literal's metadata. If - #SERD_HAS_DATATYPE is set, then this must be an absolute datatype URI. If - #SERD_HAS_LANGUAGE is set, then this must be a language tag like "en-ca". - Otherwise, it is ignored. +/** + A URI node from a string. - @return A newly allocated literal node that must be freed with - serd_node_free(), or null if the arguments are invalid or allocation failed. + @param uri The URI string. */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_literal(SerdAllocator* ZIX_NULLABLE allocator, - SerdStringView string, - SerdNodeFlags flags, - SerdStringView meta); +ZIX_CONST_FUNC static inline SerdNodeArgs +serd_a_uri_string(const char* ZIX_NONNULL uri) +{ + return serd_a_uri(serd_string(uri)); +} /** - Create a new node from a blank node label. + A literal node with a datatype. + + @param string The string body of the node. + @param datatype The absolute URI of the datatype. */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_blank(SerdAllocator* ZIX_NULLABLE allocator, SerdStringView string); +ZIX_CONST_FUNC static inline SerdNodeArgs +serd_a_typed_literal(const SerdStringView string, const SerdStringView datatype) +{ + return serd_a_literal(string, SERD_HAS_DATATYPE, datatype); +} /** - Create a new URI node from a parsed URI. + A literal node with a language. + + @param string The string body of the node. + @param language A language tag like "en-ca". */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_parsed_uri(SerdAllocator* ZIX_NULLABLE allocator, SerdURIView uri); +ZIX_CONST_FUNC static inline SerdNodeArgs +serd_a_plain_literal(const SerdStringView string, const SerdStringView language) +{ + return serd_a_literal(string, SERD_HAS_LANGUAGE, language); +} /** - Create a new URI node from a string. + A canonical literal for a primitive value. + + The node will be a typed literal in canonical form for the xsd datatype + corresponding to the value. */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_uri(SerdAllocator* ZIX_NULLABLE allocator, SerdStringView string); +SERD_CONST_API SerdNodeArgs +serd_a_primitive(SerdValue value); /** - Create a new file URI node from a file system path and optional hostname. + A canonical xsd:decimal literal. - Backslashes in Windows paths will be converted, and other characters will be - percent encoded as necessary. + The node will be an xsd:decimal literal, like "12.34", with datatype + xsd:decimal. - If `path` is relative, `hostname` is ignored. + The node will always contain a '.', start with a digit, and end with a digit + (a leading and/or trailing '0' will be added if necessary), for example, + "1.0". It will never be in scientific notation. */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_file_uri(SerdAllocator* ZIX_NULLABLE allocator, - SerdStringView path, - SerdStringView hostname); +SERD_CONST_API SerdNodeArgs +serd_a_decimal(double value); /** - Create a new canonical xsd:boolean node. + A canonical xsd:integer literal. + + The node will be an xsd:integer literal like "1234", with datatype + xsd:integer. */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_boolean(SerdAllocator* ZIX_NULLABLE allocator, bool b); +SERD_CONST_API SerdNodeArgs +serd_a_integer(int64_t value); /** - Create a new canonical xsd:decimal literal. + A canonical xsd:hexBinary literal. - The node will be an xsd:decimal literal, like "12.34", with - datatype xsd:decimal by default, or a custom datatype. + The node will be an xsd:hexBinary literal like "534D", with datatype + xsd:hexBinary. +*/ +SERD_CONST_API SerdNodeArgs +serd_a_hex(size_t size, const void* ZIX_NONNULL data); - The node will always contain a '.', start with a digit, and end with a digit - (a leading and/or trailing '0' will be added if necessary), for example, - "1.0". It will never be in scientific notation. +/** + A canonical xsd:base64Binary literal. - @param allocator Allocator for the returned node. - @param d The value for the new node. - @param datatype Datatype of node, or NULL for xsd:decimal. + The node will be an xsd:base64Binary literal like "Zm9vYmFy", with datatype + xsd:base64Binary. */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_decimal(SerdAllocator* ZIX_NULLABLE allocator, - double d, - const SerdNode* ZIX_NULLABLE datatype); +SERD_CONST_API SerdNodeArgs +serd_a_base64(size_t size, const void* ZIX_NONNULL data); /** - Create a new canonical xsd:double literal. + @} + @defgroup serd_node_construction Construction - The node will be in scientific notation, like "1.23E4", except for NaN and - negative/positive infinity, which are "NaN", "-INF", and "INF", - respectively. + This is the low-level node construction API, which can be used to construct + nodes into existing buffers. Advanced applications can use this to + specially manage node memory, for example by allocating nodes on the stack, + or with a special allocator. - Uses the shortest possible representation that precisely describes the - value, which has at most 17 significant digits (under 24 characters total). + Note that nodes are "plain old data", so there is no need to destroy a + constructed node, and nodes may be trivially copied, for example with + memcpy(). - @param allocator Allocator for the returned node. - @param d Double value to write. - @return A literal node with datatype xsd:double. + @{ */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_double(SerdAllocator* ZIX_NULLABLE allocator, double d); /** - Create a new canonical xsd:float literal. + Construct a node into an existing buffer. - Uses identical formatting to serd_new_double(), except with at most 9 - significant digits (under 14 characters total). + This is the universal node constructor which can construct any node. The + type of node is specified in a #SerdNodeArgs tagged union, to avoid API + bloat and allow this function to be used with data-based dispatch. - @param allocator Allocator for the returned node. - @param f Float value of literal. - @return A literal node with datatype xsd:float. + This function may also be used to determine the size of buffer required by + passing a null buffer with zero size. + + @param buf_size The size of `buf` in bytes, or zero to only measure. + + @param buf Buffer where the node will be written, or null to only measure. + + @param args Arguments describing the node to construct. + + @return A result with a `status` and a `count` of bytes written. If the + buffer is too small for the node, then `status` will be #SERD_OVERFLOW, and + `count` will be set to the number of bytes required to successfully + construct the node. */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_float(SerdAllocator* ZIX_NULLABLE allocator, float f); +SERD_API SerdWriteResult +serd_node_construct(size_t buf_size, void* ZIX_NULLABLE buf, SerdNodeArgs args); /** - Create a new canonical xsd:integer literal. + @} + @defgroup serd_node_dynamic_allocation Dynamic Allocation - The node will be an xsd:integer literal like "1234", with datatype - xsd:integer. + This is a convenient higher-level node construction API which allocates + nodes with an allocator. The returned nodes must be freed with + serd_node_free() using the same allocator. - @param allocator Allocator for the returned node. - @param i Integer value of literal. + @{ */ -SERD_API SerdNode* ZIX_ALLOCATED -serd_new_integer(SerdAllocator* ZIX_NULLABLE allocator, int64_t i); /** - Create a new canonical xsd:base64Binary literal. + Create a new node. - This function can be used to make a node out of arbitrary binary data, which - can be decoded using serd_base64_decode(). + This allocates and constructs a new node of any type. - @param allocator Allocator for the returned node. - @param buf Raw binary data to encode in node. - @param size Size of `buf` in bytes. + @return A newly allocated node that must be freed with serd_node_free(), or + null. */ SERD_API SerdNode* ZIX_ALLOCATED -serd_new_base64(SerdAllocator* ZIX_NULLABLE allocator, - const void* ZIX_NONNULL buf, - size_t size); +serd_node_new(SerdAllocator* ZIX_NULLABLE allocator, SerdNodeArgs args); /** Return a deep copy of `node`. @@ -353,45 +510,37 @@ SERD_PURE_API const SerdNode* ZIX_NULLABLE serd_node_language(const SerdNode* ZIX_NONNULL node); /** - Return the value of `node` as a boolean. + Return the primitive value of a literal node. - This will work for booleans, and numbers of any datatype if they are 0 or - 1. + This will return a typed numeric value if the node can be read as one, or + nothing otherwise. - @return The value of `node` as a `bool`, or `false` on error. + @return The primitive value of `node`, if possible and supported. */ -SERD_API bool -serd_get_boolean(const SerdNode* ZIX_NONNULL node); +SERD_API SerdValue +serd_node_value(const SerdNode* ZIX_NONNULL node); /** - Return the value of `node` as a double. + Return the primitive value of a node as a specific type of number. - This will coerce numbers of any datatype to double, if the value fits. + This is like serd_node_value(), but will coerce the value of the node to the + requested type if possible. - @return The value of `node` as a `double`, or NaN on error. -*/ -SERD_API double -serd_get_double(const SerdNode* ZIX_NONNULL node); + @param node The node to interpret as a number. -/** - Return the value of `node` as a float. + @param type The desired numeric datatype of the result. - This will coerce numbers of any datatype to float, if the value fits. + @param lossy Whether lossy conversions can be used. If this is false, then + this function only succeeds if the value could be converted back to the + original datatype of the node without loss. Otherwise, precision may be + reduced or values may be truncated to fit the result. - @return The value of `node` as a `float`, or NaN on error. + @return The value of `node` as a #SerdValue, or nothing. */ -SERD_API float -serd_get_float(const SerdNode* ZIX_NONNULL node); - -/** - Return the value of `node` as a long (signed 64-bit integer). - - This will coerce numbers of any datatype to long, if the value fits. - - @return The value of `node` as a `int64_t`, or 0 on error. -*/ -SERD_API int64_t -serd_get_integer(const SerdNode* ZIX_NONNULL node); +SERD_API SerdValue +serd_node_value_as(const SerdNode* ZIX_NONNULL node, + SerdValueType type, + bool lossy); /** Return the maximum size of a decoded binary node in bytes. @@ -400,16 +549,21 @@ serd_get_integer(const SerdNode* ZIX_NONNULL node); decode to. This is calculated as a simple constant-time arithmetic expression based on the length of the encoded string, so may be larger than the actual size of the data due to things like additional whitespace. + + @return The size of the decoded hex or base64 blob `node`, or zero if it + does not have datatype <http://www.w3.org/2001/XMLSchema#hexBinary> or + <http://www.w3.org/2001/XMLSchema#base64Binary>. */ SERD_PURE_API size_t -serd_get_base64_size(const SerdNode* ZIX_NONNULL node); +serd_node_decoded_size(const SerdNode* ZIX_NONNULL node); /** - Decode a base64 node. + Decode a binary (base64 or hex) node. - This function can be used to decode a node created with serd_new_base64(). + This function can be used to decode a node created with serd_a_base64() or + serd_a_hex() and retrieve the original unencoded binary data. - @param node A literal node which is an encoded base64 string. + @param node A literal node which is an encoded base64 or hex string. @param buf_size The size of `buf` in bytes. @@ -420,9 +574,9 @@ serd_get_base64_size(const SerdNode* ZIX_NONNULL node); along with the number of bytes required for successful decoding. */ SERD_API SerdWriteResult -serd_get_base64(const SerdNode* ZIX_NONNULL node, - size_t buf_size, - void* ZIX_NONNULL buf); +serd_node_decode(const SerdNode* ZIX_NONNULL node, + size_t buf_size, + void* ZIX_NONNULL buf); /** @} diff --git a/include/serd/serd.h b/include/serd/serd.h index 6103c543..88be5daa 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -61,6 +61,7 @@ #include "serd/node.h" #include "serd/statement.h" #include "serd/uri.h" +#include "serd/value.h" /** @} diff --git a/include/serd/string.h b/include/serd/string.h index 11e55e6e..028a1a29 100644 --- a/include/serd/string.h +++ b/include/serd/string.h @@ -18,19 +18,15 @@ SERD_BEGIN_DECLS */ /** - Decode a base64 string. + Compare two strings ignoring case. - This function can be used to decode a node created with serd_new_base64(). - - @param str Base64 string to decode. - @param len The length of `str`. - @param size Set to the size of the returned blob in bytes. - @return A newly allocated blob which must be freed with serd_free(). + @return Less than, equal to, or greater than zero if `s1` is less than, + equal to, or greater than `s2`, respectively. */ -SERD_API void* ZIX_ALLOCATED -serd_base64_decode(const char* ZIX_NONNULL str, - size_t len, - size_t* ZIX_NONNULL size); +SERD_PURE_API int +serd_strncasecmp(const char* ZIX_NONNULL s1, + const char* ZIX_NONNULL s2, + size_t n); /** @} diff --git a/include/serd/value.h b/include/serd/value.h new file mode 100644 index 00000000..a31268e0 --- /dev/null +++ b/include/serd/value.h @@ -0,0 +1,118 @@ +// Copyright 2011-2022 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#ifndef SERD_VALUE_H +#define SERD_VALUE_H + +#include "serd/attributes.h" + +#include <stdbool.h> +#include <stdint.h> + +SERD_BEGIN_DECLS + +/** + @defgroup serd_node_value Values + @ingroup serd_data + + Serd supports reading and writing machine-native numbers, called "values", + in a standards-conformant and portable way. The value structure is used in + the API to allow passing and returning a primitive value of any supported + type. Note that this is just an API convenience, literal nodes themselves + always store their values as strings. + + @{ +*/ + +/// The type of a #SerdValue +typedef enum { + SERD_NOTHING, ///< Sentinel for unknown datatypes or errors + SERD_BOOL, ///< xsd:boolean (bool) + SERD_DOUBLE, ///< xsd:double (double) + SERD_FLOAT, ///< xsd:float (float) + SERD_LONG, ///< xsd:long (int64_t) + SERD_INT, ///< xsd:integer (int32_t) + SERD_SHORT, ///< xsd:short (int16_t) + SERD_BYTE, ///< xsd:byte (int8_t) + SERD_ULONG, ///< xsd:unsignedLong (uint64_t) + SERD_UINT, ///< xsd:unsignedInt (uint32_t) + SERD_USHORT, ///< xsd:unsignedShort (uint16_t) + SERD_UBYTE, ///< xsd:unsignedByte (uint8_t) +} SerdValueType; + +/// The data of a #SerdValue (the actual machine-native primitive) +typedef union { + bool as_bool; + double as_double; + float as_float; + int64_t as_long; + int32_t as_int; + int16_t as_short; + int8_t as_byte; + uint64_t as_ulong; + uint32_t as_uint; + uint16_t as_ushort; + uint8_t as_ubyte; +} SerdValueData; + +/// A primitive value with a type tag +typedef struct { + SerdValueType type; + SerdValueData data; +} SerdValue; + +/// Convenience constructor to make a #SERD_NOTHING (non-)value +SERD_CONST_API SerdValue +serd_nothing(void); + +/// Convenience constructor to make a #SERD_BOOL value +SERD_CONST_API SerdValue +serd_bool(bool v); + +/// Convenience constructor to make a #SERD_DOUBLE value +SERD_CONST_API SerdValue +serd_double(double v); + +/// Convenience constructor to make a #SERD_FLOAT value +SERD_CONST_API SerdValue +serd_float(float v); + +/// Convenience constructor to make a #SERD_LONG value +SERD_CONST_API SerdValue +serd_long(int64_t v); + +/// Convenience constructor to make a #SERD_INT value +SERD_CONST_API SerdValue +serd_int(int32_t v); + +/// Convenience constructor to make a #SERD_SHORT value +SERD_CONST_API SerdValue +serd_short(int16_t v); + +/// Convenience constructor to make a #SERD_BYTE value +SERD_CONST_API SerdValue +serd_byte(int8_t v); + +/// Convenience constructor to make a #SERD_ULONG value +SERD_CONST_API SerdValue +serd_ulong(uint64_t v); + +/// Convenience constructor to make a #SERD_UINT value +SERD_CONST_API SerdValue +serd_uint(uint32_t v); + +/// Convenience constructor to make a #SERD_USHORT value +SERD_CONST_API SerdValue +serd_ushort(uint16_t v); + +/// Convenience constructor to make a #SERD_UBYTE value +SERD_CONST_API SerdValue +serd_ubyte(uint8_t v); + +/** + @} +*/ + +SERD_END_DECLS + +#endif // SERD_VALUE_H diff --git a/meson.build b/meson.build index ad745658..7bd2e560 100644 --- a/meson.build +++ b/meson.build @@ -147,6 +147,7 @@ c_headers = files( 'include/serd/string_view.h', 'include/serd/syntax.h', 'include/serd/uri.h', + 'include/serd/value.h', 'include/serd/version.h', 'include/serd/world.h', 'include/serd/write_result.h', @@ -177,6 +178,7 @@ sources = files( 'src/syntax.c', 'src/system.c', 'src/uri.c', + 'src/value.c', 'src/world.c', 'src/writer.c', ) 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 <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "base64.h" - -#include "exess/exess.h" -#include "serd/string.h" - -#include <stdlib.h> - -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 <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#ifndef SERD_SRC_BASE64_H -#define SERD_SRC_BASE64_H - -#include "serd/attributes.h" - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -/** - 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 <assert.h> #include <stdbool.h> @@ -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; @@ -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 @@ -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 @@ -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 <assert.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -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 <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include "serd/value.h" + +#include <stdbool.h> +#include <stdint.h> + +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 <assert.h> @@ -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); } diff --git a/test/test_caret.c b/test/test_caret.c index a97b3d0f..9374f65b 100644 --- a/test/test_caret.c +++ b/test/test_caret.c @@ -7,7 +7,6 @@ #include "serd/caret.h" #include "serd/node.h" -#include "serd/string_view.h" #include <assert.h> #include <stddef.h> @@ -16,7 +15,7 @@ static int test_caret(void) { - SerdNode* const node = serd_new_string(NULL, serd_string("node")); + SerdNode* const node = serd_node_new(NULL, serd_a_string("node")); SerdCaret* const caret = serd_caret_new(NULL, node, 46, 2); assert(serd_caret_equals(caret, caret)); @@ -29,7 +28,7 @@ test_caret(void) assert(serd_caret_equals(caret, copy)); assert(!serd_caret_copy(NULL, NULL)); - SerdNode* const other_node = serd_new_string(NULL, serd_string("other")); + SerdNode* const other_node = serd_node_new(NULL, serd_a_string("other")); SerdCaret* const other_file = serd_caret_new(NULL, other_node, 46, 2); SerdCaret* const other_line = serd_caret_new(NULL, node, 47, 2); SerdCaret* const other_col = serd_caret_new(NULL, node, 46, 3); @@ -54,8 +53,12 @@ test_caret(void) static void test_failed_alloc(void) { - SerdNode* node = serd_new_token(NULL, SERD_LITERAL, serd_string("node")); + char node_buf[32]; + assert(!serd_node_construct(sizeof(node_buf), node_buf, serd_a_string("node")) + .status); + + const SerdNode* node = (const SerdNode*)node_buf; SerdFailingAllocator allocator = serd_failing_allocator(); // Successfully allocate a new caret to count the number of allocations @@ -84,7 +87,6 @@ test_failed_alloc(void) serd_caret_free(&allocator.base, copy); serd_caret_free(&allocator.base, caret); - serd_node_free(NULL, node); } int diff --git a/test/test_env.c b/test/test_env.c index 89947826..eec8a2ef 100644 --- a/test/test_env.c +++ b/test/test_env.c @@ -202,7 +202,7 @@ static void test_base_uri(void) { SerdEnv* const env = serd_env_new(NULL, serd_empty_string()); - SerdNode* const eg = serd_new_uri(NULL, serd_string(NS_EG)); + SerdNode* const eg = serd_node_new(NULL, serd_a_uri_string(NS_EG)); // Test that invalid calls work as expected assert(!serd_env_base_uri(env)); @@ -262,7 +262,7 @@ test_set_prefix(void) static void test_expand_untyped_literal(void) { - SerdNode* const untyped = serd_new_string(NULL, serd_string("data")); + SerdNode* const untyped = serd_node_new(NULL, serd_a_string("data")); SerdEnv* const env = serd_env_new(NULL, serd_empty_string()); assert(!serd_env_expand_node(env, untyped)); @@ -277,7 +277,7 @@ test_expand_bad_uri_datatype(void) const SerdStringView type = serd_string("Type"); SerdNode* const typed = - serd_new_literal(NULL, serd_string("data"), SERD_HAS_DATATYPE, type); + serd_node_new(NULL, serd_a_typed_literal(serd_string("data"), type)); SerdEnv* const env = serd_env_new(NULL, serd_empty_string()); @@ -293,9 +293,9 @@ test_expand_uri(void) const SerdStringView base = serd_string("http://example.org/b/"); SerdEnv* const env = serd_env_new(NULL, base); - SerdNode* const rel = serd_new_uri(NULL, serd_string("rel")); + SerdNode* const rel = serd_node_new(NULL, serd_a_uri_string("rel")); SerdNode* const rel_out = serd_env_expand_node(env, rel); - SerdNode* const empty = serd_new_uri(NULL, serd_empty_string()); + SerdNode* const empty = serd_node_new(NULL, serd_a_uri_string("")); SerdNode* const empty_out = serd_env_expand_node(env, empty); assert(!strcmp(serd_node_string(rel_out), "http://example.org/b/rel")); @@ -313,7 +313,7 @@ test_expand_empty_uri_ref(void) { const SerdStringView base = serd_string("http://example.org/b/"); - SerdNode* const rel = serd_new_uri(NULL, serd_string("rel")); + SerdNode* const rel = serd_node_new(NULL, serd_a_uri_string("rel")); SerdEnv* const env = serd_env_new(NULL, base); SerdNode* const rel_out = serd_env_expand_node(env, rel); @@ -327,7 +327,7 @@ test_expand_empty_uri_ref(void) static void test_expand_bad_uri(void) { - SerdNode* const bad_uri = serd_new_uri(NULL, serd_string("rel")); + SerdNode* const bad_uri = serd_node_new(NULL, serd_a_uri_string("rel")); SerdEnv* const env = serd_env_new(NULL, serd_empty_string()); assert(!serd_env_expand_node(env, bad_uri)); @@ -372,7 +372,7 @@ test_expand_bad_curie(void) static void test_expand_blank(void) { - SerdNode* const blank = serd_new_blank(NULL, serd_string("b1")); + SerdNode* const blank = serd_node_new(NULL, serd_a_blank_string("b1")); SerdEnv* const env = serd_env_new(NULL, serd_empty_string()); assert(!serd_env_expand_node(env, blank)); diff --git a/test/test_log.c b/test/test_log.c index 5da8ec09..aba9fe23 100644 --- a/test/test_log.c +++ b/test/test_log.c @@ -83,7 +83,8 @@ static void test_caret(void) { SerdWorld* const world = serd_world_new(NULL); - SerdNode* const name = serd_new_string(NULL, serd_string("filename")); + + SerdNode* const name = serd_node_new(NULL, serd_a_string("filename")); SerdCaret* const caret = serd_caret_new(NULL, name, 46, 2); serd_logf_at(world, SERD_LOG_LEVEL_NOTICE, caret, "is just ahead of me"); diff --git a/test/test_node.c b/test/test_node.c index 23d1a45a..6b159007 100644 --- a/test/test_node.c +++ b/test/test_node.c @@ -7,6 +7,7 @@ #include "serd/status.h" #include "serd/string_view.h" #include "serd/uri.h" +#include "serd/value.h" #include "serd/write_result.h" #include <assert.h> @@ -48,54 +49,178 @@ #endif static void +test_new(void) +{ + const SerdNodeArgs bad_args = {(SerdNodeArgsType)-1, + {{(SerdNodeType)-1, {NULL, 0U}}}}; + + assert(!serd_node_new(NULL, bad_args)); +} + +static void +test_value(void) +{ + static const double double_one = 1.0; + static const float float_two = 2.0f; + + SerdNode* const null_node = + serd_node_new(NULL, serd_a_primitive(serd_nothing())); + SerdNode* const bool_node = + serd_node_new(NULL, serd_a_primitive(serd_bool(false))); + SerdNode* const double_node = + serd_node_new(NULL, serd_a_primitive(serd_double(1.0))); + SerdNode* const float_node = + serd_node_new(NULL, serd_a_primitive(serd_float(2.0f))); + SerdNode* const long_node = + serd_node_new(NULL, serd_a_primitive(serd_long(3))); + SerdNode* const int_node = serd_node_new(NULL, serd_a_primitive(serd_int(4))); + SerdNode* const short_node = + serd_node_new(NULL, serd_a_primitive(serd_short(5))); + SerdNode* const byte_node = + serd_node_new(NULL, serd_a_primitive(serd_byte(6))); + SerdNode* const ulong_node = + serd_node_new(NULL, serd_a_primitive(serd_ulong(7U))); + SerdNode* const uint_node = + serd_node_new(NULL, serd_a_primitive(serd_uint(8U))); + SerdNode* const ushort_node = + serd_node_new(NULL, serd_a_primitive(serd_ushort(9U))); + SerdNode* const ubyte_node = + serd_node_new(NULL, serd_a_primitive(serd_ubyte(10U))); + + assert(!null_node); + + assert(!strcmp(serd_node_string(bool_node), "false")); + assert(serd_node_value(bool_node).type == SERD_BOOL); + assert(serd_node_value(bool_node).data.as_bool == false); + + assert(!strcmp(serd_node_string(double_node), "1.0E0")); + assert(serd_node_value(double_node).type == SERD_DOUBLE); + { + const double double_value = serd_node_value(double_node).data.as_double; + assert(!memcmp(&double_value, &double_one, sizeof(double))); + } + + assert(!strcmp(serd_node_string(float_node), "2.0E0")); + assert(serd_node_value(float_node).type == SERD_FLOAT); + { + const float float_value = serd_node_value(float_node).data.as_float; + assert(!memcmp(&float_value, &float_two, sizeof(float))); + } + + assert(!strcmp(serd_node_string(long_node), "3")); + assert(serd_node_value(long_node).type == SERD_LONG); + assert(serd_node_value(long_node).data.as_long == 3); + + assert(!strcmp(serd_node_string(int_node), "4")); + assert(serd_node_value(int_node).type == SERD_INT); + assert(serd_node_value(int_node).data.as_int == 4); + + assert(!strcmp(serd_node_string(short_node), "5")); + assert(serd_node_value(short_node).type == SERD_SHORT); + assert(serd_node_value(short_node).data.as_short == 5); + + assert(!strcmp(serd_node_string(byte_node), "6")); + assert(serd_node_value(byte_node).type == SERD_BYTE); + assert(serd_node_value(byte_node).data.as_byte == 6); + + assert(!strcmp(serd_node_string(ulong_node), "7")); + assert(serd_node_value(ulong_node).type == SERD_ULONG); + assert(serd_node_value(ulong_node).data.as_ulong == 7U); + + assert(!strcmp(serd_node_string(uint_node), "8")); + assert(serd_node_value(uint_node).type == SERD_UINT); + assert(serd_node_value(uint_node).data.as_uint == 8U); + + assert(!strcmp(serd_node_string(ushort_node), "9")); + assert(serd_node_value(ushort_node).type == SERD_USHORT); + assert(serd_node_value(ushort_node).data.as_ushort == 9U); + + assert(!strcmp(serd_node_string(ubyte_node), "10")); + assert(serd_node_value(ubyte_node).type == SERD_UBYTE); + assert(serd_node_value(ubyte_node).data.as_ubyte == 10U); + + serd_node_free(NULL, bool_node); + serd_node_free(NULL, double_node); + serd_node_free(NULL, float_node); + serd_node_free(NULL, long_node); + serd_node_free(NULL, int_node); + serd_node_free(NULL, short_node); + serd_node_free(NULL, byte_node); + serd_node_free(NULL, ulong_node); + serd_node_free(NULL, uint_node); + serd_node_free(NULL, ushort_node); + serd_node_free(NULL, ubyte_node); +} + +static void test_boolean(void) { - SerdNode* const true_node = serd_new_boolean(NULL, true); - assert(!strcmp(serd_node_string(true_node), "true")); - assert(serd_get_boolean(true_node)); - - const SerdNode* const true_datatype = serd_node_datatype(true_node); - assert(true_datatype); - assert(!strcmp(serd_node_string(true_datatype), NS_XSD "boolean")); - serd_node_free(NULL, true_node); - - SerdNode* const false_node = serd_new_boolean(NULL, false); - assert(!strcmp(serd_node_string(false_node), "false")); - assert(!serd_get_boolean(false_node)); - - const SerdNode* const false_datatype = serd_node_datatype(false_node); - assert(false_datatype); - assert(!strcmp(serd_node_string(false_datatype), NS_XSD "boolean")); - serd_node_free(NULL, false_node); + { + SerdNode* const true_node = + serd_node_new(NULL, serd_a_primitive(serd_bool(true))); + assert(true_node); + assert(!strcmp(serd_node_string(true_node), "true")); + assert(serd_node_value(true_node).data.as_bool); + + const SerdNode* const true_datatype = serd_node_datatype(true_node); + assert(true_datatype); + assert(!strcmp(serd_node_string(true_datatype), NS_XSD "boolean")); + serd_node_free(NULL, true_node); + } + { + SerdNode* const false_node = + serd_node_new(NULL, serd_a_primitive(serd_bool(false))); + assert(false_node); + assert(!strcmp(serd_node_string(false_node), "false")); + assert(!serd_node_value(false_node).data.as_bool); + + const SerdNode* const false_datatype = serd_node_datatype(false_node); + assert(false_datatype); + assert(!strcmp(serd_node_string(false_datatype), NS_XSD "boolean")); + serd_node_free(NULL, false_node); + } } static void -check_get_boolean(const char* string, - const char* datatype_uri, - const bool expected) +check_get_bool(const char* string, + const char* datatype_uri, + const bool lossy, + const SerdValueType value_type, + const bool expected) { - SerdNode* const node = serd_new_literal( - NULL, serd_string(string), SERD_HAS_DATATYPE, serd_string(datatype_uri)); + SerdNode* const node = serd_node_new( + NULL, serd_a_typed_literal(serd_string(string), serd_string(datatype_uri))); assert(node); - assert(serd_get_boolean(node) == expected); + + const SerdValue value = serd_node_value_as(node, SERD_BOOL, lossy); + + assert(value.type == value_type); + assert(value.data.as_bool == expected); serd_node_free(NULL, node); } static void -test_get_boolean(void) +test_get_bool(void) { - check_get_boolean("false", NS_XSD "boolean", false); - check_get_boolean("true", NS_XSD "boolean", true); - check_get_boolean("0", NS_XSD "boolean", false); - check_get_boolean("1", NS_XSD "boolean", true); - check_get_boolean("0", NS_XSD "integer", false); - check_get_boolean("1", NS_XSD "integer", true); - check_get_boolean("0.0", NS_XSD "double", false); - check_get_boolean("1.0", NS_XSD "double", true); - check_get_boolean("unknown", NS_XSD "string", false); - check_get_boolean("!invalid", NS_XSD "long", false); + check_get_bool("false", NS_XSD "boolean", false, SERD_BOOL, false); + check_get_bool("true", NS_XSD "boolean", false, SERD_BOOL, true); + check_get_bool("0", NS_XSD "boolean", false, SERD_BOOL, false); + check_get_bool("1", NS_XSD "boolean", false, SERD_BOOL, true); + check_get_bool("0", NS_XSD "integer", false, SERD_BOOL, false); + check_get_bool("1", NS_XSD "integer", false, SERD_BOOL, true); + check_get_bool("0.0", NS_XSD "double", false, SERD_BOOL, false); + check_get_bool("1.0", NS_XSD "double", false, SERD_BOOL, true); + + check_get_bool("2", NS_XSD "integer", false, SERD_NOTHING, false); + check_get_bool("1.5", NS_XSD "double", false, SERD_NOTHING, false); + + check_get_bool("2", NS_XSD "integer", true, SERD_BOOL, true); + check_get_bool("1.5", NS_XSD "double", true, SERD_BOOL, true); + + check_get_bool("unknown", NS_XSD "string", true, SERD_NOTHING, false); + check_get_bool("!invalid", NS_XSD "long", true, SERD_NOTHING, false); } static void @@ -114,7 +239,7 @@ test_decimal(void) "0.0000000001"}; for (size_t i = 0; i < sizeof(test_values) / sizeof(double); ++i) { - SerdNode* node = serd_new_decimal(NULL, test_values[i], NULL); + SerdNode* node = serd_node_new(NULL, serd_a_decimal(test_values[i])); const char* node_str = serd_node_string(node); assert(!strcmp(node_str, test_strings[i])); @@ -125,8 +250,8 @@ test_decimal(void) assert(datatype); assert(!strcmp(serd_node_string(datatype), NS_XSD "decimal")); - const double value = serd_get_double(node); - assert(!memcmp(&value, &test_values[i], sizeof(value))); + const SerdValue value = serd_node_value(node); + assert(!memcmp(&value.data.as_double, &test_values[i], sizeof(double))); serd_node_free(NULL, node); } } @@ -139,7 +264,8 @@ test_double(void) "0.0E0", "-0.0E0", "1.2E0", "-2.3E0", "4.56789E6"}; for (size_t i = 0; i < sizeof(test_values) / sizeof(double); ++i) { - SerdNode* node = serd_new_double(NULL, test_values[i]); + SerdNode* node = + serd_node_new(NULL, serd_a_primitive(serd_double(test_values[i]))); const char* node_str = serd_node_string(node); assert(!strcmp(node_str, test_strings[i])); @@ -150,24 +276,35 @@ test_double(void) assert(datatype); assert(!strcmp(serd_node_string(datatype), NS_XSD "double")); - const double value = serd_get_double(node); - assert(!memcmp(&value, &test_values[i], sizeof(value))); + const SerdValue value = serd_node_value(node); + assert(!memcmp(&value.data.as_double, &test_values[i], sizeof(double))); serd_node_free(NULL, node); } } static void -check_get_double(const char* string, - const char* datatype_uri, - const double expected) +check_get_double(const char* string, + const char* datatype_uri, + const bool lossy, + const SerdValueType value_type, + const double expected) { - SerdNode* const node = serd_new_literal( - NULL, serd_string(string), SERD_HAS_DATATYPE, serd_string(datatype_uri)); + SerdNode* const node = serd_node_new( + NULL, serd_a_typed_literal(serd_string(string), serd_string(datatype_uri))); assert(node); - const double value = serd_get_double(node); - assert(!memcmp(&value, &expected, sizeof(value))); + const SerdValue value = serd_node_value_as(node, SERD_DOUBLE, lossy); + + assert(value.type == value_type); + + SERD_DISABLE_CONVERSION_WARNINGS + + assert(value_type == SERD_NOTHING || + ((isnan(value.data.as_double) && isnan(expected)) || + !memcmp(&value.data.as_double, &expected, sizeof(double)))); + + SERD_RESTORE_WARNINGS serd_node_free(NULL, node); } @@ -175,34 +312,19 @@ check_get_double(const char* string, static void test_get_double(void) { - check_get_double("1.2", NS_XSD "double", 1.2); - check_get_double("-.5", NS_XSD "float", -0.5); - check_get_double("-67", NS_XSD "long", -67.0); - check_get_double("8.9", NS_XSD "decimal", 8.9); - check_get_double("false", NS_XSD "boolean", 0.0); - check_get_double("true", NS_XSD "boolean", 1.0); - - static const uint8_t blob[] = {1U, 2U, 3U, 4U}; + check_get_double("1.2", NS_XSD "double", false, SERD_DOUBLE, 1.2); + check_get_double("-.5", NS_XSD "float", false, SERD_DOUBLE, -0.5); + check_get_double("-67", NS_XSD "long", false, SERD_DOUBLE, -67.0); + check_get_double("67", NS_XSD "unsignedLong", false, SERD_DOUBLE, 67.0); + check_get_double("8.9", NS_XSD "decimal", false, SERD_DOUBLE, 8.9); + check_get_double("false", NS_XSD "boolean", false, SERD_DOUBLE, 0.0); + check_get_double("true", NS_XSD "boolean", false, SERD_DOUBLE, 1.0); SERD_DISABLE_CONVERSION_WARNINGS - - SerdNode* const nan = serd_new_string(NULL, serd_string("unknown")); - assert(isnan(serd_get_double(nan))); - serd_node_free(NULL, nan); - - SerdNode* const invalid = serd_new_literal(NULL, - serd_string("!invalid"), - SERD_HAS_DATATYPE, - serd_string(NS_XSD "long")); - - assert(isnan(serd_get_double(invalid))); - serd_node_free(NULL, invalid); - - SerdNode* const base64 = serd_new_base64(NULL, blob, sizeof(blob)); - - assert(isnan(serd_get_double(base64))); - serd_node_free(NULL, base64); - + check_get_double("str", NS_XSD "string", true, SERD_NOTHING, NAN); + check_get_double("!invalid", NS_XSD "long", true, SERD_NOTHING, NAN); + check_get_double("D3AD", NS_XSD "hexBinary", true, SERD_NOTHING, NAN); + check_get_double("Zm9v", NS_XSD "base64Binary", true, SERD_NOTHING, NAN); SERD_RESTORE_WARNINGS } @@ -214,7 +336,8 @@ test_float(void) "0.0E0", "-0.0E0", "1.5E0", "-2.5E0", "4.56789E6"}; for (size_t i = 0; i < sizeof(test_values) / sizeof(float); ++i) { - SerdNode* node = serd_new_float(NULL, test_values[i]); + SerdNode* node = + serd_node_new(NULL, serd_a_primitive(serd_float(test_values[i]))); const char* node_str = serd_node_string(node); assert(!strcmp(node_str, test_strings[i])); @@ -225,24 +348,35 @@ test_float(void) assert(datatype); assert(!strcmp(serd_node_string(datatype), NS_XSD "float")); - const float value = serd_get_float(node); - assert(!memcmp(&value, &test_values[i], sizeof(value))); + const SerdValue value = serd_node_value(node); + assert(!memcmp(&value.data.as_float, &test_values[i], sizeof(float))); serd_node_free(NULL, node); } } static void -check_get_float(const char* string, - const char* datatype_uri, - const float expected) +check_get_float(const char* string, + const char* datatype_uri, + const bool lossy, + const SerdValueType value_type, + const float expected) { - SerdNode* const node = serd_new_literal( - NULL, serd_string(string), SERD_HAS_DATATYPE, serd_string(datatype_uri)); + SerdNode* const node = serd_node_new( + NULL, serd_a_typed_literal(serd_string(string), serd_string(datatype_uri))); assert(node); - const float value = serd_get_float(node); - assert(!memcmp(&value, &expected, sizeof(value))); + const SerdValue value = serd_node_value_as(node, SERD_FLOAT, lossy); + + assert(value.type == value_type); + + SERD_DISABLE_CONVERSION_WARNINGS + + assert(value_type == SERD_NOTHING || + ((isnan(value.data.as_float) && isnan(expected)) || + !memcmp(&value.data.as_float, &expected, sizeof(float)))); + + SERD_RESTORE_WARNINGS serd_node_free(NULL, node); } @@ -250,29 +384,20 @@ check_get_float(const char* string, static void test_get_float(void) { - check_get_float("1.2", NS_XSD "float", 1.2f); - check_get_float("-.5", NS_XSD "float", -0.5f); - check_get_float("-67", NS_XSD "long", -67.0f); - check_get_float("1.5", NS_XSD "decimal", 1.5f); - check_get_float("false", NS_XSD "boolean", 0.0f); - check_get_float("true", NS_XSD "boolean", 1.0f); + check_get_float("1.2", NS_XSD "float", false, SERD_FLOAT, 1.2f); + check_get_float("-.5", NS_XSD "float", false, SERD_FLOAT, -0.5f); + check_get_float("-67", NS_XSD "long", false, SERD_FLOAT, -67.0f); + check_get_float("false", NS_XSD "boolean", false, SERD_FLOAT, 0.0f); + check_get_float("true", NS_XSD "boolean", false, SERD_FLOAT, 1.0f); - SERD_DISABLE_CONVERSION_WARNINGS - - SerdNode* const nan = serd_new_string(NULL, serd_string("unknown")); - assert(isnan(serd_get_float(nan))); - serd_node_free(NULL, nan); - - SerdNode* const invalid = serd_new_literal(NULL, - serd_string("!invalid"), - SERD_HAS_DATATYPE, - serd_string(NS_XSD "long")); - - assert(isnan(serd_get_double(invalid))); + check_get_float("1.5", NS_XSD "decimal", true, SERD_FLOAT, 1.5f); + SERD_DISABLE_CONVERSION_WARNINGS + check_get_float("str", NS_XSD "string", true, SERD_NOTHING, NAN); + check_get_float("!invalid", NS_XSD "long", true, SERD_NOTHING, NAN); + check_get_float("D3AD", NS_XSD "hexBinary", true, SERD_NOTHING, NAN); + check_get_float("Zm9v", NS_XSD "base64Binary", true, SERD_NOTHING, NAN); SERD_RESTORE_WARNINGS - - serd_node_free(NULL, invalid); } static void @@ -283,7 +408,7 @@ test_integer(void) "0", "0", "-23", "23", "-12340", "1000", "-1000"}; for (size_t i = 0; i < sizeof(test_values) / sizeof(double); ++i) { - SerdNode* node = serd_new_integer(NULL, test_values[i]); + SerdNode* node = serd_node_new(NULL, serd_a_integer(test_values[i])); const char* node_str = serd_node_string(node); assert(!strcmp(node_str, test_strings[i])); const size_t len = strlen(node_str); @@ -293,21 +418,27 @@ test_integer(void) assert(datatype); assert(!strcmp(serd_node_string(datatype), NS_XSD "integer")); - assert(serd_get_integer(node) == test_values[i]); + assert(serd_node_value(node).data.as_long == test_values[i]); serd_node_free(NULL, node); } } static void -check_get_integer(const char* string, - const char* datatype_uri, - const int64_t expected) +check_get_integer(const char* string, + const char* datatype_uri, + const bool lossy, + const SerdValueType value_type, + const int64_t expected) { - SerdNode* const node = serd_new_literal( - NULL, serd_string(string), SERD_HAS_DATATYPE, serd_string(datatype_uri)); + SerdNode* const node = serd_node_new( + NULL, serd_a_typed_literal(serd_string(string), serd_string(datatype_uri))); assert(node); - assert(serd_get_integer(node) == expected); + + const SerdValue value = serd_node_value_as(node, SERD_LONG, lossy); + + assert(value.type == value_type); + assert(value_type == SERD_NOTHING || value.data.as_long == expected); serd_node_free(NULL, node); } @@ -315,20 +446,66 @@ check_get_integer(const char* string, static void test_get_integer(void) { - check_get_integer("12", NS_XSD "long", 12); - check_get_integer("-34", NS_XSD "long", -34); - check_get_integer("56", NS_XSD "integer", 56); - check_get_integer("false", NS_XSD "boolean", 0); - check_get_integer("true", NS_XSD "boolean", 1); - check_get_integer("78.0", NS_XSD "decimal", 78); - check_get_integer("unknown", NS_XSD "string", 0); - check_get_integer("!invalid", NS_XSD "long", 0); + check_get_integer("12", NS_XSD "long", false, SERD_LONG, 12); + check_get_integer("-34", NS_XSD "long", false, SERD_LONG, -34); + check_get_integer("56", NS_XSD "integer", false, SERD_LONG, 56); + check_get_integer("false", NS_XSD "boolean", false, SERD_LONG, 0); + check_get_integer("true", NS_XSD "boolean", false, SERD_LONG, 1); + check_get_integer("78.0", NS_XSD "decimal", false, SERD_LONG, 78); + + check_get_integer("0", NS_XSD "nonPositiveInteger", false, SERD_LONG, 0); + check_get_integer("-1", NS_XSD "negativeInteger", false, SERD_LONG, -1); + check_get_integer("2", NS_XSD "nonNegativeInteger", false, SERD_LONG, 2); + check_get_integer("3", NS_XSD "positiveInteger", false, SERD_LONG, 3); + + check_get_integer("78.5", NS_XSD "decimal", false, SERD_NOTHING, 0); + check_get_integer("78.5", NS_XSD "decimal", true, SERD_LONG, 78); + + check_get_integer("unknown", NS_XSD "string", true, SERD_NOTHING, 0); + check_get_integer("!invalid", NS_XSD "long", true, SERD_NOTHING, 0); +} + +static void +test_hex(void) +{ + assert(!serd_node_new(NULL, serd_a_hex(0, &SERD_URI_NULL))); + + // Test valid hex blobs with a range of sizes + for (size_t size = 1; size < 256; ++size) { + uint8_t* const data = (uint8_t*)malloc(size); + for (size_t i = 0; i < size; ++i) { + data[i] = (uint8_t)((size + i) % 256); + } + + SerdNode* blob = serd_node_new(NULL, serd_a_hex(size, data)); + const char* blob_str = serd_node_string(blob); + const size_t max_size = serd_node_decoded_size(blob); + uint8_t* out = (uint8_t*)calloc(1, max_size); + + const SerdWriteResult r = serd_node_decode(blob, max_size, out); + assert(r.status == SERD_SUCCESS); + assert(r.count == size); + assert(r.count <= max_size); + assert(serd_node_length(blob) == strlen(blob_str)); + + for (size_t i = 0; i < size; ++i) { + assert(out[i] == data[i]); + } + + const SerdNode* const datatype = serd_node_datatype(blob); + assert(datatype); + assert(!strcmp(serd_node_string(datatype), NS_XSD "hexBinary")); + + serd_node_free(NULL, blob); + free(out); + free(data); + } } static void test_base64(void) { - assert(!serd_new_base64(NULL, &SERD_URI_NULL, 0)); + assert(!serd_node_new(NULL, serd_a_base64(0, &SERD_URI_NULL))); // Test valid base64 blobs with a range of sizes for (size_t size = 1; size < 256; ++size) { @@ -337,12 +514,12 @@ test_base64(void) data[i] = (uint8_t)((size + i) % 256); } - SerdNode* blob = serd_new_base64(NULL, data, size); + SerdNode* blob = serd_node_new(NULL, serd_a_base64(size, data)); const char* blob_str = serd_node_string(blob); - const size_t max_size = serd_get_base64_size(blob); + const size_t max_size = serd_node_decoded_size(blob); uint8_t* out = (uint8_t*)calloc(1, max_size); - const SerdWriteResult r = serd_get_base64(blob, max_size, out); + const SerdWriteResult r = serd_node_decode(blob, max_size, out); assert(r.status == SERD_SUCCESS); assert(r.count == size); assert(r.count <= max_size); @@ -363,19 +540,17 @@ test_base64(void) } static void -check_get_base64(const char* string, - const char* datatype_uri, - const char* expected) +check_decode(const char* string, const char* datatype_uri, const char* expected) { - SerdNode* const node = serd_new_literal( - NULL, serd_string(string), SERD_HAS_DATATYPE, serd_string(datatype_uri)); + SerdNode* const node = serd_node_new( + NULL, serd_a_typed_literal(serd_string(string), serd_string(datatype_uri))); assert(node); - const size_t max_size = serd_get_base64_size(node); + const size_t max_size = serd_node_decoded_size(node); char* const decoded = (char*)calloc(1, max_size + 1); - const SerdWriteResult r = serd_get_base64(node, max_size, decoded); + const SerdWriteResult r = serd_node_decode(node, max_size, decoded); assert(!r.status); assert(r.count <= max_size); @@ -387,22 +562,53 @@ check_get_base64(const char* string, } static void -test_get_base64(void) +test_decode(void) { - check_get_base64("Zm9vYmFy", NS_XSD "base64Binary", "foobar"); - check_get_base64("Zm9vYg==", NS_XSD "base64Binary", "foob"); - check_get_base64(" \f\n\r\t\vZm9v \f\n\r\t\v", NS_XSD "base64Binary", "foo"); + check_decode("666F6F626172", NS_XSD "hexBinary", "foobar"); + check_decode("666F6F62", NS_XSD "hexBinary", "foob"); - SerdNode* const node = serd_new_literal(NULL, - serd_string("Zm9v"), - SERD_HAS_DATATYPE, - serd_string(NS_XSD "base64Binary")); + check_decode("Zm9vYmFy", NS_XSD "base64Binary", "foobar"); + check_decode("Zm9vYg==", NS_XSD "base64Binary", "foob"); + check_decode(" \f\n\r\t\vZm9v \f\n\r\t\v", NS_XSD "base64Binary", "foo"); - char small[2] = {0}; - const SerdWriteResult r = serd_get_base64(node, sizeof(small), small); + char small[2] = {0}; - assert(r.status == SERD_OVERFLOW); - serd_node_free(NULL, node); + { + SerdNode* const node = + serd_node_new(NULL, + serd_a_typed_literal(serd_string("Zm9v"), + serd_string(NS_XSD "base64Binary"))); + + const SerdWriteResult r = serd_node_decode(node, sizeof(small), small); + + assert(r.status == SERD_OVERFLOW); + serd_node_free(NULL, node); + } + { + SerdNode* const string = serd_node_new(NULL, serd_a_string("string")); + + assert(serd_node_decoded_size(string) == 0U); + + const SerdWriteResult r = serd_node_decode(string, sizeof(small), small); + + assert(r.status == SERD_BAD_ARG); + assert(r.count == 0U); + serd_node_free(NULL, string); + } + { + SerdNode* const unknown = serd_node_new( + NULL, + serd_a_typed_literal(serd_string("secret"), + serd_string("http://example.org/Datatype"))); + + assert(serd_node_decoded_size(unknown) == 0U); + + const SerdWriteResult r = serd_node_decode(unknown, sizeof(small), small); + + assert(r.status == SERD_BAD_ARG); + assert(r.count == 0U); + serd_node_free(NULL, unknown); + } } static void @@ -413,8 +619,8 @@ test_node_equals(void) static const SerdStringView replacement_char = { (const char*)replacement_char_str, 3}; - SerdNode* lhs = serd_new_string(NULL, replacement_char); - SerdNode* rhs = serd_new_string(NULL, serd_string("123")); + SerdNode* lhs = serd_node_new(NULL, serd_a_string_view(replacement_char)); + SerdNode* rhs = serd_node_new(NULL, serd_a_string("123")); assert(serd_node_equals(lhs, lhs)); assert(!serd_node_equals(lhs, rhs)); @@ -428,7 +634,7 @@ test_node_equals(void) static void test_node_from_string(void) { - SerdNode* const hello = serd_new_string(NULL, serd_string("hello\"")); + SerdNode* const hello = serd_node_new(NULL, serd_a_string("hello\"")); assert(serd_node_length(hello) == 6); assert(!serd_node_flags(hello)); assert(!strncmp(serd_node_string(hello), "hello\"", 6)); @@ -436,7 +642,8 @@ test_node_from_string(void) assert(serd_node_string_view(hello).length == 6); serd_node_free(NULL, hello); - SerdNode* const uri = serd_new_uri(NULL, serd_string("http://example.org/")); + SerdNode* const uri = + serd_node_new(NULL, serd_a_uri_string("http://example.org/")); assert(serd_node_length(uri) == 19); assert(!strcmp(serd_node_string(uri), "http://example.org/")); assert(serd_node_uri_view(uri).authority.length == 11); @@ -447,7 +654,8 @@ test_node_from_string(void) static void test_node_from_substring(void) { - SerdNode* const a_b = serd_new_string(NULL, serd_substring("a\"bc", 3)); + SerdNode* const a_b = + serd_node_new(NULL, serd_a_string_view(serd_substring("a\"bc", 3))); assert(serd_node_length(a_b) == 3); assert(!serd_node_flags(a_b)); assert(strlen(serd_node_string(a_b)) == 3); @@ -471,48 +679,42 @@ test_literal(void) const SerdStringView hello_str = serd_string("hello"); const SerdStringView empty_str = serd_empty_string(); - assert(!serd_new_literal(NULL, - hello_str, - SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE, - serd_string("whatever"))); - - assert(!serd_new_literal(NULL, hello_str, SERD_HAS_DATATYPE, empty_str)); - assert(!serd_new_literal(NULL, hello_str, SERD_HAS_LANGUAGE, empty_str)); + assert(!serd_node_new(NULL, serd_a_typed_literal(hello_str, empty_str))); + assert(!serd_node_new(NULL, serd_a_plain_literal(hello_str, empty_str))); assert( - !serd_new_literal(NULL, hello_str, SERD_HAS_DATATYPE, serd_string("Type"))); + !serd_node_new(NULL, serd_a_typed_literal(hello_str, serd_string("Type")))); assert( - !serd_new_literal(NULL, hello_str, SERD_HAS_DATATYPE, serd_string("de"))); + !serd_node_new(NULL, serd_a_typed_literal(hello_str, serd_string("de")))); assert( - !serd_new_literal(NULL, hello_str, SERD_HAS_LANGUAGE, serd_string("3n"))); + !serd_node_new(NULL, serd_a_plain_literal(hello_str, serd_string("3n")))); assert( - !serd_new_literal(NULL, hello_str, SERD_HAS_LANGUAGE, serd_string("d3"))); + !serd_node_new(NULL, serd_a_plain_literal(hello_str, serd_string("d3")))); assert( - !serd_new_literal(NULL, hello_str, SERD_HAS_LANGUAGE, serd_string("d3"))); + !serd_node_new(NULL, serd_a_plain_literal(hello_str, serd_string("d3")))); assert( - !serd_new_literal(NULL, hello_str, SERD_HAS_LANGUAGE, serd_string("en-!"))); + !serd_node_new(NULL, serd_a_plain_literal(hello_str, serd_string("en-!")))); - SerdNode* hello2 = serd_new_string(NULL, serd_string("hello\"")); + SerdNode* hello2 = serd_node_new(NULL, serd_a_string("hello\"")); assert(serd_node_length(hello2) == 6 && !strcmp(serd_node_string(hello2), "hello\"")); check_copy_equals(hello2); - assert(!serd_new_literal(NULL, - serd_string("plain"), - SERD_HAS_DATATYPE, - serd_string(NS_RDF "langString"))); + assert( + !serd_node_new(NULL, + serd_a_typed_literal(serd_string("plain"), + serd_string(NS_RDF "langString")))); serd_node_free(NULL, hello2); const char* lang_lit_str = "\"Hello\"@en-ca"; SerdNode* sliced_lang_lit = - serd_new_literal(NULL, - serd_substring(lang_lit_str + 1, 5), - SERD_HAS_LANGUAGE, - serd_substring(lang_lit_str + 8, 5)); + serd_node_new(NULL, + serd_a_plain_literal(serd_substring(lang_lit_str + 1, 5), + serd_substring(lang_lit_str + 8, 5))); assert(!strcmp(serd_node_string(sliced_lang_lit), "Hello")); @@ -524,10 +726,9 @@ test_literal(void) const char* type_lit_str = "\"Hallo\"^^<http://example.org/Greeting>"; SerdNode* sliced_type_lit = - serd_new_literal(NULL, - serd_substring(type_lit_str + 1, 5), - SERD_HAS_DATATYPE, - serd_substring(type_lit_str + 10, 27)); + serd_node_new(NULL, + serd_a_typed_literal(serd_substring(type_lit_str + 1, 5), + serd_substring(type_lit_str + 10, 27))); assert(!strcmp(serd_node_string(sliced_type_lit), "Hallo")); @@ -540,7 +741,7 @@ test_literal(void) static void test_blank(void) { - SerdNode* blank = serd_new_blank(NULL, serd_string("b0")); + SerdNode* blank = serd_node_new(NULL, serd_a_blank_string("b0")); assert(serd_node_length(blank) == 2); assert(serd_node_flags(blank) == 0); assert(!strcmp(serd_node_string(blank), "b0")); @@ -550,37 +751,34 @@ test_blank(void) static void test_compare(void) { - SerdNode* xsd_short = - serd_new_uri(NULL, serd_string("http://www.w3.org/2001/XMLSchema#short")); + SerdNode* angst = serd_node_new(NULL, serd_a_string("angst")); - SerdNode* angst = serd_new_string(NULL, serd_string("angst")); - - SerdNode* angst_de = serd_new_literal( - NULL, serd_string("angst"), SERD_HAS_LANGUAGE, serd_string("de")); + SerdNode* angst_de = serd_node_new( + NULL, serd_a_plain_literal(serd_string("angst"), serd_string("de"))); assert(angst_de); - SerdNode* angst_en = serd_new_literal( - NULL, serd_string("angst"), SERD_HAS_LANGUAGE, serd_string("en")); + SerdNode* angst_en = serd_node_new( + NULL, serd_a_plain_literal(serd_string("angst"), serd_string("en"))); - SerdNode* hallo = serd_new_literal( - NULL, serd_string("Hallo"), SERD_HAS_LANGUAGE, serd_string("de")); + SerdNode* hallo = serd_node_new( + NULL, serd_a_typed_literal(serd_string("Hallo"), serd_string("de"))); - SerdNode* hello = serd_new_string(NULL, serd_string("Hello")); - SerdNode* universe = serd_new_string(NULL, serd_string("Universe")); - SerdNode* integer = serd_new_integer(NULL, 4); - SerdNode* blank = serd_new_blank(NULL, serd_string("b1")); - SerdNode* uri = serd_new_uri(NULL, serd_string("http://example.org/")); + SerdNode* hello = serd_node_new(NULL, serd_a_string("Hello")); + SerdNode* universe = serd_node_new(NULL, serd_a_string("Universe")); + SerdNode* integer = serd_node_new(NULL, serd_a_integer(4)); + SerdNode* short_int = serd_node_new(NULL, serd_a_primitive(serd_short(4))); + SerdNode* blank = serd_node_new(NULL, serd_a_blank_string("b1")); + SerdNode* uri = serd_node_new(NULL, serd_a_uri_string("http://example.org/")); - SerdNode* aardvark = - serd_new_literal(NULL, - serd_string("alex"), - SERD_HAS_DATATYPE, - serd_string("http://example.org/Aardvark")); + SerdNode* aardvark = serd_node_new( + NULL, + serd_a_typed_literal(serd_string("alex"), + serd_string("http://example.org/Aardvark"))); - SerdNode* badger = serd_new_literal(NULL, - serd_string("bobby"), - SERD_HAS_DATATYPE, - serd_string("http://example.org/Badger")); + SerdNode* badger = serd_node_new( + NULL, + serd_a_typed_literal(serd_string("bobby"), + serd_string("http://example.org/Badger"))); // Types are ordered according to their SerdNodeType (more or less arbitrary) assert(serd_node_compare(integer, hello) < 0); @@ -594,26 +792,29 @@ test_compare(void) assert(serd_node_compare(angst, angst_de) < 0); assert(serd_node_compare(angst_de, angst_en) < 0); assert(serd_node_compare(aardvark, badger) < 0); + assert(serd_node_compare(integer, short_int) < 0); + serd_node_free(NULL, badger); + serd_node_free(NULL, aardvark); serd_node_free(NULL, uri); serd_node_free(NULL, blank); + serd_node_free(NULL, short_int); serd_node_free(NULL, integer); - serd_node_free(NULL, badger); - serd_node_free(NULL, aardvark); serd_node_free(NULL, universe); serd_node_free(NULL, hello); serd_node_free(NULL, hallo); serd_node_free(NULL, angst_en); serd_node_free(NULL, angst_de); serd_node_free(NULL, angst); - serd_node_free(NULL, xsd_short); } int main(void) { + test_new(); + test_value(); test_boolean(); - test_get_boolean(); + test_get_bool(); test_decimal(); test_double(); test_get_double(); @@ -621,8 +822,9 @@ main(void) test_get_float(); test_integer(); test_get_integer(); + test_hex(); test_base64(); - test_get_base64(); + test_decode(); test_node_equals(); test_node_from_string(); test_node_from_substring(); diff --git a/test/test_overflow.c b/test/test_overflow.c index 7b1693a8..8b47cd2b 100644 --- a/test/test_overflow.c +++ b/test/test_overflow.c @@ -29,7 +29,7 @@ test_size(SerdWorld* const world, return SERD_BAD_STACK; } - SerdNode* string_name = serd_new_string(NULL, serd_string("string")); + SerdNode* string_name = serd_node_new(NULL, serd_a_string("string")); const char* position = str; SerdInputStream in = serd_open_input_string(&position); serd_reader_start(reader, &in, string_name, 1); diff --git a/test/test_reader.c b/test/test_reader.c index a0eaee5c..54dbca9f 100644 --- a/test/test_reader.c +++ b/test/test_reader.c @@ -624,7 +624,7 @@ test_error_cursor(void) "<http://example.org/s> <http://example.org/p> " "<http://example.org/o> ."; - SerdNode* const string_name = serd_new_string(NULL, serd_string("string")); + SerdNode* const string_name = serd_node_new(NULL, serd_a_string("string")); const char* position = string; SerdInputStream in = serd_open_input_string(&position); diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c index d6a2675d..aea0976d 100644 --- a/test/test_reader_writer.c +++ b/test/test_reader_writer.c @@ -154,7 +154,7 @@ test_writer(const char* const path) serd_writer_chop_blank_prefix(writer, "tmp"); serd_writer_chop_blank_prefix(writer, NULL); - SerdNode* lit = serd_new_string(NULL, serd_string("hello")); + SerdNode* lit = serd_node_new(NULL, serd_a_string("hello")); const SerdSink* const iface = serd_writer_sink(writer); assert(serd_sink_write_base(iface, lit)); @@ -164,9 +164,10 @@ test_writer(const char* const path) static const uint8_t bad_buf[] = {0xEF, 0xBF, 0xBD, 0}; const SerdStringView bad_buf_view = {(const char*)bad_buf, 3}; - SerdNode* s = serd_new_uri(NULL, serd_string("http://example.org")); - SerdNode* p = serd_new_uri(NULL, serd_string("http://example.org/pred")); - SerdNode* bad = serd_new_string(NULL, bad_buf_view); + SerdNode* s = serd_node_new(NULL, serd_a_uri_string("http://example.org")); + SerdNode* p = + serd_node_new(NULL, serd_a_uri_string("http://example.org/pred")); + SerdNode* bad = serd_node_new(NULL, serd_a_string_view(bad_buf_view)); // Write 3 invalid statements (should write nothing) const SerdNode* junk[][3] = {{s, bad, bad}, {bad, p, bad}, {s, bad, p}}; @@ -179,13 +180,13 @@ test_writer(const char* const path) const SerdStringView urn_Type = serd_string("urn:Type"); const SerdStringView en = serd_string("en"); - SerdNode* const o = serd_new_string(NULL, serd_string("o")); + SerdNode* const o = serd_node_new(NULL, serd_a_string("o")); SerdNode* const t = - serd_new_literal(NULL, serd_string("t"), SERD_HAS_DATATYPE, urn_Type); + serd_node_new(NULL, serd_a_typed_literal(serd_string("t"), urn_Type)); SerdNode* const l = - serd_new_literal(NULL, serd_string("l"), SERD_HAS_LANGUAGE, en); + serd_node_new(NULL, serd_a_plain_literal(serd_string("l"), en)); const SerdNode* good[][3] = {{s, p, o}, {s, p, t}, {s, p, l}}; @@ -200,15 +201,15 @@ test_writer(const char* const path) static const char* const bad_uri_str = (const char*)bad_uri_buf; // Write statements with bad UTF-8 (should be replaced) - SerdNode* bad_lit = serd_new_string(NULL, serd_string(bad_lit_str)); - SerdNode* bad_uri = serd_new_uri(NULL, serd_string(bad_uri_str)); + SerdNode* bad_lit = serd_node_new(NULL, serd_a_string(bad_lit_str)); + SerdNode* bad_uri = serd_node_new(NULL, serd_a_uri_string(bad_uri_str)); assert(!serd_sink_write(iface, 0, s, p, bad_lit, 0)); assert(!serd_sink_write(iface, 0, s, p, bad_uri, 0)); serd_node_free(NULL, bad_uri); serd_node_free(NULL, bad_lit); // Write 1 valid statement - SerdNode* const hello = serd_new_string(NULL, serd_string("hello")); + SerdNode* const hello = serd_node_new(NULL, serd_a_string("hello")); assert(!serd_sink_write(iface, 0, s, p, hello, 0)); serd_node_free(NULL, hello); @@ -223,7 +224,7 @@ test_writer(const char* const path) // Test buffer sink SerdBuffer buffer = {NULL, NULL, 0}; SerdNode* const base = - serd_new_uri(NULL, serd_string("http://example.org/base")); + serd_node_new(NULL, serd_a_uri_string("http://example.org/base")); output = serd_open_output_buffer(&buffer); writer = serd_writer_new(world, SERD_TURTLE, 0, env, &output, 1U); diff --git a/test/test_sink.c b/test/test_sink.c index 9c7959de..72c45df6 100644 --- a/test/test_sink.c +++ b/test/test_sink.c @@ -11,7 +11,6 @@ #include "serd/sink.h" #include "serd/statement.h" #include "serd/status.h" -#include "serd/string_view.h" #include <assert.h> #include <stddef.h> @@ -111,10 +110,10 @@ test_failed_alloc(void) static void test_callbacks(void) { - SerdNode* const base = serd_new_uri(NULL, serd_string(NS_EG)); - SerdNode* const name = serd_new_string(NULL, serd_string("eg")); - SerdNode* const uri = serd_new_uri(NULL, serd_string(NS_EG "uri")); - SerdNode* const blank = serd_new_blank(NULL, serd_string("b1")); + SerdNode* const base = serd_node_new(NULL, serd_a_uri_string(NS_EG)); + SerdNode* const name = serd_node_new(NULL, serd_a_string("eg")); + SerdNode* const uri = serd_node_new(NULL, serd_a_uri_string(NS_EG "uri")); + SerdNode* const blank = serd_node_new(NULL, serd_a_blank_string("b1")); SerdEnv* env = serd_env_new(NULL, serd_node_string_view(base)); State state = {0, 0, 0, 0, 0, SERD_SUCCESS}; diff --git a/test/test_statement.c b/test/test_statement.c index 0127b7bf..fe1ce818 100644 --- a/test/test_statement.c +++ b/test/test_statement.c @@ -6,7 +6,6 @@ #include "serd/caret.h" #include "serd/node.h" #include "serd/statement.h" -#include "serd/string_view.h" #include <assert.h> #include <stddef.h> @@ -16,9 +15,9 @@ static void test_new(void) { - SerdNode* const u = serd_new_uri(NULL, serd_string(NS_EG "s")); - SerdNode* const b = serd_new_blank(NULL, serd_string("b0")); - SerdNode* const l = serd_new_string(NULL, serd_string("str")); + SerdNode* const u = serd_node_new(NULL, serd_a_uri_string(NS_EG "s")); + SerdNode* const b = serd_node_new(NULL, serd_a_blank_string("b0")); + SerdNode* const l = serd_node_new(NULL, serd_a_string("str")); assert(!serd_statement_new(NULL, u, b, u, NULL, NULL)); assert(!serd_statement_new(NULL, l, u, u, NULL, NULL)); @@ -37,11 +36,11 @@ test_copy(void) { assert(!serd_statement_copy(NULL, NULL)); - SerdNode* const f = serd_new_string(NULL, serd_string("file")); - SerdNode* const s = serd_new_uri(NULL, serd_string(NS_EG "s")); - SerdNode* const p = serd_new_uri(NULL, serd_string(NS_EG "p")); - SerdNode* const o = serd_new_uri(NULL, serd_string(NS_EG "o")); - SerdNode* const g = serd_new_uri(NULL, serd_string(NS_EG "g")); + SerdNode* const f = serd_node_new(NULL, serd_a_string("file")); + SerdNode* const s = serd_node_new(NULL, serd_a_uri_string(NS_EG "s")); + SerdNode* const p = serd_node_new(NULL, serd_a_uri_string(NS_EG "p")); + SerdNode* const o = serd_node_new(NULL, serd_a_uri_string(NS_EG "o")); + SerdNode* const g = serd_node_new(NULL, serd_a_uri_string(NS_EG "g")); SerdCaret* const caret = serd_caret_new(NULL, f, 1, 1); SerdStatement* const statement = serd_statement_new(NULL, s, p, o, g, caret); @@ -69,11 +68,11 @@ test_free(void) static void test_fields(void) { - SerdNode* const f = serd_new_string(NULL, serd_string("file")); - SerdNode* const s = serd_new_uri(NULL, serd_string(NS_EG "s")); - SerdNode* const p = serd_new_uri(NULL, serd_string(NS_EG "p")); - SerdNode* const o = serd_new_uri(NULL, serd_string(NS_EG "o")); - SerdNode* const g = serd_new_uri(NULL, serd_string(NS_EG "g")); + SerdNode* const f = serd_node_new(NULL, serd_a_string("file")); + SerdNode* const s = serd_node_new(NULL, serd_a_uri_string(NS_EG "s")); + SerdNode* const p = serd_node_new(NULL, serd_a_uri_string(NS_EG "p")); + SerdNode* const o = serd_node_new(NULL, serd_a_uri_string(NS_EG "o")); + SerdNode* const g = serd_node_new(NULL, serd_a_uri_string(NS_EG "g")); SerdCaret* const caret = serd_caret_new(NULL, f, 1, 1); SerdStatement* const statement = serd_statement_new(NULL, s, p, o, g, caret); diff --git a/test/test_terse_write.c b/test/test_terse_write.c index 0bdb0280..3fa08fbd 100644 --- a/test/test_terse_write.c +++ b/test/test_terse_write.c @@ -43,16 +43,16 @@ test(void) SerdWorld* world = serd_world_new(NULL); SerdEnv* env = serd_env_new(NULL, serd_empty_string()); - SerdNode* b1 = serd_new_blank(NULL, serd_string("b1")); - SerdNode* l1 = serd_new_blank(NULL, serd_string("l1")); - SerdNode* l2 = serd_new_blank(NULL, serd_string("l2")); - SerdNode* s1 = serd_new_string(NULL, serd_string("s1")); - SerdNode* s2 = serd_new_string(NULL, serd_string("s2")); - - SerdNode* rdf_first = serd_new_uri(NULL, serd_string(NS_RDF "first")); - SerdNode* rdf_value = serd_new_uri(NULL, serd_string(NS_RDF "value")); - SerdNode* rdf_rest = serd_new_uri(NULL, serd_string(NS_RDF "rest")); - SerdNode* rdf_nil = serd_new_uri(NULL, serd_string(NS_RDF "nil")); + SerdNode* b1 = serd_node_new(NULL, serd_a_blank_string("b1")); + SerdNode* l1 = serd_node_new(NULL, serd_a_blank_string("l1")); + SerdNode* l2 = serd_node_new(NULL, serd_a_blank_string("l2")); + SerdNode* s1 = serd_node_new(NULL, serd_a_string("s1")); + SerdNode* s2 = serd_node_new(NULL, serd_a_string("s2")); + + SerdNode* rdf_first = serd_node_new(NULL, serd_a_uri_string(NS_RDF "first")); + SerdNode* rdf_value = serd_node_new(NULL, serd_a_uri_string(NS_RDF "value")); + SerdNode* rdf_rest = serd_node_new(NULL, serd_a_uri_string(NS_RDF "rest")); + SerdNode* rdf_nil = serd_node_new(NULL, serd_a_uri_string(NS_RDF "nil")); serd_env_set_prefix(env, serd_string("rdf"), serd_string(NS_RDF)); diff --git a/test/test_uri.c b/test/test_uri.c index 79d3f73e..7a15ffb8 100644 --- a/test/test_uri.c +++ b/test/test_uri.c @@ -75,8 +75,8 @@ test_file_uri(const char* const hostname, expected_path = path; } - SerdNode* node = - serd_new_file_uri(NULL, serd_string(path), serd_string(hostname)); + SerdNode* node = serd_node_new( + NULL, serd_a_file_uri(serd_string(path), serd_string(hostname))); const char* node_str = serd_node_string(node); char* out_hostname = NULL; @@ -166,8 +166,8 @@ test_parse_uri(void) const SerdURIView base_uri = serd_parse_uri(base.data); const SerdURIView empty_uri = serd_parse_uri(""); - SerdNode* const nil = - serd_new_parsed_uri(NULL, serd_resolve_uri(empty_uri, base_uri)); + SerdNode* const nil = serd_node_new( + NULL, serd_a_parsed_uri(serd_resolve_uri(empty_uri, base_uri))); assert(serd_node_type(nil) == SERD_URI); assert(!strcmp(serd_node_string(nil), base.data)); @@ -227,21 +227,26 @@ check_relative_uri(const char* const uri_string, assert(base_string); assert(expected_string); - SerdNode* const uri_node = serd_new_uri(NULL, serd_string(uri_string)); - const SerdURIView uri = serd_node_uri_view(uri_node); - SerdNode* const base_node = serd_new_uri(NULL, serd_string(base_string)); - const SerdURIView base = serd_node_uri_view(base_node); + SerdNode* const uri_node = serd_node_new(NULL, serd_a_uri_string(uri_string)); + const SerdURIView uri = serd_node_uri_view(uri_node); + SerdNode* const base_node = + serd_node_new(NULL, serd_a_uri_string(base_string)); + const SerdURIView base = serd_node_uri_view(base_node); SerdNode* result_node = NULL; if (!root_string) { - result_node = serd_new_parsed_uri(NULL, serd_relative_uri(uri, base)); + result_node = + serd_node_new(NULL, serd_a_parsed_uri(serd_relative_uri(uri, base))); } else { - SerdNode* const root_node = serd_new_uri(NULL, serd_string(root_string)); - const SerdURIView root = serd_node_uri_view(root_node); + SerdNode* const root_node = + serd_node_new(NULL, serd_a_uri_string(root_string)); + const SerdURIView root = serd_node_uri_view(root_node); + + result_node = + serd_uri_is_within(uri, root) + ? serd_node_new(NULL, serd_a_parsed_uri(serd_relative_uri(uri, base))) + : serd_node_new(NULL, serd_a_uri_string(uri_string)); - result_node = serd_uri_is_within(uri, root) - ? serd_new_parsed_uri(NULL, serd_relative_uri(uri, base)) - : serd_new_uri(NULL, serd_string(uri_string)); serd_node_free(NULL, root_node); } @@ -359,7 +364,7 @@ test_relative_uri(void) static void check_uri_string(const SerdURIView uri, const char* const expected) { - SerdNode* const node = serd_new_parsed_uri(NULL, uri); + SerdNode* const node = serd_node_new(NULL, serd_a_parsed_uri(uri)); assert(!strcmp(serd_node_string(node), expected)); serd_node_free(NULL, node); } diff --git a/test/test_writer.c b/test/test_writer.c index 39bcd34a..8f4fef5c 100644 --- a/test/test_writer.c +++ b/test/test_writer.c @@ -78,13 +78,14 @@ test_write_failed_alloc(void) SerdBuffer buffer = {&allocator.base, NULL, 0}; SerdOutputStream output = serd_open_output_buffer(&buffer); - SerdNode* s = serd_new_uri(NULL, serd_string("http://example.org/s")); - SerdNode* p1 = serd_new_uri(NULL, serd_string("http://example.org/p")); + SerdNode* s = serd_node_new(NULL, serd_a_uri_string("http://example.org/s")); + SerdNode* p1 = serd_node_new(NULL, serd_a_uri_string("http://example.org/p")); - SerdNode* p2 = serd_new_uri( - NULL, serd_string("http://example.org/dramatically/longer/predicate")); + SerdNode* p2 = serd_node_new( + NULL, + serd_a_uri_string("http://example.org/dramatically/longer/predicate")); - SerdNode* o = serd_new_token(NULL, SERD_BLANK, serd_string("o")); + SerdNode* o = serd_node_new(NULL, serd_a_blank_string("o")); const size_t n_setup_allocs = allocator.n_allocations; @@ -168,12 +169,13 @@ test_write_long_literal(void) serd_writer_new(world, SERD_TURTLE, 0U, env, &output, 1U); assert(writer); - SerdNode* s = serd_new_uri(NULL, serd_string("http://example.org/s")); - SerdNode* p = serd_new_uri(NULL, serd_string("http://example.org/p")); - SerdNode* o = serd_new_literal(NULL, - serd_string("hello \"\"\"world\"\"\"!"), + SerdNode* s = serd_node_new(NULL, serd_a_uri_string("http://example.org/s")); + SerdNode* p = serd_node_new(NULL, serd_a_uri_string("http://example.org/p")); + SerdNode* o = + serd_node_new(NULL, + serd_a_literal(serd_string("hello \"\"\"world\"\"\"!"), SERD_IS_LONG, - serd_empty_string()); + serd_empty_string())); assert(serd_node_flags(o) & SERD_IS_LONG); assert(!serd_sink_write(serd_writer_sink(writer), 0, s, p, o, NULL)); @@ -224,9 +226,9 @@ test_writer_cleanup(void) const SerdSink* sink = serd_writer_sink(writer); - SerdNode* s = serd_new_uri(NULL, serd_string("http://example.org/s")); - SerdNode* p = serd_new_uri(NULL, serd_string("http://example.org/p")); - SerdNode* o = serd_new_blank(NULL, serd_string("start")); + SerdNode* s = serd_node_new(NULL, serd_a_uri_string("http://example.org/s")); + SerdNode* p = serd_node_new(NULL, serd_a_uri_string("http://example.org/p")); + SerdNode* o = serd_node_new(NULL, serd_a_blank_string("start")); st = serd_sink_write(sink, SERD_ANON_O, s, p, o, NULL); assert(!st); @@ -236,7 +238,7 @@ test_writer_cleanup(void) char buf[12] = {0}; snprintf(buf, sizeof(buf), "b%u", i); - SerdNode* next_o = serd_new_blank(NULL, serd_string(buf)); + SerdNode* next_o = serd_node_new(NULL, serd_a_blank_string(buf)); st = serd_sink_write(sink, SERD_ANON_O, o, p, next_o, NULL); @@ -248,7 +250,7 @@ test_writer_cleanup(void) assert(!(st = serd_writer_finish(writer))); // Set the base to an empty URI - SerdNode* empty_uri = serd_new_uri(NULL, serd_string("")); + SerdNode* empty_uri = serd_node_new(NULL, serd_a_uri_string("")); assert(!(st = serd_sink_write_base(sink, empty_uri))); serd_node_free(NULL, empty_uri); @@ -280,11 +282,12 @@ test_strict_write(void) const uint8_t bad_str[] = {0xFF, 0x90, 'h', 'i', 0}; - SerdNode* s = serd_new_uri(NULL, serd_string("http://example.org/s")); - SerdNode* p = serd_new_uri(NULL, serd_string("http://example.org/p")); + SerdNode* s = serd_node_new(NULL, serd_a_uri_string("http://example.org/s")); + SerdNode* p = serd_node_new(NULL, serd_a_uri_string("http://example.org/p")); - SerdNode* bad_lit = serd_new_string(NULL, serd_string((const char*)bad_str)); - SerdNode* bad_uri = serd_new_uri(NULL, serd_string((const char*)bad_str)); + SerdNode* bad_lit = serd_node_new(NULL, serd_a_string((const char*)bad_str)); + SerdNode* bad_uri = + serd_node_new(NULL, serd_a_uri_string((const char*)bad_str)); assert(serd_sink_write(sink, 0, s, p, bad_lit, NULL) == SERD_BAD_TEXT); assert(serd_sink_write(sink, 0, s, p, bad_uri, NULL) == SERD_BAD_TEXT); @@ -322,7 +325,7 @@ test_write_error(void) SerdOutputStream out = serd_open_output_stream(error_sink, NULL, NULL, NULL); SerdStatus st = SERD_SUCCESS; - SerdNode* u = serd_new_uri(NULL, serd_string("http://example.com/u")); + SerdNode* u = serd_node_new(NULL, serd_a_uri_string("http://example.com/u")); SerdWriter* const writer = serd_writer_new(world, SERD_TURTLE, (SerdWriterFlags)0, env, &out, 1U); @@ -353,10 +356,12 @@ test_writer_stack_overflow(void) const SerdSink* sink = serd_writer_sink(writer); - SerdNode* const s = serd_new_uri(NULL, serd_string("http://example.org/s")); - SerdNode* const p = serd_new_uri(NULL, serd_string("http://example.org/p")); + SerdNode* const s = + serd_node_new(NULL, serd_a_uri_string("http://example.org/s")); + SerdNode* const p = + serd_node_new(NULL, serd_a_uri_string("http://example.org/p")); - SerdNode* o = serd_new_blank(NULL, serd_string("blank")); + SerdNode* o = serd_node_new(NULL, serd_a_blank_string("blank")); SerdStatus st = serd_sink_write(sink, SERD_ANON_O, s, p, o, NULL); assert(!st); @@ -365,7 +370,7 @@ test_writer_stack_overflow(void) char buf[1024]; snprintf(buf, sizeof(buf), "b%u", i); - SerdNode* next_o = serd_new_blank(NULL, serd_string(buf)); + SerdNode* next_o = serd_node_new(NULL, serd_a_blank_string(buf)); st = serd_sink_write(sink, SERD_ANON_O, o, p, next_o, NULL); @@ -395,9 +400,9 @@ test_write_empty_syntax(void) SerdWorld* world = serd_world_new(NULL); SerdEnv* env = serd_env_new(NULL, serd_empty_string()); - SerdNode* s = serd_new_uri(NULL, serd_string("http://example.org/s")); - SerdNode* p = serd_new_uri(NULL, serd_string("http://example.org/p")); - SerdNode* o = serd_new_uri(NULL, serd_string("http://example.org/o")); + SerdNode* s = serd_node_new(NULL, serd_a_uri_string("http://example.org/s")); + SerdNode* p = serd_node_new(NULL, serd_a_uri_string("http://example.org/p")); + SerdNode* o = serd_node_new(NULL, serd_a_uri_string("http://example.org/o")); SerdBuffer buffer = {NULL, NULL, 0}; SerdOutputStream output = serd_open_output_buffer(&buffer); @@ -441,14 +446,14 @@ check_pname_escape(const char* const lname, const char* const expected) serd_env_set_prefix(env, serd_string("eg"), serd_string(prefix)); - SerdNode* s = serd_new_uri(NULL, serd_string("http://example.org/s")); - SerdNode* p = serd_new_uri(NULL, serd_string("http://example.org/p")); + SerdNode* s = serd_node_new(NULL, serd_a_uri_string("http://example.org/s")); + SerdNode* p = serd_node_new(NULL, serd_a_uri_string("http://example.org/p")); char* const uri = (char*)calloc(1, prefix_len + strlen(lname) + 1); memcpy(uri, prefix, prefix_len + 1); memcpy(uri + prefix_len, lname, strlen(lname) + 1); - SerdNode* node = serd_new_uri(NULL, serd_string(uri)); + SerdNode* node = serd_node_new(NULL, serd_a_uri_string(uri)); assert(!serd_sink_write(serd_writer_sink(writer), 0, s, p, node, NULL)); serd_node_free(NULL, node); @@ -503,9 +508,9 @@ test_write_bad_uri(void) { SerdWorld* world = serd_world_new(NULL); SerdEnv* env = serd_env_new(NULL, serd_empty_string()); - SerdNode* s = serd_new_uri(NULL, serd_string("http://example.org/s")); - SerdNode* p = serd_new_uri(NULL, serd_string("http://example.org/p")); - SerdNode* rel = serd_new_uri(NULL, serd_string("rel")); + SerdNode* s = serd_node_new(NULL, serd_a_uri_string("http://example.org/s")); + SerdNode* p = serd_node_new(NULL, serd_a_uri_string("http://example.org/p")); + SerdNode* rel = serd_node_new(NULL, serd_a_uri_string("rel")); SerdBuffer buffer = {NULL, NULL, 0}; SerdOutputStream output = serd_open_output_buffer(&buffer); diff --git a/tools/console.c b/tools/console.c index 012c50a3..72b9b222 100644 --- a/tools/console.c +++ b/tools/console.c @@ -53,8 +53,8 @@ serd_set_base_uri_from_path(SerdEnv* const env, const char* const path) return SERD_BAD_ARG; } - SerdNode* const file_uri = - serd_new_file_uri(NULL, serd_string(input_path), serd_empty_string()); + SerdNode* const file_uri = serd_node_new( + NULL, serd_a_file_uri(serd_string(input_path), serd_empty_string())); serd_env_set_base_uri(env, serd_node_string_view(file_uri)); serd_node_free(NULL, file_uri); diff --git a/tools/serd-pipe.c b/tools/serd-pipe.c index 9be11d66..3f1c3f53 100644 --- a/tools/serd-pipe.c +++ b/tools/serd-pipe.c @@ -163,7 +163,7 @@ main(int argc, char** argv) return missing_arg(prog, 'B'); } - base = serd_new_uri(NULL, serd_string(argv[a])); + base = serd_node_new(NULL, serd_a_uri_string(argv[a])); break; } else if (opt == 'b') { if (argv[a][o + 1] || ++a == argc) { @@ -281,8 +281,9 @@ main(int argc, char** argv) // Choose base URI from the single input path char* const input_path = zix_canonical_path(NULL, inputs[0]); if (!input_path || - !(base = serd_new_file_uri( - NULL, serd_string(input_path), serd_empty_string()))) { + !(base = serd_node_new( + NULL, + serd_a_file_uri(serd_string(input_path), serd_empty_string())))) { SERDI_ERRORF("unable to determine base URI from path %s\n", inputs[0]); } zix_free(NULL, input_path); |