Browse Source

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
zrythm_meson
David Robillard 11 years ago
parent
commit
847e56d9e6
  1. 2
      ChangeLog
  2. 26
      serd/serd.h
  3. 42
      src/node.c
  4. 6
      src/serd_internal.h
  5. 46
      src/string.c
  6. 35
      tests/serd_test.c
  7. 2
      wscript

2
ChangeLog

@ -21,6 +21,8 @@ serd (UNRELEASED) unstable; urgency=low @@ -21,6 +21,8 @@ serd (UNRELEASED) unstable; urgency=low
* Improve URI resolution to cover most of the abnormal cases from RFC3986
* Support file://localhost/foo URIs in serd_uri_to_path()
* Support Windows file://c:/foo URIs in serd_uri_to_path() on all platforms
* Add serd_node_new_blob and serd_base64_decode for handling arbitrary
binary data via base64 encoding.
-- David Robillard <d@drobilla.net> (UNRELEASED)

26
serd/serd.h

@ -277,6 +277,20 @@ SERD_API @@ -277,6 +277,20 @@ SERD_API
double
serd_strtod(const char* str, char** endptr);
/**
Decode a base64 string.
This function can be used to deserialise a blob node created with
serd_node_new_blob().
@param str Base64 string to decode.
@param len The length of @c str.
@param size Set to the size of the returned blob in bytes.
@return A newly allocated blob which must be freed with free().
*/
SERD_API
void*
serd_base64_decode(const uint8_t* str, size_t len, size_t* size);
/**
@}
@name URI
@ -415,6 +429,18 @@ SERD_API @@ -415,6 +429,18 @@ SERD_API
SerdNode
serd_node_new_integer(long i);
/**
Create a node by serialising @c buf into an xsd:base64Binary string.
This function can be used to make a serialisable node out of arbitrary
binary data, which can be decoded using serd_base64_decode().
@param wrap_lines If true lines are wrapped at 76 characters to conform
to RFC 2045.
*/
SERD_API
SerdNode
serd_node_new_blob(const void* buf, size_t size, bool wrap_lines);
/**
Free any data owned by @c node.

42
src/node.c

@ -218,6 +218,48 @@ serd_node_new_integer(long i) @@ -218,6 +218,48 @@ serd_node_new_integer(long 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[64] =
"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)'=');
}
SERD_API
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 = { calloc(1, len + 2), len, len, 0, SERD_LITERAL };
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) {
((uint8_t*)node.buf)[j++] = '\n';
node.flags |= SERD_HAS_NEWLINE;
}
encode_chunk((uint8_t*)node.buf + j, in, n_in);
}
return node;
}
SERD_API
void
serd_node_free(SerdNode* node)

6
src/serd_internal.h

@ -219,4 +219,10 @@ is_space(const char c) @@ -219,4 +219,10 @@ is_space(const char c)
}
}
static inline bool
is_base64(const uint8_t c)
{
return is_alpha(c) || is_digit(c) || c == '+' || c == '/' || c == '=';
}
#endif // SERD_INTERNAL_H

46
src/string.c

@ -112,3 +112,49 @@ serd_strtod(const char* str, char** endptr) @@ -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;
}

35
tests/serd_test.c

@ -133,7 +133,6 @@ main() @@ -133,7 +133,6 @@ main()
};
for (unsigned i = 0; i < sizeof(int_test_nums) / sizeof(double); ++i) {
fprintf(stderr, "\n*** TEST %ld\n", int_test_nums[i]);
SerdNode node = serd_node_new_integer(int_test_nums[i]);
if (strcmp((const char*)node.buf, (const char*)int_test_strs[i])) {
fprintf(stderr, "error: Serialised `%s' != %s\n",
@ -149,6 +148,40 @@ main() @@ -149,6 +148,40 @@ main()
serd_node_free(&node);
}
// Test serd_node_new_blob
for (size_t size = 0; size < 256; ++size) {
uint8_t* data = malloc(size);
for (size_t i = 0; i < size; ++i) {
data[i] = (uint8_t)(rand() % 256);
}
SerdNode blob = serd_node_new_blob(data, size, size % 5);
if (blob.n_bytes != blob.n_chars) {
fprintf(stderr, "error: Blob %zu bytes != %zu chars\n",
blob.n_bytes, blob.n_chars);
return 1;
}
size_t out_size;
uint8_t* out = serd_base64_decode(blob.buf, blob.n_bytes, &out_size);
if (out_size != size) {
fprintf(stderr, "error: Blob size %zu != %zu\n", out_size, size);
return 1;
}
for (size_t i = 0; i < size; ++i) {
if (out[i] != data[i]) {
fprintf(stderr, "error: Corrupt blob at byte %zu\n", i);
return 1;
}
}
serd_node_free(&blob);
free(out);
free(data);
}
// Test serd_strlen
const uint8_t str[] = { '"', '5', 0xE2, 0x82, 0xAC, '"', '\n', 0 };

2
wscript

@ -10,7 +10,7 @@ from waflib.extras import autowaf as autowaf @@ -10,7 +10,7 @@ from waflib.extras import autowaf as autowaf
import waflib.Logs as Logs, waflib.Options as Options
# Version of this package (even if built as a child)
SERD_VERSION = '0.8.0'
SERD_VERSION = '0.9.0'
SERD_MAJOR_VERSION = '0'
# Library version (UNIX style major, minor, micro)

Loading…
Cancel
Save