aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/serd/node.h32
-rw-r--r--include/serd/serd.h1
-rw-r--r--include/serd/write_result.h50
-rw-r--r--meson.build2
-rw-r--r--src/node.c29
-rw-r--r--test/test_node.c68
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',
diff --git a/src/node.c b/src/node.c
index ca173a51..c2f6ebab 100644
--- a/src/node.c
+++ b/src/node.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();