Есть ли в C++ свободно доступный фрагмент кода декодирования Base64?
Декодирование Base64 с использованием библиотеки Boost C++: stackoverflow.com/questions/34680998/…
В Windows для этого можно использовать atlenc.h или wincrypt.h.





См. Кодирование и декодирование base 64 с помощью C++.
Вот реализация с этой страницы:
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger [email protected]
*/
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
нам следует избегать всех этих ненужных конкатенаций строк - поскольку мы знаем in_len, мы знаем длину ret, почему бы не задать ему фиксированную длину при инициализации?
В функции декодирования после if (i) первый цикл for не требуется. Если байты в char_array_4 заполнены 0, find () возвращает -1, но он вообще не используется. Следовательно, второй цикл for может быть: for (j = 0; j < i; j++). Также третья строчка с char_array_3[2] совершенно бесполезна и может быть отброшена. Почему? Поскольку этот второй блок обрабатывает только остальное (когда i не равно нулю), и это может быть только один или два байта исходного текста (если их было три, то не было бы остатка, потому что три байта можно плавно закодировать в 4 символа.)
Google не всегда ваш друг. Эта реализация - едва ли не худшая из возможных. Смотрите это: stackoverflow.com/questions/342409/…
ЭТОТ КОД НЕВЕРОЯТНЫЙ. Неправильно хранить декодированное значение в строке! Представьте, что у вас есть кодируемый массив байтов, какое-то значение будет '\ 0', часть кодирования абсолютно в порядке. Но когда вы перевернете его обратно, значение будет усечено до '\ 0'. См. Ответ @LihO.
Вот моя модификация реализация, изначально написанная Рене Ниффенеггер. И почему я его модифицировал? Ну, потому что мне казалось неподходящим, что я должен работать с двоичными данными, хранящимися в объекте std::string;)
base64.h:
#ifndef _BASE64_H_
#define _BASE64_H_
#include <vector>
#include <string>
typedef unsigned char BYTE;
std::string base64_encode(BYTE const* buf, unsigned int bufLen);
std::vector<BYTE> base64_decode(std::string const&);
#endif
base64.cpp:
#include "base64.h"
#include <iostream>
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(BYTE c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(BYTE const* buf, unsigned int bufLen) {
std::string ret;
int i = 0;
int j = 0;
BYTE char_array_3[3];
BYTE char_array_4[4];
while (bufLen--) {
char_array_3[i++] = *(buf++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::vector<BYTE> base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
BYTE char_array_4[4], char_array_3[3];
std::vector<BYTE> ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret.push_back(char_array_3[i]);
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
}
return ret;
}
Вот использование:
std::vector<BYTE> myData;
...
std::string encodedData = base64_encode(&myData[0], myData.size());
std::vector<BYTE> decodedData = base64_decode(encodedData);
Спасибо. Есть ли ограничения на использование вашего кода? (Моим личным кейсом будет академический CFD-код).
Я попытался декодировать файл jpg с помощью вашего метода, записав весь вектор в CFile с указанием его размера, но не было большим сюрпризом, что заголовок изображения был поврежден. Хотя размер такой же. Есть лучшие идеи, как восстановить файлы изображений?
@masche: речь идет о кодировании и декодировании данных любого типа на уровне байтов. Изображение -> необработанные данные (байты) -> кодировать в строку base64, затем вернуться в строку base64 -> декодировать в необработанные данные (байты) -> создать некоторый входной поток или объект или что-то еще поверх него, чтобы работать с ним как с изображением очередной раз...
Извините - ваш код работает отлично, и даже изображение можно восстановить. С моей стороны было глупо просто втиснуть весь вектор в CFile, это не сработает! Если я повторяю вектор и записываю каждый байт в файл, он работает. Возможно, файловый поток - лучшее решение.
Измените значение на template <typename Vec> Vec base64_decode(str const&) { Vec ret;, и при необходимости вы сможете использовать строку как (векторный) тип.
@LihO в Visual Studio 2015 сгенерировано следующее предупреждение: предупреждение C4267: '=': преобразование из size_t в BYTE, возможная потеря данных. Char_array_4 [j] = static_cast <BYTE> (base64_chars.find (char_array_4 [j])); в порядке или требуется что-то еще
Что не так с хранением двоичных данных в std :: string?
@GaspardP Обычно по байтам вам нужны вкладки, если вы динамически используете std::vector<char>, это непрерывная память. Более того, std::string имеет метод c_str(), который в данном случае недействителен. Можно using bytes_t = std::vector<char>, что намного понятнее. Но если вы храните двоичные данные внутри std :: string, это будет работать.
@GaspardP, @ S.R Я бы также возражал против использования символов (которые могут быть подписаны) для представления байтов. Например, результат std::string("\xff")[0] == 0xFF может удивить вас или пользователя вашего кода.
Спасибо, попробую использовать с MQTT, вроде достаточно шифрования для заказов в ресторане? Или хакеры могут легко распознать это и преобразовать данные в ...
Используя мини-библиотеку база-н, вы можете делать следующее:
some_data_t in[] { ... };
constexpr int len = sizeof(in)/sizeof(in[0]);
std::string encoded;
bn::encode_b64(in, in + len, std::back_inserter(encoded));
some_data_t out[len];
bn::decode_b64(encoded.begin(), encoded.end(), out);
API является универсальным, основанным на итераторах.
Раскрытие: я автор.
std::size будет лучше, чем sizeof hax
В самом деле, начиная с C++ 17, это явно самое подходящее время для этого.
Это был правильный способ сделать это с самого начала. Просто до C++ 17 вам приходилось реализовывать его самостоятельно (но это практически однострочник). Хак sizeof - это никогда в C++.
Согласно это отличное сравнение от GaspardP я бы не выбрал это решение. Это не самое худшее, но и не самое лучшее. Единственное, что у него получилось, это то, что его, возможно, легче понять.
Мне показалось, что два других ответа довольно сложно понять. Они также выдают некоторые предупреждения в моем компиляторе, и использование функции поиска в части декодирования должно привести к довольно низкой эффективности. Вот и решил накатить свой.
Заголовок:
#ifndef _BASE64_H_
#define _BASE64_H_
#include <vector>
#include <string>
typedef unsigned char BYTE;
class Base64
{
public:
static std::string encode(const std::vector<BYTE>& buf);
static std::string encode(const BYTE* buf, unsigned int bufLen);
static std::vector<BYTE> decode(std::string encoded_string);
};
#endif
Тело:
static const BYTE from_base64[] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 62, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 63,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255};
static const char to_base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string Base64::encode(const std::vector<BYTE>& buf)
{
if (buf.empty())
return ""; // Avoid dereferencing buf if it's empty
return encode(&buf[0], (unsigned int)buf.size());
}
std::string Base64::encode(const BYTE* buf, unsigned int bufLen)
{
// Calculate how many bytes that needs to be added to get a multiple of 3
size_t missing = 0;
size_t ret_size = bufLen;
while ((ret_size % 3) != 0)
{
++ret_size;
++missing;
}
// Expand the return string size to a multiple of 4
ret_size = 4*ret_size/3;
std::string ret;
ret.reserve(ret_size);
for (unsigned int i=0; i<ret_size/4; ++i)
{
// Read a group of three bytes (avoid buffer overrun by replacing with 0)
size_t index = i*3;
BYTE b3[3];
b3[0] = (index+0 < bufLen) ? buf[index+0] : 0;
b3[1] = (index+1 < bufLen) ? buf[index+1] : 0;
b3[2] = (index+2 < bufLen) ? buf[index+2] : 0;
// Transform into four base 64 characters
BYTE b4[4];
b4[0] = ((b3[0] & 0xfc) >> 2);
b4[1] = ((b3[0] & 0x03) << 4) + ((b3[1] & 0xf0) >> 4);
b4[2] = ((b3[1] & 0x0f) << 2) + ((b3[2] & 0xc0) >> 6);
b4[3] = ((b3[2] & 0x3f) << 0);
// Add the base 64 characters to the return value
ret.push_back(to_base64[b4[0]]);
ret.push_back(to_base64[b4[1]]);
ret.push_back(to_base64[b4[2]]);
ret.push_back(to_base64[b4[3]]);
}
// Replace data that is invalid (always as many as there are missing bytes)
for (size_t i=0; i<missing; ++i)
ret[ret_size - i - 1] = '=';
return ret;
}
std::vector<BYTE> Base64::decode(std::string encoded_string)
{
// Make sure string length is a multiple of 4
while ((encoded_string.size() % 4) != 0)
encoded_string.push_back('=');
size_t encoded_size = encoded_string.size();
std::vector<BYTE> ret;
ret.reserve(3*encoded_size/4);
for (size_t i=0; i<encoded_size; i += 4)
{
// Get values for each group of four base 64 characters
BYTE b4[4];
b4[0] = (encoded_string[i+0] <= 'z') ? from_base64[encoded_string[i+0]] : 0xff;
b4[1] = (encoded_string[i+1] <= 'z') ? from_base64[encoded_string[i+1]] : 0xff;
b4[2] = (encoded_string[i+2] <= 'z') ? from_base64[encoded_string[i+2]] : 0xff;
b4[3] = (encoded_string[i+3] <= 'z') ? from_base64[encoded_string[i+3]] : 0xff;
// Transform into a group of three bytes
BYTE b3[3];
b3[0] = ((b4[0] & 0x3f) << 2) + ((b4[1] & 0x30) >> 4);
b3[1] = ((b4[1] & 0x0f) << 4) + ((b4[2] & 0x3c) >> 2);
b3[2] = ((b4[2] & 0x03) << 6) + ((b4[3] & 0x3f) >> 0);
// Add the byte to the return value if it isn't part of an '=' character (indicated by 0xff)
if (b4[1] != 0xff) ret.push_back(b3[0]);
if (b4[2] != 0xff) ret.push_back(b3[1]);
if (b4[3] != 0xff) ret.push_back(b3[2]);
}
return ret;
}
Использование:
BYTE buf[] = "ABCD";
std::string encoded = Base64::encode(buf, 4);
// encoded = "QUJDRA= = "
std::vector<BYTE> decoded = Base64::decode(encoded);
Бонусом здесь является то, что функция декодирования также может декодировать вариант URL-адреса кодировки Base64.
Бонусные баллы за отсутствие поиска () и резерв () за выходы. 1 маленький балл, потому что вы принимаете ввод как копию (так что вы можете добавить = в конце, если требуется). Было бы неплохо, если бы это была вещь без копирования.
длиннее: мне больше всего нравится этот ответ ... отлично подходит и для # строк. Бонусные баллы за отсутствие поиска () и резерв () за выходы. То, что я бы улучшил (что привело бы к увеличению кода): вы принимаете ввод как копию (так что вы можете добавить = в конце, если требуется). Было бы неплохо, если бы это была вещь без копирования. И также могли быть написаны как простые функции - в классе нет необходимости. И должен проверять наличие пустого вектора buf перед разыменованием buf [0]. И добавьте интерфейс для записи данных в ссылку (чтобы вызывающий мог повторно использовать память).
Спасибо за отзыв. Причина, по которой я взял строку по значению, заключается в том, что она упрощает код, если я всегда могу гарантировать, что она имеет допустимую длину. Я думаю, что в большинстве случаев RVO все равно должен предотвращать копирование строки, так что это не должно быть проблемой. По поводу разыменования buf [0] - хороший улов, исправлю :)
Я закончил свою модифицированную версию вашей, я отправлю здесь в качестве ответа для вашего удовольствия ... я удалил необходимость в строке как копии, добавив тест (вы уже проводите тесты, поэтому на самом деле он не добавляет любые проблемы с производительностью).
Возможно, я ошибаюсь, но не следует ли менять местами значения индексов 60 и 61 в from_base64 []? Я предполагаю, что идея состоит в том, чтобы игнорировать «<» (index = 60), возвращая 255, и распознавать символ заполнения «=» (index = 61), возвращая 0.
Вы не ошиблись, но алгоритм все еще работает с этой ошибкой;)
Ошибка в том, что символ '<' также должен иметь 255 (для символа '=' 255 является правильным, и наличие здесь 0 нарушит алгоритм). Эта ошибка приводит к тому, что результат, когда в строке находится символ «<», является неверным, но использование неподдерживаемых символов, таких как «<» в строке base64, в любом случае не определено, поэтому это не имеет значения. Я обновил свой исходный ответ, чтобы исправить ошибку.
Здесь есть несколько фрагментов. Однако этот компактный, эффективный и дружественный к C++ 11:
static std::string base64_encode(const std::string &in) {
std::string out;
int val = 0, valb = -6;
for (uchar c : in) {
val = (val << 8) + c;
valb += 8;
while (valb >= 0) {
out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val>>valb)&0x3F]);
valb -= 6;
}
}
if (valb>-6) out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val<<8)>>(valb+8))&0x3F]);
while (out.size()%4) out.push_back('=');
return out;
}
static std::string base64_decode(const std::string &in) {
std::string out;
std::vector<int> T(256,-1);
for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
int val=0, valb=-8;
for (uchar c : in) {
if (T[c] == -1) break;
val = (val << 6) + T[c];
valb += 6;
if (valb >= 0) {
out.push_back(char((val>>valb)&0xFF));
valb -= 8;
}
}
return out;
}
Я должен забрать это назад ... Мне просто не следует проводить тесты производительности в режиме отладки, извините. По крайней мере, быстрее принятого решения.
Битовый сдвиг "int val" вне его диапазона равен UB. "unsigned val = 0; int valb = ..." правильно.
typedef unsigned char uchar;
Моя вариация на Ответ Дедала Альфы:
Это позволяет избежать копирования параметров за счет пары тестов.
Использует uint8_t вместо BYTE.
Добавляет некоторые удобные функции для работы со строками, хотя обычно входные данные являются двоичными и могут иметь внутри нулевые байты, поэтому обычно не следует манипулировать как строкой (что часто подразумевает данные с завершающим нулем).
Также добавлены некоторые приведения для исправления предупреждений компилятора (по крайней мере, в GCC, я еще не запускал его через MSVC).
Часть файла base64.hpp:
void base64_encode(string & out, const vector<uint8_t>& buf);
void base64_encode(string & out, const uint8_t* buf, size_t bufLen);
void base64_encode(string & out, string const& buf);
void base64_decode(vector<uint8_t> & out, string const& encoded_string);
// Use this if you know the output should be a valid string
void base64_decode(string & out, string const& encoded_string);
static const uint8_t from_base64[128] = {
// 8 rows of 16 = 128
// Note: only requires 123 entries, as we only lookup for <= z , which z=122
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 62, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 0, 255, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 63,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255
};
static const char to_base64[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
void base64_encode(string & out, string const& buf)
{
if (buf.empty())
base64_encode(out, NULL, 0);
else
base64_encode(out, reinterpret_cast<uint8_t const*>(&buf[0]), buf.size());
}
void base64_encode(string & out, std::vector<uint8_t> const& buf)
{
if (buf.empty())
base64_encode(out, NULL, 0);
else
base64_encode(out, &buf[0], buf.size());
}
void base64_encode(string & ret, uint8_t const* buf, size_t bufLen)
{
// Calculate how many bytes that needs to be added to get a multiple of 3
size_t missing = 0;
size_t ret_size = bufLen;
while ((ret_size % 3) != 0)
{
++ret_size;
++missing;
}
// Expand the return string size to a multiple of 4
ret_size = 4*ret_size/3;
ret.clear();
ret.reserve(ret_size);
for (size_t i = 0; i < ret_size/4; ++i)
{
// Read a group of three bytes (avoid buffer overrun by replacing with 0)
const size_t index = i*3;
const uint8_t b3_0 = (index+0 < bufLen) ? buf[index+0] : 0;
const uint8_t b3_1 = (index+1 < bufLen) ? buf[index+1] : 0;
const uint8_t b3_2 = (index+2 < bufLen) ? buf[index+2] : 0;
// Transform into four base 64 characters
const uint8_t b4_0 = ((b3_0 & 0xfc) >> 2);
const uint8_t b4_1 = ((b3_0 & 0x03) << 4) + ((b3_1 & 0xf0) >> 4);
const uint8_t b4_2 = ((b3_1 & 0x0f) << 2) + ((b3_2 & 0xc0) >> 6);
const uint8_t b4_3 = ((b3_2 & 0x3f) << 0);
// Add the base 64 characters to the return value
ret.push_back(to_base64[b4_0]);
ret.push_back(to_base64[b4_1]);
ret.push_back(to_base64[b4_2]);
ret.push_back(to_base64[b4_3]);
}
// Replace data that is invalid (always as many as there are missing bytes)
for (size_t i = 0; i != missing; ++i)
ret[ret_size - i - 1] = '=';
}
template <class Out>
void base64_decode_any( Out & ret, std::string const& in)
{
typedef typename Out::value_type T;
// Make sure the *intended* string length is a multiple of 4
size_t encoded_size = in.size();
while ((encoded_size % 4) != 0)
++encoded_size;
const size_t N = in.size();
ret.clear();
ret.reserve(3*encoded_size/4);
for (size_t i = 0; i < encoded_size; i += 4)
{
// Note: 'z' == 122
// Get values for each group of four base 64 characters
const uint8_t b4_0 = ( in[i+0] <= 'z') ? from_base64[static_cast<uint8_t>(in[i+0])] : 0xff;
const uint8_t b4_1 = (i+1 < N and in[i+1] <= 'z') ? from_base64[static_cast<uint8_t>(in[i+1])] : 0xff;
const uint8_t b4_2 = (i+2 < N and in[i+2] <= 'z') ? from_base64[static_cast<uint8_t>(in[i+2])] : 0xff;
const uint8_t b4_3 = (i+3 < N and in[i+3] <= 'z') ? from_base64[static_cast<uint8_t>(in[i+3])] : 0xff;
// Transform into a group of three bytes
const uint8_t b3_0 = ((b4_0 & 0x3f) << 2) + ((b4_1 & 0x30) >> 4);
const uint8_t b3_1 = ((b4_1 & 0x0f) << 4) + ((b4_2 & 0x3c) >> 2);
const uint8_t b3_2 = ((b4_2 & 0x03) << 6) + ((b4_3 & 0x3f) >> 0);
// Add the byte to the return value if it isn't part of an '=' character (indicated by 0xff)
if (b4_1 != 0xff) ret.push_back( static_cast<T>(b3_0) );
if (b4_2 != 0xff) ret.push_back( static_cast<T>(b3_1) );
if (b4_3 != 0xff) ret.push_back( static_cast<T>(b3_2) );
}
}
void base64_decode(vector<uint8_t> & out, string const& encoded_string)
{
base64_decode_any(out, encoded_string);
}
void base64_decode(string & out, string const& encoded_string)
{
base64_decode_any(out, encoded_string);
}
Я не знал, что && и || имел определенный порядок оценки в C++, поэтому сегодня я узнал кое-что новое. В таких случаях, когда вы хотите проверить состояние индекса, но в то же время убедитесь, что индекс не выходит за пределы допустимого диапазона, это чрезвычайно полезно.
Да, я использую эту технику все время.
Думаю, этот работает лучше:
#include <string>
static const char* B64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const int B64index[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63,
0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
};
const std::string b64encode(const void* data, const size_t &len)
{
std::string result((len + 2) / 3 * 4, '=');
char *p = (char*) data, *str = &result[0];
size_t j = 0, pad = len % 3;
const size_t last = len - pad;
for (size_t i = 0; i < last; i += 3)
{
int n = int(p[i]) << 16 | int(p[i + 1]) << 8 | p[i + 2];
str[j++] = B64chars[n >> 18];
str[j++] = B64chars[n >> 12 & 0x3F];
str[j++] = B64chars[n >> 6 & 0x3F];
str[j++] = B64chars[n & 0x3F];
}
if (pad) /// Set padding
{
int n = --pad ? int(p[last]) << 8 | p[last + 1] : p[last];
str[j++] = B64chars[pad ? n >> 10 & 0x3F : n >> 2];
str[j++] = B64chars[pad ? n >> 4 & 0x03F : n << 4 & 0x3F];
str[j++] = pad ? B64chars[n << 2 & 0x3F] : '=';
}
return result;
}
const std::string b64decode(const void* data, const size_t &len)
{
if (len == 0) return "";
unsigned char *p = (unsigned char*) data;
size_t j = 0,
pad1 = len % 4 || p[len - 1] == '=',
pad2 = pad1 && (len % 4 > 2 || p[len - 2] != '=');
const size_t last = (len - pad1) / 4 << 2;
std::string result(last / 4 * 3 + pad1 + pad2, '\0');
unsigned char *str = (unsigned char*) &result[0];
for (size_t i = 0; i < last; i += 4)
{
int n = B64index[p[i]] << 18 | B64index[p[i + 1]] << 12 | B64index[p[i + 2]] << 6 | B64index[p[i + 3]];
str[j++] = n >> 16;
str[j++] = n >> 8 & 0xFF;
str[j++] = n & 0xFF;
}
if (pad1)
{
int n = B64index[p[last]] << 18 | B64index[p[last + 1]] << 12;
str[j++] = n >> 16;
if (pad2)
{
n |= B64index[p[last + 2]] << 6;
str[j++] = n >> 8 & 0xFF;
}
}
return result;
}
std::string b64encode(const std::string& str)
{
return b64encode(str.c_str(), str.size());
}
std::string b64decode(const std::string& str64)
{
return b64decode(str64.c_str(), str64.size());
}
Благодаря Йенсу Альфке за указание на проблему с производительностью я внес некоторые изменения в этот пост Старый. Этот работает намного быстрее, чем раньше. Другим его преимуществом является также плавная обработка поврежденных данных.
Последнее издание: Хотя в такого рода проблемах кажется, что скорость - это излишество, но просто для удовольствия я сделал некоторые другие модификации, чтобы сделать этот алгоритм самым быстрым алгоритмом AFAIK. Особая благодарность GaspardP за ценные предложения и хорошему тесту.
Все эти вызовы strchr замедлят работу декодера - вы в среднем зацикливаете 32 раза на каждый декодированный байт. В большинстве решений используется таблица поиска с 256 элементами, чтобы избежать этого, что намного быстрее.
Я прокомментировал stackoverflow.com/questions/342409/… - хотя этот код не самый быстрый для кодирования, он самый быстрый для декодирования (по сравнению с 16 другими реализациями).
В декодере вы можете немного повысить производительность (10-15% в моих тестах), работая с char* вместо std::string (просто сделайте char* out = &str[0], а затем используйте out[j++] вместо str[j++]). Таким образом вы пропустите ненужные проверки. сделано std::string::operator[]. Кроме того, избегайте последнего push_back, который может оказаться очень дорого, выделив еще один байт (std::string str;str.resize(3*((len+3)/4));, а затем используйте out[j++] каждый раз и str.resize(j); в конце.
Вы добавили утечку памяти при последнем редактировании, не говоря уже о буферной копии. Не используйте new без delete. Собственно, вообще не используйте new. @Gaspard, мне не известно о каких-либо «ненужных проверках, выполненных std::string::operator[]» (на самом деле я достаточно уверен, что их нет, по крайней мере, в выпуске), но вы можете использовать vector<char>, если действительно отчаялись - в любом случае ваш код замены это не то, что предлагал Гаспар, который по-прежнему использовал строку для всего, кроме доступа к элементу, и был безопасным / быстрым, несмотря на то, что я думаю, что это было ненужным;)
@LightnessRacesinOrbit: ой! Привыкание к C# в эти дни полностью заставило меня забыть об утечках памяти. Я снова его отредактировал. Кстати, спасибо за информацию.
@LightnessRacesinOrbit: в выпуске operator[] задокументирован как непроверенный - вы правы. Однако std::string имеет оптимизацию, при которой небольшие строки выделяются внутри структуры, а не в куче. Каждый раз, когда вы выполняете [] для строки, он проверяет, является ли она маленькой, чтобы знать, откуда взять буфер. Мои тесты показали, что он не всегда оптимизирован. В Windows это выглядит так: value_type *_Myptr() { // determine current pointer to buffer for mutable string return (this->_BUF_SIZE <= _Myres ? _Unfancy(_Bx._Ptr) : _Bx._Buf); }
@polfosol ఠ_ఠ, спасибо за это. Пожалуйста, подумайте об использовании unsigned char *p вместо char *p в кодировщике. У меня поврежденная строка base64, если мой ввод содержит байты >= 0x80. После добавления unsigned вроде бы все в порядке.
Как это компилируется? Вы пытаетесь отказаться от константности с помощью (char*) data и (unsigned char*) data. Это должно вызвать ошибку. Вы должны предпочесть использовать reinterpret_cast<const unsigned char*>(data); как четкое указание читателю, что происходит, или, что еще лучше, просто принимать данные как const unsigned char* вместо const void*, поскольку это идиоматический способ ссылки на любую последовательность байтов.
Небольшая вариация с более компактной таблицей поиска и использованием функций C++ 17:
std::string base64_decode(const std::string_view in) {
// table from '+' to 'z'
const uint8_t lookup[] = {
62, 255, 62, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255,
255, 0, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
255, 255, 255, 255, 63, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
static_assert(sizeof(lookup) == 'z' - '+' + 1);
std::string out;
int val = 0, valb = -8;
for (uint8_t c : in) {
if (c < '+' || c > 'z')
break;
c -= '+';
if (lookup[c] >= 64)
break;
val = (val << 6) + lookup[c];
valb += 6;
if (valb >= 0) {
out.push_back(char((val >> valb) & 0xFF));
valb -= 8;
}
}
return out;
}
Если у вас нет std :: string_view, попробуйте вместо этого std :: experimental :: string_view.
Я использую это:
class BinaryVector {
public:
std::vector<char> bytes;
uint64_t bit_count = 0;
public:
/* Add a bit to the end */
void push_back(bool bit);
/* Return false if character is unrecognized */
bool pushBase64Char(char b64_c);
};
void BinaryVector::push_back(bool bit)
{
if (!bit_count || bit_count % 8 == 0) {
bytes.push_back(bit << 7);
}
else {
uint8_t next_bit = 8 - (bit_count % 8) - 1;
bytes[bit_count / 8] |= bit << next_bit;
}
bit_count++;
}
/* Converts one Base64 character to 6 bits */
bool BinaryVector::pushBase64Char(char c)
{
uint8_t d;
// A to Z
if (c > 0x40 && c < 0x5b) {
d = c - 65; // Base64 A is 0
}
// a to z
else if (c > 0x60 && c < 0x7b) {
d = c - 97 + 26; // Base64 a is 26
}
// 0 to 9
else if (c > 0x2F && c < 0x3a) {
d = c - 48 + 52; // Base64 0 is 52
}
else if (c == '+') {
d = 0b111110;
}
else if (c == '/') {
d = 0b111111;
}
else if (c == '=') {
d = 0;
}
else {
return false;
}
push_back(d & 0b100000);
push_back(d & 0b010000);
push_back(d & 0b001000);
push_back(d & 0b000100);
push_back(d & 0b000010);
push_back(d & 0b000001);
return true;
}
bool loadBase64(std::vector<char>& b64_bin, BinaryVector& vec)
{
for (char& c : b64_bin) {
if (!vec.pushBase64Char(c)) {
return false;
}
}
return true;
}
Используйте vec.bytes для доступа к преобразованным данным.
Моя версия - простой быстрый кодировщик (декодер) Base64 для C++ Builder.
// ---------------------------------------------------------------------------
UnicodeString __fastcall TExample::Base64Encode(void *data, int length)
{
if (length <= 0)
return L"";
static const char set[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
unsigned char *in = (unsigned char*)data;
char *pos, *out = pos = new char[((length - 1) / 3 + 1) << 2];
while ((length -= 3) >= 0)
{
pos[0] = set[in[0] >> 2];
pos[1] = set[((in[0] & 0x03) << 4) | (in[1] >> 4)];
pos[2] = set[((in[1] & 0x0F) << 2) | (in[2] >> 6)];
pos[3] = set[in[2] & 0x3F];
pos += 4;
in += 3;
};
if ((length & 2) != 0)
{
pos[0] = set[in[0] >> 2];
if ((length & 1) != 0)
{
pos[1] = set[((in[0] & 0x03) << 4) | (in[1] >> 4)];
pos[2] = set[(in[1] & 0x0F) << 2];
}
else
{
pos[1] = set[(in[0] & 0x03) << 4];
pos[2] = '=';
};
pos[3] = '=';
pos += 4;
};
UnicodeString code = UnicodeString(out, pos - out);
delete[] out;
return code;
};
// ---------------------------------------------------------------------------
int __fastcall TExample::Base64Decode(const UnicodeString &code, unsigned char **data)
{
int length;
if (((length = code.Length()) == 0) || ((length & 3) != 0))
return 0;
wchar_t *str = code.c_str();
unsigned char *pos, *out = pos = new unsigned char[(length >> 2) * 3];
while (*str != 0)
{
length = -1;
int shift = 18, bits = 0;
do
{
wchar_t s = str[++length];
if ((s >= L'A') && (s <= L'Z'))
bits |= (s - L'A') << shift;
else if ((s >= L'a') && (s <= L'z'))
bits |= (s - (L'a' - 26)) << shift;
else if (((s >= L'0') && (s <= L'9')))
bits |= (s - (L'0' - 52)) << shift;
else if (s == L'+')
bits |= 62 << shift;
else if (s == L'/')
bits |= 63 << shift;
else if (s == L'=')
{
length--;
break;
}
else
{
delete[] out;
return 0;
};
}
while ((shift -= 6) >= 0);
pos[0] = bits >> 16;
pos[1] = bits >> 8;
pos[2] = bits;
pos += length;
str += 4;
};
*data = out;
return pos - out;
};
//---------------------------------------------------------------------------
Я сначала сделал свою версию, а потом нашел эту тему.
Почему моя версия выглядит проще, чем другие, представленные здесь? Я делаю что-то неправильно? На скорость не тестировал.
inline char const* b64units = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
inline char* b64encode(void const* a, int64_t b) {
ASSERT(a != nullptr);
if (b > 0) {
uint8_t const* aa = static_cast<uint8_t const*>(a);
uint8_t v = 0;
int64_t bp = 0;
int64_t sb = 0;
int8_t off = 0;
int64_t nt = ((b + 2) / 3) * 4;
int64_t nd = (b * 8) / 6;
int64_t tl = ((b * 8) % 6) ? 1 : 0;
int64_t nf = nt - nd - tl;
int64_t ri = 0;
char* r = new char[nt + 1]();
for (int64_t i = 0; i < nd; i++) {
v = (aa[sb] << off) | (aa[sb + 1] >> (8 - off));
v >>= 2;
r[ri] = b64units[v];
ri += 1;
bp += 6;
sb = (bp / 8);
off = (bp % 8);
}
if (tl > 0) {
v = (aa[sb] << off);
v >>= 2;
r[ri] = b64units[v];
ri += 1;
}
for (int64_t i = 0; i < nf; i++) {
r[ri] = '=';
ri += 1;
}
return r;
} else return nullptr;
}
P.S .: Мой метод работает хорошо. Тестировал с Node.js:
let data = 'stackabuse.com';
let buff = new Buffer(data);
let base64data = buff.toString('base64');
Мне понравился это решение на GitHub.
Это один файл hpp, и он использует тип vector <byte> для необработанных данных, в отличие от принятого ответа.
#pragma once
#include <string>
#include <vector>
#include <stdexcept>
#include <cstdint>
namespace base64
{
inline static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
inline static const char kPadCharacter = '=';
using byte = std::uint8_t;
inline std::string encode(const std::vector<byte>& input)
{
std::string encoded;
encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4);
std::uint32_t temp{};
auto it = input.begin();
for(std::size_t i = 0; i < input.size() / 3; ++i)
{
temp = (*it++) << 16;
temp += (*it++) << 8;
temp += (*it++);
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
encoded.append(1, kEncodeLookup[(temp & 0x0000003F) ]);
}
switch(input.size() % 3)
{
case 1:
temp = (*it++) << 16;
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(2, kPadCharacter);
break;
case 2:
temp = (*it++) << 16;
temp += (*it++) << 8;
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
encoded.append(1, kPadCharacter);
break;
}
return encoded;
}
std::vector<byte> decode(const std::string& input)
{
if (input.length() % 4)
throw std::runtime_error("Invalid base64 length!");
std::size_t padding{};
if (input.length())
{
if (input[input.length() - 1] == kPadCharacter) padding++;
if (input[input.length() - 2] == kPadCharacter) padding++;
}
std::vector<byte> decoded;
decoded.reserve(((input.length() / 4) * 3) - padding);
std::uint32_t temp{};
auto it = input.begin();
while(it < input.end())
{
for(std::size_t i = 0; i < 4; ++i)
{
temp <<= 6;
if (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41;
else if (*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47;
else if (*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04;
else if (*it == 0x2B) temp |= 0x3E;
else if (*it == 0x2F) temp |= 0x3F;
else if (*it == kPadCharacter)
{
switch(input.end() - it)
{
case 1:
decoded.push_back((temp >> 16) & 0x000000FF);
decoded.push_back((temp >> 8 ) & 0x000000FF);
return decoded;
case 2:
decoded.push_back((temp >> 10) & 0x000000FF);
return decoded;
default:
throw std::runtime_error("Invalid padding in base64!");
}
}
else throw std::runtime_error("Invalid character in base64!");
++it;
}
decoded.push_back((temp >> 16) & 0x000000FF);
decoded.push_back((temp >> 8 ) & 0x000000FF);
decoded.push_back((temp ) & 0x000000FF);
}
return decoded;
}
}
Вот написанное мной, в котором используются объединения и битовые поля для максимальной эффективности и удобочитаемости.
const char PADDING_CHAR = '=';
const char* ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const uint8_t DECODED_ALPHBET[128] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,0,0,0,0,0};
/**
* Given a string, this function will encode it in 64b (with padding)
*/
std::string encodeBase64(const std::string& binaryText)
{
std::string encoded((binaryText.size()/3 + (binaryText.size()%3 > 0)) << 2, PADDING_CHAR);
const char* bytes = binaryText.data();
union
{
uint32_t temp = 0;
struct
{
uint32_t first : 6, second : 6, third : 6, fourth : 6;
} tempBytes;
};
std::string::iterator currEncoding = encoded.begin();
for(uint32_t i = 0, lim = binaryText.size() / 3; i < lim; ++i, bytes+=3)
{
temp = bytes[0] << 16 | bytes[1] << 8 | bytes[2];
(*currEncoding++) = ALPHABET[tempBytes.fourth];
(*currEncoding++) = ALPHABET[tempBytes.third];
(*currEncoding++) = ALPHABET[tempBytes.second];
(*currEncoding++) = ALPHABET[tempBytes.first];
}
switch(binaryText.size() % 3)
{
case 1:
temp = bytes[0] << 16;
(*currEncoding++) = ALPHABET[tempBytes.fourth];
(*currEncoding++) = ALPHABET[tempBytes.third];
break;
case 2:
temp = bytes[0] << 16 | bytes[1] << 8;
(*currEncoding++) = ALPHABET[tempBytes.fourth];
(*currEncoding++) = ALPHABET[tempBytes.third];
(*currEncoding++) = ALPHABET[tempBytes.second];
break;
}
return encoded;
}
/**
* Given a 64b padding-encoded string, this function will decode it.
*/
std::string decodeBase64(const std::string& base64Text)
{
if ( base64Text.empty() )
return "";
assert((base64Text.size()&3) == 0 && "The base64 text to be decoded must have a length devisible by 4!");
uint32_t numPadding = (*std::prev(base64Text.end(),1) == PADDING_CHAR) + (*std::prev(base64Text.end(),2) == PADDING_CHAR);
std::string decoded((base64Text.size()*3>>2) - numPadding, '.');
union
{
uint32_t temp;
char tempBytes[4];
};
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(base64Text.data());
std::string::iterator currDecoding = decoded.begin();
for(uint32_t i = 0, lim = (base64Text.size() >> 2) - (numPadding!=0); i < lim; ++i, bytes+=4)
{
temp = DECODED_ALPHBET[bytes[0]] << 18 | DECODED_ALPHBET[bytes[1]] << 12 | DECODED_ALPHBET[bytes[2]] << 6 | DECODED_ALPHBET[bytes[3]];
(*currDecoding++) = tempBytes[2];
(*currDecoding++) = tempBytes[1];
(*currDecoding++) = tempBytes[0];
}
switch (numPadding)
{
case 2:
temp = DECODED_ALPHBET[bytes[0]] << 18 | DECODED_ALPHBET[bytes[1]] << 12;
(*currDecoding++) = tempBytes[2];
break;
case 1:
temp = DECODED_ALPHBET[bytes[0]] << 18 | DECODED_ALPHBET[bytes[1]] << 12 | DECODED_ALPHBET[bytes[2]] << 6;
(*currDecoding++) = tempBytes[2];
(*currDecoding++) = tempBytes[1];
break;
}
return decoded;
}
Другой поток показывает, как кодировать / декодировать base64 с помощью boost: stackoverflow.com/questions/10521581/…