42 #define BASE_VERSION "1.0.2" 51 template<
unsigned N>
struct base {
52 static std::string encode(
const std::string &binary );
53 static std::string decode(
const std::string &text );
54 static bool encode( std::string &out,
const std::string &binary );
55 static bool decode( std::string &out,
const std::string &text );
58 typedef base<64> base64;
59 typedef base<85> base85;
60 typedef base<91> base91;
65 bool base<91>::encode( std::string &out,
const std::string &binary ) {
67 const unsigned char enctab[91] = {
77 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
78 'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
79 'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
80 'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
81 '0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'!',
'#',
'$',
82 '%',
'&',
'(',
')',
'*',
'+',
',',
'.',
'/',
':',
';',
'-',
'=',
83 '\\',
'?',
'@',
'[',
']',
'^',
'_',
'`',
'{',
'|',
'}',
'~',
'\'' 87 const unsigned char *ib = (
unsigned char *) binary.c_str();
89 unsigned long queue = 0;
90 unsigned int nbits = 0;
92 for(
size_t len = binary.size(); len--; ) {
93 queue |= *ib++ << nbits;
96 unsigned int val = queue & 8191;
106 out.push_back( enctab[val % 91] );
107 out.push_back( enctab[val / 91] );
113 out.push_back( enctab[queue % 91] );
114 if (nbits > 7 || queue > 90)
115 out.push_back( enctab[queue / 91] );
123 bool base<91>::decode( std::string &out,
const std::string &text ) {
125 const unsigned char dectab[256] = {
144 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
145 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
146 91, 62, 91, 63, 64, 65, 66, 90, 67, 68, 69, 70, 71, 76, 72, 73,
147 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 74, 75, 91, 77, 91, 79,
148 80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
149 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 81, 78, 82, 83, 84,
150 85, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
151 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 86, 87, 88, 89, 91,
152 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
153 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
154 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
155 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
156 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
157 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
158 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
159 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91
163 const unsigned char *ib = (
unsigned char *) text.c_str();
165 unsigned long queue = 0;
166 unsigned int nbits = 0;
169 for(
size_t len = text.size(); len--; ) {
170 unsigned int d = dectab[*ib++];
177 queue |= val << nbits;
178 nbits += (val & 8191) > 88 ? 13 : 14;
180 out.push_back(
char( queue ) );
190 out.push_back(
char( queue | val << nbits ) );
197 inline bool encode85( std::string &out,
const unsigned char *raw,
size_t rawlen ) {
202 const char encoder[86] =
203 "0123456789" "abcdefghij" "klmnopqrst" "uvwxyzABCD" 204 "EFGHIJKLMN" "OPQRSTUVWX" "YZ.-:+=^!/" "*?&<>()[]{" "}@%$#";
205 out.resize( rawlen * 5 / 4 );
206 for(
size_t o = 0; o < rawlen * 5 / 4; raw += 4 ) {
207 unsigned value = (raw[0] << 24) | (raw[1] << 16) | (raw[2] << 8) | raw[3];
208 out[o++] = encoder[ (value / 0x31C84B1) % 0x55 ];
209 out[o++] = encoder[ (value / 0x95EED) % 0x55 ];
210 out[o++] = encoder[ (value / 0x1C39) % 0x55 ];
211 out[o++] = encoder[ (value / 0x55) % 0x55 ];
212 out[o++] = encoder[ value % 0x55 ];
217 inline bool decode85( std::string &out,
const unsigned char *z85,
size_t z85len ) {
222 const unsigned char decoder[128] = {
223 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
224 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
225 0, 68, 0, 84, 83, 82, 72, 0, 75, 76, 70, 65, 0, 63, 62, 69,
226 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 64, 0, 73, 66, 74, 71,
227 81, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
228 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 0, 78, 67, 0,
229 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
230 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 79, 0, 80, 0, 0,
232 out.resize( z85len * 4 / 5 );
233 for(
size_t o = 0; o < z85len * 4 / 5; z85 += 5 ) {
234 unsigned value = decoder[z85[0]] * 0x31C84B1 + decoder[z85[1]] * 0x95EED +
235 decoder[z85[2]] * 0x1C39 + decoder[z85[3]] * 0x55 + decoder[z85[4]];
236 out[o++] = (value >> 24) & 0xFF;
237 out[o++] = (value >> 16) & 0xFF;
238 out[o++] = (value >> 8) & 0xFF;
239 out[o++] = (value >> 0) & 0xFF;
247 bool base<85>::encode( std::string &out,
const std::string &rawstr ) {
249 std::string pad4 = std::string( (
const char *)&rawstr[0], rawstr.size() ) +
'\1' + std::string(
"\0\0\0\0", 4 - (rawstr.size() + 1) % 4);
250 return encode85( out, (
const unsigned char *)pad4.c_str(), pad4.size() );
254 bool base<85>::decode( std::string &out,
const std::string &z85str ) {
255 if( !decode85( out, (
const unsigned char *)&z85str[0], z85str.size() ) ) {
259 while( out.size() && *out.rbegin() ==
'\0' ) out.resize( out.size() - 1 );
260 if( out.size() && *out.rbegin() ==
'\1' ) out.resize( out.size() - 1 );
268 bool base<64>::encode( std::string &out,
const std::string &text ) {
270 const std::string chars =
271 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 272 "abcdefghijklmnopqrstuvwxyz" 275 unsigned char const* bytes_to_encode = (
unsigned char const *)text.c_str();
276 unsigned int in_len = (
unsigned int)text.size();
279 unsigned char char_array_3[3];
280 unsigned char char_array_4[4];
284 char_array_3[i++] = *(bytes_to_encode++);
286 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
287 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
288 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
289 char_array_4[3] = char_array_3[2] & 0x3f;
291 for( i = 0; (i <4) ; i++ )
292 out += chars[char_array_4[i]];
298 for( j = i; j < 3; j++ )
299 char_array_3[j] =
'\0';
301 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
302 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
303 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
304 char_array_4[3] = char_array_3[2] & 0x3f;
306 for( j = 0; (j < i + 1); j++ )
307 out += chars[char_array_4[j]];
317 bool base<64>::decode( std::string &out,
const std::string &encoded ) {
319 const std::string chars =
320 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 321 "abcdefghijklmnopqrstuvwxyz" 324 unsigned int in_len = (
unsigned int)encoded.size();
327 unsigned int in_ = 0;
328 unsigned char char_array_4[4], char_array_3[3];
331 while (in_len-- && ( encoded[in_] !=
'=') &&
332 (isalnum(encoded[in_]) || encoded[in_] ==
'+' || encoded[in_] ==
'/')) {
333 char_array_4[i++] = encoded[in_]; in_++;
335 for (i = 0; i <4; i++)
336 char_array_4[i] = chars.find(char_array_4[i]);
338 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
339 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
340 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
342 for (i = 0; (i < 3); i++)
343 out += char_array_3[i];
349 for (j = i; j <4; j++)
352 for (j = 0; j <4; j++)
353 char_array_4[j] = chars.find(char_array_4[j]);
355 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
356 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
357 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
359 for (j = 0; (j < i - 1); j++) out += char_array_3[j];
367 template<> std::string base<64>::encode(
const std::string &binary ) {
369 return base<64>::encode( out, binary ) ? out : std::string();
371 template<> std::string base<64>::decode(
const std::string &text ) {
373 return base<64>::decode( out, text ) ? out : std::string();
376 template<> std::string base<85>::encode(
const std::string &binary ) {
378 return base<85>::encode( out, binary ) ? out : std::string();
380 template<> std::string base<85>::decode(
const std::string &text ) {
382 return base<85>::decode( out, text ) ? out : std::string();
385 template<> std::string base<91>::encode(
const std::string &binary ) {
387 return base<91>::encode( out, binary ) ? out : std::string();
389 template<> std::string base<91>::decode(
const std::string &text ) {
391 return base<91>::decode( out, text ) ? out : std::string();
396 #ifdef BASE_BUILD_TESTS 402 #define suite(...) if(printf("------ " __VA_ARGS__),puts(""),true) 403 #define test(...) (errno=0,++tst,err+=!(ok=!!(__VA_ARGS__))),printf("[%s] %d %s (%s)\n",ok?" OK ":"FAIL",__LINE__,#__VA_ARGS__,strerror(errno)) 404 unsigned tst=0,err=0,ok=atexit([]{ suite(
"summary"){ printf(
"[%s] %d tests = %d passed + %d errors\n",err?
"FAIL":
" OK ",tst,tst-err,err); }});
410 void unittest(
const std::string &binary ) {
411 bool allows_preview = [&binary]{
for(
const unsigned char &ch : binary )
if( ch > 127 )
return 0;
return 1; }();
412 size_t base64len = 0, base85len = 0, base91len = 0, binarylen = binary.size();
415 std::string enc64 = base64::encode(binary);
416 std::string dec64 = base64::decode(enc64);
417 base64len = enc64.size();
420 test( dec64 == binary );
424 std::string enc85 = base85::encode(binary);
425 std::string dec85 = base85::decode(enc85);
426 base85len = enc85.size();
429 test( dec85 == binary );
432 test( enc85.find_first_of(
'"') == std::string::npos );
433 test( enc85.find_first_of(
'\\') == std::string::npos );
437 std::string enc91 = base91::encode(binary);
438 std::string dec91 = base91::decode(enc91);
439 base91len = enc91.size();
442 test( dec91 == binary );
445 test( enc91.find_first_of(
'<') == std::string::npos );
446 test( enc91.find_first_of(
'>') == std::string::npos );
447 test( enc91.find_first_of(
'"') == std::string::npos );
450 std::string white91 =
" \r\n\f\t\v\n" + enc91 +
" \r\n\f\t\v\n";
451 test( binary == base91::decode(white91));
454 std::string break91 = enc91;
455 break91.insert( break91.size() / 2,
" \r\n\f\t\v\n" );
456 test( binary == base91::decode(break91));
459 std::string split91 = enc91.substr(0, enc91.size() / 2) +
"\r\n\r\n\t\t " + enc91.substr(enc91.size() / 2);
460 test( binary == base91::decode(split91) );
464 test( binarylen <= base91len );
465 test( base91len <= base85len );
466 test( base85len - 4 <= base64len );
468 auto overhead = [&binarylen](
size_t encoding_size ) ->
size_t {
return ((encoding_size*100/binarylen)-100); };
469 std::cout <<
"\nresults: " << ( allows_preview ? std::string() +
'\"' + binary +
'\"' :
"(hidden text)" ) <<
'\n';
470 std::cout <<
"\tbinary: " << overhead(binarylen) <<
"% overhead (total: " << binarylen <<
" bytes)\n";
471 std::cout <<
"\tbase64: " << overhead(base64len) <<
"% overhead (total: " << base64len <<
" bytes)\n";
472 std::cout <<
"\tbase85: " << overhead(base85len) <<
"% overhead (total: " << base85len <<
" bytes)\n";
473 std::cout <<
"\tbase91: " << overhead(base91len) <<
"% overhead (total: " << base91len <<
" bytes)\n\n";
476 void unittest_inplace(
const std::string &binary ) {
478 std::string enc64, dec64;
479 test( base64::encode(enc64, binary) );
480 test( base64::decode(dec64, enc64) );
481 test( dec64 == binary );
484 std::string enc85, dec85;
485 test( base85::encode(enc85, binary) );
486 test( base85::decode(dec85, enc85) );
487 test( dec85 == binary );
490 std::string enc91, dec91;
491 test( base91::encode(enc91, binary) );
492 test( base91::decode(dec91, enc91) );
493 test( dec91 == binary );
499 std::string encoded_64 = base64::encode(
"Hello world from BASE64! \x1");
500 std::string decoded_64 = base64::decode(encoded_64);
501 std::cout<< decoded_64 <<
" <-> " << encoded_64 << std::endl;
503 std::string encoded_85 = base85::encode(
"Hello world from BASE85! \x1");
504 std::string decoded_85 = base85::decode(encoded_85);
505 std::cout<< decoded_85 <<
" <-> " << encoded_85 << std::endl;
507 std::string encoded_91 = base91::encode(
"Hello world from BASE91! \x1");
508 std::string decoded_91 = base91::decode(encoded_91);
509 std::cout<< decoded_91 <<
" <-> " << encoded_91 << std::endl << std::endl;
512 unittest(
"Man is distinguished, not only by his reason, but by this singular passion from\n" 513 "other animals, which is a lust of the mind, that by a perseverance of delight in the continued\n" 514 "and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure." );
517 unittest(
"hello world \x1\x2");
520 unittest( std::string(
"abc\x1\x2\0", 6 ) );
524 charset.reserve( 256 * 256 * 256 );
525 for(
int i = 256 ; --i >= 0; ) {
526 for(
int j = 256 ; --j >= 0; ) {
527 for(
int k = 256 ; --k >= 0; ) {
528 charset += char(i) + char(j) + char(k);
532 unittest_inplace( charset );
534 std::cout <<
"All ok." << std::endl;
539 #ifdef BASE_BUILD_DEMO 544 int main(
int argc,
const char **argv ) {
546 std::ifstream ifs( argv[3], std::ios::binary );
547 std::stringstream ss;
549 bool enc = argv[1][0] ==
'e', dec = argv[1][0] ==
'd';
550 bool b64 = argv[2][0] ==
'6', b85 = argv[2][0] ==
'8', b91 = argv[2][0] ==
'9';
551 if( b91 && enc )
return std::cout << base91::encode( ss.str() ), (ifs.good() ? 0 : 1);
552 else if( b91 && dec )
return std::cout << base91::decode( ss.str() ), (ifs.good() ? 0 : 1);
553 else if( b85 && enc )
return std::cout << base85::encode( ss.str() ), (ifs.good() ? 0 : 1);
554 else if( b85 && dec )
return std::cout << base85::decode( ss.str() ), (ifs.good() ? 0 : 1);
555 else if( b64 && enc )
return std::cout << base64::encode( ss.str() ), (ifs.good() ? 0 : 1);
556 else if( b64 && dec )
return std::cout << base64::decode( ss.str() ), (ifs.good() ? 0 : 1);
558 std::cout << argv[0] <<
" [e|d] [64|85|91] file" << std::endl;
A collection of functions for debugging.