/*
								+----------------------------------+
								|                                  |
								|  ***   Base64 coder class   ***  |
								|                                  |
								|   Copyright © -tHE SWINe- 2009   |
								|                                  |
								|            Base64.inl            |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __BASE64_CODING_INLINES_INCLUDED
#define __BASE64_CODING_INLINES_INCLUDED

/**
 *	@file Base64.inl
 *	@author -tHE SWINe-
 *	@date 2009
 *	@brief Base-64 encoding implementations
 */

#include "MinMax.h"

/*
 *								=== CBase64 ===
 */

inline size_t CBase64::n_Max_EncodeSize()
{
	size_t n_max_size = TBuffer::n_Max_Size();
	// maximal encoded buffer size

#ifndef BASE64_NO_WHITESPACE
	size_t n_line_count = n_max_size / 66;
	n_max_size -= 2 * n_line_count;
	// subtract size of cr/lf characters to get size of Base-64 data
#endif // BASE64_NO_WHITESPACE

	return n_max_size / 4 * 3;
	// get size of input data, round down
}

size_t CBase64::n_EncodedSize(size_t n_input)
{
	__FuncGuard("CBase64::n_EncodedSize");

	_ASSERTE(n_input <= n_Max_EncodeSize());
	size_t n_size = ((n_input + 2) / 3) * 4;
	// calculate size of raw Base-64

#ifndef BASE64_NO_WHITESPACE
	n_size += 2 * (n_size / 64);
	// MIME requires newline after each 64 characters
#endif // BASE64_NO_WHITESPACE

	return n_size * sizeof(char);
}

size_t CBase64::n_DecodedSize_UpperBound(size_t n_input)
{
	n_input /= sizeof(char);
	return n_input / 4 * 3 + ((n_input % 4)? 3 : 0); // round-up, never overflows (as (n_input + 3) / 4 * 3 does)
}

inline bool CBase64::Encode(size_t n_dest_size, void *p_dest, const TBuffer &r_t_src)
{
	return Encode(n_dest_size, p_dest, r_t_src.n_Size(), r_t_src.p_Data());
	// encode it
}

inline bool CBase64::Encode(TBuffer &r_t_dest, const TBuffer &r_t_src)
{
	return Encode(r_t_dest, r_t_src.n_Size(), r_t_src.p_Data());
	// encode it
}

inline bool CBase64::Encode(std::string &r_s_dest, const TBuffer &r_t_src)
{
	return Encode(r_s_dest, r_t_src.n_Size(), r_t_src.p_Data());
	// encode it
}

inline bool CBase64::Encode(TBuffer &r_t_dest, size_t n_src_size, const void *p_src_data)
{
	size_t n_output = n_EncodedSize(n_src_size);
	// calculate dest buffer size (6 bits of source information
	// encoded to 8 bits of output, round up)

	if(!r_t_dest.Resize(n_output, false))
		return false;
	// alloc dest buffer

	return Encode(r_t_dest.n_Size(), r_t_dest.p_Data(), n_src_size, p_src_data);
	// encode it
}

inline bool CBase64::Encode(std::string &r_s_dest, size_t n_src_size, const void *p_src_data)
{
	size_t n_output = n_EncodedSize(n_src_size);
	// calculate dest buffer size (6 bits of source information
	// encoded to 8 bits of output, round up)

	r_s_dest.erase();
	if(!stl_ut::Resize_To_N(r_s_dest, n_output))
		return false;
	// alloc dest buffer

	return Encode(r_s_dest.length() * sizeof(char), &r_s_dest[0], n_src_size, p_src_data);
	// encode it
}

inline size_t CBase64::n_Decode(TBuffer &r_t_dest, const TBuffer &r_t_src)
{
	return n_Decode(r_t_dest, r_t_src.n_Size(), r_t_src.p_Data());
	// decode data into the buffer
}

inline size_t CBase64::n_Decode(TBuffer &r_t_dest, size_t n_src_size, const void *p_src_data)
{
	size_t n_output = n_DecodedSize_UpperBound(n_src_size);
	// calculate dest buffer size

	if(!r_t_dest.Resize(n_output, false))
		return 0;
	// alloc dest buffer

	size_t n_result = n_Decode(r_t_dest.n_Size(), r_t_dest.p_Data(), n_src_size, p_src_data);
	r_t_dest.Resize(n_result);
	// decode data into the buffer

	return n_result;
}

