diff --git a/src/buffer.c b/src/buffer.c index 61cfaf9e2..ee2dd2804 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -186,6 +186,46 @@ int git_buf_puts_escaped( return 0; } +static const char b64str[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +int git_buf_put_base64(git_buf *buf, const char *data, size_t len) +{ + size_t extra = len % 3; + uint8_t *write, a, b, c; + const uint8_t *read = (const uint8_t *)data; + + ENSURE_SIZE(buf, buf->size + ((len * 4 + 3) / 3) + 1); + write = (uint8_t *)&buf->ptr[buf->size]; + + /* convert each run of 3 bytes into 4 output bytes */ + for (len -= extra; len > 0; len -= 3) { + a = *read++; + b = *read++; + c = *read++; + + *write++ = b64str[a >> 2]; + *write++ = b64str[(a & 0x03) << 4 | b >> 4]; + *write++ = b64str[(b & 0x0f) << 2 | c >> 6]; + *write++ = b64str[c & 0x3f]; + } + + if (extra > 0) { + a = *read++; + b = (extra > 1) ? *read++ : 0; + + *write++ = b64str[a >> 2]; + *write++ = b64str[(a & 0x03) << 4 | b >> 4]; + *write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '='; + *write++ = '='; + } + + buf->size = ((char *)write) - buf->ptr; + buf->ptr[buf->size] = '\0'; + + return 0; +} + int git_buf_vprintf(git_buf *buf, const char *format, va_list ap) { int len; diff --git a/src/buffer.h b/src/buffer.h index 17922e408..94b7e0e22 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -154,4 +154,7 @@ bool git_buf_is_binary(const git_buf *buf); /* Unescape all characters in a buffer */ void git_buf_unescape(git_buf *buf); +/* Write data as base64 encoded in buffer */ +int git_buf_put_base64(git_buf *buf, const char *data, size_t len); + #endif diff --git a/tests-clar/core/buffer.c b/tests-clar/core/buffer.c index 972567e55..236bf39da 100644 --- a/tests-clar/core/buffer.c +++ b/tests-clar/core/buffer.c @@ -678,3 +678,28 @@ void test_core_buffer__unescape(void) assert_unescape("\\", "\\\\"); assert_unescape("", ""); } + +void test_core_buffer__base64(void) +{ + git_buf buf = GIT_BUF_INIT; + + /* t h i s + * 0x 74 68 69 73 + * 0b 01110100 01101000 01101001 01110011 + * 0b 011101 000110 100001 101001 011100 110000 + * 0x 1d 06 21 29 1c 30 + * d G h p c w + */ + cl_git_pass(git_buf_put_base64(&buf, "this", 4)); + cl_assert_equal_s("dGhpcw==", buf.ptr); + + git_buf_clear(&buf); + cl_git_pass(git_buf_put_base64(&buf, "this!", 5)); + cl_assert_equal_s("dGhpcyE=", buf.ptr); + + git_buf_clear(&buf); + cl_git_pass(git_buf_put_base64(&buf, "this!\n", 6)); + cl_assert_equal_s("dGhpcyEK", buf.ptr); + + git_buf_free(&buf); +}