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

/**
 *	@file Base64.cpp
 *	@author -tHE SWINe-
 *	@date 2009
 *	@brief Base-64 encoding implementations
 *
 *	@date 2009-09-29
 *
 *	added CBase64 class, implementing RFC 1421 (RFC 4648 if BASE64_NO_WHITESPACE is defined)
 *
 *	the difference between CModifiedBase64 and CBase64 being CBase64 has fixed alphabet and
 *	when encoding, it pads output with '=' characters so output size is divisible by four.
 *	also, (if BASE64_NO_WHITESPACE is not defined) CBase64 adds line end/feed character pair
 *	so no output line has more than 64 (printable - not counting cr/lf) characters on it.
 *	when decoding, CBase64 ignores whitespace characters (if BASE64_NO_WHITESPACE is not
 *	defined) in the input stream and implements canonical checks (if BASE64_CANONICAL_CHECK
 *	is defined; see RFC). these efforts are to simplify processing by automatically
 *	adding / removing whitespace and padding. note it does not ignore characters other than
 *	whitespace, therefore making "covert channel" data unlikely to pass (it would have to
 *	be encoded as series of spaces in BASE64 data). it adds some issues about calculating
 *	size of decoded output - there may be arbitrary amount of whitespace - so the function
 *	returns upper bound instead, making CBase64 able to encode a bit less data (2.91 GB)
 *	than CModifiedBase64 is (3 GB), this is due to 4 GB memory cap for x86 applications.
 *
 *	on the other side CBase64 is a bit more practical / simpler to use.
 *
 *	note RFC 4648 can be effectively implemented with CModifiedBase64 using default alphabet
 *	and manually adding / removing padding characters.
 *
 *	@date 2009-10-11
 *
 *	replaced stl container ::resize() by stl_ut::Resize_*() to avoid unhandled
 *	std::bad_alloc
 *
 */

#include "NewFix.h"
#include "CallStack.h"
#include <string.h>
#include <stdlib.h>
#include <string>
#include "Buffer.h"
#include "MinMax.h"
#include "Base64.h"

#if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(for) && _MSC_VER <= 1200
#define for if(0) {} else for
#endif // _MSC_VER && !__MWERKS__ && !for && _MSC_VER <= 1200
// msvc 'for' scoping hack

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

template <const int n_char>
class CB64 {
public:
	enum {
		n_index = (n_char >= 'A' && n_char <= 'Z')? n_char - 'A' :
			((n_char >= 'a' && n_char <= 'z')? n_char - 'a' + 26 :
			((n_char >= '0' && n_char <= '9')? n_char - '0' + 52 :
			((n_char == '+')? 62 : ((n_char == '/')? 63 : -1))))
	};
};

