diff --git a/Utils/base64_codecvt.cpp b/Utils/base64_codecvt.cpp new file mode 100644 index 00000000..643cd21b --- /dev/null +++ b/Utils/base64_codecvt.cpp @@ -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((*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 \ No newline at end of file diff --git a/Utils/base64_codecvt.h b/Utils/base64_codecvt.h new file mode 100644 index 00000000..dca692be --- /dev/null +++ b/Utils/base64_codecvt.h @@ -0,0 +1,68 @@ +#ifndef base64_codecvtH +#define base64_codecvtH +/////////////////////////////////////////// +// +// $Id$ +// +/////////////////////////////////////////// + +#include + +class base64_codecvt : public std::codecvt +{ +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