
//*********************************************************************
//* Base64 - a simple base64 encoder and decoder.
//*
//*     Copyright (c) 1999, Bob Withers - bwit@pobox.com
//*
//* This code may be freely used for any purpose, either personal
//* or commercial, provided the authors copyright notice remains
//* intact.
//*
//* Enhancements by Stanley Yamane:
//*     o reverse lookup table for the decode function
//*     o reserve string buffer space in advance
//*
//*********************************************************************

#include "talk/base/base64.h"

using namespace std;

static const char          fillchar = '=';
static const string::size_type np = string::npos;

const string Base64::Base64Table(
   // 0000000000111111111122222222223333333333444444444455555555556666
   // 0123456789012345678901234567890123456789012345678901234567890123
     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");

// Decode Table gives the index of any valid base64 character in the Base64 table]
// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == /

                                               // 0  1  2  3  4  5  6  7  8  9
const string::size_type Base64::DecodeTable[] = {
      np,np,np,np,np,np,np,np,np,np,  // 0 - 9
      np,np,np,np,np,np,np,np,np,np,  //10 -19
      np,np,np,np,np,np,np,np,np,np,  //20 -29
      np,np,np,np,np,np,np,np,np,np,  //30 -39
      np,np,np,62,np,np,np,63,52,53,  //40 -49
      54,55,56,57,58,59,60,61,np,np,  //50 -59
      np,np,np,np,np, 0, 1, 2, 3, 4,  //60 -69
       5, 6, 7, 8, 9,10,11,12,13,14,  //70 -79
      15,16,17,18,19,20,21,22,23,24,  //80 -89
      25,np,np,np,np,np,np,26,27,28,  //90 -99
      29,30,31,32,33,34,35,36,37,38,  //100 -109
      39,40,41,42,43,44,45,46,47,48,  //110 -119
      49,50,51,np,np,np,np,np,np,np,  //120 -129
      np,np,np,np,np,np,np,np,np,np,  //130 -139
      np,np,np,np,np,np,np,np,np,np,  //140 -149
      np,np,np,np,np,np,np,np,np,np,  //150 -159
      np,np,np,np,np,np,np,np,np,np,  //160 -169
      np,np,np,np,np,np,np,np,np,np,  //170 -179
      np,np,np,np,np,np,np,np,np,np,  //180 -189
      np,np,np,np,np,np,np,np,np,np,  //190 -199
      np,np,np,np,np,np,np,np,np,np,  //200 -209
      np,np,np,np,np,np,np,np,np,np,  //210 -219
      np,np,np,np,np,np,np,np,np,np,  //220 -229
      np,np,np,np,np,np,np,np,np,np,  //230 -239
      np,np,np,np,np,np,np,np,np,np,  //240 -249
      np,np,np,np,np,np               //250 -256
};

string Base64::encodeFromArray(const char * data, size_t len) {
    size_t             i;
    char               c;
    string             ret;

    ret.reserve(len * 2);

    for (i = 0; i < len; ++i)
    {
        c = (data[i] >> 2) & 0x3f;
        ret.append(1, Base64Table[c]);
        c = (data[i] << 4) & 0x3f;
        if (++i < len)
            c |= (data[i] >> 4) & 0x0f;

        ret.append(1, Base64Table[c]);
        if (i < len)
        {
            c = (data[i] << 2) & 0x3f;
            if (++i < len)
                c |= (data[i] >> 6) & 0x03;

            ret.append(1, Base64Table[c]);
        }
        else
        {
            ++i;
            ret.append(1, fillchar);
        }

        if (i < len)
        {
            c = data[i] & 0x3f;
            ret.append(1, Base64Table[c]);
        }
        else
        {
            ret.append(1, fillchar);
        }
    }

    return(ret);
}


string Base64::encode(const string& data)
{
    string::size_type  i;
    char               c;
    string::size_type  len = data.length();
    string             ret;

    ret.reserve(len * 2);

    for (i = 0; i < len; ++i)
    {
        c = (data[i] >> 2) & 0x3f;
        ret.append(1, Base64Table[c]);
        c = (data[i] << 4) & 0x3f;
        if (++i < len)
            c |= (data[i] >> 4) & 0x0f;

        ret.append(1, Base64Table[c]);
        if (i < len)
        {
            c = (data[i] << 2) & 0x3f;
            if (++i < len)
                c |= (data[i] >> 6) & 0x03;

            ret.append(1, Base64Table[c]);
        }
        else
        {
            ++i;
            ret.append(1, fillchar);
        }

        if (i < len)
        {
            c = data[i] & 0x3f;
            ret.append(1, Base64Table[c]);
        }
        else
        {
            ret.append(1, fillchar);
        }
    }

    return(ret);
}

string Base64::decode(const string& data)
{
    string::size_type  i;
    char               c;
    char               c1;
    string::size_type  len = data.length();
    string             ret;

    ret.reserve(len);

    for (i = 0; i < len; ++i)
    {
        c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
        ++i;
        c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
        c = (c << 2) | ((c1 >> 4) & 0x3);
        ret.append(1, c);
        if (++i < len)
        {
            c = data[i];
            if (fillchar == c)
                break;

            c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
            c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
            ret.append(1, c1);
        }

        if (++i < len)
        {
            c1 = data[i];
            if (fillchar == c1)
                break;

            c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
            c = ((c << 6) & 0xc0) | c1;
            ret.append(1, c);
        }
    }

    return(ret);
}
