// intellectual property is bullshit bgdc

#include "ascii85.h"

#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>

#define ASCII85_BASE 33

static inline uint8_t divmod85(uint32_t * x)
{
	uint8_t mod = *x % 85;
	*x /= 85;
	return mod;
}

size_t ascii85_encode(uint8_t * dest, uint8_t const * src, size_t len)
{
	size_t total = 0;

	while (len >= 4)
	{
		uint32_t n = (
			((uint32_t) src[0] << 24uL) |
			((uint32_t) src[1] << 16uL) |
			((uint32_t) src[2] << 8uL) |
			((uint32_t) src[3] << 0uL));

		dest[4] = divmod85(&n) + ASCII85_BASE;
		dest[3] = divmod85(&n) + ASCII85_BASE;
		dest[2] = divmod85(&n) + ASCII85_BASE;
		dest[1] = divmod85(&n) + ASCII85_BASE;
		dest[0] = n + ASCII85_BASE; // Guaranteed < 85

		src += 4;
		dest += 5;
		len -= 4;
		total += 5;
	}

	if (len)
	{
		uint32_t n = 0;
		n = (uint32_t) src[0] << 24uL;
		if (len > 1) n |= ((uint32_t) src[1] << 16uL);
		if (len > 2) n |= ((uint32_t) src[2] << 8uL);
		if (len > 3) n |= ((uint32_t) src[3] << 0uL);

		char a85[5];
		a85[4] = divmod85(&n) + ASCII85_BASE;
		a85[3] = divmod85(&n) + ASCII85_BASE;
		a85[2] = divmod85(&n) + ASCII85_BASE;
		a85[1] = divmod85(&n) + ASCII85_BASE;
		a85[0] = n + ASCII85_BASE; // As above

		dest[0] = a85[0];
		dest[1] = a85[1];
		dest[2] = (len > 1) ? a85[2] : 0;
		dest[3] = (len > 2) ? a85[3] : 0;
		dest[4] = 0; // if we get here we're dropping at least one

		total += len + 1;
	}

	return total;
}

size_t ascii85_decode(uint8_t * dest, uint8_t const * src, size_t len)
{
	size_t total = 0;

	while (len >= 5)
	{
		uint32_t n = src[0] - ASCII85_BASE;
		n = 85 * n + src[1] - ASCII85_BASE;
		n = 85 * n + src[2] - ASCII85_BASE;
		n = 85 * n + src[3] - ASCII85_BASE;
		n = 85 * n + src[4] - ASCII85_BASE;

		dest[0] = (n >> 24uL) & 0xFF;
		dest[1] = (n >> 16uL) & 0xFF;
		dest[2] = (n >> 8uL) & 0xFF;
		dest[3] = (n >> 0uL) & 0xFF;

		src += 5;
		dest += 4;
		len -= 5;
		total += 4;
	}

	if (len > 1)
	{
		uint32_t n = src[0] - ASCII85_BASE;
		n = 85 * n + src[1] - ASCII85_BASE;
		n = 85 * n + (len > 2 ? src[2] : 'u') - ASCII85_BASE;
		n = 85 * n + (len > 3 ? src[3] : 'u') - ASCII85_BASE;
		n = 85 * n;

		dest[0] = (n >> 24uL) & 0xFF;
		if (len > 2) dest[1] = (n >> 16uL) & 0xFF;
		if (len > 3) dest[2] = (n >> 8uL) & 0xFF;

		total += len - 1;
	}

	return total;
}