inline size_t CBase64::n_Decode(TBuffer &r_t_dest, const std::string &r_s_src)
{
	return n_Decode(r_t_dest, r_s_src.size(), r_s_src.data());
	// decode data into the buffer
}

inline size_t CBase64::n_Decode(TBuffer &r_t_dest, const char *p_s_src)
{
	return n_Decode(r_t_dest, strlen(p_s_src), p_s_src);
	// decode data into the buffer
}

inline size_t CBase64::n_Decode(size_t n_dest_size, void *p_dest, const TBuffer &r_t_src)
{
	return n_Decode(n_dest_size, p_dest, r_t_src.n_Size(), r_t_src.p_Data());
	// decode data into the buffer
}

inline size_t CBase64::n_Decode(size_t n_dest_size, void *p_dest, const std::string &r_s_src)
{
	return n_Decode(n_dest_size, p_dest, r_s_src.size(), r_s_src.data());
	// decode data into the buffer
}

inline size_t CBase64::n_Decode(size_t n_dest_size, void *p_dest, const char *p_s_src)
{
	return n_Decode(n_dest_size, p_dest, strlen(p_s_src), p_s_src);
	// decode data into the buffer
}

/*
 *								=== ~CBase64 ===
 */

/*
 *								=== CModifiedBase64::CInverseTable ===
 */

inline const int *CModifiedBase64::CInverseTable::p_Table() const
{
	return m_p_decode;
}

/*
 *								=== ~CModifiedBase64::CInverseTable ===
 */

/*
 *								=== CModifiedBase64 ===
 */

inline size_t CModifiedBase64::n_Max_EncodeSize()
{
	return n_DecodedSize(TBuffer::n_Max_Size());
}

size_t CModifiedBase64::n_EncodedSize(size_t n_input)
{
	__FuncGuard("CModifiedBase64::n_EncodedSize");

	/*
		AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD input bytes
		00000011 11112222 22333333 44444455 output bytes

		11111111 22222222 33333333 44444444 input size

		00000000 00000000 44444444 44444444 (input size / 3) * 4
		22222222 33333333 00000000 22222222 (input size % 3)? input size % 3 + 1 : 0

		22222222 33333333 44444444 66666666 output size
	*/

	_ASSERTE(n_input <= n_Max_EncodeSize());
	return ((n_input / 3) * 4 + ((n_input % 3)? n_input % 3 + 1 : 0)) * sizeof(char);
}

size_t CModifiedBase64::n_DecodedSize(size_t n_input)
{
	/*
		AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD input bytes
		00000011 11112222 22333333 44444455 output bytes

		22222222 33333333 44444444 66666666 output size

		00000000 00000000 33333333 33333333 (output size / 4) * 3
		11111111 22222222 00000000 11111111 max((output size % 4) - 1, 0)

		11111111 22222222 33333333 44444444 input size
	*/

	n_input /= sizeof(char);
	return (n_input / 4) * 3 + max((n_input % 4), size_t(1)) - 1;
}

inline const char *CModifiedBase64::p_s_DefaultEncodeChars()
{
	return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
}

inline const int *CModifiedBase64::p_DefaultDecodeTable()
{
	return m_default_inverse_table.p_Table();
}

inline bool CModifiedBase64::Encode(size_t n_dest_size, void *p_dest, const TBuffer &r_t_src,
	const char *p_s_encode_chars /*= p_s_DefaultEncodeChars()*/)
{
	return Encode(n_dest_size, p_dest, r_t_src.n_Size(), r_t_src.p_Data(), p_s_encode_chars);
	// encode it
}

inline bool CModifiedBase64::Encode(TBuffer &r_t_dest, const TBuffer &r_t_src,
	const char *p_s_encode_chars /*= p_s_DefaultEncodeChars()*/)
{
	return Encode(r_t_dest, r_t_src.n_Size(),
		r_t_src.p_Data(), p_s_encode_chars);
	// encode it
}

inline bool CModifiedBase64::Encode(std::string &r_s_dest, const TBuffer &r_t_src,
	const char *p_s_encode_chars /*= p_s_DefaultEncodeChars()*/)
{
	return Encode(r_s_dest, r_t_src.n_Size(), r_t_src.p_Data(), p_s_encode_chars);
	// encode it
}

