7 #include <glog/logging.h>
11 constexpr char const CHARSET
[]{
12 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"};
15 auto CHARSET_find(unsigned char ch
)
17 return static_cast<unsigned char>(
18 std::find(std::begin(CHARSET
), std::end(CHARSET
), ch
)
19 - std::begin(CHARSET
));
23 std::string
enc(std::string_view text
, std::string::size_type wrap
)
25 unsigned char group_8bit
[3];
26 unsigned char group_6bit
[4];
27 int count_3_chars
= 0;
29 auto const input_size
= text
.length();
30 auto const padding
= ((input_size
% 3) ? (3 - (input_size
% 3)) : 0);
31 auto const code_padded_size
= ((input_size
+ padding
) / 3) * 4;
32 auto const newline_size
= wrap
? ((code_padded_size
) / wrap
) * 2 : 0;
33 auto const total_size
= code_padded_size
+ newline_size
;
36 enc_text
.reserve(total_size
);
37 std::string::size_type line_len
= 0;
39 for (std::string::size_type ch
= 0; ch
< text
.length(); ch
++) {
40 group_8bit
[count_3_chars
++] = text
[ch
];
41 if (count_3_chars
== 3) {
42 group_6bit
[0] = (group_8bit
[0] & 0xfc) >> 2;
44 = ((group_8bit
[0] & 0x03) << 4) + ((group_8bit
[1] & 0xf0) >> 4);
46 = ((group_8bit
[1] & 0x0f) << 2) + ((group_8bit
[2] & 0xc0) >> 6);
47 group_6bit
[3] = group_8bit
[2] & 0x3f;
49 for (int i
= 0; i
< 4; i
++)
50 enc_text
+= CHARSET
[group_6bit
[i
]];
55 if (wrap
&& (line_len
== wrap
)) {
61 // encode remaining characters if any
63 if (count_3_chars
> 0) {
64 for (int i
= count_3_chars
; i
< 3; i
++)
67 group_6bit
[0] = (group_8bit
[0] & 0xfc) >> 2;
69 = ((group_8bit
[0] & 0x03) << 4) + ((group_8bit
[1] & 0xf0) >> 4);
71 = ((group_8bit
[1] & 0x0f) << 2) + ((group_8bit
[2] & 0xc0) >> 6);
72 group_6bit
[3] = group_8bit
[2] & 0x3f;
74 for (int i
= 0; i
< count_3_chars
+ 1; i
++) {
75 if (wrap
&& (line_len
== wrap
)) {
79 enc_text
+= CHARSET
[group_6bit
[i
]];
83 while (count_3_chars
++ < 3) {
84 if (wrap
&& (line_len
== wrap
)) {
93 CHECK_EQ(enc_text
.length(), total_size
);
98 bool is_base64char(char ch
)
100 return std::isalnum(ch
) || ch
== '+' || ch
== '/';
103 std::string
dec(std::string_view text
)
105 auto const input_size
= text
.length();
106 auto const max_size
= (input_size
/ 4) * 3;
108 std::string dec_text
;
109 dec_text
.reserve(max_size
);
110 unsigned char group_6bit
[4];
111 unsigned char group_8bit
[3];
112 int count_4_chars
= 0;
114 for (std::string::size_type ch
= 0; ch
< text
.length(); ch
++) {
118 if ((text
[ch
] == '\r') || (text
[ch
] == '\n'))
121 if (!is_base64char(text
[ch
]))
122 throw std::invalid_argument("bad character in decode");
124 group_6bit
[count_4_chars
++] = text
[ch
];
125 if (count_4_chars
== 4) {
126 for (int i
= 0; i
< 4; i
++)
127 group_6bit
[i
] = CHARSET_find(group_6bit
[i
]);
129 group_8bit
[0] = (group_6bit
[0] << 2) + ((group_6bit
[1] & 0x30) >> 4);
131 = ((group_6bit
[1] & 0xf) << 4) + ((group_6bit
[2] & 0x3c) >> 2);
132 group_8bit
[2] = ((group_6bit
[2] & 0x3) << 6) + group_6bit
[3];
134 for (int i
= 0; i
< 3; i
++)
135 dec_text
+= group_8bit
[i
];
140 // decode remaining characters if any
142 if (count_4_chars
> 0) {
143 for (int i
= count_4_chars
; i
< 4; i
++)
144 group_6bit
[i
] = '\0';
146 for (int i
= 0; i
< 4; i
++)
147 group_6bit
[i
] = CHARSET_find(group_6bit
[i
]);
149 group_8bit
[0] = (group_6bit
[0] << 2) + ((group_6bit
[1] & 0x30) >> 4);
151 = ((group_6bit
[1] & 0xf) << 4) + ((group_6bit
[2] & 0x3c) >> 2);
152 group_8bit
[2] = ((group_6bit
[2] & 0x3) << 6) + group_6bit
[3];
154 for (int i
= 0; i
< count_4_chars
- 1; i
++)
155 dec_text
+= group_8bit
[i
];
160 } // namespace Base64