const int CBase64::m_p_inverse_table[256] = {
	CB64<0>::n_index, CB64<1>::n_index, CB64<2>::n_index, CB64<3>::n_index,
	CB64<4>::n_index, CB64<5>::n_index, CB64<6>::n_index, CB64<7>::n_index,
	CB64<8>::n_index, CB64<9>::n_index, CB64<10>::n_index, CB64<11>::n_index,
	CB64<12>::n_index, CB64<13>::n_index, CB64<14>::n_index, CB64<15>::n_index,
	CB64<16>::n_index, CB64<17>::n_index, CB64<18>::n_index, CB64<19>::n_index,
	CB64<20>::n_index, CB64<21>::n_index, CB64<22>::n_index, CB64<23>::n_index,
	CB64<24>::n_index, CB64<25>::n_index, CB64<26>::n_index, CB64<27>::n_index,
	CB64<28>::n_index, CB64<29>::n_index, CB64<30>::n_index, CB64<31>::n_index,
	CB64<32>::n_index, CB64<33>::n_index, CB64<34>::n_index, CB64<35>::n_index,
	CB64<36>::n_index, CB64<37>::n_index, CB64<38>::n_index, CB64<39>::n_index,
	CB64<40>::n_index, CB64<41>::n_index, CB64<42>::n_index, CB64<43>::n_index,
	CB64<44>::n_index, CB64<45>::n_index, CB64<46>::n_index, CB64<47>::n_index,
	CB64<48>::n_index, CB64<49>::n_index, CB64<50>::n_index, CB64<51>::n_index,
	CB64<52>::n_index, CB64<53>::n_index, CB64<54>::n_index, CB64<55>::n_index,
	CB64<56>::n_index, CB64<57>::n_index, CB64<58>::n_index, CB64<59>::n_index,
	CB64<60>::n_index, CB64<61>::n_index, CB64<62>::n_index, CB64<63>::n_index,
	CB64<64>::n_index, CB64<65>::n_index, CB64<66>::n_index, CB64<67>::n_index,
	CB64<68>::n_index, CB64<69>::n_index, CB64<70>::n_index, CB64<71>::n_index,
	CB64<72>::n_index, CB64<73>::n_index, CB64<74>::n_index, CB64<75>::n_index,
	CB64<76>::n_index, CB64<77>::n_index, CB64<78>::n_index, CB64<79>::n_index,
	CB64<80>::n_index, CB64<81>::n_index, CB64<82>::n_index, CB64<83>::n_index,
	CB64<84>::n_index, CB64<85>::n_index, CB64<86>::n_index, CB64<87>::n_index,
	CB64<88>::n_index, CB64<89>::n_index, CB64<90>::n_index, CB64<91>::n_index,
	CB64<92>::n_index, CB64<93>::n_index, CB64<94>::n_index, CB64<95>::n_index,
	CB64<96>::n_index, CB64<97>::n_index, CB64<98>::n_index, CB64<99>::n_index,
	CB64<100>::n_index, CB64<101>::n_index, CB64<102>::n_index, CB64<103>::n_index,
	CB64<104>::n_index, CB64<105>::n_index, CB64<106>::n_index, CB64<107>::n_index,
	CB64<108>::n_index, CB64<109>::n_index, CB64<110>::n_index, CB64<111>::n_index,
	CB64<112>::n_index, CB64<113>::n_index, CB64<114>::n_index, CB64<115>::n_index,
	CB64<116>::n_index, CB64<117>::n_index, CB64<118>::n_index, CB64<119>::n_index,
	CB64<120>::n_index, CB64<121>::n_index, CB64<122>::n_index, CB64<123>::n_index,
	CB64<124>::n_index, CB64<125>::n_index, CB64<126>::n_index, CB64<127>::n_index,
	CB64<128>::n_index, CB64<129>::n_index, CB64<130>::n_index, CB64<131>::n_index,
	CB64<132>::n_index, CB64<133>::n_index, CB64<134>::n_index, CB64<135>::n_index,
	CB64<136>::n_index, CB64<137>::n_index, CB64<138>::n_index, CB64<139>::n_index,
	CB64<140>::n_index, CB64<141>::n_index, CB64<142>::n_index, CB64<143>::n_index,
	CB64<144>::n_index, CB64<145>::n_index, CB64<146>::n_index, CB64<147>::n_index,
	CB64<148>::n_index, CB64<149>::n_index, CB64<150>::n_index, CB64<151>::n_index,
	CB64<152>::n_index, CB64<153>::n_index, CB64<154>::n_index, CB64<155>::n_index,
	CB64<156>::n_index, CB64<157>::n_index, CB64<158>::n_index, CB64<159>::n_index,
	CB64<160>::n_index, CB64<161>::n_index, CB64<162>::n_index, CB64<163>::n_index,
	CB64<164>::n_index, CB64<165>::n_index, CB64<166>::n_index, CB64<167>::n_index,
	CB64<168>::n_index, CB64<169>::n_index, CB64<170>::n_index, CB64<171>::n_index,
	CB64<172>::n_index, CB64<173>::n_index, CB64<174>::n_index, CB64<175>::n_index,
	CB64<176>::n_index, CB64<177>::n_index, CB64<178>::n_index, CB64<179>::n_index,
	CB64<180>::n_index, CB64<181>::n_index, CB64<182>::n_index, CB64<183>::n_index,
	CB64<184>::n_index, CB64<185>::n_index, CB64<186>::n_index, CB64<187>::n_index,
	CB64<188>::n_index, CB64<189>::n_index, CB64<190>::n_index, CB64<191>::n_index,
	CB64<192>::n_index, CB64<193>::n_index, CB64<194>::n_index, CB64<195>::n_index,
	CB64<196>::n_index, CB64<197>::n_index, CB64<198>::n_index, CB64<199>::n_index,
	CB64<200>::n_index, CB64<201>::n_index, CB64<202>::n_index, CB64<203>::n_index,
	CB64<204>::n_index, CB64<205>::n_index, CB64<206>::n_index, CB64<207>::n_index,
	CB64<208>::n_index, CB64<209>::n_index, CB64<210>::n_index, CB64<211>::n_index,
	CB64<212>::n_index, CB64<213>::n_index, CB64<214>::n_index, CB64<215>::n_index,
	CB64<216>::n_index, CB64<217>::n_index, CB64<218>::n_index, CB64<219>::n_index,
	CB64<220>::n_index, CB64<221>::n_index, CB64<222>::n_index, CB64<223>::n_index,
	CB64<224>::n_index, CB64<225>::n_index, CB64<226>::n_index, CB64<227>::n_index,
	CB64<228>::n_index, CB64<229>::n_index, CB64<230>::n_index, CB64<231>::n_index,
	CB64<232>::n_index, CB64<233>::n_index, CB64<234>::n_index, CB64<235>::n_index,
	CB64<236>::n_index, CB64<237>::n_index, CB64<238>::n_index, CB64<239>::n_index,
	CB64<240>::n_index, CB64<241>::n_index, CB64<242>::n_index, CB64<243>::n_index,
	CB64<244>::n_index, CB64<245>::n_index, CB64<246>::n_index, CB64<247>::n_index,
	CB64<248>::n_index, CB64<249>::n_index, CB64<250>::n_index, CB64<251>::n_index,
	CB64<252>::n_index, CB64<253>::n_index, CB64<254>::n_index, CB64<255>::n_index,
};

