aboutsummaryrefslogtreecommitdiffstats
path: root/src/string.c
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2011-12-30 08:10:23 +0000
committerDavid Robillard <d@drobilla.net>2011-12-30 08:10:23 +0000
commit847e56d9e696b813d1cdf3da6d54df5e7b389eae (patch)
treec4492906739264f39a3cdd9a0cacc3c6a4d908c7 /src/string.c
parente0f18e34021004a19709f0c627db51af1a27afcf (diff)
downloadserd-847e56d9e696b813d1cdf3da6d54df5e7b389eae.tar.gz
serd-847e56d9e696b813d1cdf3da6d54df5e7b389eae.tar.bz2
serd-847e56d9e696b813d1cdf3da6d54df5e7b389eae.zip
Add serd_node_new_blob and serd_base64_decode for handling arbitrary binary
data via base64 encoding. git-svn-id: http://svn.drobilla.net/serd/trunk@280 490d8e77-9747-427b-9fa3-0b8f29cee8a0
Diffstat (limited to 'src/string.c')
-rw-r--r--src/string.c46
1 files changed, 46 insertions, 0 deletions
diff --git a/src/string.c b/src/string.c
index 968a20e1..a97c6f98 100644
--- a/src/string.c
+++ b/src/string.c
@@ -112,3 +112,49 @@ serd_strtod(const char* str, char** endptr)
*endptr = (char*)s;
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[255] =
+ "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$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] != '='));
+}
+
+SERD_API
+void*
+serd_base64_decode(const uint8_t* str, size_t len, size_t* size)
+{
+ void* buf = malloc((len * 3) / 4 + 2);
+ *size = 0;
+ for (size_t i = 0, j = 0; i < len; j += 3) {
+ uint8_t in[4] = "====";
+ size_t n_in = 0;
+ for (; i < len && n_in < 4; ++n_in) {
+ for (; i < len && !is_base64(str[i]); ++i) {} // Skip junk
+ in[n_in] = str[i++];
+ }
+ if (n_in > 1) {
+ *size += decode_chunk(in, (uint8_t*)buf + j);
+ }
+ }
+ return buf;
+}