#include "encode.h"

#define PADDING '='

unsigned char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
                      "abcdefghijklmnopqrstuvwxyz" \
                      "0123456789" \
                      "+/";

int
encode(unsigned char *op, int olen, unsigned char *sp)
{
    int n;
    unsigned char *sb;

    sb = sp;
    switch (olen) {
        case 3:
            *sp++ = b64[(*op & ~3) >> 2];
            n = (*op++ & 3) << 4;
            *sp++ = b64[n + ((*op & ~15) >> 4)];
            n = (*op++ & 15) << 2;
            *sp++ = b64[n + ((*op & 192) >> 6)];
            *sp = b64[*op & ~192];
            break;
        case 2:
            *sp++ = b64[(*op & ~3) >> 2];
            n = (*op++ & 3) << 4;
            *sp++ = b64[n + ((*op & ~15) >> 4)];
            *sp++ = b64[(*op & 15) << 2];
            *sp = PADDING;
            break;
        case 1:
            *sp++ = b64[(*op & ~3) >> 2];
            *sp++ = b64[(*op & 3) << 4];
            *sp++ = PADDING;
            *sp = PADDING;
            break;
    }

    return sp-sb+1;
}

int
decode(unsigned char *sp, int slen, unsigned char *op)
{
    int n, b, atob(int c);
    unsigned char *ob;

    ob = op;
    switch (slen) {
        case 4:
            n = atob(*sp++) << 2;
            b = atob(*sp++);
            *op++ = n + ((b & ~15) >> 4);

            n = (b & 15) << 4;
            b = atob(*sp++);
            *op++ = n + ((b & ~3) >> 2);

            *op = ((b & 3) << 6) + atob(*sp);
            break;
        case 3:
            n = atob(*sp++) << 2;
            b = atob(*sp++);
            *op++ = n + ((b & ~15) >> 4);

            n = (b & 15) << 4;
            *op = n + ((atob(*sp) & ~3) >> 2);
            break;
        case 2:
            n = atob(*sp++) << 2;
            *op = n + ((atob(*sp) & ~15) >> 4);
            break;
    }

    return op-ob+1;
}

int atob(int c)
{
    if (c >= 'A' && c <= 'Z')
        c -= 'A';
    else if (c >= 'a' && c <= 'z')
        c = c - 'a' + 26;
    else if (c >= '0' && c <= '9')
        c = c - '0' + 26 * 2;
    else if (c == '+')
        c = 62;
    else
        c = 63;

    return c;
}