/*
 *	static bool CBase64::Encode(void *p_dest, size_t n_src_size)
 *		- generic data encoding routine; encodes contents of p_src_data buffer and stores
 *		  it in p_dest buffer
 *		- p_dest is destination buffer (must be allocated to n_EncodedSize(n_src_size) bytes)
 *		- n_src_size is input data size (must be less or equal to n_Max_EncodeSize() bytes)
 *		- p_src_data is buffer, containing data to be encoded
 *		- note encoding increases data size in 4:3 ratio, so there's n_Max_EncodeSize() limit
 *		  (for x86 application it's 3GB, so the result wouldn't exceed 4GB)
 *		- returns true on success, false on failure (input buffer size
 *		  exceeding n_Max_EncodeSize())
 */
bool CBase64::Encode(size_t n_dest_size, void *p_dest, size_t n_src_size, const void *p_src_data)
{
	__FuncGuard("CBase64::Encode");

	_ASSERTE(p_dest);
	_ASSERTE(p_src_data);
	if(n_src_size > n_Max_EncodeSize() || n_dest_size / sizeof(char) < n_EncodedSize(n_src_size))
		return false;
	// maximal input data size check

	const char *p_s_encode_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	// encode characters

	int n_line_length = 0;
	// line length counter

	const uint8_t *p_src = (const uint8_t*)p_src_data;
	uint8_t *p_dst = (uint8_t*)p_dest;
	size_t n_triplets_size = n_src_size - n_src_size % 3;
	for(const uint8_t *p_end = p_src + n_triplets_size;
	   p_src != p_end; p_src += 3, p_dst += 4) {
#ifndef BASE64_NO_WHITESPACE
		if(n_line_length ++ == 64 / 4) {
			p_dst[0] = '\r';
			p_dst[1] = '\n';
			p_dst += 2;
			n_line_length = 1; // !!
		}
		// maintain 64 chatracters long lines (16 quadruples)
#endif // BASE64_NO_WHITESPACE

		uint32_t n_24bits = ((p_src[0]) << 16) | ((p_src[1]) << 8) | p_src[2];
		p_dst[0] = p_s_encode_chars[n_24bits >> 18];
		p_dst[1] = p_s_encode_chars[(n_24bits >> 12) & 0x3f];
		p_dst[2] = p_s_encode_chars[(n_24bits >> 6) & 0x3f];
		p_dst[3] = p_s_encode_chars[n_24bits & 0x3f];
	}
	// process triplets of input data, generating quadruples of output

	if(n_src_size - n_triplets_size) {
#ifndef BASE64_NO_WHITESPACE
		if(n_line_length ++ == 64 / 4) {
			p_dst[0] = '\r';
			p_dst[1] = '\n';
			p_dst += 2;
			//n_line_length = 1; // this doesn't matter anymore
		}
		// maintain 64 chatracters long lines (16 quadruples)
#endif // BASE64_NO_WHITESPACE

		if(n_src_size - n_triplets_size == 2) {
			uint32_t n_24bits = ((p_src[0]) << 16) | ((p_src[1]) << 8) /*| 0xff*/; // 0xff trips canonical checks in n_Decode
			p_dst[0] = p_s_encode_chars[n_24bits >> 18];
			p_dst[1] = p_s_encode_chars[(n_24bits >> 12) & 0x3f];
			p_dst[2] = p_s_encode_chars[(n_24bits >> 6) & 0x3f];
			p_dst[3] = '=';
			p_dst += 4;
			p_src += 2;
		} else /*if(n_src_size - n_triplets_size == 1)*/ {
			_ASSERTE(n_src_size - n_triplets_size == 1);
			uint32_t n_24bits = ((p_src[0]) << 16) /*| 0xffff*/; // 0xffff trips canonical checks in n_Decode
			p_dst[0] = p_s_encode_chars[n_24bits >> 18];
			p_dst[1] = p_s_encode_chars[(n_24bits >> 12) & 0x3f];
			p_dst[2] = '=';
			p_dst[3] = '=';
			p_dst += 4;
			p_src += 1;
		}
	}
	// process the rest of data

	_ASSERTE(p_src == ((const uint8_t*)p_src_data) + n_src_size);
	_ASSERTE(p_dst == ((uint8_t*)p_dest) + n_EncodedSize(n_src_size));
	// make sure we used up both buffers

	return true;
}