inline bool CModifiedBase64::Encode(TBuffer &r_t_dest, size_t n_src_size, const void *p_src_data,
	const char *p_s_encode_chars /*= p_s_DefaultEncodeChars()*/)
{
	size_t n_output = n_EncodedSize(n_src_size);
	_ASSERTE(n_DecodedSize(n_output) == n_src_size);
	// calculate dest buffer size (6 bits of source information
	// encoded to 8 bits of output, round up)

	if(!r_t_dest.Resize(n_output, false))
		return false;
	// alloc dest buffer

	return Encode(r_t_dest.n_Size(), r_t_dest.p_Data(), n_src_size, p_src_data, p_s_encode_chars);
	// encode it
}

inline bool CModifiedBase64::Encode(std::string &r_s_dest, size_t n_src_size, const void *p_src_data,
	const char *p_s_encode_chars /*= p_s_DefaultEncodeChars()*/)
{
	size_t n_output = n_EncodedSize(n_src_size);
	_ASSERTE(n_DecodedSize(n_output) == n_src_size);
	// calculate dest buffer size (6 bits of source information
	// encoded to 8 bits of output, round up)

	r_s_dest.erase();
	if(!stl_ut::Resize_To_N(r_s_dest, n_output))
		return false;
	// alloc dest buffer

	return Encode(r_s_dest.length() * sizeof(char), &r_s_dest[0],
		n_src_size, p_src_data, p_s_encode_chars);
	// encode it
}

inline bool CModifiedBase64::Decode(TBuffer &r_t_dest, const TBuffer &r_t_src,
	const int *p_inverse_coding_table /*= p_DefaultDecodeTable()*/)
{
	return Decode(r_t_dest, r_t_src.n_Size(),
		r_t_src.p_Data(), p_inverse_coding_table);
	// decode data into the buffer
}

inline bool CModifiedBase64::Decode(TBuffer &r_t_dest, size_t n_src_size, const void *p_src_data,
	const int *p_inverse_coding_table /*= p_DefaultDecodeTable()*/)
{
	size_t n_output = n_DecodedSize(n_src_size);
	_ASSERTE(n_EncodedSize(n_output) == n_src_size);
	// calculate dest buffer size

	if(!r_t_dest.Resize(n_output, false))
		return false;
	// alloc dest buffer

	return Decode(r_t_dest.n_Size(), r_t_dest.p_Data(),
		n_src_size, p_src_data, p_inverse_coding_table);
	// decode data into the buffer
}

inline bool CModifiedBase64::Decode(TBuffer &r_t_dest, const std::string &r_s_src,
	const int *p_inverse_coding_table /*= p_DefaultDecodeTable()*/)
{
	return Decode(r_t_dest, r_s_src.size(),
		r_s_src.data(), p_inverse_coding_table);
	// decode data into the buffer
}

inline bool CModifiedBase64::Decode(TBuffer &r_t_dest, const char *p_s_src,
	const int *p_inverse_coding_table /*= p_DefaultDecodeTable()*/)
{
	return Decode(r_t_dest, strlen(p_s_src), p_s_src, p_inverse_coding_table);
	// decode data into the buffer
}

inline bool CModifiedBase64::Decode(size_t n_dest_size, void *p_dest, const TBuffer &r_t_src,
	const int *p_inverse_coding_table /*= p_DefaultDecodeTable()*/)
{
	return Decode(n_dest_size, p_dest, r_t_src.n_Size(),
		r_t_src.p_Data(), p_inverse_coding_table);
	// decode data into the buffer
}

inline bool CModifiedBase64::Decode(size_t n_dest_size, void *p_dest, const std::string &r_s_src,
	const int *p_inverse_coding_table /*= p_DefaultDecodeTable()*/)
{
	return Decode(n_dest_size, p_dest, r_s_src.size(), r_s_src.data(), p_inverse_coding_table);
	// decode data into the buffer
}

inline bool CModifiedBase64::Decode(size_t n_dest_size, void *p_dest, const char *p_s_src,
	const int *p_inverse_coding_table /*= p_DefaultDecodeTable()*/)
{
	return Decode(n_dest_size, p_dest, strlen(p_s_src), p_s_src, p_inverse_coding_table);
	// decode data into the buffer
}

/*
 *								=== ~CModifiedBase64 ===
 */

#endif // !__BASE64_CODING_INLINES_INCLUDED
