]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: base64 - Add base64_encode_get_full_space().
authorStephan Bosch <stephan.bosch@open-xchange.com>
Tue, 15 Sep 2020 23:03:53 +0000 (01:03 +0200)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Wed, 7 Oct 2020 14:08:11 +0000 (14:08 +0000)
This translates the space in the destination buffer to the number of bytes that
can be encoded at most to complete the full base64 encoding, including padding
and newlines if configured.

src/lib/base64.c
src/lib/base64.h
src/lib/test-base64.c

index de3bfaaeb866099752a5656b797453028fbbb89e..7d4163831371f3f43061fe55d8d9f0149f74df5b 100644 (file)
@@ -137,6 +137,80 @@ size_t base64_encode_get_size(struct base64_encoder *enc, size_t src_size)
        return out_size;
 }
 
+size_t base64_encode_get_full_space(struct base64_encoder *enc,
+                                   size_t dst_space)
+{
+       bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
+       bool no_padding = HAS_ALL_BITS(enc->flags,
+                                      BASE64_ENCODE_FLAG_NO_PADDING);
+       size_t src_space = 0;
+
+       i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
+
+       if (enc->max_line_len < SIZE_MAX) {
+               size_t max_line_space, lines, nl_space;
+
+               /* Calculate how many line endings must be added if all space
+                  were used. */
+               max_line_space = enc->max_line_len + (crlf ? 2 : 1);
+               lines = dst_space / max_line_space;
+
+               /* Calculate how much space is used by newline characters and
+                  subtract this from the available space. */
+               nl_space = lines * (crlf ? 2 : 1);
+               if (dst_space <= nl_space)
+                       return 0;
+               dst_space -= nl_space;
+       }
+
+       if (dst_space <= enc->w_buf_len)
+               return 0;
+       dst_space -= enc->w_buf_len;
+
+       if (enc->pending_lf)
+               dst_space--;
+       if (dst_space == 0)
+               return 0;
+
+       /* Handle sub-position */
+       switch (enc->sub_pos) {
+       case 0:
+               break;
+       case 1:
+               dst_space--;
+               src_space++;
+               /* fall through */
+       case 2:
+               if (dst_space < 2)
+                       return src_space;
+               dst_space -= 2;
+               src_space++;
+               break;
+       default:
+               i_unreached();
+       }
+
+       if (dst_space == 0)
+               return src_space;
+
+       src_space += dst_space / 4 * 3;
+       if (no_padding) {
+               switch (dst_space % 4) {
+               case 0:
+               case 1:
+                       break;
+               case 2:
+                       src_space += 1;
+                       break;
+               case 3:
+                       src_space += 2;
+                       break;
+               }
+       }
+
+       return src_space;
+}
+
 static void
 base64_encode_more_data(struct base64_encoder *enc,
                        const unsigned char *src_c, size_t src_size,
index e879433f47ef7450671d76058dd58b3774a3c6de..ec6ac17ae189b8bdcc41a15c7e9eaeefca97b643 100644 (file)
@@ -89,6 +89,12 @@ uoff_t base64_get_full_encoded_size(struct base64_encoder *enc,
    base64_encode_more() with the indicated src_size. */
 size_t base64_encode_get_size(struct base64_encoder *enc, size_t src_size);
 
+/* Translate the space in the destination buffer to the number of bytes that can
+   be encoded at most to complete the full base64 encoding, including padding
+   and newlines if configured. */
+size_t base64_encode_get_full_space(struct base64_encoder *enc,
+                                   size_t dst_space);
+
 /* Translates binary data into some form of Base64. The src must not point to
    dest buffer. Returns TRUE when all the provided data is encoded. Returns
    FALSE when the space in the provided buffer is insufficient. The return value
index 662e1f208590842d2ece24a61de585326b174cae..5231c454b54a25721439e964e78ef382cca5ebe3 100644 (file)
@@ -970,7 +970,7 @@ test_base64_random_lowlevel_stream(const struct base64_scheme *b64,
        struct base64_decoder dec;
        const unsigned char *buf_p, *buf_begin, *buf_end;
        int ret;
-       size_t out_space;
+       size_t out_space, out_full_size;
        void *out_data;
        buffer_t out;
 
@@ -982,12 +982,13 @@ test_base64_random_lowlevel_stream(const struct base64_scheme *b64,
        buf_end = buf_begin + in_buf_size;
 
        base64_encode_init(&enc, b64, enc_flags, max_line_len);
+       out_full_size = base64_get_full_encoded_size(&enc, in_buf_size);
        out_space = 0;
        for (buf_p = buf_begin; buf_p < buf_end; ) {
                size_t buf_ch, out_ch;
                size_t left = (buf_end - buf_p);
                size_t used = buf1->used;
-               size_t src_pos, out_size;
+               size_t src_pos, out_size, src_full_space;
                bool eres;
 
                if (chunk_size == 0) {
@@ -1005,6 +1006,11 @@ test_base64_random_lowlevel_stream(const struct base64_scheme *b64,
                if (buf_ch > left)
                        buf_ch = left;
 
+               src_full_space = base64_encode_get_full_space(
+                       &enc, out_full_size - used);
+               test_assert_idx(src_full_space >= (size_t)(buf_end - buf_p),
+                               test_idx);
+
                out_size = base64_encode_get_size(&enc, buf_ch);
 
                eres = base64_encode_more(&enc, buf_p, buf_ch, &src_pos, &out);
@@ -1020,8 +1026,7 @@ test_base64_random_lowlevel_stream(const struct base64_scheme *b64,
 
        /* Verify encode */
 
-       test_assert(base64_get_full_encoded_size(&enc, in_buf_size) ==
-                   buf1->used);
+       test_assert(out_full_size == buf1->used);
 
        buffer_set_used_size(buf2, 0);
        base64_encode_init(&enc, b64, enc_flags, max_line_len);