--- /dev/null
+#include <cassert>
+#include <string>
+#include <vector>
+#include <bitset>
+
+#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<unsigned char> 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<unsigned char> 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<unsigned char>
+bytesFromBase64(string const& base64) {
+
+ vector<unsigned char> 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