#include "base64.h"

static const char BASE64_CHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

int base64_encode(char *out, const uint8_t *xin, int N)
{
    int i, j;
    uint32_t tmp;
    for (i = 0, j = 0; i < N; i += 3)
    {
        tmp = (xin[i] >> 2) & 0x3F;
        out[j++] = BASE64_CHARS[tmp];
        tmp = (xin[i] << 4) & 0x30;
        if (i + 1 >= N)
        {
            out[j++] = BASE64_CHARS[tmp];
            out[j++] = '=';
            out[j++] = '=';
            break;
        }
        tmp |= (xin[i + 1] >> 4) & 0x0F;
        out[j++] = BASE64_CHARS[tmp];
        tmp = (xin[i + 1] << 2) & 0x3C;
        if (i + 2 >= N)
        {
            out[j++] = BASE64_CHARS[tmp];
            out[j++] = '=';
            break;
        }
        tmp |= (xin[i + 2] >> 6) & 0x03;
        out[j++] = BASE64_CHARS[tmp];
        tmp = (xin[i + 2]) & 0x3F;
        out[j++] = BASE64_CHARS[tmp];
    }
    out[j] = '\0';
    return j;
}

int base64_decode(uint8_t *out, const char *xin)
{
    int i, j, k;
    uint8_t temp[4];
    for (i = 0, j = 0; xin[i] != '\0'; i += 4)
    {
        memset(temp, 0xFF, sizeof(temp));
        for (k = 0; k < 64; k++)
        {
            if (BASE64_CHARS[k] == xin[i + 0])
                temp[0] = k;
            if (BASE64_CHARS[k] == xin[i + 1])
                temp[1] = k;
            if (BASE64_CHARS[k] == xin[i + 2])
                temp[2] = k;
            if (BASE64_CHARS[k] == xin[i + 3])
                temp[3] = k;
        }
        out[j++] = ((uint8_t)(((uint8_t)(temp[0] << 2)) & 0xFC)) | ((uint8_t)((uint8_t)(temp[1] >> 4) & 0x03));
        if (xin[i + 2] == '=')
            break;
        out[j++] = ((uint8_t)(((uint8_t)(temp[1] << 4)) & 0xF0)) | ((uint8_t)((uint8_t)(temp[2] >> 2) & 0x0F));
        if (xin[i + 3] == '=')
            break;
        out[j++] = ((uint8_t)(((uint8_t)(temp[2] << 6)) & 0xF0)) | ((uint8_t)(temp[3] & 0x3F));
    }
    return j;
}