/*---------------------------------------------------------------------------*/
/* base64.cpp                                                                */
/* copyright (c) innovaphone 2002, 2007                                      */
/*                                                                           */
/* BASE64 encoding/decoding                                                  */
/*---------------------------------------------------------------------------*/

#include <platform.h>
#include "../ilib/base64.h"

NAMESPACE_BEGIN

unsigned decode_base64_bin(const char * in, byte * out, unsigned size_of_out)
{
    dword triple=0;
    int i=18;
    int padding=0;
    byte cin, cout=0;
    unsigned r = 0;

    while (size_of_out>0 && (cin=*in++)) {
        if      (cin>='A' && cin<='Z') cout=cin-'A';
        else if (cin>='a' && cin<='z') cout=cin-'a'+26;
        else if (cin>='0' && cin<='9') cout=cin-'0'+52;
        else if (cin=='+') cout=62;
        else if (cin=='/') cout=63;
        else if (cin=='=') padding+=8;
        else break;
        triple += (cout<<i);
        if(i==0) {
            for(i=16; i>=padding; i-=8) {
                *out++ = triple>>i;
                r++;
                if (--size_of_out<=0) return r;
            }
            i=18;
            triple=0;
        }
        else {
            i-=6;
        }
    }
    return r;
}

dword decode_base64(const char * in, byte * out, int n)
{
    dword triple=0;
    int i=18;
    int padding=0;
    byte cin, cout=0;
	dword r = 0;

    --n;
    while (n>=0 && (cin=*in++)) {
        if(cin>='A' && cin<='Z') cout=cin-'A';
        if(cin>='a' && cin<='z') cout=cin-'a'+26;
        if(cin>='0' && cin<='9') cout=cin-'0'+52;
        if(cin=='+') cout=62;
        if(cin=='/') cout=63;
        if(cin=='=') padding+=8;
        triple=triple+(cout<<i);
        if(i==0) {
			for(i=16;i>=padding;i-=8,n--) {
                *out++=triple>>i;
				r++;
			}
            i=18;
            triple=0;
        }
        else
            i-=6;
    }
    *out=0;
	return r;
}

dword encode_base64(const byte * in, char * out, int len) 
{
    static const char cb64 [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    char * o = out;
    while (len>0) {
        out[0] = cb64[ in[0] >> 2 ];
        out[1] = cb64[ ((in[0] & 0x03) << 4) | (len > 1 ? ((in[1] & 0xf0) >> 4) : 0) ];
        out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | (len > 2 ? ((in[2] & 0xc0) >> 6) : 0)] : '=');
        out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
        out +=4;
        in  +=3;
        len -=3;
    }
    *out=0;
    return (dword)(out-o);
}

dword encode_base64_buffer_len(dword len)
{
    return ((len + 3) * 4) / 3 + 1;
}

unsigned decode_base64url_bin(const char * in, byte * out, unsigned size_of_out)
{
	dword triple = 0;
	int i = 18;
	int padding;
	byte cin, cout = 0;
	unsigned r = 0;

	while (size_of_out>0 && (cin = *in++)) {
		if (cin >= 'A' && cin <= 'Z') cout = cin - 'A';
		else if (cin >= 'a' && cin <= 'z') cout = cin - 'a' + 26;
		else if (cin >= '0' && cin <= '9') cout = cin - '0' + 52;
		else if (cin == '-') cout = 62;
		else if (cin == '_') cout = 63;
		else return r;
		triple += (cout << i);
		if (i == 0) {
			for (i = 16; i >= 0; i -= 8) {
				*out++ = triple >> i;
				r++;
				if (--size_of_out <= 0) return r;
			}
			i = 18;
			triple = 0;
		}
		else {
			i -= 6;
		}
	}
	padding = i + 6;
	for (i = 16; i >= padding; i -= 8) {
		*out++ = triple >> i;
		r++;
		if (--size_of_out <= 0) return r;
	}
	return r;
}

dword decode_base64url(const char * in, byte * out, int n)
{
    dword triple=0;
    int i=18;
    int padding;
    byte cin, cout=0;
	dword r = 0;

    --n;
    while (n>=0 && (cin=*in++)) {
        if(cin>='A' && cin<='Z') cout=cin-'A';
        if(cin>='a' && cin<='z') cout=cin-'a'+26;
        if(cin>='0' && cin<='9') cout=cin-'0'+52;
        if(cin=='-') cout=62;
        if(cin=='_') cout=63;
        triple=triple+(cout<<i);
        if(i==0) {
			for(i=16;i>=0;i-=8,n--) {
                *out++=triple>>i;
				r++;
			}
            i=18;
            triple=0;
        }
        else
            i-=6;
    }
    padding=i+6;
	for(i=16;i>=padding;i-=8,n--) {
        *out++=triple>>i;
		r++;
	}
    *out=0;
	return r;
}

dword encode_base64url(const byte * in, char * out, int len) 
{
    static const char cb64 [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
    char * o = out;
    while (len>0) {
        *(out++) = cb64[ in[0] >> 2 ];
        *(out++) = cb64[ ((in[0] & 0x03) << 4) | (len > 1 ? ((in[1] & 0xf0) >> 4) : 0) ];
        if (len > 1) *(out++) = (unsigned char) (cb64[ ((in[1] & 0x0f) << 2) | (len > 2 ? ((in[2] & 0xc0) >> 6) : 0)]);
        if (len > 2) *(out++) = cb64[ in[2] & 0x3f ];
        in  +=3;
        len -=3;
    }
    *out=0;
    return (dword)(out-o);
}

NAMESPACE_END
