aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2019-03-17 23:49:10 +0100
committerDavid Robillard <d@drobilla.net>2019-04-13 19:15:32 +0200
commitdd7f57a7d955a323c5691ec64dd96e9b0a5a2553 (patch)
tree58f62490c75c52891d900139aa630bbd04d1e979
parent14c2fe14b90c1057b2829b7008423ef9bf79edef (diff)
downloadserd-dd7f57a7d955a323c5691ec64dd96e9b0a5a2553.tar.gz
serd-dd7f57a7d955a323c5691ec64dd96e9b0a5a2553.tar.bz2
serd-dd7f57a7d955a323c5691ec64dd96e9b0a5a2553.zip
Clean up and expose base64 implementation
-rw-r--r--serd/serd.h49
-rw-r--r--src/base64.c20
-rw-r--r--src/base64.h45
-rw-r--r--src/node.c3
-rw-r--r--tests/base64_test.c120
-rw-r--r--tests/serd_test.c13
-rw-r--r--wscript2
7 files changed, 187 insertions, 65 deletions
diff --git a/serd/serd.h b/serd/serd.h
index e764d069..d87896ea 100644
--- a/serd/serd.h
+++ b/serd/serd.h
@@ -378,18 +378,59 @@ double
serd_strtod(const char* str, size_t* end);
/**
+ @}
+ @name Base64
+ @{
+*/
+
+/**
+ 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_API
+size_t
+serd_base64_encoded_length(size_t size, bool wrap_lines);
+
+/**
+ Return the maximum number of bytes required to decode `size` bytes of base64.
+
+ @param len The number of input (text) bytes to decode.
+ @return The required buffer size to decode `size` bytes of base64.
+*/
+SERD_API
+size_t
+serd_base64_decoded_size(size_t len);
+
+/**
+ Encode `size` bytes of `buf` into `str`, which must be large enough.
+
+ @param str Output buffer of at least serd_base64_encoded_length(size) bytes.
+ @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.
+*/
+SERD_API
+bool
+serd_base64_encode(char* str, const void* buf, size_t size, bool wrap_lines);
+
+/**
Decode a base64 string.
+
This function can be used to deserialise a blob node created with
serd_new_blob().
+ @param buf Output buffer of at least serd_base64_decoded_size(size) bytes.
+ @param size Set to the size of the decoded data in bytes.
@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_base64_decode(const char* str, size_t len, size_t* size);
+SerdStatus
+serd_base64_decode(void* buf, size_t* size, const char* str, size_t len);
/**
@}
diff --git a/src/base64.c b/src/base64.c
index b82f0389..e557fdf8 100644
--- a/src/base64.c
+++ b/src/base64.c
@@ -14,8 +14,6 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "base64.h"
-
#include "serd_internal.h"
#include "string_utils.h"
@@ -59,11 +57,17 @@ encode_chunk(uint8_t out[4], const uint8_t in[3], size_t n_in)
}
size_t
-serd_base64_get_length(const size_t size, const bool wrap_lines)
+serd_base64_encoded_length(const size_t size, const bool wrap_lines)
{
return (size + 2) / 3 * 4 + (wrap_lines * ((size - 1) / 57));
}
+size_t
+serd_base64_decoded_size(const size_t len)
+{
+ return (len * 3) / 4 + 2;
+}
+
bool
serd_base64_encode(char* const str,
const void* const buf,
@@ -104,13 +108,12 @@ decode_chunk(const uint8_t in[4], uint8_t out[3])
return 1 + (in[2] != '=') + ((in[2] != '=') && (in[3] != '='));
}
-void*
-serd_base64_decode(const char* str, size_t len, size_t* size)
+SerdStatus
+serd_base64_decode(void* buf, size_t* size, const char* str, size_t len)
{
const uint8_t* ustr = (const uint8_t*)str;
- void* buf = malloc((len * 3) / 4 + 2);
- *size = 0;
+ *size = 0;
for (size_t i = 0, j = 0; i < len; j += 3) {
uint8_t in[] = "====";
size_t n_in = 0;
@@ -122,5 +125,6 @@ serd_base64_decode(const char* str, size_t len, size_t* size)
*size += decode_chunk(in, (uint8_t*)buf + j);
}
}
- return buf;
+
+ return SERD_SUCCESS;
}
diff --git a/src/base64.h b/src/base64.h
deleted file mode 100644
index cb89491c..00000000
--- a/src/base64.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- Copyright 2011-2018 David Robillard <http://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 <stdbool.h>
-#include <stddef.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.
-*/
-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(char* 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 20312815..af18d095 100644
--- a/src/node.c
+++ b/src/node.c
@@ -16,7 +16,6 @@
#include "node.h"
-#include "base64.h"
#include "serd_internal.h"
#include "string_utils.h"
@@ -623,7 +622,7 @@ serd_new_blob(const void* buf,
}
const SerdNode* type = datatype ? datatype : &serd_xsd_base64Binary.node;
- const size_t len = serd_base64_get_length(size, wrap_lines);
+ const size_t len = serd_base64_encoded_length(size, wrap_lines);
const size_t type_len = serd_node_total_size(type);
const size_t total_len = len + 1 + type_len;
diff --git a/tests/base64_test.c b/tests/base64_test.c
new file mode 100644
index 00000000..b279b46b
--- /dev/null
+++ b/tests/base64_test.c
@@ -0,0 +1,120 @@
+/*
+ Copyright 2011-2019 David Robillard <http://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.
+*/
+
+#undef NDEBUG
+
+#include "serd/serd.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int
+test_round_trip(void)
+{
+ for (size_t size = 1; size < 1024; ++size) {
+ const size_t len = serd_base64_encoded_length(size, true);
+
+ char* buf = (char*)malloc(size);
+ for (size_t i = 0; i < size; ++i) {
+ buf[i] = (char)i;
+ }
+
+ char* str = (char*)calloc(1, len + 1);
+ serd_base64_encode(str, buf, size, true);
+
+ const size_t max_size = serd_base64_decoded_size(len);
+ size_t copy_size = 0;
+ char* copy = (char*)malloc(max_size);
+ serd_base64_decode(copy, &copy_size, str, len);
+ assert(copy_size == size);
+ assert(!memcmp(buf, copy, size));
+
+ free(copy);
+ free(str);
+ free(buf);
+ }
+
+ return 0;
+}
+
+static void
+test_encoding_equals(const char* const input, const char* const expected)
+{
+ const size_t size = strlen(input);
+ const size_t len = serd_base64_encoded_length(size, true);
+
+ char* str = (char*)calloc(1, len + 1);
+ serd_base64_encode(str, input, size, true);
+
+ assert(!strcmp(str, expected));
+
+ free(str);
+}
+
+static int
+test_rfc4648_vectors(void)
+{
+ test_encoding_equals("f", "Zg==");
+ test_encoding_equals("fo", "Zm8=");
+ test_encoding_equals("foo", "Zm9v");
+ test_encoding_equals("foob", "Zm9vYg==");
+ test_encoding_equals("fooba", "Zm9vYmE=");
+ test_encoding_equals("foobar", "Zm9vYmFy");
+ return 0;
+}
+
+static void
+test_decoding_equals(const char* const base64, const char* const expected)
+{
+ const size_t len = strlen(base64);
+ const size_t size = serd_base64_decoded_size(len);
+
+ size_t buf_size = 0;
+ char* buf = (char*)malloc(size);
+ serd_base64_decode(buf, &buf_size, base64, len);
+
+ assert(buf_size <= size);
+ assert(!memcmp(buf, expected, buf_size));
+
+ free(buf);
+}
+
+static int
+test_junk(void)
+{
+ test_decoding_equals("?Zm9vYmFy", "foobar");
+ test_decoding_equals("Z?m9vYmFy", "foobar");
+ test_decoding_equals("?Z?m9vYmFy", "foobar");
+ test_decoding_equals("?Z??m9vYmFy", "foobar");
+ test_decoding_equals("?Z???m9vYmFy", "foobar");
+ test_decoding_equals("?Z????m9vYmFy", "foobar");
+
+ test_decoding_equals("Zm9vYmFy?", "foobar");
+ test_decoding_equals("Zm9vYmF?y?", "foobar");
+ test_decoding_equals("Zm9vYmF?y??", "foobar");
+ test_decoding_equals("Zm9vYmF?y???", "foobar");
+ test_decoding_equals("Zm9vYmF?y????", "foobar");
+
+ return 0;
+}
+
+int
+main(void)
+{
+ return test_round_trip() || test_rfc4648_vectors() || test_junk();
+}
diff --git a/tests/serd_test.c b/tests/serd_test.c
index d6c2d1fc..a461e22a 100644
--- a/tests/serd_test.c
+++ b/tests/serd_test.c
@@ -212,12 +212,13 @@ main(void)
data[i] = (uint8_t)(rand() % 256);
}
- size_t out_size;
- SerdNode* blob = serd_new_blob(data, size, size % 5, NULL);
- const char* blob_str = serd_node_get_string(blob);
- uint8_t* out = (uint8_t*)serd_base64_decode(
- blob_str, serd_node_get_length(blob), &out_size);
+ size_t out_size = 0;
+ SerdNode* blob = serd_new_blob(data, size, size % 5, NULL);
+ const char* blob_str = serd_node_get_string(blob);
+ const size_t len = serd_node_get_length(blob);
+ uint8_t* out = (uint8_t*)malloc(serd_base64_decoded_size(len));
+ assert(!serd_base64_decode(out, &out_size, blob_str, len));
assert(serd_node_get_length(blob) == strlen(blob_str));
assert(out_size == size);
@@ -229,7 +230,7 @@ main(void)
NS_XSD "base64Binary"));
serd_node_free(blob);
- serd_free(out);
+ free(out);
free(data);
}
diff --git a/wscript b/wscript
index 95cb0c86..e8abe917 100644
--- a/wscript
+++ b/wscript
@@ -148,6 +148,7 @@ def build(bld):
# Test programs
for prog in [('serdi_static', 'src/serdi.c'),
+ ('base64_test', 'tests/base64_test.c'),
('cursor_test', 'tests/cursor_test.c'),
('serd_test', 'tests/serd_test.c'),
('read_chunk_test', 'tests/read_chunk_test.c'),
@@ -422,6 +423,7 @@ def test(tst):
srcdir = tst.path.abspath()
with tst.group('Unit') as check:
+ check(['./base64_test'])
check(['./cursor_test'])
check(['./nodes_test'])
check(['./overflow_test'])