diff options
author | David Robillard <d@drobilla.net> | 2021-07-17 17:31:53 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-01-13 23:03:45 -0500 |
commit | 30f3e6fc2c1e24c429d5d0b7100dc449ade6703f (patch) | |
tree | b8511de2276fbc23d06dab1c83fd86b4f1a96b10 | |
parent | d88b5a797f8502c40d0da964d653a1cd3028c872 (diff) | |
download | serd-30f3e6fc2c1e24c429d5d0b7100dc449ade6703f.tar.gz serd-30f3e6fc2c1e24c429d5d0b7100dc449ade6703f.tar.bz2 serd-30f3e6fc2c1e24c429d5d0b7100dc449ade6703f.zip |
Clean up base64 node construction and access API
-rw-r--r-- | include/serd/serd.h | 74 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/base64.c | 38 | ||||
-rw-r--r-- | src/base64.h | 49 | ||||
-rw-r--r-- | src/node.c | 27 | ||||
-rw-r--r-- | test/test_node.c | 65 |
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 @@ -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(); |