>From 55a38a1d32758ac31746c2c9c8d54934f36c55ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20T=C3=A9choueyres?= Date: Tue, 21 May 2019 23:00:13 +0200 Subject: [PATCH] Add support for base64url variant. Implement the RFC4648 variant of base64 encoding used in URL (base64url). * doc/lispref/text.texi: add notice for optionals parameters of base64-(encode|decode)-(string|region). * etc/NEWS: announce variants for previous functions. * src/fns.c (base64-encode-region, base64-encode-string) : add optional parameters to manage padding and url variant. (base64-decode-region, base64-decode-string): add optional parameter to indicate use of url-variant. (base64-encode-1): add parameters to manage padding and url variant. (base64-decode-1): add parameter to manage url variant. * test/src/fns-tests.el: add tests for encoding / decoding variants. --- doc/lispref/text.texi | 28 +++++++-- etc/NEWS | 7 +++ src/fns.c | 129 ++++++++++++++++++++++++++++++----------- test/src/fns-tests.el | 131 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 257 insertions(+), 38 deletions(-) diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index 278bc3c268..30b458c19e 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -4541,10 +4541,10 @@ Base 64 usually written by technical experts acting on their own initiative, and are traditionally written in a pragmatic, experience-driven manner. -}2045. This section describes the functions for +}2045 and also in RFC4648. This section describes the functions for converting to and from this code. address@hidden Command base64-encode-region beg end &optional no-line-break address@hidden Command base64-encode-region beg end &optional no-line-break no-pad url-variant This function converts the region from @var{beg} to @var{end} into base 64 code. It returns the length of the encoded text. An error is signaled if a character in the region is multibyte, i.e., in a @@ -4556,9 +4556,15 @@ Base 64 text, to avoid overlong lines. However, if the optional argument @var{no-line-break} is address@hidden, these newlines are not added, so the output is just one long line. + +If optional argument @var{no-pad} is set then padding isn't +generated. + +If optional argument @var{url-variant} is set, then chars @code{+} and address@hidden/} are replaced by @code{-} and @code{_} (See RFC4648). @end deffn address@hidden base64-encode-string string &optional no-line-break address@hidden base64-encode-string string &optional no-line-break no-pad url-variant This function converts the string @var{string} into base 64 code. It returns a string containing the encoded text. As for @code{base64-encode-region}, an error is signaled if a character in the @@ -4568,22 +4574,34 @@ Base 64 text, to avoid overlong lines. However, if the optional argument @var{no-line-break} is address@hidden, these newlines are not added, so the result string is just one long line. + +If optional argument @var{no-pad} is set then padding isn't +generated. + +If optional argument @var{url-variant} is set then chars @code{+} and address@hidden/} are replaced by @code{-} abd @code{_} (See RFC4648). @end defun address@hidden Command base64-decode-region beg end address@hidden Command base64-decode-region beg end &optional url-variant This function converts the region from @var{beg} to @var{end} from base 64 code into the corresponding decoded text. It returns the length of the decoded text. The decoding functions ignore newline characters in the encoded text. + +If optional argument @var{url-variant} is set then padding become +optionnal and url variant is used (See RFC4648). @end deffn address@hidden base64-decode-string string address@hidden base64-decode-string string &optional url-variant This function converts the string @var{string} from base 64 code into the corresponding decoded text. It returns a unibyte string containing the decoded text. The decoding functions ignore newline characters in the encoded text. + +If optional argument @var{url-variant} is set then padding become +optionnal and url variant is used (See RFC4648). @end defun @node Checksum/Hash diff --git a/etc/NEWS b/etc/NEWS index 72702a9aaa..a87c7d140a 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -364,6 +364,13 @@ in tooltips, as it is not useful there. There are 2 new buffer local variables and 1 face to customize this mode they are described in the manual "(emacs) Display". ++++ +** Functions 'base64-(encode|decode)-(string|region)' now manage url variant (RFC4648) +The functions 'base64-encode-(region|string)' now have optionals +arguments to mange padding and url-variant. +Mirror function 'base64-decode-(region|string)' now have an optional +argument url-variant to manage this RFC. + * Editing Changes in Emacs 27.1 diff --git a/src/fns.c b/src/fns.c index c3202495da..f868f13214 100644 --- a/src/fns.c +++ b/src/fns.c @@ -3171,7 +3171,7 @@ #define MIME_LINE_LENGTH 76 #define IS_ASCII(Character) \ ((Character) < 128) #define IS_BASE64(Character) \ - (IS_ASCII (Character) && base64_char_to_value[Character] >= 0) + (IS_ASCII (Character) && b64_char_to_value[Character] >= 0) #define IS_BASE64_IGNORABLE(Character) \ ((Character) == ' ' || (Character) == '\t' || (Character) == '\n' \ || (Character) == '\f' || (Character) == '\r') @@ -3204,6 +3204,17 @@ #define READ_QUADRUPLET_BYTE(retval) \ '8', '9', '+', '/' /* 60-63 */ }; +static const char base64url_value_to_char[64] = +{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 0- 9 */ + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 10-19 */ + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', /* 20-29 */ + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 30-39 */ + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', /* 40-49 */ + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', /* 50-59 */ + '8', '9', '-', '_' /* 60-63 */ +}; + /* Table of base64 values for first 128 characters. */ static const short base64_char_to_value[128] = { @@ -3222,6 +3233,23 @@ #define READ_QUADRUPLET_BYTE(retval) \ 49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */ }; +static const short base64url_char_to_value[128] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0- 9 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10- 19 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20- 29 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 30- 39 */ + -1, -1, -1, -1, -1, 62, -1, -1, 52, 53, /* 40- 49 */ + 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, /* 50- 59 */ + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, /* 60- 69 */ + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 70- 79 */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, /* 80- 89 */ + 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, /* 90- 99 */ + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, /* 100-109 */ + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, /* 110-119 */ + 49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */ +}; + /* The following diagram shows the logical steps by which three octets get transformed into four base64 characters. @@ -3241,17 +3269,21 @@ #define READ_QUADRUPLET_BYTE(retval) \ base64 characters. */ -static ptrdiff_t base64_encode_1 (const char *, char *, ptrdiff_t, bool, bool); +static ptrdiff_t base64_encode_1 (const char *, char *, ptrdiff_t, bool, bool, + bool, bool); static ptrdiff_t base64_decode_1 (const char *, char *, ptrdiff_t, bool, - ptrdiff_t *); + bool, ptrdiff_t *); DEFUN ("base64-encode-region", Fbase64_encode_region, Sbase64_encode_region, - 2, 3, "r", + 2, 5, "r", doc: /* Base64-encode the region between BEG and END. Return the length of the encoded text. Optional third argument NO-LINE-BREAK means do not break long lines -into shorter lines. */) - (Lisp_Object beg, Lisp_Object end, Lisp_Object no_line_break) +into shorter lines. +Optional fourth argument NO-PAD means do not add padding char =. +Optional fifth argument URL-VARIANT means use Url variant (RFC4648). */) + (Lisp_Object beg, Lisp_Object end, Lisp_Object no_line_break, + Lisp_Object no_pad, Lisp_Object url_variant) { char *encoded; ptrdiff_t allength, length; @@ -3275,6 +3307,7 @@ DEFUN ("base64-encode-region", Fbase64_encode_region, Sbase64_encode_region, encoded = SAFE_ALLOCA (allength); encoded_length = base64_encode_1 ((char *) BYTE_POS_ADDR (ibeg), encoded, length, NILP (no_line_break), + NILP(no_pad), !NILP(url_variant), !NILP (BVAR (current_buffer, enable_multibyte_characters))); if (encoded_length > allength) emacs_abort (); @@ -3306,11 +3339,14 @@ DEFUN ("base64-encode-region", Fbase64_encode_region, Sbase64_encode_region, } DEFUN ("base64-encode-string", Fbase64_encode_string, Sbase64_encode_string, - 1, 2, 0, + 1, 4, 0, doc: /* Base64-encode STRING and return the result. Optional second argument NO-LINE-BREAK means do not break long lines -into shorter lines. */) - (Lisp_Object string, Lisp_Object no_line_break) +into shorter lines. +Optional third argument NO-PAD means do not add padding char =. +Optional fourth argument URL-VARIANT means use Url variant (RFC4648). */) + (Lisp_Object string, Lisp_Object no_line_break, + Lisp_Object no_pad, Lisp_Object url_variant) { ptrdiff_t allength, length, encoded_length; char *encoded; @@ -3331,6 +3367,7 @@ DEFUN ("base64-encode-string", Fbase64_encode_string, Sbase64_encode_string, encoded_length = base64_encode_1 (SSDATA (string), encoded, length, NILP (no_line_break), + NILP(no_pad), !NILP(url_variant), STRING_MULTIBYTE (string)); if (encoded_length > allength) emacs_abort (); @@ -3349,7 +3386,8 @@ DEFUN ("base64-encode-string", Fbase64_encode_string, Sbase64_encode_string, static ptrdiff_t base64_encode_1 (const char *from, char *to, ptrdiff_t length, - bool line_break, bool multibyte) + bool line_break, bool pad, bool url_variant, + bool multibyte) { int counter = 0; ptrdiff_t i = 0; @@ -3357,6 +3395,7 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length, int c; unsigned int value; int bytes; + char const *b64_value_to_char = (url_variant) ? base64url_value_to_char : base64_value_to_char; while (i < length) { @@ -3387,16 +3426,18 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length, /* Process first byte of a triplet. */ - *e++ = base64_value_to_char[0x3f & c >> 2]; + *e++ = b64_value_to_char[0x3f & c >> 2]; value = (0x03 & c) << 4; /* Process second byte of a triplet. */ if (i == length) { - *e++ = base64_value_to_char[value]; - *e++ = '='; - *e++ = '='; + *e++ = b64_value_to_char[value]; + if (pad) { + *e++ = '='; + *e++ = '='; + } break; } @@ -3412,15 +3453,17 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length, else c = from[i++]; - *e++ = base64_value_to_char[value | (0x0f & c >> 4)]; + *e++ = b64_value_to_char[value | (0x0f & c >> 4)]; value = (0x0f & c) << 2; /* Process third byte of a triplet. */ if (i == length) { - *e++ = base64_value_to_char[value]; - *e++ = '='; + *e++ = b64_value_to_char[value]; + if (pad) { + *e++ = '='; + } break; } @@ -3436,8 +3479,8 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length, else c = from[i++]; - *e++ = base64_value_to_char[value | (0x03 & c >> 6)]; - *e++ = base64_value_to_char[0x3f & c]; + *e++ = b64_value_to_char[value | (0x03 & c >> 6)]; + *e++ = b64_value_to_char[0x3f & c]; } return e - to; @@ -3445,11 +3488,13 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length, DEFUN ("base64-decode-region", Fbase64_decode_region, Sbase64_decode_region, - 2, 2, "r", + 2, 3, "r", doc: /* Base64-decode the region between BEG and END. Return the length of the decoded text. -If the region can't be decoded, signal an error and don't modify the buffer. */) - (Lisp_Object beg, Lisp_Object end) +If the region can't be decoded, signal an error and don't modify the buffer. +Optional third argument URL-VARIANT define if base64Url variant will be used +see RFC4648. */) + (Lisp_Object beg, Lisp_Object end, Lisp_Object url_variant) { ptrdiff_t ibeg, iend, length, allength; char *decoded; @@ -3474,7 +3519,7 @@ DEFUN ("base64-decode-region", Fbase64_decode_region, Sbase64_decode_region, move_gap_both (XFIXNAT (beg), ibeg); decoded_length = base64_decode_1 ((char *) BYTE_POS_ADDR (ibeg), - decoded, length, + decoded, length, !NILP(url_variant), multibyte, &inserted_chars); if (decoded_length > allength) emacs_abort (); @@ -3508,9 +3553,11 @@ DEFUN ("base64-decode-region", Fbase64_decode_region, Sbase64_decode_region, } DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string, - 1, 1, 0, - doc: /* Base64-decode STRING and return the result. */) - (Lisp_Object string) + 1, 2, 0, + doc: /* Base64-decode STRING and return the result +Optional argument URL-VARIANT define if base64Url variant will be used +see RFC4648. */) + (Lisp_Object string, Lisp_Object url_variant) { char *decoded; ptrdiff_t length, decoded_length; @@ -3525,7 +3572,7 @@ DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string, /* The decoded result should be unibyte. */ decoded_length = base64_decode_1 (SSDATA (string), decoded, length, - 0, NULL); + !NILP(url_variant), 0, NULL); if (decoded_length > length) emacs_abort (); else if (decoded_length >= 0) @@ -3547,6 +3594,7 @@ DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string, static ptrdiff_t base64_decode_1 (const char *from, char *to, ptrdiff_t length, + bool url_variant, bool multibyte, ptrdiff_t *nchars_return) { ptrdiff_t i = 0; /* Used inside READ_QUADRUPLET_BYTE */ @@ -3554,6 +3602,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, unsigned char c; unsigned long value; ptrdiff_t nchars = 0; + short const *b64_char_to_value = (url_variant) ? base64url_char_to_value : base64_char_to_value; while (1) { @@ -3563,7 +3612,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, if (!IS_BASE64 (c)) return -1; - value = base64_char_to_value[c] << 18; + value = b64_char_to_value[c] << 18; /* Process second byte of a quadruplet. */ @@ -3571,7 +3620,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, if (!IS_BASE64 (c)) return -1; - value |= base64_char_to_value[c] << 12; + value |= b64_char_to_value[c] << 12; c = (unsigned char) (value >> 16); if (multibyte && c >= 128) @@ -3582,7 +3631,14 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, /* Process third byte of a quadruplet. */ - READ_QUADRUPLET_BYTE (-1); + if (!url_variant) + { + READ_QUADRUPLET_BYTE (-1); + } + else + { + READ_QUADRUPLET_BYTE (e-to); + } if (c == '=') { @@ -3595,7 +3651,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, if (!IS_BASE64 (c)) return -1; - value |= base64_char_to_value[c] << 6; + value |= b64_char_to_value[c] << 6; c = (unsigned char) (0xff & value >> 8); if (multibyte && c >= 128) @@ -3606,14 +3662,21 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, /* Process fourth byte of a quadruplet. */ - READ_QUADRUPLET_BYTE (-1); + if (!url_variant) + { + READ_QUADRUPLET_BYTE (-1); + } + else + { + READ_QUADRUPLET_BYTE (e-to); + } if (c == '=') continue; if (!IS_BASE64 (c)) return -1; - value |= base64_char_to_value[c]; + value |= b64_char_to_value[c]; c = (unsigned char) (0xff & value); if (multibyte && c >= 128) diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el index 6ebab4287f..3301129567 100644 --- a/test/src/fns-tests.el +++ b/test/src/fns-tests.el @@ -233,6 +233,137 @@ fns-tests-func-arity (should (equal (func-arity (eval (lambda (x &optional y)) t)) '(1 . 2))) (should (equal (func-arity 'let) '(1 . unevalled)))) +(defun string-repeat (s o) + (apply 'concat (make-list o s))) + +(ert-deftest fns-tests-base64-encode-string () + ;; standard variant RFC2045 + (should (equal (base64-encode-string "") "")) + (should (equal (base64-encode-string "f") "Zg==")) + (should (equal (base64-encode-string "fo") "Zm8=")) + (should (equal (base64-encode-string "foo") "Zm9v")) + (should (equal (base64-encode-string "foob") "Zm9vYg==")) + (should (equal (base64-encode-string "fooba") "Zm9vYmE=")) + (should (equal (base64-encode-string "foobar") "Zm9vYmFy")) + (should (equal (base64-encode-string "\x14\xfb\x9c\x03\xd9\x7e") "FPucA9l+")) + (should (equal (base64-encode-string "\x14\xfb\x9c\x03\xd9\x7f") "FPucA9l/")) + + ;; no line break + (should (equal (base64-encode-string "") "")) + (should (equal (base64-encode-string (string-repeat "f" 100) t) (concat (string-repeat "Zm" 66) "Zg=="))) + (should (equal (base64-encode-string (string-repeat "fo" 50) t) (concat (string-repeat "Zm9mb2Zv" 16) "Zm9mbw=="))) + (should (equal (base64-encode-string (string-repeat "foo" 25) t) (string-repeat "Zm9v" 25))) + (should (equal (base64-encode-string (string-repeat "foob" 15) t) (string-repeat "Zm9vYmZvb2Jmb29i" 5))) + (should (equal (base64-encode-string (string-repeat "fooba" 15) t) (string-repeat "Zm9vYmFmb29iYWZvb2Jh" 5))) + (should (equal (base64-encode-string (string-repeat "foobar" 15) t) (concat (string-repeat "Zm9vYmFyZm9vYmFy" 7) "Zm9vYmFy"))) + (should (equal (base64-encode-string (string-repeat "\x14\xfb\x9c\x03\xd9\x7e" 10) t) (string-repeat "FPucA9l+" 10))) + (should (equal (base64-encode-string (string-repeat "\x14\xfb\x9c\x03\xd9\x7f" 10) t) (string-repeat "FPucA9l/" 10))) + + ;; no paddign + (should (equal (base64-encode-string "") "")) + (should (equal (base64-encode-string "f" nil t) "Zg")) + (should (equal (base64-encode-string "fo" nil t) "Zm8")) + (should (equal (base64-encode-string "foo" nil t) "Zm9v")) + (should (equal (base64-encode-string "foob" nil t) "Zm9vYg")) + (should (equal (base64-encode-string "fooba" nil t) "Zm9vYmE")) + (should (equal (base64-encode-string "foobar" nil t) "Zm9vYmFy")) + + ;; url variant wih padding + (should (equal (base64-encode-string "" nil nil t) "")) + (should (equal (base64-encode-string "f" nil nil t) "Zg==")) + (should (equal (base64-encode-string "fo" nil nil t) "Zm8=")) + (should (equal (base64-encode-string "foo" nil nil t) "Zm9v")) + (should (equal (base64-encode-string "foob" nil nil t) "Zm9vYg==")) + (should (equal (base64-encode-string "fooba" nil nil t) "Zm9vYmE=")) + (should (equal (base64-encode-string "foobar" nil nil t) "Zm9vYmFy")) + (should (equal (base64-encode-string "\x14\xfb\x9c\x03\xd9\x7e" nil nil t) "FPucA9l-")) + (should (equal (base64-encode-string "\x14\xfb\x9c\x03\xd9\x7f" nil nil t) "FPucA9l_")) + + ;; url variant no padding + (should (equal (base64-encode-string "" nil t t) "")) + (should (equal (base64-encode-string "f" nil t t) "Zg")) + (should (equal (base64-encode-string "fo" nil t t) "Zm8")) + (should (equal (base64-encode-string "foo" nil t t) "Zm9v")) + (should (equal (base64-encode-string "foob" nil t t) "Zm9vYg")) + (should (equal (base64-encode-string "fooba" nil t t) "Zm9vYmE")) + (should (equal (base64-encode-string "foobar" nil t t) "Zm9vYmFy")) + (should (equal (base64-encode-string "\x14\xfb\x9c\x03\xd9\x7e" nil t t) "FPucA9l-")) + (should (equal (base64-encode-string "\x14\xfb\x9c\x03\xd9\x7f" nil t t) "FPucA9l_")) + + + ;; url variant no line break no padding + (should (equal (base64-encode-string (string-repeat "f" 100) t t t) (concat (string-repeat "Zm" 66) "Zg"))) + (should (equal (base64-encode-string (string-repeat "fo" 50) t t t) (concat (string-repeat "Zm9mb2Zv" 16) "Zm9mbw"))) + (should (equal (base64-encode-string (string-repeat "foo" 25) t t t) (string-repeat "Zm9v" 25))) + (should (equal (base64-encode-string (string-repeat "foob" 15) t t t) (string-repeat "Zm9vYmZvb2Jmb29i" 5))) + (should (equal (base64-encode-string (string-repeat "fooba" 15) t t t) (string-repeat "Zm9vYmFmb29iYWZvb2Jh" 5))) + (should (equal (base64-encode-string (string-repeat "foobar" 15) t t t) (concat (string-repeat "Zm9vYmFyZm9vYmFy" 7) "Zm9vYmFy"))) + (should (equal (base64-encode-string (string-repeat "\x14\xfb\x9c\x03\xd9\x7e" 10) t t t) (string-repeat "FPucA9l-" 10))) + (should (equal (base64-encode-string (string-repeat "\x14\xfb\x9c\x03\xd9\x7f" 10) t t t) (string-repeat "FPucA9l_" 10))) + + ) + +(ert-deftest fns-tests-base64-decode-string () + ;; standard variant RFC2045 + (should (equal (base64-decode-string "") "")) + (should (equal (base64-decode-string "Zg==") "f")) + (should (equal (base64-decode-string "Zm8=") "fo")) + (should (equal (base64-decode-string "Zm9v") "foo")) + (should (equal (base64-decode-string "Zm9vYg==") "foob")) + (should (equal (base64-decode-string "Zm9vYmE=") "fooba")) + (should (equal (base64-decode-string "Zm9vYmFy") "foobar")) + (should (equal (base64-decode-string "FPucA9l+") "\x14\xfb\x9c\x03\xd9\x7e")) + (should (equal (base64-decode-string "FPucA9l/") "\x14\xfb\x9c\x03\xd9\x7f")) + + ;; no paddign + (should (equal (base64-decode-string "" t) "")) + (should (equal (base64-decode-string "Zg" t) "f")) + (should (equal (base64-decode-string "Zm8" t) "fo")) + (should (equal (base64-decode-string "Zm9v" t) "foo")) + (should (equal (base64-decode-string "Zm9vYg" t) "foob")) + (should (equal (base64-decode-string "Zm9vYmE" t) "fooba")) + (should (equal (base64-decode-string "Zm9vYmFy" t) "foobar")) + + ;; url variant wih padding + (should (equal (base64-decode-string "") "")) + (should (equal (base64-decode-string "Zg==" t) "f") ) + (should (equal (base64-decode-string "Zm8=" t) "fo")) + (should (equal (base64-decode-string "Zm9v" t) "foo")) + (should (equal (base64-decode-string "Zm9vYg==" t) "foob")) + (should (equal (base64-decode-string "Zm9vYmE=" t) "fooba")) + (should (equal (base64-decode-string "Zm9vYmFy" t) "foobar")) + (should (equal (base64-decode-string "FPucA9l-" t) "\x14\xfb\x9c\x03\xd9\x7e")) + (should (equal (base64-decode-string "FPucA9l_" t) "\x14\xfb\x9c\x03\xd9\x7f")) + + ;; url variant no padding + (should (equal (base64-decode-string "") "")) + (should (equal (base64-decode-string "Zg" t) "f")) + (should (equal (base64-decode-string "Zm8" t) "fo")) + (should (equal (base64-decode-string "Zm9v" t) "foo")) + (should (equal (base64-decode-string "Zm9vYg" t) "foob")) + (should (equal (base64-decode-string "Zm9vYmE" t) "fooba")) + (should (equal (base64-decode-string "Zm9vYmFy" t) "foobar")) + (should (equal (base64-decode-string "FPucA9l-" t) "\x14\xfb\x9c\x03\xd9\x7e")) + (should (equal (base64-decode-string "FPucA9l_" t) "\x14\xfb\x9c\x03\xd9\x7f")) + + + ;; url variant no line break no padding + (should (equal (base64-decode-string (concat (string-repeat "Zm" 66) "Zg") t) (string-repeat "f" 100))) + (should (equal (base64-decode-string (concat (string-repeat "Zm9mb2Zv" 16) "Zm9mbw") t) (string-repeat "fo" 50))) + (should (equal (base64-decode-string (string-repeat "Zm9v" 25) t) (string-repeat "foo" 25))) + (should (equal (base64-decode-string (string-repeat "Zm9vYmZvb2Jmb29i" 5) t) (string-repeat "foob" 15))) + (should (equal (base64-decode-string (string-repeat "Zm9vYmFmb29iYWZvb2Jh" 5) t) (string-repeat "fooba" 15))) + (should (equal (base64-decode-string (concat (string-repeat "Zm9vYmFyZm9vYmFy" 7) "Zm9vYmFy") t) (string-repeat "foobar" 15))) + (should (equal (base64-decode-string (string-repeat "FPucA9l-" 10) t) (string-repeat "\x14\xfb\x9c\x03\xd9\x7e" 10))) + (should (equal (base64-decode-string (string-repeat "FPucA9l_" 10) t) (string-repeat "\x14\xfb\x9c\x03\xd9\x7f" 10))) + + ;; errors check + (should (eq :got-error (condition-case () (base64-decode-string "Zg=") (error :got-error)))) + (should (eq :got-error (condition-case () (base64-decode-string "Zm9vYmE") (error :got-error)))) + (should (eq :got-error (condition-case () (base64-decode-string "Zm9vYmFy=") (error :got-error)))) + (should (eq :got-error (condition-case () (base64-decode-string "Zg=Zg=") (error :got-error)))) + ) + (ert-deftest fns-tests-hash-buffer () (should (equal (sha1 "foo") "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")) (should (equal (with-temp-buffer -- 2.21.0