diff options
-rw-r--r-- | src/basic/hexdecoct.c | 47 | ||||
-rw-r--r-- | src/basic/hexdecoct.h | 6 | ||||
-rw-r--r-- | src/test/test-hexdecoct.c | 33 |
3 files changed, 82 insertions, 4 deletions
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index da1add7c76..172ae8a921 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -565,38 +565,79 @@ int unbase64char(char c) { return -EINVAL; } -ssize_t base64mem(const void *p, size_t l, char **out) { - char *r, *z; +static void maybe_line_break(char **x, char *start, size_t line_break) { + size_t n; + + assert(x); + assert(*x); + assert(start); + assert(*x >= start); + + if (line_break == SIZE_MAX) + return; + + n = *x - start; + + if (n % (line_break + 1) == line_break) + *((*x)++) = '\n'; +} + +ssize_t base64mem_full( + const void *p, + size_t l, + size_t line_break, + char **out) { + const uint8_t *x; + char *r, *z; + size_t m; assert(p || l == 0); assert(out); + assert(line_break > 0); /* three input bytes makes four output bytes, padding is added so we must round up */ - z = r = malloc(4 * (l + 2) / 3 + 1); + m = 4 * (l + 2) / 3 + 1; + + if (line_break != SIZE_MAX) + m += m / line_break; + + z = r = malloc(m); if (!r) return -ENOMEM; for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */ + maybe_line_break(&z, r, line_break); *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ + maybe_line_break(&z, r, line_break); *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ + maybe_line_break(&z, r, line_break); *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */ + maybe_line_break(&z, r, line_break); *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */ } switch (l % 3) { case 2: + maybe_line_break(&z, r, line_break); *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ + maybe_line_break(&z, r, line_break); *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ + maybe_line_break(&z, r, line_break); *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */ + maybe_line_break(&z, r, line_break); *(z++) = '='; break; case 1: + maybe_line_break(&z, r, line_break); *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ + maybe_line_break(&z, r, line_break); *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */ + maybe_line_break(&z, r, line_break); *(z++) = '='; + maybe_line_break(&z, r, line_break); *(z++) = '='; break; diff --git a/src/basic/hexdecoct.h b/src/basic/hexdecoct.h index 4ace5b7a99..5218f78665 100644 --- a/src/basic/hexdecoct.h +++ b/src/basic/hexdecoct.h @@ -33,7 +33,11 @@ int unbase64char(char c) _const_; char *base32hexmem(const void *p, size_t l, bool padding); int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len); -ssize_t base64mem(const void *p, size_t l, char **out); +ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret); +static inline ssize_t base64mem(const void *p, size_t l, char **ret) { + return base64mem_full(p, l, SIZE_MAX, ret); +} + int base64_append(char **prefix, int plen, const void *p, size_t l, int margin, int width); diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c index c9d318b8d1..a3767b47be 100644 --- a/src/test/test-hexdecoct.c +++ b/src/test/test-hexdecoct.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "hexdecoct.h" #include "macro.h" +#include "random-util.h" #include "string-util.h" static void test_hexchar(void) { @@ -275,6 +276,37 @@ static void test_base64mem(void) { free(b64); } +static void test_base64mem_linebreak(void) { + uint8_t data[4096]; + + for (size_t i = 0; i < 20; i++) { + _cleanup_free_ char *encoded = NULL; + _cleanup_free_ void *decoded = NULL; + size_t decoded_size; + uint64_t n, m; + ssize_t l; + + /* Try a bunch of differently sized blobs */ + n = random_u64_range(sizeof(data)); + random_bytes(data, n); + + /* Break at various different columns */ + m = 1 + random_u64_range(n + 5); + + l = base64mem_full(data, n, m, &encoded); + assert_se(l >= 0); + assert_se(encoded); + assert_se((size_t) l == strlen(encoded)); + + assert_se(unbase64mem(encoded, SIZE_MAX, &decoded, &decoded_size) >= 0); + assert_se(decoded_size == n); + assert_se(memcmp(data, decoded, n) == 0); + + for (size_t j = 0; j < (size_t) l; j++) + assert_se((encoded[j] == '\n') == (j % (m + 1) == m)); + } +} + static void test_unbase64mem_one(const char *input, const char *output, int ret) { _cleanup_free_ void *buffer = NULL; size_t size = 0; @@ -348,6 +380,7 @@ int main(int argc, char *argv[]) { test_base32hexmem(); test_unbase32hexmem(); test_base64mem(); + test_base64mem_linebreak(); test_unbase64mem(); test_hexdump(); |