#define _IsSpace(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n')
#define _IsBase64Char(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') || \
	((c) >= '0' && (c) <= '9') || (c) == '+' || (c) == '/')
#define _IsBase64(c) (_IsBase64Char(c) || (c) == '=')

/*
 *	static size_t CBase64::n_Decode(void *p_dest, size_t n_src_size, const void *p_src_data)
 *		- generic data decoding routine; decodes contents of p_src_data buffer and stores
 *		  it in p_dest buffer (must be allocated to n_DecodedSize_UpperBound(n_src_size) bytes)
 *		- p_dest is destination buffer
 *		- p_src_data is pointer to Base-64 encoded data
 *		- n_src_size is size of input data buffer in bytes
 *		- returns number of bytes written to dest buffer on success, zero on failure
 *		  (watch out for empty input buffer!; can't return negative number,
 *		  size_t may be unsigned)
 */
size_t CBase64::n_Decode(size_t n_dest_size, void *p_dest, size_t n_src_size, const void *p_src_data)
{
	__FuncGuard("CBase64::n_Decode");

	_ASSERTE(p_dest);
	_ASSERTE(p_src_data);
	const int *p_decode_table = m_p_inverse_table;
	// fetch pointer onto the stack

	if(n_dest_size < n_DecodedSize_UpperBound(n_src_size))
		return 0; // error

	size_t n_decoded = n_DecodedSize_UpperBound(n_src_size);
	uint8_t *p_dest_end = ((uint8_t*)p_dest) + n_decoded;

	size_t n_base64_char_num = 0; // debug

	const uint8_t *p_src = (const uint8_t*)p_src_data;
	const uint8_t *p_end = p_src + n_src_size;
	uint8_t *p_dst = (uint8_t*)p_dest;
	while(p_src != p_end) {
		int p_quad[4];
		for(int i = 0; i < 4; ++ i, ++ p_src) {
#ifndef BASE64_NO_WHITESPACE
			while(p_src != p_end && _IsSpace(*p_src))
				++ p_src;
			// skip whitespace
#endif // BASE64_NO_WHITESPACE

			if(p_src == p_end) {
				if(!i)
					return p_dst - (uint8_t*)p_dest; // that was the end of it
				return 0; // that is error
			}
			// check for end

			if(!_IsBase64(*p_src))
				return 0;
			// check for invalid characters

			++ n_base64_char_num;
			p_quad[i] = p_decode_table[*p_src];
		}
		// get four characters

		_ASSERTE(p_dst < p_dest_end);
		*p_dst ++ = (p_quad[0] << 2) | (p_quad[1] >> 4);
		if(p_quad[2] == -1) { // -1 = p_decode_table['=']
			if(p_quad[3] == -1) { // -1 = p_decode_table['=']
#ifdef BASE64_CANONICAL_CHECK
				if(p_quad[1] & 0xf)
					return 0; // some of unused bits were set (strange encoder / error in data)
#endif // BASE64_CANONICAL_CHECK
				break; // end of Base-64 data
			}
			return 0; // error
		}
		_ASSERTE(p_dst < p_dest_end);
		*p_dst ++ = (p_quad[1] << 4) | (p_quad[2] >> 2);
		if(p_quad[3] == -1) { // -1 = p_decode_table['=']
#ifdef BASE64_CANONICAL_CHECK
			if(p_quad[2] & 0x3)
				return 0; // some of unused bits were set (strange encoder / error in data)
#endif // BASE64_CANONICAL_CHECK
			break; // end of Base-64 data
		}
		_ASSERTE(p_dst < p_dest_end);
		*p_dst ++ = (p_quad[2] << 6) | p_quad[3];
		// Base-64 decode
	}

	return p_dst - (uint8_t*)p_dest;
	// return number of written characters
}

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

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

