#include <stddef.h>
#include <stdint.h>
#include "types.h"
#include "base64.h"

static const u8 base64f[256] = {
	//00    01    02    03    04    05    06    07
	//08    09    0A    0B    0C    0D    0E    0F
	// 0x00..0x3F
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x00
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x08
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x18
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20
	0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F, // 0x28
	0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, // 0x30
	0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38
	// 0x40..0x7F
	0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // 0x40
	0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 0x48
	0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 0x50
	0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58
	0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, // 0x60
	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, // 0x68
	0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, // 0x70
	0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x78
	// 0x80..0xBF
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	// 0xC0..0xFF
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};

size_t base64_from(u8 *dst,const char *src,size_t srclen)
{
	if (srclen % 4) {
		return -1;
	} else if (!srclen) {
		return 0;
	}

	size_t dstlen = BASE64_FROM_LEN(srclen);
	dstlen -= (src[srclen - 1] == '=');
	dstlen -= (src[srclen - 2] == '=');

	for (size_t i = 0, j = 0; i < srclen;) {
		u32 sixbits[4];

		sixbits[0] = base64f[(unsigned char)src[i++]];
		sixbits[1] = base64f[(unsigned char)src[i++]];
		sixbits[2] = (src[i] == '=' ? (0 & i++) : base64f[(unsigned char)src[i++]]);
		sixbits[3] = (src[i] == '=' ? (0 & i++) : base64f[(unsigned char)src[i++]]);

		u32 threebytes = 0
			| (sixbits[0] << (3 * 6))
			| (sixbits[1] << (2 * 6))
			| (sixbits[2] << (1 * 6))
			| (sixbits[3] << (0 * 6));

		if (j < dstlen) dst[j++] = (threebytes >> (2 * 8));
		if (j < dstlen) dst[j++] = (threebytes >> (1 * 8)) & 0xff;
		if (j < dstlen) dst[j++] = (threebytes >> (0 * 8)) & 0xff;
	}
	return dstlen;
}

int base64_valid(const char *src,size_t *count)
{
	const char *p;

	for (p = src;base64f[(u8)*p] != 0xFF;++p)
		;

	for (;((size_t) (p - src)) % 4 != 0 && *p == '=';++p)
		;

	if (count)
		*count = (size_t) (p - src);
	return !*p && ((size_t) (p - src)) % 4 == 0;
}
