X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=src%2Fcpp%2Fbase64.cpp;fp=src%2Fcpp%2Fbase64.cpp;h=f9c0dacd37cf1608660cdf3f684c63dad4a261db;hb=ce67d0cdeaa37c3e856e23ae4010480887165630;hp=0000000000000000000000000000000000000000;hpb=e355d4e7962400470f467b88f5568de9c8324475;p=xmlrpc-c diff --git a/src/cpp/base64.cpp b/src/cpp/base64.cpp new file mode 100644 index 0000000..f9c0dac --- /dev/null +++ b/src/cpp/base64.cpp @@ -0,0 +1,234 @@ +#include +#include +#include +#include + +#include "xmlrpc-c/girerr.hpp" +using girerr::error; +using girerr::throwf; +#include "xmlrpc-c/base64.hpp" + +using namespace std; +using namespace xmlrpc_c; + + +namespace { + +char const table_a2b_base64[] = { + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, /* Note PAD->0 */ + -1, 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,-1, -1,-1,-1,-1, + -1,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,-1, -1,-1,-1,-1 +}; + +char const base64Pad('='); +size_t const base64MaxChunkSize(57); + // Max binary chunk size (76 character line) +#define BASE64_LINE_SZ 128 /* Buffer size for a single line. */ + +unsigned char const table_b2a_base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +} // namespace + + + +class bitBuffer { +public: + bitBuffer() : bitsInBuffer(0) {}; + + void + shiftIn8Bits(unsigned char const newBits) { + // Shift in 8 bits to the right end of the buffer + + this->buffer = (this->buffer << 8) | newBits; + this->bitsInBuffer += 8; + + assert(this->bitsInBuffer <= 12); + } + + void + shiftIn6Bits(unsigned char const newBits) { + // Shift in 6 bits to the right end of the buffer + + this->buffer = (this->buffer << 6) | newBits; + this->bitsInBuffer += 6; + + assert(this->bitsInBuffer <= 12); + } + + void + shiftOut6Bits(unsigned char * const outputP) { + // Shift out 6 bits from the left end of the buffer + + assert(bitsInBuffer >= 6); + + *outputP = (this->buffer >> (this->bitsInBuffer - 6)) & 0x3f; + this->bitsInBuffer -= 6; + } + + void + shiftOut8Bits(unsigned char * const outputP) { + // Shift out 8 bits from the left end of the buffer + + assert(bitsInBuffer >= 8); + + *outputP = (this->buffer >> (this->bitsInBuffer - 8)) & 0x3f; + this->bitsInBuffer -= 8; + } + + void + shiftOutResidue(unsigned char * const outputP) { + // Shift out the residual 2 or 4 bits, padded on the right with 0 + // to 6 bits. + + while (this->bitsInBuffer < 6) { + this->buffer <<= 2; + this->bitsInBuffer += 2; + } + + this->shiftOut6Bits(outputP); + } + + void + discardResidue() { + assert(bitsInBuffer < 8); + + this->bitsInBuffer = 0; + } + + unsigned int + bitCount() { + return bitsInBuffer; + } + +private: + unsigned int buffer; + unsigned int bitsInBuffer; +}; + + +namespace xmlrpc_c { + + +static void +encodeChunk(vector const& bytes, + size_t const lineStart, + size_t const chunkSize, + string * const outputP) { + + bitBuffer buffer; + // A buffer in which we accumulate bits (up to 12 bits) + // until we have enough (6) for a base64 character. + + // I suppose this would be more efficient with an iterator on + // 'bytes' and/or *outputP. I'd have to find out how to use one. + + for (size_t linePos = 0; linePos < chunkSize; ++linePos) { + // Shift the data into our buffer + buffer.shiftIn8Bits(bytes[lineStart + linePos]); + + // Encode any complete 6 bit groups + while (buffer.bitCount() >= 6) { + unsigned char theseBits; + buffer.shiftOut6Bits(&theseBits); + outputP->append(1, table_b2a_base64[theseBits]); + } + } + if (buffer.bitCount() > 0) { + // Handle residual bits in the buffer + unsigned char theseBits; + buffer.shiftOutResidue(&theseBits); + + outputP->append(1, table_b2a_base64[theseBits]); + + // Pad to a multiple of 4 characters (24 bits) + assert(outputP->length() % 4 > 0); + outputP->append(4-outputP->length() % 4, base64Pad); + } else { + assert(outputP->length() % 4 == 0); + } +} + + + +string +base64FromBytes(vector const& bytes, + newlineCtl const newlineCtl) { + + string retval; + + if (bytes.size() == 0) { + if (newlineCtl == NEWLINE_YES) + retval = "\r\n"; + else + retval = ""; + } else { + // It would be good to preallocate retval. Need to look up + // how to do that. + for (size_t chunkStart = 0; + chunkStart < bytes.size(); + chunkStart += base64MaxChunkSize) { + + size_t const chunkSize( + min(base64MaxChunkSize, bytes.size() - chunkStart)); + + encodeChunk(bytes, chunkStart, chunkSize, &retval); + + if (newlineCtl == NEWLINE_YES) + // Append a courtesy crlf + retval += "\r\n"; + } + } + return retval; +} + + + +vector +bytesFromBase64(string const& base64) { + + vector retval; + bitBuffer buffer; + unsigned int npad; + + npad = 0; // No pad characters seen yet + + for (unsigned int cursor = 0; cursor < base64.length(); ++cursor) { + char const thisChar(base64[cursor] & 0x7f); + + if (thisChar == '\r' || thisChar == '\n' || thisChar == ' ') { + // ignore this punctuation + } else { + if (thisChar == base64Pad) { + // This pad character is here to synchronize a chunk to + // a multiple of 24 bits (4 base64 characters; 3 bytes). + buffer.discardResidue(); + } else { + unsigned int const tableIndex(thisChar); + if (table_a2b_base64[tableIndex] == -1) + throwf("Contains non-base64 character " + "with ASCII code 0x%02x", thisChar); + + buffer.shiftIn6Bits(table_a2b_base64[tableIndex]); + + if (buffer.bitCount() >= 8) { + unsigned char thisByte; + buffer.shiftOut8Bits(&thisByte); + retval.push_back(thisByte); + } + } + } + } + + if (buffer.bitCount() > 0) + throwf("Not a multiple of 4 characters"); + + return retval; +} + +} //namespace