summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/basic/hexdecoct.c47
-rw-r--r--src/basic/hexdecoct.h6
-rw-r--r--src/test/test-hexdecoct.c33
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();