mirror of
https://github.com/jezhiggins/arabica
synced 2025-01-17 18:12:04 +01:00
Base64 code conversion facet
This commit is contained in:
parent
4747203622
commit
ebfbb594e3
2 changed files with 277 additions and 0 deletions
209
Utils/base64_codecvt.cpp
Normal file
209
Utils/base64_codecvt.cpp
Normal file
|
@ -0,0 +1,209 @@
|
|||
///////////////////////////////////////////
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
///////////////////////////////////////////
|
||||
|
||||
#include "base64_codecvt.h"
|
||||
|
||||
static const std::string base64_charset("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
|
||||
static const int NO_MORE = 256;
|
||||
|
||||
base64_codecvt::~base64_codecvt()
|
||||
{
|
||||
} // ~base64_codecvt
|
||||
|
||||
std::codecvt_base::result base64_codecvt::do_out(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 base64_codecvt::do_in(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 c = base64_charset.find(*from_next++);
|
||||
if(c == std::string::npos)
|
||||
continue;
|
||||
|
||||
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 base64_codecvt::do_unshift(mbstate_t& state,
|
||||
char* to,
|
||||
char* /* to_limit */,
|
||||
char*& to_next) const
|
||||
{
|
||||
to_next = to;
|
||||
state = 0;
|
||||
return codecvt_base::ok;
|
||||
} // do_unshift
|
||||
|
||||
mbstate_t base64_codecvt::do_encoding() const throw()
|
||||
{
|
||||
return 0;
|
||||
} // do_encoding
|
||||
|
||||
bool base64_codecvt::do_always_noconv() const throw()
|
||||
{
|
||||
return false;
|
||||
} // do_always_noconv
|
||||
|
||||
mbstate_t base64_codecvt::do_length(const 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
|
||||
|
||||
mbstate_t base64_codecvt::do_max_length() const throw()
|
||||
{
|
||||
return 2;
|
||||
} // do_max_length
|
||||
|
||||
mbstate_t base64_codecvt::getState() const
|
||||
{
|
||||
return (*state_) & 0xff;
|
||||
} // getState
|
||||
|
||||
void base64_codecvt::nextState() const
|
||||
{
|
||||
mbstate_t s = getState();
|
||||
if(++s == 4)
|
||||
s = 0;
|
||||
|
||||
*state_ &= 0xffffff00;
|
||||
*state_ |= s;
|
||||
} // nextOutState
|
||||
|
||||
int base64_codecvt::getCurrentOutChar() const
|
||||
{
|
||||
if(*from_next_ != *from_end_)
|
||||
return **from_next_;
|
||||
|
||||
return NO_MORE;
|
||||
} // getCurrentOutChar
|
||||
|
||||
char base64_codecvt::getPreviousChar() const
|
||||
{
|
||||
return static_cast<char>((*state_ &0xff00) >> 8);
|
||||
} // getCurrentOutChar
|
||||
|
||||
void base64_codecvt::setPreviousChar(char c) const
|
||||
{
|
||||
int bc(c);
|
||||
bc <<= 8;
|
||||
bc &= 0xff00;
|
||||
|
||||
*state_ &= 0xffff00ff;
|
||||
*state_ |= bc;
|
||||
} // setPreviousOutChar
|
||||
|
||||
void base64_codecvt::consumeOutChar() const
|
||||
{
|
||||
if(*from_next_ != *from_end_)
|
||||
{
|
||||
setPreviousChar(**from_next_);
|
||||
*from_next_ = *from_next_ + 1;
|
||||
}
|
||||
else
|
||||
setPreviousChar(0);
|
||||
} // consumeOutChar
|
||||
|
||||
// end of file
|
68
Utils/base64_codecvt.h
Normal file
68
Utils/base64_codecvt.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
#ifndef base64_codecvtH
|
||||
#define base64_codecvtH
|
||||
///////////////////////////////////////////
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
///////////////////////////////////////////
|
||||
|
||||
#include <locale>
|
||||
|
||||
class base64_codecvt : public std::codecvt<char, char, mbstate_t>
|
||||
{
|
||||
protected:
|
||||
virtual ~base64_codecvt();
|
||||
|
||||
virtual result do_out(mbstate_t& state,
|
||||
const char* from,
|
||||
const char* from_end,
|
||||
const char*& from_next,
|
||||
char* to,
|
||||
char* to_limit,
|
||||
char*& to_next) const;
|
||||
|
||||
virtual result do_in(mbstate_t& state,
|
||||
const char* from,
|
||||
const char* from_end,
|
||||
const char*& from_next,
|
||||
char* to,
|
||||
char* to_limit,
|
||||
char*& to_next) const;
|
||||
|
||||
virtual result do_unshift(mbstate_t& state,
|
||||
char* to,
|
||||
char* to_limit,
|
||||
char*& to_next) const;
|
||||
|
||||
virtual mbstate_t do_encoding() const throw();
|
||||
|
||||
virtual bool do_always_noconv() const throw();
|
||||
|
||||
virtual mbstate_t do_length(const mbstate_t&,
|
||||
const char* from,
|
||||
const char* end,
|
||||
size_t max) const;
|
||||
|
||||
virtual mbstate_t do_max_length() const throw();
|
||||
|
||||
private:
|
||||
// state here is a little tricky - we need the previous char and
|
||||
// the state counter, and in some case we need to "pad" the input
|
||||
// strings. I use these helper functions to mungle them
|
||||
// together and keep the details neater (or try to anyway)
|
||||
mutable mbstate_t* state_;
|
||||
mutable const char** from_next_;
|
||||
mutable const char** from_end_;
|
||||
void grabState(mbstate_t& state) const { state_ = &state; }
|
||||
void grabFromNext(const char*& from_next) const { from_next_ = &from_next; }
|
||||
void grabFromEnd(const char*& from_end) const { from_end_ = &from_end; }
|
||||
|
||||
mbstate_t getState() const;
|
||||
void nextState() const;
|
||||
int getCurrentOutChar() const;
|
||||
void consumeOutChar() const;
|
||||
char getPreviousChar() const;
|
||||
void setPreviousChar(char c) const;
|
||||
}; // class base64_codecvt
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue