Kotucode's Base 项目
本项目提供了 C++ 接口的 Base62 和 Base64 相关的编解码方法。
载入中...
搜索中...
未找到
base.hpp
浏览该文件的文档.
1// Distributed under the MIT License that can be found in the LICENSE file.
2// https://github.com/kotucode/base
3//
4// Author: Keunlas <keunlaz at gmail dot com>
5
6#ifndef KEUNLAS_KOTUCODE_BASE_H_
7#define KEUNLAS_KOTUCODE_BASE_H_
8
9#include <algorithm>
10#include <array>
11#include <cstdint>
12#include <string>
13#include <vector>
14
15namespace kotucode {
16namespace base {
17
18namespace alphabet {
19
23struct base62 {
24 static const std::array<char, 62>& data() noexcept {
25 static constexpr std::array<char, 62> data{
26 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
27 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
28 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
29 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
30 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}};
31 return data;
32 }
33 static const std::array<int8_t, 256>& rdata() noexcept {
34 static constexpr std::array<int8_t, 256> rdata{
35 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
36 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
37 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5,
38 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16,
39 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
40 35, -1, -1, -1, -1, -1, -1, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
41 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1,
42 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
43 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
44 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
45 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
46 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
47 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
48 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
49 -1, -1, -1, -1}};
50 return rdata;
51 }
52};
53
58struct base64 {
59 static const std::array<char, 64>& data() {
60 static constexpr std::array<char, 64> data{
61 {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
62 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
63 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
64 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
65 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}};
66 return data;
67 }
68 static const std::array<int8_t, 256>& rdata() {
69 static constexpr std::array<int8_t, 256> rdata{
70 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
71 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
72 -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57,
73 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
74 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
75 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
76 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1,
77 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
78 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
79 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
80 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
81 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
82 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
83 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
84 -1, -1, -1, -1}};
85 return rdata;
86 }
87 static const std::string& fill() {
88 static const std::string fill{"="};
89 return fill;
90 }
91};
92
98struct base64url {
99 static const std::array<char, 64>& data() {
100 static constexpr std::array<char, 64> data{
101 {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
102 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
103 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
104 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
105 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}};
106 return data;
107 }
108 static const std::array<int8_t, 256>& rdata() {
109 static constexpr std::array<int8_t, 256> rdata{
110 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
111 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
112 -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, 52, 53, 54, 55, 56, 57,
113 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
114 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
115 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
116 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1,
117 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
118 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
119 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
120 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
121 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
122 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
123 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
124 -1, -1, -1, -1}};
125 return rdata;
126 }
127 static const std::string& fill() {
128 static const std::string fill{"%3d"};
129 return fill;
130 }
131};
132
133} // namespace alphabet
134
135namespace base62 {
136
137namespace details {
138
139inline std::string encode(const std::string& binary_data,
140 const std::array<char, 62>& alphabet) {
141 if (binary_data.empty()) return "";
142
143 // 统计输入中的前导零字节(0x00)个数
144 std::size_t zeros = 0;
145 while (zeros < binary_data.size() && binary_data[zeros] == 0) {
146 zeros += 1;
147 }
148
149 // 全零输入直接返回对应数量的 alphabet[0] 字符
150 if (zeros == binary_data.size()) {
151 return std::string(zeros, alphabet[0]);
152 }
153
154 // 将剩余字节复制到大整数向量(big-endian 表示,索引 0 为最高字节)
155 std::vector<uint8_t> bignum;
156 bignum.reserve(binary_data.size() - zeros);
157 for (auto i = zeros; i < binary_data.size(); ++i) {
158 bignum.push_back(static_cast<uint8_t>(binary_data[i]));
159 }
160
161 // 临时存放逆序的编码字符(低位在前)
162 std::string tmp;
163 tmp.reserve(binary_data.size() * 2);
164
165 // 反复执行“大整数 / 62”操作,直到商为 0
166 while (!bignum.empty()) {
167 int carry = 0;
168 // 从高位到低位处理每个字节
169 for (std::size_t i = 0; i < bignum.size(); ++i) {
170 int value = (carry << 8) + bignum[i];
171 bignum[i] = static_cast<uint8_t>(value / 62);
172 carry = value % 62;
173 }
174 // 移除商的前导零,保持规范化
175 std::size_t first_nonzero = 0;
176 while (first_nonzero < bignum.size() && bignum[first_nonzero] == 0) {
177 ++first_nonzero;
178 }
179 if (first_nonzero == bignum.size()) {
180 bignum.clear();
181 } else if (first_nonzero > 0) {
182 bignum.erase(bignum.begin(), bignum.begin() + first_nonzero);
183 }
184 // carry 即为本次除法的余数,对应一个 base 字符
185 tmp.push_back(alphabet[carry]);
186 }
187
188 // 组合最终结果:前导“0”字符 + 反转后的 tmp
189 std::string result;
190 result.reserve(zeros + tmp.size());
191 result.append(zeros, alphabet[0]);
192 result.append(tmp.rbegin(), tmp.rend());
193
194 return result;
195}
196
197inline std::string decode(const std::string& base_string,
198 const std::array<int8_t, 256>& rdata) {
199 if (base_string.empty()) return "";
200
201 // 找到 alphabet[0] 对应的字符(用于前导零处理)
202 char zero_char = 0;
203 bool found_zero = false;
204 for (int i = 0; i < 256; ++i) {
205 if (rdata[i] == 0) {
206 zero_char = static_cast<char>(i);
207 found_zero = true;
208 break;
209 }
210 }
211 if (!found_zero) {
212 zero_char = '0'; // 安全回退(Base62 中 '0' 即为 0 值字符)
213 }
214
215 // 统计输入字符串前导“零字符”的个数
216 std::size_t zeros = 0;
217 while (zeros < base_string.size() && base_string[zeros] == zero_char) {
218 ++zeros;
219 }
220
221 // 初始化大整数(little-endian)
222 std::vector<uint8_t> bignum(1, 0);
223
224 // 遍历剩余字符,进行 bignum = bignum * base + digit
225 for (std::size_t i = zeros; i < base_string.size(); ++i) {
226 auto c = static_cast<uint8_t>(base_string[i]);
227 auto digit = static_cast<int>(rdata[c]);
228 if (digit < 0) {
229 return ""; // 包含非法字符,直接返回空表示失败
230 }
231
232 // --- 乘法:bignum *= base ---
233 int carry = 0;
234 // 从低位(末尾)向高位(开头)处理
235 for (auto&& term : bignum) {
236 int product = static_cast<int>(term) * 62 + carry;
237 term = static_cast<uint8_t>(product % 256);
238 carry = product / 256;
239 }
240 // 处理乘法产生的额外进位
241 while (carry > 0) {
242 bignum.push_back(static_cast<uint8_t>(carry % 256));
243 carry >>= 8;
244 }
245
246 // --- 加法:bignum += digit ---
247 if (digit > 0) {
248 int add_carry = digit;
249 for (auto&& term : bignum) {
250 int sum = static_cast<int>(term) + add_carry;
251 term = static_cast<uint8_t>(sum % 256);
252 add_carry = sum >> 8;
253 }
254 while (add_carry > 0) {
255 bignum.push_back(static_cast<uint8_t>(add_carry % 256));
256 add_carry >>= 8;
257 }
258 }
259 }
260
261 // 移除大整数前导零
262 while (!bignum.empty() && bignum.back() == 0) {
263 bignum.pop_back();
264 }
265
266 // 构造最终二进制结果:zeros 个 0x00 + bignum 字节序列
267 std::string result;
268 result.reserve(zeros + bignum.size());
269 result.append(zeros, '\0');
270 for (auto it = bignum.rbegin(); it != bignum.rend(); ++it) {
271 result.push_back(static_cast<char>(*it));
272 }
273
274 return result;
275}
276
277} // namespace details
278
288template <typename Alphabets = alphabet::base62>
289std::string encode(const std::string& binary_data) {
290 return details::encode(binary_data, Alphabets::data());
291}
292
300template <typename Alphabets = alphabet::base62>
301std::string decode(const std::string& base_string) {
302 return details::decode(base_string, Alphabets::rdata());
303}
304
305} // namespace base62
306
307namespace base64 {
308
309namespace details {
310
311inline std::string encode(const std::string& binary_data,
312 const std::array<char, 64>& alphabet,
313 const std::string& fill) {
314 if (binary_data.empty()) return "";
315
316 std::string result;
317 result.reserve(((binary_data.size() + 2) / 3) * 4);
318
319 auto iter = binary_data.begin();
320 for (; binary_data.end() - iter >= 3; iter += 3) {
321 auto index1 = ((*iter) & 0xfc) >> 2;
322 auto index2 = (((*iter) & 0x03) << 4) + (((*(iter + 1)) & 0xf0) >> 4);
323 auto index3 = (((*(iter + 1)) & 0x0f) << 2) + (((*(iter + 2)) & 0xc0) >> 6);
324 auto index4 = ((*(iter + 2)) & 0x3f);
325 result.push_back(alphabet.at(index1));
326 result.push_back(alphabet.at(index2));
327 result.push_back(alphabet.at(index3));
328 result.push_back(alphabet.at(index4));
329 }
330
331 if (auto remains = binary_data.end() - iter; remains == 1) {
332 auto index1 = ((*iter) & 0xfc) >> 2;
333 auto index2 = (((*iter) & 0x03) << 4);
334 result.push_back(alphabet.at(index1));
335 result.push_back(alphabet.at(index2));
336 result.append(fill);
337 result.append(fill);
338 } else if (remains == 2) {
339 auto index1 = ((*iter) & 0xfc) >> 2;
340 auto index2 = (((*iter) & 0x03) << 4) + (((*(iter + 1)) & 0xf0) >> 4);
341 auto index3 = (((*(iter + 1)) & 0x0f) << 2);
342 result.push_back(alphabet.at(index1));
343 result.push_back(alphabet.at(index2));
344 result.push_back(alphabet.at(index3));
345 result.append(fill);
346 }
347
348 return result;
349}
350
351inline std::string decode(const std::string& base_string,
352 const std::array<int8_t, 256>& rdata,
353 const std::string& fill) {
354 if (base_string.empty()) return "";
355
356 std::string result;
357 result.reserve(((base_string.size() + 3) / 4) * 3);
358
359 auto iter = base_string.begin();
360 for (; base_string.end() - iter > 4; iter += 4) {
361 auto index1 = rdata.at(*iter);
362 auto index2 = rdata.at(*(iter + 1));
363 auto index3 = rdata.at(*(iter + 2));
364 auto index4 = rdata.at(*(iter + 3));
365
366 if (index1 < 0 || index2 < 0 || index3 < 0 || index4 < 0) {
367 return "";
368 }
369
370 auto char1 = ((index1 & 0x3f) << 2) | ((index2 & 0x30) >> 4);
371 auto char2 = ((index2 & 0x0f) << 4) | ((index3 & 0x3c) >> 2);
372 auto char3 = ((index3 & 0x03) << 6) | (index4 & 0x3f);
373
374 result.push_back(static_cast<char>(char1));
375 result.push_back(static_cast<char>(char2));
376 result.push_back(static_cast<char>(char3));
377 }
378
379 if (iter == base_string.end()) {
380 return result;
381 }
382
383 size_t paddings = 0;
384 auto pad_pos = base_string.find(fill);
385 if (pad_pos != std::string_view::npos) {
386 paddings = base_string.size() - pad_pos;
387 }
388
389 size_t remains = 0;
390 if (paddings == 0) {
391 remains = base_string.end() - iter;
392 } else {
393 remains = 4 - paddings;
394 }
395
396 if (remains > 1) {
397 auto char1 =
398 ((rdata.at(*iter) & 0x3f) << 2) | ((rdata.at(*(iter + 1)) & 0x30) >> 4);
399 result.push_back(static_cast<char>(char1));
400 }
401
402 if (remains > 2) {
403 auto char2 = ((rdata.at(*(iter + 1)) & 0x0f) << 4) |
404 ((rdata.at(*(iter + 2)) & 0x3c) >> 2);
405 result.push_back(static_cast<char>(char2));
406 }
407
408 if (remains > 3) {
409 auto char3 =
410 ((rdata.at(*(iter + 2)) & 0x03) << 6) | (rdata.at(*(iter + 3)) & 0x3f);
411 result.push_back(static_cast<char>(char3));
412 }
413
414 return result;
415}
416
417inline std::string pad(const std::string& base_string,
418 const std::string& fill) {
419 std::string padding;
420 for (std::size_t i = 0; i < base_string.size() % 4; ++i) {
421 padding += fill;
422 }
423 return base_string + padding;
424}
425
426inline std::string trim(const std::string& base_string,
427 const std::string& fill) {
428 auto pos = base_string.find(fill);
429 return base_string.substr(0, pos);
430}
431
432} // namespace details
433
441template <typename Alphabets = alphabet::base64>
442std::string encode(const std::string& binary_data) {
443 return details::encode(binary_data, Alphabets::data(), Alphabets::fill());
444}
445
453template <typename Alphabets = alphabet::base64>
454std::string decode(const std::string& base_string) {
455 return details::decode(base_string, Alphabets::rdata(), Alphabets::fill());
456}
457
469template <typename Alphabets = alphabet::base64>
470std::string pad(const std::string& base_string) {
471 return details::pad(base_string, Alphabets::fill());
472}
473
484template <typename Alphabets = alphabet::base64>
485std::string trim(const std::string& base_string) {
486 return details::trim(base_string, Alphabets::fill());
487}
488
489} // namespace base64
490
491} // namespace base
492} // namespace kotucode
493
494#endif // !KEUNLAS_KOTUCODE_BASE_H_
定义 base.hpp:18
定义 base.hpp:135
std::string decode(const std::string &base_string)
将 Base62 编码字符串解码为原始二进制数据。
定义 base.hpp:301
std::string encode(const std::string &binary_data)
将二进制数据编码为 Base62 字符串。
定义 base.hpp:289
定义 base.hpp:307
std::string encode(const std::string &binary_data)
将二进制数据编码为 Base64 字符串。
定义 base.hpp:442
std::string pad(const std::string &base_string)
给修剪过的 Base64 编码字符串重新添加上填充。
定义 base.hpp:470
std::string trim(const std::string &base_string)
修剪 Base64 编码字符串,去掉末尾的填充。
定义 base.hpp:485
std::string decode(const std::string &base_string)
将 Base64 编码字符串解码为原始二进制数据。
定义 base.hpp:454
定义 base.hpp:16
定义 base.hpp:15
Base62 编码字符集,提供 0-9、A-Z、a-z 共 62 个字符的正向与反向映射表。
定义 base.hpp:23
static const std::array< char, 62 > & data() noexcept
定义 base.hpp:24
static const std::array< int8_t, 256 > & rdata() noexcept
定义 base.hpp:33
Base64 编码字符集, 提供合法的 Base64 字符的正向与反向映射表及其填充字符串。
定义 base.hpp:58
static const std::string & fill()
定义 base.hpp:87
static const std::array< char, 64 > & data()
定义 base.hpp:59
static const std::array< int8_t, 256 > & rdata()
定义 base.hpp:68
Base64 编码字符集, url-safe 并且 filename-safe, 提供合法的 Base64 字符的正向与反向映射表及其填充字符串。
定义 base.hpp:98
static const std::array< int8_t, 256 > & rdata()
定义 base.hpp:108
static const std::string & fill()
定义 base.hpp:127
static const std::array< char, 64 > & data()
定义 base.hpp:99