diff options
-rw-r--r-- | include/serd/node.h | 32 | ||||
-rw-r--r-- | include/serd/serd.h | 1 | ||||
-rw-r--r-- | include/serd/write_result.h | 50 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | src/node.c | 29 | ||||
-rw-r--r-- | test/test_node.c | 68 |
6 files changed, 162 insertions, 20 deletions
diff --git a/include/serd/node.h b/include/serd/node.h index 427bbdd1..50108f57 100644 --- a/include/serd/node.h +++ b/include/serd/node.h @@ -7,6 +7,7 @@ #include "serd/attributes.h" #include "serd/string_view.h" #include "serd/uri.h" +#include "serd/write_result.h" #include <stdbool.h> #include <stddef.h> @@ -360,6 +361,37 @@ SERD_API int64_t serd_get_integer(const SerdNode* SERD_NONNULL node); /** + Return the maximum size of a decoded binary node in bytes. + + This returns an upper bound on the number of bytes that the node would + 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. +*/ +SERD_PURE_API size_t +serd_get_base64_size(const SerdNode* SERD_NONNULL node); + +/** + Decode a base64 node. + + This function can be used to decode a node created with serd_new_base64(). + + @param node A literal node which is an encoded base64 string. + + @param buf_size The size of `buf` in bytes. + + @param buf Buffer where decoded data will be written. + + @return On success, #SERD_SUCCESS is returned along with the number of bytes + written. If the output buffer is too small, then #SERD_OVERFLOW is returned + along with the number of bytes required for successful decoding. +*/ +SERD_API SerdWriteResult +serd_get_base64(const SerdNode* SERD_NONNULL node, + size_t buf_size, + void* SERD_NONNULL buf); + +/** @} @defgroup serd_node_operators Operators @{ diff --git a/include/serd/serd.h b/include/serd/serd.h index 70168997..03243c76 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -48,6 +48,7 @@ #include "serd/string.h" #include "serd/string_view.h" #include "serd/syntax.h" +#include "serd/write_result.h" /** @} diff --git a/include/serd/write_result.h b/include/serd/write_result.h new file mode 100644 index 00000000..14211354 --- /dev/null +++ b/include/serd/write_result.h @@ -0,0 +1,50 @@ +// Copyright 2011-2023 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#ifndef SERD_WRITE_RESULT_H +#define SERD_WRITE_RESULT_H + +#include "serd/attributes.h" +#include "serd/status.h" + +#include <stddef.h> + +SERD_BEGIN_DECLS + +/** + @defgroup serd_write_result Write Result + @ingroup serd_utilities + @{ +*/ + +/** + A status code with an associated byte count. + + This is returned by functions which write to a buffer to inform the caller + about the size written, or in case of overflow, size required. +*/ +typedef struct { + /** + Status code. + + This reports the status of the operation as usual, and also dictates the + meaning of `count`. + */ + SerdStatus status; + + /** + Number of bytes written or required. + + On success, this is the total number of bytes written. On #SERD_OVERFLOW, + this is the number of bytes of output space that are required for success. + */ + size_t count; +} SerdWriteResult; + +/** + @} +*/ + +SERD_END_DECLS + +#endif // SERD_WRITE_RESULT_H diff --git a/meson.build b/meson.build index faf2f2fd..e658426f 100644 --- a/meson.build +++ b/meson.build @@ -132,11 +132,11 @@ c_headers = files( 'include/serd/syntax.h', 'include/serd/uri.h', 'include/serd/version.h', + 'include/serd/write_result.h', 'include/serd/writer.h', ) sources = files( - 'src/base64.c', 'src/byte_source.c', 'src/env.c', 'src/n3.c', @@ -11,9 +11,11 @@ #include "serd/attributes.h" #include "serd/buffer.h" #include "serd/node.h" +#include "serd/status.h" #include "serd/string.h" #include "serd/string_view.h" #include "serd/uri.h" +#include "serd/write_result.h" #include <assert.h> #include <math.h> @@ -185,6 +187,13 @@ serd_node_zero_pad(SerdNode* node) } } +static SerdWriteResult +result(const SerdStatus status, const size_t count) +{ + const SerdWriteResult result = {status, count}; + return result; +} + SerdNode* serd_new_token(const SerdNodeType type, const SerdStringView str) { @@ -386,6 +395,26 @@ serd_get_integer(const SerdNode* const node) return value; } +size_t +serd_get_base64_size(const SerdNode* const node) +{ + return exess_base64_decoded_size(serd_node_length(node)); +} + +SerdWriteResult +serd_get_base64(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)); + + return r.status == EXESS_NO_SPACE ? result(SERD_OVERFLOW, max_size) + : r.status ? result(SERD_BAD_SYNTAX, 0U) + : result(SERD_SUCCESS, r.write_count); +} + SerdNode* serd_node_copy(const SerdNode* node) { diff --git a/test/test_node.c b/test/test_node.c index f0092553..ab0ceca8 100644 --- a/test/test_node.c +++ b/test/test_node.c @@ -5,9 +5,10 @@ #include "serd/memory.h" #include "serd/node.h" -#include "serd/string.h" +#include "serd/status.h" #include "serd/string_view.h" #include "serd/uri.h" +#include "serd/write_result.h" #include <assert.h> #include <math.h> @@ -322,7 +323,7 @@ test_get_integer(void) } static void -test_blob_to_node(void) +test_base64(void) { assert(!serd_new_base64(&SERD_URI_NULL, 0, NULL)); @@ -333,14 +334,16 @@ test_blob_to_node(void) data[i] = (uint8_t)((size + i) % 256); } - size_t out_size = 0; - 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); + SerdNode* blob = serd_new_base64(data, size, NULL); + const char* blob_str = serd_node_string(blob); + const size_t max_size = serd_get_base64_size(blob); + uint8_t* out = (uint8_t*)calloc(1, max_size); + const SerdWriteResult r = serd_get_base64(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)); - assert(out_size == size); for (size_t i = 0; i < size; ++i) { assert(out[i] == data[i]); @@ -354,21 +357,47 @@ test_blob_to_node(void) serd_free(out); free(data); } +} + +static void +check_get_base64(const char* string, + const char* datatype_uri, + const char* expected) +{ + SerdNode* const node = + serd_new_typed_literal(serd_string(string), serd_string(datatype_uri)); + + assert(node); - // Test invalid base64 blob + const size_t max_size = serd_get_base64_size(node); + char* const decoded = (char*)calloc(1, max_size + 1); - SerdNode* const blob = serd_new_typed_literal( - serd_string("!nval!d$"), serd_string(NS_XSD "base64Binary")); + const SerdWriteResult r = serd_get_base64(node, max_size, decoded); + assert(!r.status); + assert(r.count <= max_size); - 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(!strcmp(decoded, expected)); + assert(strlen(decoded) <= max_size); - assert(!out); - assert(out_size == 0); + free(decoded); + serd_node_free(node); +} + +static void +test_get_base64(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"); + + SerdNode* const node = serd_new_typed_literal( + serd_string("Zm9v"), serd_string(NS_XSD "base64Binary")); - serd_node_free(blob); + char small[2] = {0}; + const SerdWriteResult r = serd_get_base64(node, sizeof(small), small); + + assert(r.status == SERD_OVERFLOW); + serd_node_free(node); } static void @@ -514,7 +543,8 @@ main(void) test_get_float(); test_integer(); test_get_integer(); - test_blob_to_node(); + test_base64(); + test_get_base64(); test_node_equals(); test_node_from_string(); test_node_from_substring(); |