/*
 *	CModifiedBase64::CInverseTable::CInverseTable(const char *p_s_encode_chars =
 *		p_s_DefaultEncodeChars())
 *		- default constructor; creates table of 256 indices,
 *		  containing positions of characters in p_s_encode_chars, or -1 in
 *		  case character with given ordinal value doesn't occur in p_s_encode_chars
 */
CModifiedBase64::CInverseTable::CInverseTable(const char *p_s_encode_chars)
{
	for(int i = 0; i < 256; ++ i)
		m_p_decode[i] = -1;
	for(int i = 0; i < 64; ++ i)
		m_p_decode[(unsigned char)(p_s_encode_chars[i])] = i;
	// build simple inverse-mapping table
}

/*
 *	const int *CModifiedBase64::CInverseTable::p_Table() const
 *		- returns table with 256 elements
 *		- when indexed by character codes, yields zero-based index of a given
 *		  character in the string, given to constructor or -1 in case the string
 *		  didn't contain such character
 *		- note use of type int here is justified by fact that on most of today's
 *		  architectures it's faster to work with data on 4B boundaries
 */
/*const int *CModifiedBase64::CInverseTable::p_Table() const
{
	return m_p_decode;
}*/

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

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

const CModifiedBase64::CInverseTable CModifiedBase64::m_default_inverse_table;

/*
 *	static bool CModifiedBase64::Encode(void *p_dest, size_t n_src_size,
 *		const void *p_src_data, const char *p_s_encode_chars = p_s_DefaultEncodeChars())
 *		- generic data encoding routine; encodes contents of p_src_data buffer and stores
 *		  it in p_dest buffer
 *		- p_dest is destination buffer (must be allocated to n_EncodedSize(n_src_size) bytes)
 *		- n_src_size is input data size (must be less or equal to n_Max_EncodeSize() bytes)
 *		- p_src_data is buffer, containing data to be encoded
 *		- p_s_encode_chars must be string containing 64 different characters,
 *		  preferably representable using some basic encoding such as iso-8859-1
 *		  so they can be printed / viewed by pretty much any kind of system
 *		- note inverse mapping table of p_s_encode_chars is required for decoding
 *		- note encoding increases data size in 4:3 ratio, so there's n_Max_EncodeSize() limit
 *		  (for x86 application it's 3GB, so the result wouldn't exceed 4GB)
 *		- returns true on success, false on failure (input buffer size
 *		  exceeding n_Max_EncodeSize())
 */
bool CModifiedBase64::Encode(size_t n_dest_size, void *p_dest, size_t n_src_size,
	const void *p_src_data, const char *p_s_encode_chars)
{
	__FuncGuard("CModifiedBase64::Encode");

	_ASSERTE(p_dest);
	_ASSERTE(p_src_data);
	if(n_src_size > n_Max_EncodeSize() || n_dest_size / sizeof(char) < n_EncodedSize(n_src_size))
		return false;
	// maximal input data size check

	const uint8_t *p_src = (const uint8_t*)p_src_data;
	uint8_t *p_dst = (uint8_t*)p_dest;
	size_t n_triplets_size = n_src_size - n_src_size % 3;
	for(const uint8_t *p_end = p_src + n_triplets_size;
	   p_src != p_end; p_src += 3, p_dst += 4) {
		uint32_t n_24bits = ((p_src[0]) << 16) | ((p_src[1]) << 8) | p_src[2];
		p_dst[0] = p_s_encode_chars[n_24bits >> 18];
		p_dst[1] = p_s_encode_chars[(n_24bits >> 12) & 0x3f];
		p_dst[2] = p_s_encode_chars[(n_24bits >> 6) & 0x3f];
		p_dst[3] = p_s_encode_chars[n_24bits & 0x3f];
	}
	// process triplets of input data, generating quadruples of output

	if(n_src_size - n_triplets_size == 2) {
		uint32_t n_24bits = ((p_src[0]) << 16) | ((p_src[1]) << 8);
		p_dst[0] = p_s_encode_chars[n_24bits >> 18];
		p_dst[1] = p_s_encode_chars[(n_24bits >> 12) & 0x3f];
		p_dst[2] = p_s_encode_chars[(n_24bits >> 6) & 0x3f];
		p_dst += 3;
		p_src += 2;
	} else if(n_src_size - n_triplets_size == 1) {
		uint32_t n_24bits = (p_src[0]) << 16;
		p_dst[0] = p_s_encode_chars[n_24bits >> 18];
		p_dst[1] = p_s_encode_chars[(n_24bits >> 12) & 0x3f];
		p_dst += 2;
		p_src += 1;
	}
	// process the rest of data

	_ASSERTE(p_src == ((const uint8_t*)p_src_data) + n_src_size);
	_ASSERTE(p_dst == ((uint8_t*)p_dest) + n_EncodedSize(n_src_size));
	// make sure we used up both buffers

	return true;
}

