aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-05-12 12:47:18 +0200
committerDavid Robillard <d@drobilla.net>2018-05-27 18:23:15 +0200
commit7953d861d83abd1a30ae2d620a8ba691bdf2ca1d (patch)
treeb07436aa1a1fbef16c9a515ff930d4fa978f93e8
parent2ab12963fd1fb90d254028ff5766afd88a2da54e (diff)
downloadserd-7953d861d83abd1a30ae2d620a8ba691bdf2ca1d.tar.gz
serd-7953d861d83abd1a30ae2d620a8ba691bdf2ca1d.tar.bz2
serd-7953d861d83abd1a30ae2d620a8ba691bdf2ca1d.zip
Separate base64 implementation
-rw-r--r--src/base64.c124
-rw-r--r--src/base64.h47
-rw-r--r--src/node.c40
-rw-r--r--src/string.c47
-rw-r--r--wscript3
5 files changed, 179 insertions, 82 deletions
diff --git a/src/base64.c b/src/base64.c
new file mode 100644
index 00000000..49cae6bc
--- /dev/null
+++ b/src/base64.c
@@ -0,0 +1,124 @@
+/*
+ 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.
+*/
+
+#include "base64.h"
+
+#include "serd_internal.h"
+#include "string_utils.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ Base64 encoding table.
+
+ @see <a href="http://tools.ietf.org/html/rfc3548#section-3">RFC3986 S3</a>.
+*/
+static const uint8_t b64_map[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ Base64 decoding table.
+
+ This is indexed by encoded characters and returns the numeric value used
+ for decoding, shifted up by 47 to be in the range of printable ASCII.
+ A '$' is a placeholder for characters not in the base64 alphabet.
+*/
+static const char b64_unmap[] =
+ "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$m$$$ncdefghijkl$$$$$$"
+ "$/0123456789:;<=>?@ABCDEFGH$$$$$$IJKLMNOPQRSTUVWXYZ[\\]^_`ab$$$$"
+ "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"
+ "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$";
+
+/** Encode 3 raw bytes to 4 base64 characters. */
+static inline void
+encode_chunk(uint8_t out[4], const uint8_t in[3], size_t n_in)
+{
+ out[0] = b64_map[in[0] >> 2];
+ out[1] = b64_map[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)];
+ out[2] = ((n_in > 1)
+ ? (b64_map[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)])
+ : (uint8_t)'=');
+ out[3] = ((n_in > 2) ? b64_map[in[2] & 0x3F] : (uint8_t)'=');
+}
+
+size_t
+serd_base64_get_length(const size_t size, const bool wrap_lines)
+{
+ return ((size + 2) / 3) * 4 + (wrap_lines ? (size / 57) : 0);
+}
+
+bool
+serd_base64_encode(char* const str,
+ const void* const buf,
+ const size_t size,
+ const bool wrap_lines)
+{
+ uint8_t* const out = (uint8_t*)str;
+ bool has_newline = false;
+ for (size_t i = 0, j = 0; i < size; i += 3, j += 4) {
+ uint8_t in[4] = { 0, 0, 0, 0 };
+ size_t n_in = MIN(3, size - i);
+ memcpy(in, (const uint8_t*)buf + i, n_in);
+
+ if (wrap_lines && i > 0 && (i % 57) == 0) {
+ out[j++] = '\n';
+ has_newline = true;
+ }
+
+ encode_chunk(out + j, in, n_in);
+ }
+
+ return has_newline;
+}
+
+static inline uint8_t
+unmap(const uint8_t in)
+{
+ return b64_unmap[in] - 47;
+}
+
+/** Decode 4 base64 characters to 3 raw bytes. */
+static inline size_t
+decode_chunk(const uint8_t in[4], uint8_t out[3])
+{
+ out[0] = (uint8_t)(((unmap(in[0]) << 2)) | unmap(in[1]) >> 4);
+ out[1] = (uint8_t)(((unmap(in[1]) << 4) & 0xF0) | unmap(in[2]) >> 2);
+ out[2] = (uint8_t)(((unmap(in[2]) << 6) & 0xC0) | unmap(in[3]));
+ return 1 + (in[2] != '=') + ((in[2] != '=') && (in[3] != '='));
+}
+
+void*
+serd_base64_decode(const char* str, size_t len, size_t* size)
+{
+ const uint8_t* ustr = (const uint8_t*)str;
+
+ void* buf = malloc((len * 3) / 4 + 2);
+ *size = 0;
+ for (size_t i = 0, j = 0; i < len; j += 3) {
+ uint8_t in[] = "====";
+ size_t n_in = 0;
+ for (; i < len && n_in < 4; ++n_in) {
+ for (; i < len && !is_base64(ustr[i]); ++i) {} // Skip junk
+ in[n_in] = ustr[i++];
+ }
+ if (n_in > 1) {
+ *size += decode_chunk(in, (uint8_t*)buf + j);
+ }
+ }
+ return buf;
+}
diff --git a/src/base64.h b/src/base64.h
new file mode 100644
index 00000000..b5e04373
--- /dev/null
+++ b/src/base64.h
@@ -0,0 +1,47 @@
+/*
+ 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 "serd/serd.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 Whether to wrap lines at a fixed length.
+ @return The length of the base64 encoding, exclusing 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 Whether to wrap lines at a fixed length.
+ @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 7f124f13..36c2c8ca 100644
--- a/src/node.c
+++ b/src/node.c
@@ -16,6 +16,7 @@
#include "node.h"
+#include "base64.h"
#include "serd_internal.h"
#include "string_utils.h"
@@ -470,45 +471,16 @@ serd_node_new_integer(int64_t i)
return node;
}
-/**
- Base64 encoding table.
- @see <a href="http://tools.ietf.org/html/rfc3548#section-3">RFC3986 S3</a>.
-*/
-static const uint8_t b64_map[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-/**
- Encode 3 raw bytes to 4 base64 characters.
-*/
-static inline void
-encode_chunk(uint8_t out[4], const uint8_t in[3], size_t n_in)
-{
- out[0] = b64_map[in[0] >> 2];
- out[1] = b64_map[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)];
- out[2] = ((n_in > 1)
- ? (b64_map[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)])
- : (uint8_t)'=');
- out[3] = ((n_in > 2) ? b64_map[in[2] & 0x3F] : (uint8_t)'=');
-}
-
SerdNode*
serd_node_new_blob(const void* buf, size_t size, bool wrap_lines)
{
- const size_t len = ((size + 2) / 3) * 4 + (wrap_lines ? (size / 57) : 0);
- SerdNode* node = serd_node_malloc(len + 1, 0, SERD_LITERAL);
- uint8_t* str = (uint8_t*)serd_node_buffer(node);
- for (size_t i = 0, j = 0; i < size; i += 3, j += 4) {
- uint8_t in[4] = { 0, 0, 0, 0 };
- size_t n_in = MIN(3, size - i);
- memcpy(in, (const uint8_t*)buf + i, n_in);
-
- if (wrap_lines && i > 0 && (i % 57) == 0) {
- str[j++] = '\n';
- node->flags |= SERD_HAS_NEWLINE;
- }
+ const size_t len = serd_base64_get_length(size, wrap_lines);
+ SerdNode* const node = serd_node_malloc(len + 1, 0, SERD_LITERAL);
- encode_chunk(str + j, in, n_in);
+ if (serd_base64_encode(serd_node_buffer(node), buf, size, wrap_lines)) {
+ node->flags |= SERD_HAS_NEWLINE;
}
+
node->n_bytes = len + 1;
return node;
}
diff --git a/src/string.c b/src/string.c
index 0ab6cb4a..1755d48a 100644
--- a/src/string.c
+++ b/src/string.c
@@ -107,50 +107,3 @@ serd_strtod(const char* str, size_t* end)
return result * sign;
}
-
-/**
- Base64 decoding table.
- This is indexed by encoded characters and returns the numeric value used
- for decoding, shifted up by 47 to be in the range of printable ASCII.
- A '$' is a placeholder for characters not in the base64 alphabet.
-*/
-static const char b64_unmap[] =
- "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$m$$$ncdefghijkl$$$$$$"
- "$/0123456789:;<=>?@ABCDEFGH$$$$$$IJKLMNOPQRSTUVWXYZ[\\]^_`ab$$$$"
- "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"
- "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$";
-
-static inline uint8_t unmap(const uint8_t in) { return b64_unmap[in] - 47; }
-
-/**
- Decode 4 base64 characters to 3 raw bytes.
-*/
-static inline size_t
-decode_chunk(const uint8_t in[4], uint8_t out[3])
-{
- out[0] = (uint8_t)(((unmap(in[0]) << 2)) | unmap(in[1]) >> 4);
- out[1] = (uint8_t)(((unmap(in[1]) << 4) & 0xF0) | unmap(in[2]) >> 2);
- out[2] = (uint8_t)(((unmap(in[2]) << 6) & 0xC0) | unmap(in[3]));
- return 1 + (in[2] != '=') + ((in[2] != '=') && (in[3] != '='));
-}
-
-void*
-serd_base64_decode(const char* str, size_t len, size_t* size)
-{
- const uint8_t* ustr = (const uint8_t*)str;
-
- void* buf = malloc((len * 3) / 4 + 2);
- *size = 0;
- for (size_t i = 0, j = 0; i < len; j += 3) {
- uint8_t in[] = "====";
- size_t n_in = 0;
- for (; i < len && n_in < 4; ++n_in) {
- for (; i < len && !is_base64(ustr[i]); ++i) {} // Skip junk
- in[n_in] = ustr[i++];
- }
- if (n_in > 1) {
- *size += decode_chunk(in, (uint8_t*)buf + j);
- }
- }
- return buf;
-}
diff --git a/wscript b/wscript
index 47b2c10e..ea5d2ba7 100644
--- a/wscript
+++ b/wscript
@@ -75,7 +75,8 @@ def configure(conf):
autowaf.display_msg(conf, 'Unit tests', bool(conf.env.BUILD_TESTS))
print('')
-lib_source = ['src/byte_source.c',
+lib_source = ['src/base64.c',
+ 'src/byte_source.c',
'src/env.c',
'src/n3.c',
'src/node.c',