Related
I am using the following code snippet to base64 encode and decode a string using Boost C++ library.
//Base64 Encode Implementation using Boost C++ library
const std::string base64_padding[] = {"", "=", "=="};
std::string X_Privet_Token_Generator::base64_encode(const std::string & s)
{
namespace bai = boost::archive::iterators;
std::stringstream os;
// convert binary values to base64 characters
typedef bai::base64_from_binary
// retrieve 6 bit integers from a sequence of 8 bit bytes
<bai::transform_width<const char *, 6, 8> > base64_enc; // compose all the above operations in to a new iterator
std::copy(base64_enc(s.c_str()), base64_enc(s.c_str() + s.size()), std::ostream_iterator<char>(os));
os << base64_padding[s.size() % 3];
return os.str();
}
std::string X_Privet_Token_Generator::base64_decode(std::string & s)
{
namespace bai = boost::archive::iterators;
std::stringstream os;
// convert binary values to base64 characters
typedef bai::binary_from_base64
<bai::transform_width<const char *, 8, 6> > base64_dec;
unsigned int size = s.size();
// Remove the padding characters, cf.
if (size && s[size - 1] == '=')
{
--size;
if (size && s[size - 1] == '=')
--size;
}
if (size == 0)
return std::string();
LOGINFO("Hash decoded token : %s", s.c_str());
std::copy(base64_dec(s.data()), base64_dec(s.data() + size), std::ostream_iterator<char>(os));
std::cout<< os.str();
return os.str();
}
Encoding works well, however, while decoding I get the following error:
terminate called after throwing an instance of boost::archive::iterators::dataflow_exception
what(): attempt to decode a value not in base64 char set
Is it one of the padded characters that is causing this issue? Am I missing something here?
The padding characters '=' are part of the b64 encoded data and should not be removed before decoding.
b64 is encoded in blocks of 4 character, I suspect that while decoding it reads a '\0' instead of an expected '=' at the end of the string.
Changing the
std::copy(base64_dec(s.data()), base64_dec(s.data() + size), std::ostream_iterator<char>(os))
to
return std::string( base64_dec(s.c_str()), base64_dec(s.c_str() + size))
resolved the issue.
A more efficient solution for base64 encoding and decoding is as follows:
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/insert_linebreaks.hpp>
#include <boost/archive/iterators/remove_whitespace.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/ostream_iterator.hpp>
#include <boost/algorithm/string.hpp>
#include <bits/stl_algo.h>
std::string X_Privet_Token_Generator::base64_encode(std::string s)
{
namespace bai = boost::archive::iterators;
std::stringstream os;
// convert binary values to base64 characters
typedef bai::base64_from_binary
// retrieve 6 bit integers from a sequence of 8 bit bytes
<bai::transform_width<char *, 6, 8> > base64_enc; // compose all the above operations in to a new iterator
std::copy(base64_enc(s.c_str()), base64_enc(s.c_str() + s.size()), std::ostream_iterator<char>(os));
os << base64_padding[s.size() % 3];
return os.str();
}
std::string X_Privet_Token_Generator::base64_decode(std::string s)
{
namespace bai = boost::archive::iterators;
std::stringstream os;
typedef bai::transform_width<bai::binary_from_base64<char * >, 8, 6>
base64_dec;
unsigned int size = s.size();
// Remove the padding characters.
if(size && s[size - 1] == '=') {
--size;
if(size && s[size - 1] == '=')
--size;
}
if(size == 0) return std::string();
unsigned int paddChars = count(s.begin(), s.end(), '=');
std::replace(s.begin(),s.end(), '=', 'A');
std::string decoded_token(base64_dec(s.c_str()), base64_dec(s.c_str() + size));
decoded_token.erase(decoded_token.end()-paddChars,decoded_token.end());
return decoded_token;
}
I am having a string that has some binary data in it. The string in xml format, so before i am gonna proccess it i need to convert the binary data in the base64 format.
I am using a function called findXMLTag that will find the position of the start and the end of the data given the xml tag that contains it.
Now i am able to convert that data into base64 but i am having problems replacing the old binary data with my new base64 data.
The thing is that i can't use any type of string because when it locates a null char it will consider it as the terminating point of the string but in fact since i have binary data stored in the string then that null char can be part of my binary data.
So i guess i am looking for some kind of binary replacement and i can't figure out how to make it work.
Thanks in advance for any kind of help.
This is the code that i am using to locate the start and the end of the data in an xml string.
std::vector<TForm1::Pair> TForm1::findXMLTag(char *XMLString, char* XMLTag, int XMLSize)
{
void *found = XMLString;
int XMLTagLen = strlen(XMLTag);
std::vector<TForm1::Pair> result;
TForm1::Pair pair;
AnsiString XMLTagEnd = "</";
XMLTagEnd += &XMLTag[1];
while(found = memmem(XMLString, XMLSize - ((char*)found - XMLString), XMLTag, XMLTagLen))
{
if(found == NULL)
return result;
found = (char*)found + XMLTagLen;
pair.start = int((char*)found - XMLString);
found = memmem(found, XMLSize - ((char*)found - XMLString), XMLTagEnd.c_str(), XMLTagEnd.Length());
pair.end = int((char*)found - XMLString);
found = (char*)found + XMLTagEnd.Length();
result.push_back(pair);
}
return result;
}
Translating your C-style answer to C++, we are left with a one-liner which is safe (for valid indices), efficient and readable:
std::string binary_replace(
std::string const& bin, unsigned bin_start, unsigned bin_end,
std::string const& replace_with
) {
assert(bin_start < bin.size() and bin_end < bin.size());
return bin.substr(0, bin_start) + replace_with + bin.substr(bin_end);
}
This can be made even simpler by using the replace function for this purpose:
std::string binary_replace(
std::string bin, unsigned bin_start, unsigned bin_end,
std::string const& replace_with
) {
assert(bin_start < bin.size() and bin_end < bin.size());
return bin.replace(bin_start, bin_end - bin_start, replace_with);
}
(Take heed that bin is passed by value here since replace modifies it.)
Essentially there’s a direct substitute for most C-string functions in C++ – in this case, have a look at the documentation of std::basic_string::substr.
Here's a little self-contained example that might help you. Note that there is no error or bounds checking, it's just meant to demonstrate a concept.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// stub for real base64_encode
std::string base64_encode(const string &data)
{
return "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
}
// search without terminating on NULL
size_t binary_find(const string &str, size_t offset, const string &s)
{
for (size_t i=offset; i<str.length(); i++)
if (str.compare(i, string::npos, s) == 0)
return i;
return string::npos;
}
int main()
{
string tag = "<data>";
string endtag = "</data>";
string xml("<data>\0\0\0\0\0\0\0\0\0\0</data>", 23);
size_t start = xml.find(tag) + tag.length();
size_t end = binary_find(xml, start, endtag);
string binary = xml.substr(start, end-start);
string base64 = base64_encode(binary);
xml.replace(start, end-start, base64);
cout << xml << endl;
}
char *binary_replace(char *binString, int _strlen, int binDataStart, int binDataEnd, char* replaceWith)
{
char *buffer = (char*)malloc( (strlen(replaceWith)+(_strlen - (binDataEnd-binDataStart)))*sizeof(char) );
memcpy(buffer, binString, binDataStart);
strcat(buffer, replaceWith);
memmove(buffer+binDataStart+strlen(replaceWith), binString+binDataEnd, _strlen - binDataEnd);
return buffer;
}
I know that this is not c++ but it solved my problem.
Let say you have a string:
std::string s = "ABCD\t1234";
I can use std::string::find to get an offset to the '\t' character, but as far as I know there are no functions with the following signature:
int atoi_n(char *, int len);
An I missing anything? strtok replaces the \t with \0, and I don't want to touch the original buffer. I find it hard to believe that there aren't instances of the atoi, atof, etc which take a length parameter, but I can't find anything.
Anyone know if there is something I'm missing? I know boost has some tokenizers but I'd like avoid having to add the dependency of boost.
Looking the comments so far I'd like to clarify. Let's change the scenario:
char buffer[1024];
char *pStartPos;
char *pEndPost;
pStartPos = buffer + 5;
pEndPos = buffer + 10;
Let's also say you can't make any assumptions about the memory outside pStartPos and pEndPos. How do you convert the charaters between pStartPos and pEndPos to an int without adding a '\0' to buffer or copying using substr?
If you want to parse only the end of the string (from the character after \t to the end) you just need to pass a pointer to the first character to parse to atoi...
int n = atoi(s.c_str()+s.find('\t')+1);
(error checking omitted for brevity - in particular, we are always assuming that a \t is actually present)
If, instead, you want to parse from the beginning of the string up to \t you can just do
int n = atoi(s.c_str());
since atoi stops at the first non-numeric character anyway.
By the way, you should consider using more robust solutions for parsing the number, like strtol, sscanf or the C++ streams - they all can report a parsing error in some way, while atoi just returns 0 (which isn't distinguishable from a 0 that comes from parsing the string).
Incidentally, atoi is not in the "STL" by any means - it's just part of the C standard library.
I know that atoi is not in the STL. I was wondering if there was anything in STL like it where you can specify the last character which you want to include in the conversion. Basically I have a buffer which may be partially filled with garbage. I know the start of possible valid data and the end of possible valid data. I don't want to depend on whitespace to end the conversion, I want to be explicit about the length of the "field" because it also may not be /0 terminated.
If you are sure that the garbage doesn't start with digits you can use atoi/strtol/istringstream as is - they automatically stop just when they see the garbage. Otherwise, use the substr method to extract the exact substring you need:
std::string mayContainGarbage="alcak123456amaclmò";
std::string onlyTheDigits=mayContainGarbage.substr(5, 6);
// now parse onlyTheDigits as you prefer
To my knowledge, there is no such function out-of-box, but it shouldn't be difficult to implement.
For example:
template <typename ForwardIterator>
int range_to_int(ForwardIterator begin, ForwardIterator past_end) {
if (begin != past_end) {
bool negative = false;
auto ch = *begin;
if (ch == '-') {
negative = true;
++begin;
}
else if (ch == '+')
++begin;
if (begin != past_end) {
int result = 0;
do {
auto ch = *begin;
if (ch < '0' || ch > '9')
throw std::invalid_argument("Invalid digit.");
result = result * 10 + (ch - '0');
++begin;
} while (begin != past_end);
if (negative)
result = -result;
return result;
}
throw std::invalid_argument("+ or - must be followed by at least one digit.");
}
throw std::invalid_argument("Empty range.");
}
And you can use it like this:
int main() {
const char* buffer = "abc-123def";
int i = range_to_int(buffer + 4, buffer + 7);
assert(i == 123);
i = range_to_int(buffer + 3, buffer + 7);
assert(i == -123);
try {
i = range_to_int(buffer + 3, buffer + 8);
assert(false);
}
catch (const std::exception& ex) {
std::cout << ex.what() << std::endl;
}
try {
i = range_to_int(buffer + 3, buffer + 4);
assert(false);
}
catch (const std::exception& ex) {
std::cout << ex.what() << std::endl;
}
try {
i = range_to_int(buffer + 4, buffer + 4);
assert(false);
}
catch (const std::exception& ex) {
std::cout << ex.what() << std::endl;
}
// You can use it on std::string as well....
const std::string str = buffer;
i = range_to_int(str.begin() + 4, str.begin() + 7);
assert(i == 123);
// Etc...
return EXIT_SUCCESS;
}
I'm reading a string from a file so it's in the form of a char array. I need to tokenize the string and save each char array token as a uint8_t hex value in an array.
char* starting = "001122AABBCC";
// ...
uint8_t[] ending = {0x00,0x11,0x22,0xAA,0xBB,0xCC}
How can I convert from starting to ending? Thanks.
Here is a complete working program. It is based on Rob I's solution, but fixes several problems has been tested to work.
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
const char* starting = "001122AABBCC";
int main()
{
std::string starting_str = starting;
std::vector<unsigned char> ending;
ending.reserve( starting_str.size());
for (int i = 0 ; i < starting_str.length() ; i+=2) {
std::string pair = starting_str.substr( i, 2 );
ending.push_back(::strtol( pair.c_str(), 0, 16 ));
}
for(int i=0; i<ending.size(); ++i) {
printf("0x%X\n", ending[i]);
}
}
strtoul will convert text in any base you choose into bytes. You have to do a little work to chop the input string into individual digits, or you can convert 32 or 64bits at a time.
ps uint8_t[] ending = {0x00,0x11,0x22,0xAA,0xBB,0xCC}
Doesn't mean anything, you aren't storing the data in a uint8 as 'hex', you are storing bytes, it's upto how you (or your debugger) interpretes the binary data
With C++11, you may use std::stoi for that :
std::vector<uint8_t> convert(const std::string& s)
{
if (s.size() % 2 != 0) {
throw std::runtime_error("Bad size argument");
}
std::vector<uint8_t> res;
res.reserve(s.size() / 2);
for (std::size_t i = 0, size = s.size(); i != size; i += 2) {
std::size_t pos = 0;
res.push_back(std::stoi(s.substr(i, 2), &pos, 16));
if (pos != 2) {
throw std::runtime_error("bad character in argument");
}
}
return res;
}
Live example.
I think any canonical answer (w.r.t. the bounty notes) would involve some distinct phases in the solution:
Error checking for valid input
Length check and
Data content check
Element conversion
Output creation
Given the usefulness of such conversions, the solution should probably include some flexibility w.r.t. the types being used and the locale required.
From the outset, given the date of the request for a "more canonical answer" (circa August 2014) liberal use of C++11 will be applied.
An annotated version of the code, with types corresponding to the OP:
std::vector<std::uint8_t> convert(std::string const& src)
{
// error check on the length
if ((src.length() % 2) != 0) {
throw std::invalid_argument("conversion error: input is not even length");
}
auto ishex = [] (decltype(*src.begin()) c) {
return std::isxdigit(c, std::locale()); };
// error check on the data contents
if (!std::all_of(std::begin(src), std::end(src), ishex)) {
throw std::invalid_argument("conversion error: input values are not not all xdigits");
}
// allocate the result, initialised to 0 and size it to the correct length
std::vector<std::uint8_t> result(src.length() / 2, 0);
// run the actual conversion
auto str = src.begin(); // track the location in the string
std::for_each(result.begin(), result.end(), [&str](decltype(*result.begin())& element) {
element = static_cast<std::uint8_t>(std::stoul(std::string(str, str + 2), nullptr, 16));
std::advance(str, 2); // next two elements
});
return result;
}
The template version of the code adds flexibility;
template <typename Int /*= std::uint8_t*/,
typename Char = char,
typename Traits = std::char_traits<Char>,
typename Allocate = std::allocator<Char>,
typename Locale = std::locale>
std::vector<Int> basic_convert(std::basic_string<Char, Traits, Allocate> const& src, Locale locale = Locale())
{
using string_type = std::basic_string<Char, Traits, Allocate>;
auto ishex = [&locale] (decltype(*src.begin()) c) {
return std::isxdigit(c, locale); };
if ((src.length() % 2) != 0) {
throw std::invalid_argument("conversion error: input is not even length");
}
if (!std::all_of(std::begin(src), std::end(src), ishex)) {
throw std::invalid_argument("conversion error: input values are not not all xdigits");
}
std::vector<Int> result(src.length() / 2, 0);
auto str = std::begin(src);
std::for_each(std::begin(result), std::end(result), [&str](decltype(*std::begin(result))& element) {
element = static_cast<Int>(std::stoul(string_type(str, str + 2), nullptr, 16));
std::advance(str, 2);
});
return result;
}
The convert() function can then be based on the basic_convert() as follows:
std::vector<std::uint8_t> convert(std::string const& src)
{
return basic_convert<std::uint8_t>(src, std::locale());
}
Live sample.
uint8_t is typically no more than a typedef of an unsigned char. If you're reading characters from a file, you should be able to read them into an unsigned char array just as easily as a signed char array, and an unsigned char array is a uint8_t array.
I'd try something like this:
std::string starting_str = starting;
uint8_t[] ending = new uint8_t[starting_str.length()/2];
for (int i = 0 ; i < starting_str.length() ; i+=2) {
std::string pair = starting_str.substr( i, i+2 );
ending[i/2] = ::strtol( pair.c_str(), 0, 16 );
}
Didn't test it but it looks good to me...
You may add your own conversion from set of char { '0','1',...'E','F' } to uint8_t:
uint8_t ctoa(char c)
{
if( c >= '0' && c <= '9' ) return c - '0';
else if( c >= 'a' && c <= 'f' ) return 0xA + c - 'a';
else if( c >= 'A' && c <= 'F' ) return 0xA + c - 'A';
else return 0;
}
Then it will be easy to convert a string in to array:
uint32_t endingSize = strlen(starting)/2;
uint8_t* ending = new uint8_t[endingSize];
for( uint32_t i=0; i<endingSize; i++ )
{
ending[i] = ( ctoa( starting[i*2] ) << 4 ) + ctoa( starting[i*2+1] );
}
This simple solution should work for your problem
char* starting = "001122AABBCC";
uint8_t ending[12];
// This algo will work for any size of starting
// However, you have to make sure that the ending have enough space.
int i=0;
while (i<strlen(starting))
{
// convert the character to string
char str[2] = "\0";
str[0] = starting[i];
// convert string to int base 16
ending[i]= (uint8_t)atoi(str,16);
i++;
}
uint8_t* ending = static_cast<uint8_t*>(starting);
I'm trying to quickly encode a simple ASCII string to base64 (Basic HTTP Authentication using boost::asio) and not paste in any new code code or use any libraries beyond boost.
Simple signature would look like:
string Base64Encode(const string& text);
Again I realize the algorithm is easy and there are many libraries/examples doing this but I'm looking for a clean boost example. I found boost serialization but no clear examples there or from Google.
http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/dataflow.html
Is this possible without adding the actual base64 algorithm explicitly to my code?
Here is my solution. It uses the same basic technique as the other solutions on this page, but solves the problem of the padding in what I feel is a more elegant way. This solution also makes use of C++11.
I think that most of the code is self explanatory. The bit of math in the encode function calculates the number of '=' characters we need to add. The modulo 3 of val.size() the remainder, but what we really want is the difference between val.size() and the next number divisible by three. Since we have the remainder we can just subtract the remainder from 3, but that leaves 3 in the case that we want 0, so we have to modulo 3 one more time.
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/algorithm/string.hpp>
std::string decode64(const std::string &val) {
using namespace boost::archive::iterators;
using It = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
return boost::algorithm::trim_right_copy_if(std::string(It(std::begin(val)), It(std::end(val))), [](char c) {
return c == '\0';
});
}
std::string encode64(const std::string &val) {
using namespace boost::archive::iterators;
using It = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
auto tmp = std::string(It(std::begin(val)), It(std::end(val)));
return tmp.append((3 - val.size() % 3) % 3, '=');
}
I improved the example in the link you provided a little:
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/insert_linebreaks.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/ostream_iterator.hpp>
#include <sstream>
#include <string>
#include <iostream>
int main()
{
using namespace boost::archive::iterators;
std::string test = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce ornare ullamcorper ipsum ac gravida.";
std::stringstream os;
typedef
insert_linebreaks< // insert line breaks every 72 characters
base64_from_binary< // convert binary values to base64 characters
transform_width< // retrieve 6 bit integers from a sequence of 8 bit bytes
const char *,
6,
8
>
>
,72
>
base64_text; // compose all the above operations in to a new iterator
std::copy(
base64_text(test.c_str()),
base64_text(test.c_str() + test.size()),
ostream_iterator<char>(os)
);
std::cout << os.str();
}
This prints the string encoded base64 nicely formated with a line break every 72 characters onto the console, ready to be put into an email. If you don't like the linebreaks, just stay with this:
typedef
base64_from_binary<
transform_width<
const char *,
6,
8
>
>
base64_text;
You could use beast's implementation.
For boost version 1.71, the functions are:
boost::beast::detail::base64::encode()
boost::beast::detail::base64::encoded_size()
boost::beast::detail::base64::decode()
boost::beast::detail::base64::decoded_size()
From #include <boost/beast/core/detail/base64.hpp>
For older versions back to beast's inclusion in 1.66, the functions are:
boost::beast::detail::base64_encode()
boost::beast::detail::base64_decode()
From #include <boost/beast/core/detail/base64.hpp>
Another solution using boost base64 encode decode:
const std::string base64_padding[] = {"", "==","="};
std::string base64_encode(const std::string& s) {
namespace bai = boost::archive::iterators;
std::stringstream os;
// convert binary values to base64 characters
typedef bai::base64_from_binary
// retrieve 6 bit integers from a sequence of 8 bit bytes
<bai::transform_width<const char *, 6, 8> > base64_enc; // compose all the above operations in to a new iterator
std::copy(base64_enc(s.c_str()), base64_enc(s.c_str() + s.size()),
std::ostream_iterator<char>(os));
os << base64_padding[s.size() % 3];
return os.str();
}
std::string base64_decode(const std::string& s) {
namespace bai = boost::archive::iterators;
std::stringstream os;
typedef bai::transform_width<bai::binary_from_base64<const char *>, 8, 6> base64_dec;
unsigned int size = s.size();
// Remove the padding characters, cf. https://svn.boost.org/trac/boost/ticket/5629
if (size && s[size - 1] == '=') {
--size;
if (size && s[size - 1] == '=') --size;
}
if (size == 0) return std::string();
std::copy(base64_dec(s.data()), base64_dec(s.data() + size),
std::ostream_iterator<char>(os));
return os.str();
}
And here are the test cases:
std::string t_e[TESTSET_SIZE] = {
""
, "M"
, "Ma"
, "Man"
, "pleasure."
, "leasure."
, "easure."
, "asure."
, "sure."
};
std::string t_d[TESTSET_SIZE] = {
""
, "TQ=="
, "TWE="
, "TWFu"
, "cGxlYXN1cmUu"
, "bGVhc3VyZS4="
, "ZWFzdXJlLg=="
, "YXN1cmUu"
, "c3VyZS4="
};
Hope this helps
For anyone coming here from Google, here's my base64 encode/decode functions based off boost. It handles padding correctly as per DanDan's comment above. The decode functions stops when it encounters an illegal character, and returns a pointer to that character, which is great if you're parsing base64 in json or xml.
///
/// Convert up to len bytes of binary data in src to base64 and store it in dest
///
/// \param dest Destination buffer to hold the base64 data.
/// \param src Source binary data.
/// \param len The number of bytes of src to convert.
///
/// \return The number of characters written to dest.
/// \remarks Does not store a terminating null in dest.
///
uint base64_encode(char* dest, const char* src, uint len)
{
char tail[3] = {0,0,0};
typedef base64_from_binary<transform_width<const char *, 6, 8> > base64_enc;
uint one_third_len = len/3;
uint len_rounded_down = one_third_len*3;
uint j = len_rounded_down + one_third_len;
std::copy(base64_enc(src), base64_enc(src + len_rounded_down), dest);
if (len_rounded_down != len)
{
uint i=0;
for(; i < len - len_rounded_down; ++i)
{
tail[i] = src[len_rounded_down+i];
}
std::copy(base64_enc(tail), base64_enc(tail + 3), dest + j);
for(i=len + one_third_len + 1; i < j+4; ++i)
{
dest[i] = '=';
}
return i;
}
return j;
}
///
/// Convert null-terminated string src from base64 to binary and store it in dest.
///
/// \param dest Destination buffer
/// \param src Source base64 string
/// \param len Pointer to unsigned int representing size of dest buffer. After function returns this is set to the number of character written to dest.
///
/// \return Pointer to first character in source that could not be converted (the terminating null on success)
///
const char* base64_decode(char* dest, const char* src, uint* len)
{
uint output_len = *len;
typedef transform_width<binary_from_base64<const char*>, 8, 6> base64_dec;
uint i=0;
try
{
base64_dec src_it(src);
for(; i < output_len; ++i)
{
*dest++ = *src_it;
++src_it;
}
}
catch(dataflow_exception&)
{
}
*len = i;
return src + (i+2)/3*4; // bytes in = bytes out / 3 rounded up * 4
}
While the encoding works, the decoder is certainly broken. Also there is a bug opened: https://svn.boost.org/trac/boost/ticket/5629.
I have not found a fix for that.
This is another answer:
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
std::string ToBase64(const std::vector<unsigned char>& binary)
{
using namespace boost::archive::iterators;
using It = base64_from_binary<transform_width<std::vector<unsigned char>::const_iterator, 6, 8>>;
auto base64 = std::string(It(binary.begin()), It(binary.end()));
// Add padding.
return base64.append((3 - binary.size() % 3) % 3, '=');
}
std::vector<unsigned char> FromBase64(const std::string& base64)
{
using namespace boost::archive::iterators;
using It = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
auto binary = std::vector<unsigned char>(It(base64.begin()), It(base64.end()));
// Remove padding.
auto length = base64.size();
if(binary.size() > 2 && base64[length - 1] == '=' && base64[length - 2] == '=')
{
binary.erase(binary.end() - 2, binary.end());
}
else if(binary.size() > 1 && base64[length - 1] == '=')
{
binary.erase(binary.end() - 1, binary.end());
}
return binary;
}
Base64 encode text and data
const std::string base64_padding[] = {"", "==","="};
std::string base64EncodeText(std::string text) {
using namespace boost::archive::iterators;
typedef std::string::const_iterator iterator_type;
typedef base64_from_binary<transform_width<iterator_type, 6, 8> > base64_enc;
std::stringstream ss;
std::copy(base64_enc(text.begin()), base64_enc(text.end()), ostream_iterator<char>(ss));
ss << base64_padding[text.size() % 3];
return ss.str();
}
std::string base64EncodeData(std::vector<uint8_t> data) {
using namespace boost::archive::iterators;
typedef std::vector<uint8_t>::const_iterator iterator_type;
typedef base64_from_binary<transform_width<iterator_type, 6, 8> > base64_enc;
std::stringstream ss;
std::copy(base64_enc(data.begin()), base64_enc(data.end()), ostream_iterator<char>(ss));
ss << base64_padding[data.size() % 3];
return ss.str();
}
I modified the Answer 8 because it's not functional on my platform.
const std::string base64_padding[] = {"", "==","="};
std::string *m_ArchiveData;
/// \brief To Base64 string
bool Base64Encode(string* output)
{
try
{
UInt32 iPadding_Mask = 0;
typedef boost::archive::iterators::base64_from_binary
<boost::archive::iterators::transform_width<const char *, 6, 8> > Base64EncodeIterator;
UInt32 len = m_ArchiveData->size();
std::stringstream os;
std::copy(Base64EncodeIterator(m_ArchiveData->c_str()),
Base64EncodeIterator(m_ArchiveData->c_str()+len),
std::ostream_iterator<char>(os));
iPadding_Mask = m_ArchiveData->size() % 3;
os << base64_padding[iPadding_Pask];
*output = os.str();
return output->empty() == false;
}
catch (...)
{
PLOG_ERROR_DEV("unknown error happens");
return false;
}
}
/// \brief From Base64 string
bool mcsf_data_header_byte_stream_archive::Base64Decode(const std::string *input)
{
try
{
std::stringstream os;
bool bPaded = false;
typedef boost::archive::iterators::transform_width<boost::archive::iterators::
binary_from_base64<const char *>, 8, 6> Base64DecodeIterator;
UInt32 iLength = input->length();
// Remove the padding characters, cf. https://svn.boost.org/trac/boost/ticket/5629
if (iLength && (*input)[iLength-1] == '=') {
bPaded = true;
--iLength;
if (iLength && (*input)[iLength - 1] == '=')
{
--iLength;
}
}
if (iLength == 0)
{
return false;
}
if(bPaded)
{
iLength --;
}
copy(Base64DecodeIterator(input->c_str()) ,
Base64DecodeIterator(input->c_str()+iLength),
ostream_iterator<char>(os));
*m_ArchiveData = os.str();
return m_ArchiveData->empty() == false;
}
catch (...)
{
PLOG_ERROR_DEV("unknown error happens");
return false;
}
}