/*
 *	static bool CModifiedBase64::Decode(void *p_dest, size_t n_src_size,
 *		const void *p_src_data, const int *p_inverse_coding_table = p_DefaultDecodeTable())
 *		- generic data decoding routine; decodes contents of p_src_data buffer and stores
 *		  it in p_dest buffer (must be allocated to n_DecodedSize(n_src_size) bytes)
 *		- p_dest is destination buffer
 *		- p_src_data is pointer to Base-64 encoded data
 *		- n_src_size is size of input data buffer in bytes
 *		- p_inverse_coding_table is pointer to inverse coding table, such as the one
 *		  returned by CModifiedBase64::CInverseTable::p_Table() (refer to function
 *		  documentation for further details)
 *		- returns true on success, false on failure (invalid characters being
 *		  present in input buffer)
 */
bool CModifiedBase64::Decode(size_t n_dest_size, void *p_dest, size_t n_src_size,
	const void *p_src_data, const int *p_inverse_coding_table)
{
	__FuncGuard("CModifiedBase64::Decode");

	_ASSERTE(p_dest);
	_ASSERTE(p_src_data);
	if(n_dest_size < n_DecodedSize(n_src_size))
		return false;

	const uint8_t *p_src = (const uint8_t*)p_src_data;
	uint8_t *p_dst = (uint8_t*)p_dest;
	size_t n_quadruples_size = n_src_size - n_src_size % 4;
	for(const uint8_t *p_end = p_src + n_quadruples_size;
	   p_src != p_end; p_src += 4, p_dst += 3) {
		uint32_t n_24bits = 0;
		for(int i = 0; i < 4; ++ i) {
			int n_src = p_inverse_coding_table[p_src[i]];
			if(n_src & ~63)
				return false; // detect invalid codes
			n_24bits |= n_src << (6 * (3 - i));
		}
		// translate source data

		p_dst[0] = uint8_t(n_24bits >> 16);
		p_dst[1] = uint8_t((n_24bits >> 8) & 0xff);
		p_dst[2] = uint8_t((n_24bits) & 0xff);
		// write result
	}
	// process quadruples of input data, generating triplets of output

	if(n_src_size - n_quadruples_size > 1) {
		uint32_t n_24bits = 0;
		for(size_t i = 0, n = n_src_size - n_quadruples_size; i < n; ++ i) {
			int n_src = p_inverse_coding_table[p_src[i]];
			if(n_src & ~63)
				return false; // detect invalid codes
			n_24bits |= n_src << (6 * (3 - i));
		}
		// translate source data

		if(n_src_size - n_quadruples_size == 3) {
			p_dst[0] = uint8_t(n_24bits >> 16);
			p_dst[1] = uint8_t((n_24bits >> 8) & 0xff);
			// write result

			p_src += 3;
			p_dst += 2;
		} else if(n_src_size - n_quadruples_size == 2) {
			p_dst[0] = uint8_t(n_24bits >> 16);
			// write result

			p_src += 2;
			p_dst += 1;
		}
	}
	// process the rest of data

	_ASSERTE(p_src == ((const uint8_t*)p_src_data) + n_src_size);
	_ASSERTE(p_dst == ((uint8_t*)p_dest) + n_DecodedSize(n_src_size));
	// make sure we used up both buffers

	return true;
}

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