mirror of
https://github.com/jezhiggins/arabica
synced 2024-12-27 21:58:30 +01:00
220 lines
5.2 KiB
C++
220 lines
5.2 KiB
C++
///////////////////////////////////////////
|
|
//
|
|
// $Id$
|
|
//
|
|
///////////////////////////////////////////
|
|
|
|
#include "base64codecvt.h"
|
|
|
|
static const std::string base64_charset("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
|
|
static const int NO_MORE = 256;
|
|
|
|
using namespace Arabica::convert;
|
|
|
|
std::codecvt_base::result base64codecvt::do_out(std::mbstate_t& state,
|
|
const char* from,
|
|
const char* from_end,
|
|
const char*& from_next,
|
|
char* to,
|
|
char* to_limit,
|
|
char*& to_next) const
|
|
{
|
|
// convert to Base64
|
|
grabState(state);
|
|
grabFromNext(from_next);
|
|
grabFromEnd(from_end);
|
|
|
|
// base64 encode
|
|
from_next = from;
|
|
to_next = to;
|
|
|
|
// generate the quad
|
|
while((to_next != to_limit) && !((getCurrentOutChar() == NO_MORE) && (getPreviousChar() == 0)))
|
|
{
|
|
char c = getCurrentOutChar();
|
|
switch(getState())
|
|
{
|
|
case 0:
|
|
*to_next++ = base64_charset[(c>>2) & 63];
|
|
consumeOutChar();
|
|
break;
|
|
case 1:
|
|
*to_next++ = base64_charset[((getPreviousChar() & 3) << 4) + ((c >> 4) & 15)];
|
|
consumeOutChar();
|
|
break;
|
|
case 2:
|
|
*to_next++ = base64_charset[((getPreviousChar() & 15) << 2) + ((c >> 6) & 3)];
|
|
setPreviousChar(0);
|
|
break;
|
|
case 3:
|
|
*to_next++ = base64_charset[(c & 63)];
|
|
consumeOutChar();
|
|
setPreviousChar(0);
|
|
break;
|
|
} // switch(s)
|
|
|
|
nextState();
|
|
|
|
if((getState() == 0) && (getCurrentOutChar() == NO_MORE) && (getPreviousChar() == 0))
|
|
break;
|
|
} // while((from_next < from_end) && (to_next != to_end))
|
|
|
|
// add padding if needed
|
|
while((getState() != 0) && (to_next != to_limit))
|
|
{
|
|
*to_next++ = '=';
|
|
nextState();
|
|
} // while((state != 4) && (to_next != to_limit))
|
|
|
|
return ((getState() == 0) && (getCurrentOutChar() == NO_MORE) && (getPreviousChar() == 0)) ? std::codecvt_base::ok : std::codecvt_base::partial;
|
|
} // do_out
|
|
|
|
std::codecvt_base::result base64codecvt::do_in(std::mbstate_t& state,
|
|
const char* from,
|
|
const char* from_end,
|
|
const char*& from_next,
|
|
char* to,
|
|
char* to_limit,
|
|
char*& to_next) const
|
|
{
|
|
// decode Base64
|
|
grabState(state);
|
|
from_next = from;
|
|
to_next = to;
|
|
|
|
while((from_next != from_end) && (to != to_limit))
|
|
{
|
|
char b = *from_next++;
|
|
size_t i = base64_charset.find(b);
|
|
if(i == std::string::npos)
|
|
{
|
|
if(b == '=')
|
|
nextState();
|
|
continue;
|
|
}
|
|
char c = static_cast<char>(i);
|
|
|
|
char p = getPreviousChar();
|
|
switch(getState())
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
*to_next++ = (p << 2) | (c >> 4);
|
|
break;
|
|
case 2:
|
|
*to_next++ = (p << 4) | (c >> 2);
|
|
break;
|
|
case 3:
|
|
*to_next++ = (p << 6) | c;
|
|
break;
|
|
} // switch(getState())
|
|
setPreviousChar(c);
|
|
nextState();
|
|
} // while(to != to_limit)
|
|
|
|
return (from_next == from_end) ? std::codecvt_base::ok : std::codecvt_base::partial;
|
|
} // do_in
|
|
|
|
std::codecvt_base::result base64codecvt::do_unshift(std::mbstate_t& state,
|
|
char* to,
|
|
char* to_limit,
|
|
char*& to_next) const
|
|
{
|
|
to_next = to;
|
|
|
|
// add padding if needed
|
|
while((getState() != 0) && (to_next != to_limit))
|
|
{
|
|
*to_next++ = '=';
|
|
nextState();
|
|
} // while((state != 4) && (to_next != to_limit))
|
|
|
|
return (to_next == to_limit) ? codecvt_base::ok : codecvt_base::partial;
|
|
} // do_unshift
|
|
|
|
int base64codecvt::do_length(const std::mbstate_t&,
|
|
const char* from,
|
|
const char* end,
|
|
size_t max) const
|
|
{
|
|
// 4 base64 chars = 3 chars
|
|
size_t length(end - from);
|
|
size_t quads(((length-1) / 4) + 1);
|
|
size_t chars(quads*3);
|
|
|
|
// htis next bit isn't exactly right, but it's close enough
|
|
while(chars > max)
|
|
{
|
|
length -= 4;
|
|
chars -= 3;
|
|
} // while(chars > max)
|
|
|
|
return length;
|
|
} // do_length
|
|
|
|
int base64codecvt::do_max_length() const throw()
|
|
{
|
|
return 2;
|
|
} // do_max_length
|
|
|
|
void base64codecvt::grabState(std::mbstate_t& state) const
|
|
{
|
|
state_ = reinterpret_cast<int*>(&state);
|
|
int s = getState();
|
|
if((s < 0) || (s > 4))
|
|
*state_ = 0;
|
|
} // grabState
|
|
|
|
int base64codecvt::getState() const
|
|
{
|
|
return (*state_) & 0xff;
|
|
} // getState
|
|
|
|
void base64codecvt::nextState() const
|
|
{
|
|
int s = getState();
|
|
s = s + 1;
|
|
if(s == 4)
|
|
s = 0;
|
|
|
|
*state_ &= 0xffffff00;
|
|
*state_ |= s;
|
|
} // nextOutState
|
|
|
|
int base64codecvt::getCurrentOutChar() const
|
|
{
|
|
if(*from_next_ != *from_end_)
|
|
return **from_next_;
|
|
|
|
return NO_MORE;
|
|
} // getCurrentOutChar
|
|
|
|
char base64codecvt::getPreviousChar() const
|
|
{
|
|
return static_cast<char>((*state_ &0xff00) >> 8);
|
|
} // getCurrentOutChar
|
|
|
|
void base64codecvt::setPreviousChar(char c) const
|
|
{
|
|
int bc(c);
|
|
bc <<= 8;
|
|
bc &= 0xff00;
|
|
|
|
*state_ &= 0xffff00ff;
|
|
*state_ |= bc;
|
|
} // setPreviousOutChar
|
|
|
|
void base64codecvt::consumeOutChar() const
|
|
{
|
|
if(*from_next_ != *from_end_)
|
|
{
|
|
setPreviousChar(**from_next_);
|
|
*from_next_ = *from_next_ + 1;
|
|
}
|
|
else
|
|
setPreviousChar(0);
|
|
} // consumeOutChar
|
|
|
|
// end of file
|
|
|