diff options
author | David Robillard <d@drobilla.net> | 2021-04-15 18:34:17 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2023-12-02 18:49:06 -0500 |
commit | a970f06aba98736223214a6fa995f4e82acd7132 (patch) | |
tree | fc243e4bf26f4c1f95df6f62abdd6740d87d8afd | |
parent | 44feb2724a8fe34992999867f5b6468228b6fc01 (diff) | |
download | serd-a970f06aba98736223214a6fa995f4e82acd7132.tar.gz serd-a970f06aba98736223214a6fa995f4e82acd7132.tar.bz2 serd-a970f06aba98736223214a6fa995f4e82acd7132.zip |
[WIP] Use exess for reading and writing numeric and binary literals
-rw-r--r-- | .gitlab-ci.yml | 12 | ||||
-rw-r--r-- | .gitmodules | 6 | ||||
-rw-r--r-- | include/serd/node.h | 40 | ||||
-rw-r--r-- | include/serd/string.h | 7 | ||||
-rw-r--r-- | meson.build | 12 | ||||
-rw-r--r-- | meson/suppressions/meson.build | 3 | ||||
-rw-r--r-- | src/base64.c | 112 | ||||
-rw-r--r-- | src/node.c | 186 | ||||
-rw-r--r-- | src/string.c | 67 | ||||
-rw-r--r-- | src/string_utils.h | 6 | ||||
m--------- | subprojects/exess | 0 | ||||
-rw-r--r-- | subprojects/exess.wrap | 8 | ||||
-rw-r--r-- | test/meson.build | 35 | ||||
-rw-r--r-- | test/test_node.c | 84 |
14 files changed, 219 insertions, 359 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ec20e2f7..4c4cd015 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ static: stage: build image: lv2plugin/debian-x64 script: - - meson setup build -Ddefault_library=static -Ddocs=disabled -Dstatic=true -Dwarning_level=3 -Dwerror=true + - meson setup build -Ddefault_library=static -Ddocs=disabled -Dexess:default_library=static -Dstatic=true -Dwarning_level=3 -Dwerror=true - ninja -C build test sanitize: @@ -46,9 +46,9 @@ sanitize: variables: CC: "clang" CXX: "clang++" - CFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" - CXXFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" - LDFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" + CFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" + CXXFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" + LDFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" # Linux Distributions @@ -99,7 +99,7 @@ mingw32: - meson configure -Dbuildtype=release build - ninja -C build test variables: - WINEPATH: "Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\10-win32" + WINEPATH: "Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\10-win32;Z:\\builds\\drobilla\\serd\\build\\subprojects\\exess" mingw64: stage: build @@ -110,7 +110,7 @@ mingw64: - meson configure -Dbuildtype=release build - ninja -C build test variables: - WINEPATH: "Z:\\usr\\lib\\gcc\\x86_64-w64-mingw32\\8.3-win32" + WINEPATH: "Z:\\usr\\lib\\gcc\\x86_64-w64-mingw32\\8.3-win32;Z:\\builds\\drobilla\\serd\\build\\subprojects\\exess" # Non-Linux/Docker rows (not hosted) diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..c7cd26e0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +# Copyright 2021-2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +[submodule "subprojects/exess"] + path = subprojects/exess + url = ../exess.git diff --git a/include/serd/node.h b/include/serd/node.h index 73569b44..79c9ffe1 100644 --- a/include/serd/node.h +++ b/include/serd/node.h @@ -175,28 +175,26 @@ serd_new_boolean(bool b); /** Create a new canonical xsd:decimal literal. - The resulting node will always contain a '.', start with a digit, and end - with a digit (i.e. will have a leading and/or trailing '0' if necessary). - It will never be in scientific notation. A maximum of `frac_digits` digits - will be written after the decimal point, but trailing zeros will - automatically be omitted (except one if `d` is a round integer). + The node will be an xsd:decimal literal, like "12.34", with + datatype xsd:decimal by default, or a custom datatype. - Note that about 16 and 8 fractional digits are required to precisely - represent a double and float, respectively. + 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. @param d The value for the new node. - @param frac_digits The maximum number of digits after the decimal place. @param datatype Datatype of node, or NULL for xsd:decimal. */ SERD_API SerdNode* SERD_ALLOCATED -serd_new_decimal(double d, - unsigned frac_digits, - const SerdNode* SERD_NULLABLE datatype); +serd_new_decimal(double d, const SerdNode* SERD_NULLABLE datatype); /** Create a new canonical xsd:integer literal. - @param i Integer value to serialise. + The node will be an xsd:integer literal like "1234", with datatype + xsd:integer. + + @param i Integer value of literal. @param datatype Datatype of node, or NULL for xsd:integer. */ SERD_API SerdNode* SERD_ALLOCATED @@ -205,19 +203,17 @@ serd_new_integer(int64_t i, const SerdNode* SERD_NULLABLE datatype); /** Create a new canonical xsd:base64Binary literal. - This function can be used to make a serialisable node out of arbitrary - binary data, which can be decoded using serd_base64_decode(). + This function can be used to make a node out of arbitrary binary data, which + can be decoded using serd_base64_decode(). - @param buf Raw binary input data. - @param size Size of `buf`. - @param wrap_lines Wrap lines at 76 characters to conform to RFC 2045. - @param datatype Datatype of node, or NULL for xsd:base64Binary. + @param buf Raw binary data to encode in node. + @param size Size of `buf` in bytes. + @param datatype Datatype of node, or null for xsd:base64Binary. */ SERD_API SerdNode* SERD_ALLOCATED -serd_new_blob(const void* SERD_NONNULL buf, - size_t size, - bool wrap_lines, - const SerdNode* SERD_NULLABLE datatype); +serd_new_base64(const void* SERD_NONNULL buf, + size_t size, + const SerdNode* SERD_NULLABLE datatype); /// Return a deep copy of `node` SERD_API SerdNode* SERD_ALLOCATED diff --git a/include/serd/string.h b/include/serd/string.h index 7652b05f..b5ba7154 100644 --- a/include/serd/string.h +++ b/include/serd/string.h @@ -35,14 +35,13 @@ serd_strlen(const char* SERD_NONNULL str, SerdNodeFlags* SERD_NULLABLE flags); format used in the Turtle grammar (the decimal point is always "."). */ SERD_API double -serd_strtod(const char* SERD_NONNULL str, - char* SERD_NONNULL* SERD_NULLABLE endptr); +serd_strtod(const char* SERD_NONNULL str, + const char* SERD_NONNULL* SERD_NULLABLE endptr); /** Decode a base64 string. - This function can be used to deserialise a blob node created with - serd_new_blob(). + 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`. diff --git a/meson.build b/meson.build index a4b7d168..faf2f2fd 100644 --- a/meson.build +++ b/meson.build @@ -98,6 +98,16 @@ else endforeach endif +################ +# Dependencies # +################ + +exess_dep = dependency( + 'exess-0', + fallback: ['exess', 'exess_dep'], + version: '>= 0.0.1', +) + ########### # Library # ########### @@ -157,7 +167,7 @@ libserd = library( '-DSERD_MINOR_VERSION=@0@'.format(serd_minor_version), '-DSERD_VERSION="@0@"'.format(meson.project_version()), ] + c_suppressions + extra_c_args + platform_c_args, - dependencies: m_dep, + dependencies: [exess_dep, m_dep], gnu_symbol_visibility: 'hidden', include_directories: include_dirs, install: true, diff --git a/meson/suppressions/meson.build b/meson/suppressions/meson.build index 579d3851..2ad63b00 100644 --- a/meson/suppressions/meson.build +++ b/meson/suppressions/meson.build @@ -17,9 +17,7 @@ if is_variable('cc') c_suppressions += [ '-Wno-cast-align', '-Wno-cast-function-type-strict', - '-Wno-cast-qual', '-Wno-declaration-after-statement', - '-Wno-double-promotion', '-Wno-format-nonliteral', '-Wno-nullable-to-nonnull-conversion', '-Wno-padded', @@ -55,7 +53,6 @@ if is_variable('cc') if warning_level == 'everything' c_suppressions += [ '-Wno-cast-align', - '-Wno-cast-qual', '-Wno-format-nonliteral', '-Wno-inline', '-Wno-padded', diff --git a/src/base64.c b/src/base64.c index 14f45b71..50ec3981 100644 --- a/src/base64.c +++ b/src/base64.c @@ -3,113 +3,25 @@ #include "base64.h" -#include "serd_internal.h" -#include "string_utils.h" - +#include "exess/exess.h" #include "serd/string.h" -#include <stdbool.h> -#include <stdint.h> #include <stdlib.h> -#include <string.h> - -/** - Base64 encoding table. - - @see <a href="http://tools.ietf.org/html/rfc3548#section-3">RFC3548 S3</a>. -*/ -static const uint8_t b64_map[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -/** - Base64 decoding table. - - This is indexed by encoded characters and returns the numeric value used - for decoding, shifted up by 47 to be in the range of printable ASCII. - A '$' is a placeholder for characters not in the base64 alphabet. -*/ -static const char b64_unmap[] = - "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$m$$$ncdefghijkl$$$$$$" - "$/0123456789:;<=>?@ABCDEFGH$$$$$$IJKLMNOPQRSTUVWXYZ[\\]^_`ab$$$$" - "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" - "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"; - -/** Encode 3 raw bytes to 4 base64 characters. */ -static void -encode_chunk(uint8_t out[4], const uint8_t in[3], const size_t n_in) -{ - out[0] = b64_map[in[0] >> 2]; - out[1] = b64_map[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)]; - - out[2] = (n_in > 1) ? (b64_map[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)]) - : (uint8_t)'='; - - out[3] = ((n_in > 2) ? b64_map[in[2] & 0x3F] : (uint8_t)'='); -} - -size_t -serd_base64_get_length(const size_t size, const bool wrap_lines) -{ - return (size + 2) / 3 * 4 + (wrap_lines * ((size - 1) / 57)); -} - -bool -serd_base64_encode(uint8_t* const str, - const void* const buf, - const size_t size, - const bool wrap_lines) -{ - bool has_newline = false; - for (size_t i = 0, j = 0; i < size; i += 3, j += 4) { - uint8_t in[4] = {0, 0, 0, 0}; - size_t n_in = MIN(3, size - i); - memcpy(in, (const uint8_t*)buf + i, n_in); - - if (wrap_lines && i > 0 && (i % 57) == 0) { - str[j++] = '\n'; - has_newline = true; - } - - encode_chunk(str + j, in, n_in); - } - - return has_newline; -} - -static uint8_t -unmap(const uint8_t in) -{ - return (uint8_t)(b64_unmap[in] - 47); -} - -/** Decode 4 base64 characters to 3 raw bytes. */ -static size_t -decode_chunk(const uint8_t in[4], uint8_t out[3]) -{ - out[0] = (uint8_t)(((unmap(in[0]) << 2)) | unmap(in[1]) >> 4); - out[1] = (uint8_t)(((unmap(in[1]) << 4) & 0xF0) | unmap(in[2]) >> 2); - out[2] = (uint8_t)(((unmap(in[2]) << 6) & 0xC0) | unmap(in[3])); - return 1U + (in[2] != '=') + ((in[2] != '=') && (in[3] != '=')); -} void* serd_base64_decode(const char* const str, const size_t len, size_t* const size) { - const uint8_t* const ustr = (const uint8_t*)str; - - void* buf = malloc((len * 3) / 4 + 2); - *size = 0; - for (size_t i = 0, j = 0; i < len; j += 3) { - uint8_t in[] = "===="; - size_t n_in = 0; - for (; i < len && n_in < 4; ++n_in) { - for (; i < len && !is_base64(ustr[i]); ++i) { - } // Skip junk - in[n_in] = ustr[i++]; - } - if (n_in > 1) { - *size += decode_chunk(in, (uint8_t*)buf + j); - } + 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; } @@ -3,11 +3,11 @@ #include "node.h" -#include "base64.h" #include "serd_internal.h" #include "string_utils.h" #include "system.h" +#include "exess/exess.h" #include "serd/attributes.h" #include "serd/buffer.h" #include "serd/node.h" @@ -16,22 +16,16 @@ #include "serd/uri.h" #include <assert.h> -#include <float.h> -#include <math.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#ifdef _WIN32 -# ifndef isnan -# define isnan(x) _isnan(x) -# endif -# ifndef isinf -# define isinf(x) (!_finite(x)) -# endif -#endif +typedef struct { + const void* SERD_NULLABLE buf; + size_t len; +} SerdConstBuffer; #define NS_XSD "http://www.w3.org/2001/XMLSchema#" @@ -502,12 +496,9 @@ serd_new_file_uri(const SerdStringView path, const SerdStringView hostname) return node; } -static unsigned -serd_digits(const double abs) -{ - const double lg = ceil(log10(floor(abs) + 1.0)); - return lg < 1.0 ? 1U : (unsigned)lg; -} +typedef size_t (*SerdWriteLiteralFunc)(const void* user_data, + size_t buf_size, + char* buf); SerdNode* serd_new_boolean(bool b) @@ -516,131 +507,108 @@ serd_new_boolean(bool b) serd_node_string_view(&serd_xsd_boolean.node)); } -SerdNode* -serd_new_decimal(const double d, - const unsigned frac_digits, - const SerdNode* const datatype) +static SerdNode* +serd_new_custom_literal(const void* const user_data, + const size_t len, + const SerdWriteLiteralFunc write, + const SerdNode* const datatype) { - if (isnan(d) || isinf(d)) { + if (len == 0 || !write) { return NULL; } - const SerdNode* type = datatype ? datatype : &serd_xsd_decimal.node; - const double abs_d = fabs(d); - const unsigned int_digits = serd_digits(abs_d); - const size_t len = int_digits + frac_digits + 3; - const size_t type_len = serd_node_total_size(type); - const size_t total_len = len + type_len; - - SerdNode* const node = - serd_node_malloc(total_len, SERD_HAS_DATATYPE, SERD_LITERAL); - - // Point s to decimal point location - char* const buf = serd_node_buffer(node); - const double int_part = floor(abs_d); - char* s = buf + int_digits; - if (d < 0.0) { - *buf = '-'; - ++s; - } + const size_t datatype_size = serd_node_total_size(datatype); + const size_t total_size = serd_node_pad_size(len + 1) + datatype_size; - // Write integer part (right to left) - char* t = s - 1; - uint64_t dec = (uint64_t)int_part; - do { - *t-- = (char)('0' + dec % 10); - } while ((dec /= 10) > 0); - - *s++ = '.'; - - // Write fractional part (right to left) - double frac_part = fabs(d - int_part); - if (frac_part < DBL_EPSILON) { - *s++ = '0'; - node->length = (size_t)(s - buf); - } else { - uint64_t frac = (uint64_t)llround(frac_part * pow(10.0, (int)frac_digits)); - s += frac_digits - 1; - unsigned i = 0; - - // Skip trailing zeros - for (; i < frac_digits - 1 && !(frac % 10); ++i, --s, frac /= 10) { - } + SerdNode* const node = serd_node_malloc( + total_size, datatype ? SERD_HAS_DATATYPE : 0U, SERD_LITERAL); - node->length = (size_t)(s - buf) + 1U; + node->length = write(user_data, len + 1, serd_node_buffer(node)); - // Write digits from last trailing zero to decimal point - for (; i < frac_digits; ++i) { - *s-- = (char)('0' + (frac % 10)); - frac /= 10; - } + if (datatype) { + memcpy(serd_node_meta(node), datatype, datatype_size); } - memcpy(serd_node_meta(node), type, type_len); serd_node_check_padding(node); return node; } SerdNode* -serd_new_integer(const int64_t i, const SerdNode* const datatype) +serd_new_decimal(const double d, const SerdNode* const datatype) { - const SerdNode* type = datatype ? datatype : &serd_xsd_integer.node; - uint64_t abs_i = (uint64_t)((i < 0) ? -i : i); - const unsigned digits = serd_digits((double)abs_i); - const size_t type_len = serd_node_total_size(type); - const size_t total_len = digits + 2 + type_len; - - SerdNode* node = serd_node_malloc(total_len, SERD_HAS_DATATYPE, SERD_LITERAL); - - // Point s to the end - char* buf = serd_node_buffer(node); - char* s = buf + digits - 1; - if (i < 0) { - *buf = '-'; - ++s; - } + // 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); - node->length = (size_t)(s - buf) + 1U; + // 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(serd_node_pad_size(r.count + 1) + type_size, + SERD_HAS_DATATYPE, + SERD_LITERAL); - // Write integer part (right to left) - do { - *s-- = (char)('0' + (abs_i % 10)); - } while ((abs_i /= 10) > 0); + // Write string directly into node + r = exess_write_decimal(d, r.count + 1, serd_node_buffer(node)); + assert(!r.status); - memcpy(serd_node_meta(node), type, type_len); + node->length = r.count; + memcpy(serd_node_meta(node), type, type_size); serd_node_check_padding(node); return node; } SerdNode* -serd_new_blob(const void* const buf, - const size_t size, - const bool wrap_lines, - const SerdNode* const datatype) +serd_new_integer(const int64_t i, const SerdNode* const datatype) { - if (!buf || !size) { - return NULL; - } + // Use given datatype, or xsd:integer as a default if it is null + const SerdNode* type = datatype ? datatype : &serd_xsd_integer.node; + const size_t type_size = serd_node_total_size(type); - const SerdNode* type = datatype ? datatype : &serd_xsd_base64Binary.node; - const size_t len = serd_base64_get_length(size, wrap_lines); - const size_t type_len = serd_node_total_size(type); - const size_t total_len = len + 1 + type_len; + // 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(total_len, SERD_HAS_DATATYPE, SERD_LITERAL); + serd_node_malloc(serd_node_pad_size(r.count + 1) + type_size, + SERD_HAS_DATATYPE, + SERD_LITERAL); - uint8_t* str = (uint8_t*)serd_node_buffer(node); - if (serd_base64_encode(str, buf, size, wrap_lines)) { - node->flags |= SERD_HAS_NEWLINE; - } + // Write string directly into node + r = exess_write_long(i, r.count + 1U, serd_node_buffer(node)); + assert(!r.status); - node->length = len; - memcpy(serd_node_meta(node), type, type_len); + node->length = r.count; + memcpy(serd_node_meta(node), type, type_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(const void* buf, size_t size, const SerdNode* datatype) +{ + const size_t len = exess_write_base64(size, buf, 0, NULL).count; + const SerdNode* type = datatype ? datatype : &serd_xsd_base64Binary.node; + SerdConstBuffer blob = {buf, size}; + + return serd_new_custom_literal(&blob, len, write_base64_literal, type); +} + SerdNodeType serd_node_type(const SerdNode* const node) { diff --git a/src/string.c b/src/string.c index 5281d500..ec608f25 100644 --- a/src/string.c +++ b/src/string.c @@ -3,6 +3,7 @@ #include "string_utils.h" +#include "exess/exess.h" #include "serd/memory.h" #include "serd/node.h" #include "serd/status.h" @@ -115,68 +116,14 @@ serd_strlen(const char* const str, SerdNodeFlags* const flags) return strlen(str); } -static double -read_sign(const char** const sptr) -{ - double sign = 1.0; - - switch (**sptr) { - case '-': - sign = -1.0; - ++(*sptr); - break; - case '+': - ++(*sptr); - break; - default: - break; - } - - return sign; -} - double -serd_strtod(const char* const str, char** const endptr) +serd_strtod(const char* const str, const char** const end) { - double result = 0.0; - - // Point s at the first non-whitespace character - const char* s = str; - while (is_space(*s)) { - ++s; - } - - // Read leading sign if necessary - const double sign = read_sign(&s); - - // Parse integer part - for (; is_digit(*s); ++s) { - result = (result * 10.0) + (*s - '0'); - } - - // Parse fractional part - if (*s == '.') { - double denom = 10.0; - for (++s; is_digit(*s); ++s) { - result += (*s - '0') / denom; - denom *= 10.0; - } - } - - // Parse exponent - if (*s == 'e' || *s == 'E') { - ++s; - double expt = 0.0; - double expt_sign = read_sign(&s); - for (; is_digit(*s); ++s) { - expt = (expt * 10.0) + (*s - '0'); - } - result *= pow(10, expt * expt_sign); - } - - if (endptr) { - *endptr = (char*)s; + double value = (double)NAN; + const ExessResult r = exess_read_double(&value, str); + if (end) { + *end = str + r.count; } - return result * sign; + return r.status ? (double)NAN : value; } diff --git a/src/string_utils.h b/src/string_utils.h index 86795dcb..4102a54c 100644 --- a/src/string_utils.h +++ b/src/string_utils.h @@ -71,12 +71,6 @@ is_print(const int c) } static inline bool -is_base64(const int c) -{ - return is_alpha(c) || is_digit(c) || c == '+' || c == '/' || c == '='; -} - -static inline bool is_windows_path(const char* path) { return is_alpha(path[0]) && (path[1] == ':' || path[1] == '|') && diff --git a/subprojects/exess b/subprojects/exess new file mode 160000 +Subproject 666d2e8927282310fa7314ca9e195ea20f52885 diff --git a/subprojects/exess.wrap b/subprojects/exess.wrap new file mode 100644 index 00000000..79df5833 --- /dev/null +++ b/subprojects/exess.wrap @@ -0,0 +1,8 @@ +# Copyright 2021-2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +[wrap-git] +url = https://gitlab.com/drobilla/exess.git +push-url = git@gitlab.com:drobilla/exess.git +revision = main +depth = 1 diff --git a/test/meson.build b/test/meson.build index ad3797df..0ee7cd23 100644 --- a/test/meson.build +++ b/test/meson.build @@ -131,6 +131,12 @@ unit_tests = [ 'writer', ] +test_env = [] +if build_machine.system() == 'windows' and host_machine.system() == 'windows' + # For Windows, we need to add to PATH so that DLLs are found + test_env = ['PATH=@0@;@1@'.format('subprojects' / 'exess')] +endif + foreach unit : unit_tests test( unit, @@ -140,6 +146,7 @@ foreach unit : unit_tests c_args: c_suppressions, dependencies: serd_dep, ), + env: test_env, suite: 'unit', ) endforeach @@ -186,7 +193,7 @@ if is_variable('serdi') serd_ttl = files('../serd.ttl')[0] bad_input_file = files('extra/bad/bad-base.ttl') - test('serd_ttl', serdi, args: [serd_ttl], suite: 'data') + test('serd_ttl', serdi, args: [serd_ttl], env: test_env, suite: 'data') # Command line options @@ -198,18 +205,20 @@ if is_variable('serdi') ' '.join(args).substring(1).underscorify(), serdi, args: args, + env: test_env, should_fail: kind == 'bad', suite: cmd_suite, ) endforeach endforeach - test('none', serdi, should_fail: true, suite: cmd_suite) + test('none', serdi, env: test_env, should_fail: true, suite: cmd_suite) test( 'quiet', files('test_quiet.py'), args: script_args + [bad_input_file], + env: test_env, suite: cmd_suite, ) @@ -224,10 +233,24 @@ if is_variable('serdi') } foreach name, args : bad_input_tests - test(name, serdi, args: args, should_fail: true, suite: input_suite) + test( + name, + serdi, + args: args, + env: test_env, + should_fail: true, + suite: input_suite, + ) endforeach - test('stdin', files('test_stdin.py'), args: script_args, suite: input_suite) + test( + 'stdin', + files('test_stdin.py'), + args: script_args, + env: test_env, + suite: input_suite, + ) + # IO errors @@ -238,13 +261,14 @@ if is_variable('serdi') } foreach name, args : io_error_tests - test(name, serdi, args: args, should_fail: true, suite: 'io') + test(name, serdi, args: args, env: test_env, should_fail: true, suite: 'io') endforeach test( 'write_error', files('test_write_error.py'), args: script_args + [serd_ttl], + env: test_env, suite: 'io', ) endif @@ -387,6 +411,7 @@ if is_variable('serdi') name, run_suite, args: script_args + args, + env: test_env, suite: ['suite'], timeout: 240, ) diff --git a/test/test_node.c b/test/test_node.c index b7d43972..9660f43e 100644 --- a/test/test_node.c +++ b/test/test_node.c @@ -18,23 +18,16 @@ #include <stdlib.h> #include <string.h> -#ifndef INFINITY -# define INFINITY (DBL_MAX + DBL_MAX) -#endif -#ifndef NAN -# define NAN (INFINITY - INFINITY) -#endif - #define NS_XSD "http://www.w3.org/2001/XMLSchema#" #define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" static void -test_strtod(double dbl, double max_delta) +check_strtod(const double dbl, const double max_delta) { char buf[1024]; snprintf(buf, sizeof(buf), "%f", dbl); - char* endptr = NULL; + const char* endptr = NULL; const double out = serd_strtod(buf, &endptr); const double diff = fabs(out - dbl); @@ -42,7 +35,7 @@ test_strtod(double dbl, double max_delta) } static void -test_string_to_double(void) +test_strtod(void) { const double expt_test_nums[] = { 2.0E18, -5e19, +8e20, 2e+22, -5e-5, 8e0, 9e-0, 2e+0}; @@ -55,42 +48,31 @@ test_string_to_double(void) const double delta = fabs(num - expt_test_nums[i]); assert(delta <= DBL_EPSILON); - test_strtod(expt_test_nums[i], DBL_EPSILON); + check_strtod(expt_test_nums[i], DBL_EPSILON); } } static void -test_double_to_node(void) +test_new_decimal(void) { - const double dbl_test_nums[] = {0.0, - 9.0, - 10.0, - .01, - 2.05, - -16.00001, - 5.000000005, - 0.0000000001, - NAN, - INFINITY}; - - const char* dbl_test_strs[] = {"0.0", - "9.0", - "10.0", - "0.01", - "2.05", - "-16.00001", - "5.00000001", - "0.0", - NULL, - NULL}; + static const double dbl_test_nums[] = { + 0.0, 9.0, 10.0, .01, 2.05, -16.00001, 5.000000005, 0.0000000001}; + + static const char* const dbl_test_strs[] = {"0.0", + "9.0", + "10.0", + "0.01", + "2.05", + "-16.00001", + "5.000000005", + "0.0000000001"}; for (size_t i = 0; i < sizeof(dbl_test_nums) / sizeof(double); ++i) { - SerdNode* node = serd_new_decimal(dbl_test_nums[i], 8, NULL); - const char* node_str = node ? serd_node_string(node) : NULL; - const bool pass = (node_str && dbl_test_strs[i]) - ? !strcmp(node_str, dbl_test_strs[i]) - : (node_str == dbl_test_strs[i]); - assert(pass); + SerdNode* node = serd_new_decimal(dbl_test_nums[i], NULL); + assert(node); + + const char* node_str = serd_node_string(node); + assert(!strcmp(node_str, dbl_test_strs[i])); const size_t len = node_str ? strlen(node_str) : 0; assert((!node && len == 0) || serd_node_length(node) == len); @@ -154,8 +136,9 @@ test_boolean(void) static void test_blob_to_node(void) { - assert(!serd_new_blob(&SERD_URI_NULL, 0, false, NULL)); + assert(!serd_new_base64(&SERD_URI_NULL, 0, NULL)); + // Test valid base64 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) { @@ -163,7 +146,7 @@ test_blob_to_node(void) } size_t out_size = 0; - SerdNode* blob = serd_new_blob(data, size, size % 5, NULL); + SerdNode* blob = serd_new_base64(data, size, NULL); const char* blob_str = serd_node_string(blob); uint8_t* out = (uint8_t*)serd_base64_decode(blob_str, serd_node_length(blob), &out_size); @@ -183,6 +166,21 @@ test_blob_to_node(void) serd_free(out); free(data); } + + // Test invalid base64 blob + + SerdNode* const blob = serd_new_typed_literal( + serd_string("!nval!d$"), serd_string(NS_XSD "base64Binary")); + + const char* const blob_str = serd_node_string(blob); + size_t out_size = 42; + uint8_t* out = + (uint8_t*)serd_base64_decode(blob_str, serd_node_length(blob), &out_size); + + assert(!out); + assert(out_size == 0); + + serd_node_free(blob); } static void @@ -319,8 +317,8 @@ test_blank(void) int main(void) { - test_string_to_double(); - test_double_to_node(); + test_strtod(); + test_new_decimal(); test_integer_to_node(); test_blob_to_node(); test_boolean(); |