aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-07-17 17:31:53 -0400
committerDavid Robillard <d@drobilla.net>2022-01-13 23:03:45 -0500
commit30f3e6fc2c1e24c429d5d0b7100dc449ade6703f (patch)
treeb8511de2276fbc23d06dab1c83fd86b4f1a96b10
parentd88b5a797f8502c40d0da964d653a1cd3028c872 (diff)
downloadserd-30f3e6fc2c1e24c429d5d0b7100dc449ade6703f.tar.gz
serd-30f3e6fc2c1e24c429d5d0b7100dc449ade6703f.tar.bz2
serd-30f3e6fc2c1e24c429d5d0b7100dc449ade6703f.zip
Clean up base64 node construction and access API
-rw-r--r--include/serd/serd.h74
-rw-r--r--meson.build1
-rw-r--r--src/base64.c38
-rw-r--r--src/base64.h49
-rw-r--r--src/node.c27
-rw-r--r--test/test_node.c65
6 files changed, 132 insertions, 122 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 56dbcaaa..f09d5e39 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -199,6 +199,31 @@ typedef enum {
SERD_ERR_OVERFLOW, ///< Stack overflow
} SerdStatus;
+/**
+ 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_ERR_OVERFLOW, this is the number of bytes of output space that are
+ required for success.
+ */
+ size_t count;
+} SerdWriteResult;
+
/// Return a string describing a status code
SERD_CONST_API
const char* SERD_NONNULL
@@ -222,22 +247,6 @@ size_t
serd_strlen(const char* SERD_NONNULL str, SerdNodeFlags* SERD_NULLABLE flags);
/**
- Decode a base64 string.
-
- 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().
-*/
-SERD_API
-void* SERD_ALLOCATED
-serd_base64_decode(const char* SERD_NONNULL str,
- size_t len,
- size_t* SERD_NONNULL size);
-
-/**
@}
@defgroup serd_streams Byte Streams
@{
@@ -706,6 +715,39 @@ SERD_API
int64_t
serd_get_integer(const SerdNode* SERD_NONNULL node);
+/**
+ Return the maximum size of a decoded base64 node in bytes.
+
+ This returns an upper bound on the number of bytes that would be decoded by
+ serd_get_base64(). 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_ERR_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);
+
/// Return a deep copy of `node`
SERD_API
SerdNode* SERD_ALLOCATED
diff --git a/meson.build b/meson.build
index 301b65ac..cc11ce7d 100644
--- a/meson.build
+++ b/meson.build
@@ -86,7 +86,6 @@ c_header_files = files(c_headers)
c_header = files('include/serd/serd.h')
sources = [
- 'src/base64.c',
'src/byte_source.c',
'src/env.c',
'src/n3.c',
diff --git a/src/base64.c b/src/base64.c
deleted file mode 100644
index 9dac9979..00000000
--- a/src/base64.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- Copyright 2011-2020 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#include "exess/exess.h"
-#include "serd/serd.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 6fbe6c5c..00000000
--- a/src/base64.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- Copyright 2011-2020 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#ifndef SERD_BASE64_H
-#define SERD_BASE64_H
-
-#include "serd/serd.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_BASE64_H
diff --git a/src/node.c b/src/node.c
index 5016200d..eafcec38 100644
--- a/src/node.c
+++ b/src/node.c
@@ -182,6 +182,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_simple_node(const SerdNodeType type, const SerdStringView str)
{
@@ -386,6 +393,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_ERR_OVERFLOW, max_size)
+ : r.status ? result(SERD_ERR_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 e296d0b6..1775a669 100644
--- a/test/test_node.c
+++ b/test/test_node.c
@@ -331,7 +331,7 @@ test_get_integer(void)
}
static void
-test_blob_to_node(void)
+test_base64(void)
{
assert(!serd_new_base64(&SERD_URI_NULL, 0, NULL));
@@ -342,14 +342,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]);
@@ -363,21 +365,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_ERR_OVERFLOW);
+ serd_node_free(node);
}
static void
@@ -520,7 +548,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();